示例#1
0
/// Given two class types, check whether there's a hierarchy relationship
/// between them.
static DynamicCastFeasibility
classifyClassHierarchyCast(CanType source, CanType target) {
  // Upcast: if the target type statically matches a type in the
  // source type's hierarchy, this is a static upcast and the cast
  // will always succeed.
  if (target->isExactSuperclassOf(source))
    return DynamicCastFeasibility::WillSucceed;

  // Upcast: if the target type might dynamically match a type in the
  // source type's hierarchy, this might be an upcast, in which
  // case the cast might succeed.
  if (target->isBindableToSuperclassOf(source))
    return DynamicCastFeasibility::MaySucceed;

  // Downcast: if the source type might dynamically match a type in the
  // target type's hierarchy, this might be a downcast, in which case
  // the cast might succeed.  Note that this also covers the case where
  // the source type statically matches a type in the target type's
  // hierarchy; since it's a downcast, the cast still at best might succeed.
  if (source->isBindableToSuperclassOf(target))
    return DynamicCastFeasibility::MaySucceed;

  // Otherwise, the classes are unrelated and the cast will fail (at least
  // on these grounds).
  return DynamicCastFeasibility::WillFail;
}
示例#2
0
/// Can the given cast be performed by the scalar checked-cast
/// instructions?
bool swift::canUseScalarCheckedCastInstructions(SILModule &M,
                                                CanType sourceType,
                                                CanType targetType) {
  // Look through one level of optionality on the source.
  auto objectType = sourceType;
  if (auto type = objectType.getOptionalObjectType())
    objectType = type;

  // Casting to NSError needs to go through the indirect-cast case,
  // since it may conform to Error and require Error-to-NSError
  // bridging, unless we can statically see that the source type inherits
  // NSError.
  
  // A class-constrained archetype may be bound to NSError, unless it has a
  // non-NSError superclass constraint. Casts to archetypes thus must always be
  // indirect.
  if (auto archetype = targetType->getAs<ArchetypeType>()) {
    // Only ever permit this if the source type is a reference type.
    if (!objectType.isAnyClassReferenceType())
      return false;
    
      auto super = archetype->getSuperclass();
      if (super.isNull())
        return false;

    // A base class constraint that isn't NSError rules out the archetype being
    // bound to NSError.
    if (M.getASTContext().LangOpts.EnableObjCInterop) {
      if (auto nserror = M.Types.getNSErrorType())
         return !super->isEqual(nserror);
    }
    
    // If NSError wasn't loaded, any base class constraint must not be NSError.
    return true;
  }
  
  if (M.getASTContext().LangOpts.EnableObjCInterop
      && targetType == M.Types.getNSErrorType()) {
    // If we statically know the source is an NSError subclass, then the cast
    // can go through the scalar path (and it's trivially true so can be
    // killed).
    return targetType->isExactSuperclassOf(objectType);
  }
  
  // Three supported cases:
  // - metatype to metatype
  // - metatype to object
  // - object to object
  if ((objectType.isAnyClassReferenceType() || isa<AnyMetatypeType>(objectType))
      && targetType.isAnyClassReferenceType())
    return true;

  if (isa<AnyMetatypeType>(objectType) && isa<AnyMetatypeType>(targetType))
    return true;
  
  // Otherwise, we need to use the general indirect-cast functions.
  return false;
}
示例#3
0
/// Try to classify the dynamic-cast relationship between two types.
DynamicCastFeasibility
swift::classifyDynamicCast(Module *M,
                           CanType source,
                           CanType target,
                           bool isSourceTypeExact,
                           bool isWholeModuleOpts) {
  if (source == target) return DynamicCastFeasibility::WillSucceed;

  auto sourceObject = source.getAnyOptionalObjectType();
  auto targetObject = target.getAnyOptionalObjectType();

  // A common level of optionality doesn't affect the feasibility,
  // except that we can't fold things to failure because nil inhabits
  // both types.
  if (sourceObject && targetObject) {
    return atWorst(classifyDynamicCast(M, sourceObject, targetObject),
                   DynamicCastFeasibility::MaySucceed);

  // Casting to a more optional type follows the same rule unless we
  // know that the source cannot dynamically be an optional value,
  // in which case we'll always just cast and inject into an optional.
  } else if (targetObject) {
    auto result = classifyDynamicCast(M, source, targetObject,
                                      /* isSourceTypeExact */ false,
                                      isWholeModuleOpts);
    if (canDynamicallyBeOptionalType(source))
      result = atWorst(result, DynamicCastFeasibility::MaySucceed);
    return result;

  // Casting to a less-optional type can always fail.
  } else if (sourceObject) {
    return atBest(classifyDynamicCast(M, sourceObject, target,
                                      /* isSourceTypeExact */ false,
                                      isWholeModuleOpts),
                  DynamicCastFeasibility::MaySucceed);
  }
  assert(!sourceObject && !targetObject);

  // Assume that casts to or from existential types or involving
  // dependent types can always succeed.  This is over-conservative.
  if (source->hasArchetype() || source.isExistentialType() ||
      target->hasArchetype() || target.isExistentialType()) {

    auto *SourceNominalTy = source.getAnyNominal();

    // Check conversions from non-protocol types into protocol types.
    if (!source.isExistentialType() &&
        SourceNominalTy &&
        target.isExistentialType())
      return classifyDynamicCastToProtocol(source, target, isWholeModuleOpts);

    // Check conversions from protocol types to non-protocol types.
    if (source.isExistentialType() &&
        !target.isExistentialType())
      return classifyDynamicCastFromProtocol(M, source, target,
                                             isWholeModuleOpts);

    return DynamicCastFeasibility::MaySucceed;
  }

  // Metatype casts.
  if (auto sourceMetatype = dyn_cast<AnyMetatypeType>(source)) {
    auto targetMetatype = dyn_cast<AnyMetatypeType>(target);
    if (!targetMetatype) return DynamicCastFeasibility::WillFail;

    source = sourceMetatype.getInstanceType();
    target = targetMetatype.getInstanceType();

    if (source == target &&
        targetMetatype.isAnyExistentialType() ==
            sourceMetatype.isAnyExistentialType())
      return DynamicCastFeasibility::WillSucceed;

    if (targetMetatype.isAnyExistentialType() &&
        (isa<ProtocolType>(target) || isa<ProtocolCompositionType>(target))) {
      auto Feasibility = classifyDynamicCastToProtocol(source,
                                                       target,
                                                       isWholeModuleOpts);
      // Cast from existential metatype to existential metatype may still
      // succeed, even if we cannot prove anything statically.
      if (Feasibility != DynamicCastFeasibility::WillFail ||
          !sourceMetatype.isAnyExistentialType())
        return Feasibility;
    }

    // If isSourceTypeExact is true, we know we are casting the result of a
    // MetatypeInst instruction.
    if (isSourceTypeExact) {
      // If source or target are existentials, then it can be cast
      // successfully only into itself.
      if ((target.isAnyExistentialType() || source.isAnyExistentialType()) &&
          target != source)
        return DynamicCastFeasibility::WillFail;
    }

    // Casts from class existential metatype into a concrete non-class metatype
    // can never succeed.
    if (source->isClassExistentialType() &&
        !target.isAnyExistentialType() &&
        !target.getClassOrBoundGenericClass())
      return DynamicCastFeasibility::WillFail;

    // TODO: prove that some conversions to existential metatype will
    // obviously succeed/fail.
    // TODO: prove that some conversions from class existential metatype
    // to a concrete non-class metatype will obviously fail.
    // TODO: class metatype to/from AnyObject
    // TODO: protocol concrete metatype to/from ObjCProtocol
    if (isa<ExistentialMetatypeType>(sourceMetatype) ||
        isa<ExistentialMetatypeType>(targetMetatype))
      return (getAnyMetatypeDepth(source) == getAnyMetatypeDepth(target)
              ? DynamicCastFeasibility::MaySucceed
              : DynamicCastFeasibility::WillFail);

    // If both metatypes are class metatypes, check if classes can be
    // cast.
    if (source.getClassOrBoundGenericClass() &&
        target.getClassOrBoundGenericClass())
      return classifyDynamicCast(M, source, target, false, isWholeModuleOpts);

    // Different structs cannot be cast to each other.
    if (source.getStructOrBoundGenericStruct() &&
        target.getStructOrBoundGenericStruct() &&
        source != target)
      return DynamicCastFeasibility::WillFail;

    // Different enums cannot be cast to each other.
    if (source.getEnumOrBoundGenericEnum() &&
        target.getEnumOrBoundGenericEnum() &&
        source != target)
      return DynamicCastFeasibility::WillFail;

    // If we don't know any better, assume that the cast may succeed.
    return DynamicCastFeasibility::MaySucceed;
  }
  
  // Function casts.
  if (auto sourceFunction = dyn_cast<FunctionType>(source)) {
    if (auto targetFunction = dyn_cast<FunctionType>(target)) {
      // A function cast can succeed if the function types can be identical,
      // or if the target type is throwier than the original.

      // A non-throwing source function can be cast to a throwing target type,
      // but not vice versa.
      if (sourceFunction->throws() && !targetFunction->throws())
        return DynamicCastFeasibility::WillFail;
      
      // The cast can't change the representation at runtime.
      if (targetFunction->getRepresentation()
            != sourceFunction->getRepresentation())
        return DynamicCastFeasibility::WillFail;
      
      if (sourceFunction.getInput() == targetFunction.getInput()
          && sourceFunction.getResult() == targetFunction.getResult())
        return DynamicCastFeasibility::WillSucceed;

      auto isSubstitutable = [](CanType a, CanType b) -> bool {
        // FIXME: Unnecessarily conservative; should structurally check for
        // substitutability.
        return a == b || a->hasArchetype() || b->hasArchetype();
      };
    
      if (isSubstitutable(sourceFunction.getInput(), targetFunction.getInput())
          && isSubstitutable(targetFunction.getInput(),
                             targetFunction.getResult()))
        return DynamicCastFeasibility::MaySucceed;
      
      return DynamicCastFeasibility::WillFail;
    }
  }

  // Class casts.
  auto sourceClass = source.getClassOrBoundGenericClass();
  auto targetClass = target.getClassOrBoundGenericClass();
  if (sourceClass) {
    if (targetClass) {
      // Imported Objective-C generics don't check the generic parameters, which
      // are lost at runtime.
      if (sourceClass->usesObjCGenericsModel()) {
      
        if (sourceClass == targetClass)
          return DynamicCastFeasibility::WillSucceed;
        
        if (targetClass->usesObjCGenericsModel()) {
          // If both classes are ObjC generics, the cast may succeed if the
          // classes are related, irrespective of their generic parameters.
          auto isDeclSuperclass = [&](ClassDecl *proposedSuper,
                                      ClassDecl *proposedSub) -> bool {
            do {
              if (proposedSuper == proposedSub)
                return true;
            } while ((proposedSub = proposedSub->getSuperclassDecl()));
            
            return false;
          };
          
          if (isDeclSuperclass(sourceClass, targetClass))
            return DynamicCastFeasibility::MaySucceed;
          
          if (isDeclSuperclass(targetClass, sourceClass)) {
            return DynamicCastFeasibility::WillSucceed;
          }          
          return DynamicCastFeasibility::WillFail;
        }
      }


      if (target->isExactSuperclassOf(source, nullptr))
        return DynamicCastFeasibility::WillSucceed;
      if (target->isBindableToSuperclassOf(source, nullptr))
        return DynamicCastFeasibility::MaySucceed;
      if (source->isBindableToSuperclassOf(target, nullptr))
        return DynamicCastFeasibility::MaySucceed;

      // FIXME: bridged types, e.g. CF <-> NS (but not for metatypes).
      return DynamicCastFeasibility::WillFail;
    }

    // Casts from a class into a non-class can never succeed if the target must
    // be bridged to a SwiftValueBox. You would need an AnyObject source for
    // that.
    if (!target.isAnyExistentialType() &&
        !target.getClassOrBoundGenericClass() &&
        !isa<ArchetypeType>(target) &&
        mustBridgeToSwiftValueBox(M, target)) {
      assert((target.getEnumOrBoundGenericEnum() ||
              target.getStructOrBoundGenericStruct() ||
              isa<TupleType>(target) ||
              isa<SILFunctionType>(target) ||
              isa<FunctionType>(target) ||
              isa<MetatypeType>(target)) &&
             "Target should be an enum, struct, tuple, metatype or function type");
      return DynamicCastFeasibility::WillFail;
    }


    // In the Objective-C runtime, class metatypes are also class instances.
    // The cast may succeed if the target type can be inhabited by a class
    // metatype.
    // TODO: Narrow this to the sourceClass being exactly NSObject.
    if (M->getASTContext().LangOpts.EnableObjCInterop) {
      if (auto targetMeta = dyn_cast<MetatypeType>(target)) {
        if (isa<ArchetypeType>(targetMeta.getInstanceType())
            || targetMeta.getInstanceType()->mayHaveSuperclass())
          return DynamicCastFeasibility::MaySucceed;
      } else if (isa<ExistentialMetatypeType>(target)) {
        return DynamicCastFeasibility::MaySucceed;
      }
    }
  }

  // If the source is not existential, an archetype, or (under the ObjC runtime)
  // a class, and the destination is a metatype, there is no way the cast can
  // succeed.
  if (target->is<AnyMetatypeType>()) return DynamicCastFeasibility::WillFail;

  // FIXME: tuple conversions?

  // FIXME: Be more careful with bridging conversions from
  // NSArray, NSDictionary and NSSet as they may fail?

  // We know that a cast from Int -> class foobar will fail.
  if (targetClass &&
      !source.isAnyExistentialType() &&
      !source.getClassOrBoundGenericClass() &&
      !isa<ArchetypeType>(source) &&
      mustBridgeToSwiftValueBox(M, source)) {
      assert((source.getEnumOrBoundGenericEnum() ||
              source.getStructOrBoundGenericStruct() ||
              isa<TupleType>(source) ||
              isa<SILFunctionType>(source) ||
              isa<FunctionType>(source) ||
              isa<MetatypeType>(source)) &&
             "Source should be an enum, struct, tuple, metatype or function type");
    return DynamicCastFeasibility::WillFail;
  }

  // Check if there might be a bridging conversion.
  if (source->isBridgeableObjectType() && mayBridgeToObjectiveC(M, target)) {
    // Try to get the ObjC type which is bridged to target type.
    assert(!target.isAnyExistentialType());
    Optional<Type> ObjCTy = M->getASTContext().getBridgedToObjC(
        M, target, nullptr);
    if (ObjCTy && ObjCTy.getValue()) {
      // If the bridged ObjC type is known, check if
      // source type can be cast into it.
      return classifyDynamicCast(M, source,
          ObjCTy.getValue().getCanonicalTypeOrNull(),
          /* isSourceTypeExact */ false, isWholeModuleOpts);
    }
    return DynamicCastFeasibility::MaySucceed;
  }
  
  if (target->isBridgeableObjectType() && mayBridgeToObjectiveC(M, source)) {
    // Try to get the ObjC type which is bridged to source type.
    assert(!source.isAnyExistentialType());
    Optional<Type> ObjCTy = M->getASTContext().getBridgedToObjC(
        M, source, nullptr);
    if (ObjCTy && ObjCTy.getValue()) {
      // If the bridged ObjC type is known, check if
      // this type can be cast into target type.
      return classifyDynamicCast(M,
          ObjCTy.getValue().getCanonicalTypeOrNull(),
          target,
          /* isSourceTypeExact */ false, isWholeModuleOpts);
    }
    return DynamicCastFeasibility::MaySucceed;
  }

  // Check if it is a cast between bridged error types.
  if (isError(M, source) && isError(M, target)) {
    // TODO: Cast to NSError succeeds always.
    return DynamicCastFeasibility::MaySucceed;
  }

  // Check for a viable collection cast.
  if (auto sourceStruct = dyn_cast<BoundGenericStructType>(source)) {
    if (auto targetStruct = dyn_cast<BoundGenericStructType>(target)) {
      // Both types have to be the same kind of collection.
      auto typeDecl = sourceStruct->getDecl();
      if (typeDecl == targetStruct->getDecl()) {
        auto sourceArgs = sourceStruct.getGenericArgs();
        auto targetArgs = targetStruct.getGenericArgs();

        // Note that we can never say that a collection cast is impossible:
        // a cast can always succeed on an empty collection.

        // Arrays and sets.
        if (typeDecl == M->getASTContext().getArrayDecl() ||
            typeDecl == M->getASTContext().getSetDecl()) {
          auto valueFeasibility =
            classifyDynamicCast(M, sourceArgs[0], targetArgs[0]);
          return atWorst(valueFeasibility,
                         DynamicCastFeasibility::MaySucceed);

        // Dictionaries.
        } else if (typeDecl == M->getASTContext().getDictionaryDecl()) {
          auto keyFeasibility =
            classifyDynamicCast(M, sourceArgs[0], targetArgs[0]);
          auto valueFeasibility =
            classifyDynamicCast(M, sourceArgs[1], targetArgs[1]);
          return atWorst(atBest(keyFeasibility, valueFeasibility),
                         DynamicCastFeasibility::MaySucceed);
        }
      }
    }
  }

  // Casts from AnyHashable.
  if (auto sourceStruct = dyn_cast<StructType>(source)) {
    if (sourceStruct->getDecl() == M->getASTContext().getAnyHashableDecl()) {
      if (auto hashable = getHashableExistentialType(M)) {
        // Succeeds if Hashable can be cast to the target type.
        return classifyDynamicCastFromProtocol(M, hashable, target,
                                               isWholeModuleOpts);
      }
    }
  }

  // Casts to AnyHashable.
  if (auto targetStruct = dyn_cast<StructType>(target)) {
    if (targetStruct->getDecl() == M->getASTContext().getAnyHashableDecl()) {
      // Succeeds if the source type can be dynamically cast to Hashable.
      // Hashable is not actually a legal existential type right now, but
      // the check doesn't care about that.
      if (auto hashable = getHashableExistentialType(M)) {
        return classifyDynamicCastToProtocol(source, hashable,
                                             isWholeModuleOpts);
      }
    }
  }

  return DynamicCastFeasibility::WillFail;
}
示例#4
0
/// Try to classify the dynamic-cast relationship between two types.
DynamicCastFeasibility
swift::classifyDynamicCast(Module *M,
                           CanType source,
                           CanType target,
                           bool isSourceTypeExact,
                           bool isWholeModuleOpts) {
  if (source == target) return DynamicCastFeasibility::WillSucceed;

  auto sourceObject = source.getAnyOptionalObjectType();
  auto targetObject = target.getAnyOptionalObjectType();

  // A common level of optionality doesn't affect the feasibility.
  if (sourceObject && targetObject) {
    return classifyDynamicCast(M, sourceObject, targetObject);

  // Nor does casting to a more optional type.
  } else if (targetObject) {
    return classifyDynamicCast(M, source, targetObject,
                               /* isSourceTypeExact */ false,
                               isWholeModuleOpts);

  // Casting to a less-optional type can always fail.
  } else if (sourceObject) {
    return weakenSuccess(classifyDynamicCast(M, sourceObject, target,
                                             /* isSourceTypeExact */ false,
                                             isWholeModuleOpts));
  }
  assert(!sourceObject && !targetObject);

  // Assume that casts to or from existential types or involving
  // dependent types can always succeed.  This is over-conservative.
  if (source->hasArchetype() || source.isExistentialType() ||
      target->hasArchetype() || target.isExistentialType()) {

    auto *SourceNominalTy = source.getAnyNominal();

    // Check conversions from non-protocol types into protocol types.
    if (!source.isExistentialType() &&
        SourceNominalTy &&
        target.isExistentialType())
      return classifyDynamicCastToProtocol(source, target, isWholeModuleOpts);

    // Casts from class existential into a non-class can never succeed.
    if (source->isClassExistentialType() &&
        !target.isAnyExistentialType() &&
        !target.getClassOrBoundGenericClass() &&
        !isa<ArchetypeType>(target) &&
        !mayBridgeToObjectiveC(M, target)) {
      assert((target.getEnumOrBoundGenericEnum() ||
              target.getStructOrBoundGenericStruct() ||
              isa<TupleType>(target) ||
              isa<SILFunctionType>(target) ||
              isa<FunctionType>(target) ||
              isa<MetatypeType>(target)) &&
             "Target should be an enum, struct, tuple, metatype or function type");
      return DynamicCastFeasibility::WillFail;
    }

    return DynamicCastFeasibility::MaySucceed;
  }

  // Metatype casts.
  if (auto sourceMetatype = dyn_cast<AnyMetatypeType>(source)) {
    auto targetMetatype = dyn_cast<AnyMetatypeType>(target);
    if (!targetMetatype) return DynamicCastFeasibility::WillFail;

    source = sourceMetatype.getInstanceType();
    target = targetMetatype.getInstanceType();

    if (source == target &&
        targetMetatype.isAnyExistentialType() ==
            sourceMetatype.isAnyExistentialType())
      return DynamicCastFeasibility::WillSucceed;

    if (targetMetatype.isAnyExistentialType() &&
        (isa<ProtocolType>(target) || isa<ProtocolCompositionType>(target))) {
      auto Feasibility = classifyDynamicCastToProtocol(source,
                                                       target,
                                                       isWholeModuleOpts);
      // Cast from existential metatype to existential metatype may still
      // succeed, even if we cannot prove anything statically.
      if (Feasibility != DynamicCastFeasibility::WillFail ||
          !sourceMetatype.isAnyExistentialType())
        return Feasibility;
    }

    // If isSourceTypeExact is true, we know we are casting the result of a
    // MetatypeInst instruction.
    if (isSourceTypeExact) {
      // If source or target are existentials, then it can be cast
      // successfully only into itself.
      if ((target.isAnyExistentialType() || source.isAnyExistentialType()) &&
          target != source)
        return DynamicCastFeasibility::WillFail;
    }

    // Casts from class existential metatype into a concrete non-class metatype
    // can never succeed.
    if (source->isClassExistentialType() &&
        !target.isAnyExistentialType() &&
        !target.getClassOrBoundGenericClass())
      return DynamicCastFeasibility::WillFail;

    // TODO: prove that some conversions to existential metatype will
    // obviously succeed/fail.
    // TODO: prove that some conversions from class existential metatype
    // to a concrete non-class metatype will obviously fail.
    // TODO: class metatype to/from AnyObject
    // TODO: protocol concrete metatype to/from ObjCProtocol
    if (isa<ExistentialMetatypeType>(sourceMetatype) ||
        isa<ExistentialMetatypeType>(targetMetatype))
      return (getAnyMetatypeDepth(source) == getAnyMetatypeDepth(target)
              ? DynamicCastFeasibility::MaySucceed
              : DynamicCastFeasibility::WillFail);

    // If both metatypes are class metatypes, check if classes can be
    // cast.
    if (source.getClassOrBoundGenericClass() &&
        target.getClassOrBoundGenericClass())
      return classifyDynamicCast(M, source, target, false, isWholeModuleOpts);

    // Different structs cannot be cast to each other.
    if (source.getStructOrBoundGenericStruct() &&
        target.getStructOrBoundGenericStruct() &&
        source != target)
      return DynamicCastFeasibility::WillFail;

    // Different enums cannot be cast to each other.
    if (source.getEnumOrBoundGenericEnum() &&
        target.getEnumOrBoundGenericEnum() &&
        source != target)
      return DynamicCastFeasibility::WillFail;

    // If we don't know any better, assume that the cast may succeed.
    return DynamicCastFeasibility::MaySucceed;
  }
  
  // Function casts.
  if (auto sourceFunction = dyn_cast<FunctionType>(source)) {
    if (auto targetFunction = dyn_cast<FunctionType>(target)) {
      // A function cast can succeed if the function types can be identical,
      // or if the target type is throwier than the original.

      // A non-throwing source function can be cast to a throwing target type,
      // but not vice versa.
      if (sourceFunction->throws() && !targetFunction->throws())
        return DynamicCastFeasibility::WillFail;
      
      // A noreturn source function can be cast to a returning target type,
      // but not vice versa.
      // (noreturn isn't really reified at runtime though.)
      if (targetFunction->isNoReturn() && !sourceFunction->isNoReturn())
        return DynamicCastFeasibility::WillFail;
      
      // The cast can't change the representation at runtime.
      if (targetFunction->getRepresentation()
            != sourceFunction->getRepresentation())
        return DynamicCastFeasibility::WillFail;
      
      if (sourceFunction.getInput() == targetFunction.getInput()
          && sourceFunction.getResult() == targetFunction.getResult())
        return DynamicCastFeasibility::WillSucceed;

      auto isSubstitutable = [](CanType a, CanType b) -> bool {
        // FIXME: Unnecessarily conservative; should structurally check for
        // substitutability.
        return a == b || a->hasArchetype() || b->hasArchetype();
      };
    
      if (isSubstitutable(sourceFunction.getInput(), targetFunction.getInput())
          && isSubstitutable(targetFunction.getInput(),
                             targetFunction.getResult()))
        return DynamicCastFeasibility::MaySucceed;
      
      return DynamicCastFeasibility::WillFail;
    }
  }

  // Class casts.
  auto sourceClass = source.getClassOrBoundGenericClass();
  auto targetClass = target.getClassOrBoundGenericClass();
  if (sourceClass) {
    if (targetClass) {
      if (target->isExactSuperclassOf(source, nullptr))
        return DynamicCastFeasibility::WillSucceed;
      if (target->isBindableToSuperclassOf(source, nullptr))
        return DynamicCastFeasibility::MaySucceed;
      if (source->isBindableToSuperclassOf(target, nullptr))
        return DynamicCastFeasibility::MaySucceed;

      // FIXME: bridged types, e.g. CF <-> NS (but not for metatypes).
      return DynamicCastFeasibility::WillFail;
    }

    // In the Objective-C runtime, class metatypes are also class instances.
    // The cast may succeed if the target type can be inhabited by a class
    // metatype.
    // TODO: Narrow this to the sourceClass being exactly NSObject.
    if (M->getASTContext().LangOpts.EnableObjCInterop) {
      if (auto targetMeta = dyn_cast<MetatypeType>(target)) {
        if (isa<ArchetypeType>(targetMeta.getInstanceType())
            || targetMeta.getInstanceType()->mayHaveSuperclass())
          return DynamicCastFeasibility::MaySucceed;
      } else if (isa<ExistentialMetatypeType>(target)) {
        return DynamicCastFeasibility::MaySucceed;
      }
    }
  }

  // If the source is not existential, an archetype, or (under the ObjC runtime)
  // a class, and the destination is a metatype, there is no way the cast can
  // succeed.
  if (target->is<AnyMetatypeType>()) return DynamicCastFeasibility::WillFail;

  // FIXME: tuple conversions?

  // FIXME: Be more careful with bridging conversions from
  // NSArray, NSDictionary and NSSet as they may fail?

  // Check if there might be a bridging conversion.
  if (source->isBridgeableObjectType() && mayBridgeToObjectiveC(M, target)) {
    // Try to get the ObjC type which is bridged to target type.
    assert(!target.isAnyExistentialType());
    Optional<Type> ObjCTy = M->getASTContext().getBridgedToObjC(
        M, target, nullptr);
    if (ObjCTy && ObjCTy.getValue()) {
      // If the bridged ObjC type is known, check if
      // source type can be cast into it.
      return classifyDynamicCast(M, source,
          ObjCTy.getValue().getCanonicalTypeOrNull(),
          /* isSourceTypeExact */ false, isWholeModuleOpts);
    }
    return DynamicCastFeasibility::MaySucceed;
  }
  
  if (target->isBridgeableObjectType() && mayBridgeToObjectiveC(M, source)) {
    // Try to get the ObjC type which is bridged to source type.
    assert(!source.isAnyExistentialType());
    Optional<Type> ObjCTy = M->getASTContext().getBridgedToObjC(
        M, source, nullptr);
    if (ObjCTy && ObjCTy.getValue()) {
      // If the bridged ObjC type is known, check if
      // this type can be cast into target type.
      return classifyDynamicCast(M,
          ObjCTy.getValue().getCanonicalTypeOrNull(),
          target,
          /* isSourceTypeExact */ false, isWholeModuleOpts);
    }
    return DynamicCastFeasibility::MaySucceed;
  }

  // Check if it is a cast between bridged error types.
  if (isErrorProtocol(M, source) && isErrorProtocol(M, target)) {
    // TODO: Cast to NSError succeeds always.
    return DynamicCastFeasibility::MaySucceed;
  }

  return DynamicCastFeasibility::WillFail;
}