//===--- DynamicCasts.cpp - Utilities for dynamic casts -------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "swift/SIL/DynamicCasts.h"
#include "swift/AST/ConformanceLookup.h"
#include "swift/AST/Module.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Assertions.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/TypeLowering.h"

using namespace swift;
using namespace Lowering;

static unsigned getAnyMetatypeDepth(CanType type) {
  unsigned depth = 0;
  while (auto metatype = dyn_cast<AnyMetatypeType>(type)) {
    type = metatype.getInstanceType();
    ++depth;
  }
  return depth;
}

static bool
mayBridgeToObjectiveC(ModuleDecl *M, CanType T) {
  // FIXME: Disable when we don't support Objective-C interoperability?
  return true;
}

static bool
mustBridgeToSwiftValueBox(ModuleDecl *M, CanType T) {
  // If the target type is either an unknown dynamic type, or statically
  // known to bridge, the cast may succeed.
  if (T->hasArchetype())
    return false;

  if (T->isAnyExistentialType())
    return false;

  // getBridgedToObjC() might return a null-type for some types
  // whose bridging implementation is allowed to live elsewhere. Exclude this
  // case here.
  if (auto N = T->getAnyNominal())
    if (M->getASTContext().isTypeBridgedInExternalModule(N))
      return false;

  return !M->getASTContext().getBridgedToObjC(M, T);
}

static bool canClassOrSuperclassesHaveUnknownSubclasses(ClassDecl *CD,
                                                      bool isWholeModuleOpts) {
  while (CD) {
    // Open classes can always have unknown subclasses.
    if (CD->getEffectiveAccess() == AccessLevel::Open)
      return true;

    // Internal and public classes may have unknown subclasses if we are not in
    // whole-module-optimization mode.
    if (CD->getEffectiveAccess() >= AccessLevel::Internal &&
        !isWholeModuleOpts)
      return true;

    if (!CD->hasSuperclass())
      break;

    CD = CD->getSuperclassDecl();
  }

  return false;
}

static CanType unwrapExistential(CanType e) {
  assert(e.isExistentialType());

  if (auto et = dyn_cast<ExistentialType>(e))
    return et.getConstraintType();

  return e;
}

/// Try to classify a conversion from non-existential type
/// into an existential type by performing a static check
/// of protocol conformances if it is possible.
static DynamicCastFeasibility
classifyDynamicCastToProtocol(SILFunction *function, CanType source, CanType target,
                              bool isWholeModuleOpts) {
  assert(target.isExistentialType() &&
         "target should be an existential type");

  if (source == target)
    return DynamicCastFeasibility::WillSucceed;

  auto *TargetProtocol = cast_or_null<ProtocolDecl>(target.getAnyNominal());
  if (!TargetProtocol)
    return DynamicCastFeasibility::MaySucceed;

  // If the target is a parameterized protocol type, checkConformance
  // is insufficient to prove the feasibility of the cast as it does not
  // check the additional requirements.
  // FIXME: This is a weak predicate that doesn't take into account
  // class compositions - since any C & P<T> doesn't work yet anyways.
  if (isa<ParameterizedProtocolType>(unwrapExistential(target)))
    return DynamicCastFeasibility::MaySucceed;

  // If checkConformance() returns a valid conformance, then all conditional
  // requirements were satisfied.
  if (auto conformance = checkConformance(source, TargetProtocol)) {
    if (!matchesActorIsolation(conformance, function))
      return DynamicCastFeasibility::MaySucceed;

    return DynamicCastFeasibility::WillSucceed;
  }

  auto *SourceNominalTy = source.getAnyNominal();
  if (!SourceNominalTy)
    return DynamicCastFeasibility::MaySucceed;

  // Protocol types may conform to their own protocols (or other protocols)
  // in the future.
  if (source->isAnyExistentialType()) {
    return DynamicCastFeasibility::MaySucceed;
  }

  // If it is a class and it can be proven that this class and its
  // superclasses cannot have unknown subclasses, then it is safe to proceed.
  if (auto *CD = source.getClassOrBoundGenericClass()) {
    if (canClassOrSuperclassesHaveUnknownSubclasses(CD, isWholeModuleOpts))
      return DynamicCastFeasibility::MaySucceed;
    // Derived types may conform to the protocol.
    if (!CD->isFinal()) {
      // TODO: If it is a private type or internal type and we
      // can prove that there are no derived types conforming to a
      // protocol, then we can still return WillFail.
      return DynamicCastFeasibility::MaySucceed;
    }
  }

  // The WillFail conditions below assume any possible conformance on the
  // nominal source type has been ruled out. The prior checkConformance query
  // identified any definite conformance. Now check if there is already a known
  // conditional conformance on the nominal type with requirements that were
  // not proven.
  //
  // TODO: The TypeChecker can easily prove that some requirements cannot be
  // met. Returning WillFail in those cases would be more optimal. To do that,
  // the checkConformance interface needs to be reformulated as a query, and
  // the implementation, including checkGenericArguments, needs to be taught to
  // recognize that types with archetypes may potentially succeed.
  if (auto conformance = lookupConformance(source, TargetProtocol)) {
    assert(!conformance.getConditionalRequirements().empty());
    return DynamicCastFeasibility::MaySucceed;
  }

  // If the source type is file-private or target protocol is file-private,
  // then conformances cannot be changed at run-time, because only this
  // file could have implemented them, but no conformances were found.
  // Therefore it is safe to make a negative decision at compile-time.
  if (SourceNominalTy->getEffectiveAccess() <= AccessLevel::FilePrivate ||
      TargetProtocol->getEffectiveAccess() <= AccessLevel::FilePrivate) {
    // This cast is always false. Replace it with a branch to the
    // failure block.
    return DynamicCastFeasibility::WillFail;
  }

  // AnyHashable is a special case: although it's a struct, there maybe another
  // type conforming to it and to the TargetProtocol at the same time.
  if (source->isAnyHashable())
    return DynamicCastFeasibility::MaySucceed;

  // If we are in a whole-module compilation and
  // if the source type is internal or target protocol is internal,
  // then conformances cannot be changed at run-time, because only this
  // module could have implemented them, but no conformances were found.
  // Therefore it is safe to make a negative decision at compile-time.
  if (isWholeModuleOpts &&
      (SourceNominalTy->getEffectiveAccess() <= AccessLevel::Internal ||
       TargetProtocol->getEffectiveAccess() <= AccessLevel::Internal)) {
    return DynamicCastFeasibility::WillFail;
  }

  return DynamicCastFeasibility::MaySucceed;
}

static DynamicCastFeasibility
classifyDynamicCastFromProtocol(ModuleDecl *M, CanType source, CanType target,
                                bool isWholeModuleOpts) {
  assert(source.isExistentialType() &&
         "source should be an existential type");

  if (source == target)
    return DynamicCastFeasibility::WillSucceed;

  // 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;
  }

  // TODO: maybe prove that certain conformances are impossible?

  return DynamicCastFeasibility::MaySucceed;
}

/// Returns the existential type associated with the Hashable
/// protocol, if it can be found.
static CanType getHashableExistentialType(ModuleDecl *M) {
  auto hashable =
    M->getASTContext().getProtocol(KnownProtocolKind::Hashable);
  if (!hashable) return CanType();
  return hashable->getDeclaredInterfaceType()->getCanonicalType();
}

// Distinguish between class-bound types that might be AnyObject vs other
// class-bound types. Only types that are potentially AnyObject might have a
// transparent runtime type wrapper like __SwiftValue. This must look through
// all optional types because dynamic casting sees through them.
static bool isPotentiallyAnyObject(Type type) {
  Type unwrappedTy = type->lookThroughAllOptionalTypes();
  if (auto archetype = unwrappedTy->getAs<ArchetypeType>()) {
    for (auto *proto : archetype->getConformsTo()) {
      if (!proto->getInvertibleProtocolKind())
        return false;
    }
    return !archetype->getSuperclass();
  }
  return unwrappedTy->isAnyObject();
}

// Returns true if casting \p sourceFormalType to \p targetFormalType preserves
// ownership.
//
// Casting preserves ownership when all references from the source value are
// forwarded into the result value (without unbalanced retains or releases).
//
// When both the source and target types of checked-cast preserve ownership,
// then the cast is compatible with guaranteed ownership. A guaranteed
// compatible cast cannot release any references within its operand's value
// and cannot retain any references owned by its result.
//
// A type's ownership might not be preserved by a dynamic cast if it is either
//   (A) a potentially bridged value
// or
//   (B) potentially wrapped in a transparent type, which is equivalent to
//       isPotentiallyAnyObject()
//
// Given:
//   let source: sourceType
//   let dest = source as! targetType
//
// Ownership conversion happens when
//
//   (A) one type is a bridged value and the other is an object:
//
//      (A1) Boxing: <trivial> as! Object instantiates references in Object
//           Presumably, Object's type must be class-bound, but this is not
//           currently checked.
//
//      (A2) Unboxing: Object as! <trivial> destroys references in Object
//           Object may be any type that can hold an object, including
//           non-class-bound archetypes and existentials.
//
//   (B) one type is transparently wrapped in __SwiftValue, while the other is
//       unwrapped. Given:
//
//     class C : Hashable {}
//     let a = AnyHashable(C())
//
//     (B1) When the substituted source type is AnyHashable and the
//          substituted destination type is AnyObject, the cast
//          instantiates an owned __SwiftValue:
//
//          // instantiates __SwiftValue
//          let b = a as! AnyObject
//        or
//          let b = a as! T where T.self == AnyObject.self
//
//     (B2) When the substituted source type is Any or AnyObject, and the
//          substituted destination type is not Any or AnyObject, the cast
//          releases the owned __SwiftValue:
//
//          let c = b as! C // releases __SwiftValue
//
// After unwrapping Optional, the type may fall into one of
// the following categories that are relevant for cast ownership:
//
// Class-bound types (hasReferenceSemantics() && !isPotentiallyAnyObject())
// - includes classes, class-bound existentials other than AnyObject,
//   class-bound archetypes with a superclass or protocol constraint,
//   objc types, blocks, Builtin.NativeObject, etc.
// - excludes any type that are potentially AnyObject after substitution
// - the value is a single reference
// - the single reference is "known unwrapped". It never transparently wraps the
//   underlying dynamically typed value in another type, such as __SwiftValue
// - casting directly forwards the reference
//
// Potentially bridged values:
// - includes struct, enum, non-class archetype, non-class existential,
//   and non-objc-metatype
// - these types are potentially trivial after substitution. If so, then they
//   convert to a reference when casting to AnyObject or certain classes
//
// Any and AnyObject existentials:
// - although called existentials, their type is a protocol composition
// - these do not include existentials with constraints
// - these are very special types, unlike normal existentials...
// - the immediately erased value may itself be an existential
//   (an AnyObject existential can be wrapped within an Any existential!)
// - the underlying dynamically typed value may be transparently wrapped in
//   __SwiftValue
//
// These type categories are disjoint, except that a non-class archetype is both
// potentially bridged and potentially Any or AnyObject after substitution.
//
// TODO: In the future, when the runtime stops wrapping nontrivial types inside
// __SwiftValue, cases (B1) and (B2) above will no longer apply. At that time,
// expand ownership preserving cast types to AnyObject. Then remove the
// isPotentiallyAnyObject() check.
bool swift::doesCastPreserveOwnershipForTypes(SILModule &module,
                                              CanType sourceType,
                                              CanType targetType) {
  if (!canIRGenUseScalarCheckedCastInstructions(module, sourceType, targetType))
    return false;

  // (B2) unwrapping
  if (isPotentiallyAnyObject(sourceType))
    return false;

  // (B1) wrapping
  if (isPotentiallyAnyObject(targetType)) {
    // A class type cannot be wrapped in __SwiftValue, so casting
    // from a class to AnyObject preserves ownership.
    return
      sourceType->mayHaveSuperclass() || sourceType->isClassExistentialType();
  }
  return true;
}

bool SILDynamicCastInst::isRCIdentityPreserving() const {
  // Casts which cast from a trivial type, like a metatype, to something which
  // is retainable (or vice versa), like an AnyObject, are not RC identity
  // preserving.
  // On some platforms such casts dynamically allocate a ref-counted box for the
  // metatype. Naturally that is the place where a new rc-identity begins.
  // Therefore such a cast is introducing a new rc identical object.
  //
  // If RCIdentityAnalysis would look through such a cast, ARC optimizations
  // would get confused and might eliminate a retain of such an object
  // completely.
  SILFunction &f = *getFunction();
  if (getSourceLoweredType().isTrivial(f)
      && getTargetLoweredType().isTrivial(f)) {
    return true;
  }
  return doesCastPreserveOwnershipForTypes(f.getModule(), getSourceFormalType(),
                                           getTargetFormalType());
}

/// Check if a given type conforms to _BridgedToObjectiveC protocol.
bool swift::isObjectiveCBridgeable(CanType Ty) {
  // Retrieve the _BridgedToObjectiveC protocol.
  auto bridgedProto =
      Ty->getASTContext().getProtocol(KnownProtocolKind::ObjectiveCBridgeable);

  if (bridgedProto) {
    // Find the conformance of the value type to _BridgedToObjectiveC.
    // Check whether the type conforms to _BridgedToObjectiveC.
    return (bool) lookupConformance(Ty, bridgedProto);
  }
  return false;
}

/// Check if a given type conforms to _Error protocol.
bool swift::isError(CanType Ty) {
  // Retrieve the Error protocol.
  auto errorTypeProto =
      Ty->getASTContext().getProtocol(KnownProtocolKind::Error);

  if (errorTypeProto) {
    // Find the conformance of the value type to Error.
    // Check whether the type conforms to Error.
    return (bool) lookupConformance(Ty, errorTypeProto);
  }
  return false;
}

/// Given that a type is not statically known to be an optional type, check
/// whether it might dynamically be able to store an optional.
static bool canDynamicallyStoreOptional(CanType type) {
  assert(!type.getOptionalObjectType());
  return type->canDynamicallyBeOptionalType(/* includeExistential */ true);
}
  
/// 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;
}

CanType swift::getNSBridgedClassOfCFClass(CanType type) {
  if (auto classDecl = type->getClassOrBoundGenericClass()) {
    if (classDecl->getForeignClassKind() == ClassDecl::ForeignKind::CFType) {
      if (auto bridgedAttr =
            classDecl->getAttrs().getAttribute<ObjCBridgedAttr>()) {
        auto bridgedClass = bridgedAttr->getObjCClass();
        // TODO: this should handle generic classes properly.
        if (!bridgedClass->isGenericContext()) {
          return bridgedClass->getDeclaredInterfaceType()->getCanonicalType();
        }
      }
    }
  }
  return CanType();
}

static bool isCFBridgingConversion(CanType sourceFormalType,
                                   CanType targetFormalType) {
  if (auto bridgedTarget =
        getNSBridgedClassOfCFClass(targetFormalType)) {
    return bridgedTarget->isExactSuperclassOf(sourceFormalType);
  }
  if (auto bridgedSource =
        getNSBridgedClassOfCFClass(sourceFormalType)) {
    return targetFormalType->isExactSuperclassOf(bridgedSource);
  }
  
  return false;
}

/// Try to classify the dynamic-cast relationship between two types.
DynamicCastFeasibility
swift::classifyDynamicCast(SILFunction *function,
                           CanType source,
                           CanType target,
                           bool isSourceTypeExact,
                           bool isWholeModuleOpts) {
  if (source == target) return DynamicCastFeasibility::WillSucceed;

  // Return a conservative answer for opaque archetypes for now.
  if (source->hasOpaqueArchetype() || target->hasOpaqueArchetype())
    return DynamicCastFeasibility::MaySucceed;

  auto sourceObject = source.getOptionalObjectType();
  auto targetObject = target.getOptionalObjectType();
  ModuleDecl *M = function->getModule().getSwiftModule();

  // 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(function, 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(function, source, targetObject,
                                      /* isSourceTypeExact */ false,
                                      isWholeModuleOpts);
    if (canDynamicallyStoreOptional(source))
      result = atWorst(result, DynamicCastFeasibility::MaySucceed);
    return result;

  // Casting to a less-optional type can always fail.
  } else if (sourceObject) {
    auto result = atBest(classifyDynamicCast(function, sourceObject, target,
                                             /* isSourceTypeExact */ false,
                                             isWholeModuleOpts),
                         DynamicCastFeasibility::MaySucceed);
    if (target.isExistentialType()) {
      result = atWorst(result, classifyDynamicCastToProtocol(function,
                                   source, target, isWholeModuleOpts));
    }
    return result;
  }
  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()) {

    // Check conversions from non-protocol types into protocol types.
    if (!source.isExistentialType() &&
        target.isExistentialType())
      return classifyDynamicCastToProtocol(function, 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;
  }

  // Casts from AnyHashable.
  if (auto sourceStruct = dyn_cast<StructType>(source)) {
    if (sourceStruct->isAnyHashable()) {
      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->isAnyHashable()) {
      // 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(function, source, hashable,
                                             isWholeModuleOpts);
      }
    }
  }

  // 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 the source and target are the same existential type, but the source is
    // P.Protocol and the dest is P.Type, then we need to consider whether the
    // protocol is self-conforming.
    // The only cases where a protocol self-conforms are objc protocols, but
    // we're going to expect P.Type to hold a class object. And this case
    // doesn't matter since for a self-conforming protocol type there can't be
    // any type-level methods.
    // Thus we consider this kind of cast to always fail. The only exception
    // from this rule is when the target is Any.Type, because *.Protocol
    // can always be casted to Any.Type.
    if (source->isAnyExistentialType() && isa<MetatypeType>(sourceMetatype) &&
        isa<ExistentialMetatypeType>(targetMetatype)) {
      return target->isAny() ? DynamicCastFeasibility::WillSucceed
                             : DynamicCastFeasibility::WillFail;
    }

    if (targetMetatype.isAnyExistentialType() && target->isExistentialType()) {
      auto Feasibility =
          classifyDynamicCastToProtocol(function, 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 classifyClassHierarchyCast(source, target);

    // 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.

      // An async function cannot be cast to a non-async function and
      // vice-versa.
      if (sourceFunction->isAsync() != targetFunction->isAsync())
        return DynamicCastFeasibility::WillFail;

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

      if (AnyFunctionType::equalParams(sourceFunction.getParams(),
                                       targetFunction.getParams()) &&
          sourceFunction.getResult() == targetFunction.getResult())
        return DynamicCastFeasibility::WillSucceed;

      // Be conservative about function type relationships we may add in
      // the future.
      return DynamicCastFeasibility::MaySucceed;
    }
  }

  // Tuple casts.
  if (auto sourceTuple = dyn_cast<TupleType>(source)) {
    if (auto targetTuple = dyn_cast<TupleType>(target)) {
      // # of elements must coincide.
      if (sourceTuple->getNumElements() != targetTuple->getNumElements())
        return DynamicCastFeasibility::WillFail;

      DynamicCastFeasibility result = DynamicCastFeasibility::WillSucceed;
      for (unsigned i : range(sourceTuple->getNumElements())) {
        const auto &sourceElt = sourceTuple->getElement(i);
        const auto &targetElt = targetTuple->getElement(i);

        // If both have names and the names mismatch, the cast will fail.
        if (sourceElt.hasName() && targetElt.hasName() &&
            sourceElt.getName() != targetElt.getName())
          return DynamicCastFeasibility::WillFail;

        // Combine the result of prior elements with this element type.
        result = std::max(result,
                          classifyDynamicCast(function,
                            sourceElt.getType()->getCanonicalType(),
                            targetElt.getType()->getCanonicalType(),
                            isSourceTypeExact,
                            isWholeModuleOpts));

        // If this element failed, we're done.
        if (result == DynamicCastFeasibility::WillFail)
          break;
      }

      return result;
    }
  }

  // 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->isTypeErasedGenericClass()) {
      
        if (sourceClass == targetClass)
          return DynamicCastFeasibility::WillSucceed;
        
        if (targetClass->isTypeErasedGenericClass()) {
          // If both classes are ObjC generics, the cast may succeed if the
          // classes are related, irrespective of their generic parameters.

          if (sourceClass->isSuperclassOf(targetClass))
            return DynamicCastFeasibility::MaySucceed;
          
          if (targetClass->isSuperclassOf(sourceClass))
            return DynamicCastFeasibility::WillSucceed;

          // In case of ObjectiveC classes, the runtime type can differ from its
          // declared type. Therefore a cast between (compile-time) unrelated
          // classes may succeed at runtime.
          return DynamicCastFeasibility::MaySucceed;
        }
      }

      // Try a hierarchy cast.  If that isn't failure, we can report it.
      auto hierarchyResult = classifyClassHierarchyCast(source, target);
      if (hierarchyResult != DynamicCastFeasibility::WillFail)
        return hierarchyResult;

      // In case of ObjectiveC classes, the runtime type can differ from its
      // declared type. Therefore a cast between (compile-time) unrelated
      // classes may succeed at runtime.
      if (sourceClass->hasClangNode())
        return DynamicCastFeasibility::MaySucceed;

      // As a backup, consider whether either type is a CF class type
      // with an NS bridged equivalent.
      CanType bridgedSource = getNSBridgedClassOfCFClass(source);
      CanType bridgedTarget = getNSBridgedClassOfCFClass(target);

      // If neither type qualifies, we're done.
      if (!bridgedSource && !bridgedTarget)
        return DynamicCastFeasibility::WillFail;

      // Otherwise, map over to the bridged types and try to answer the
      // question there.
      if (bridgedSource) source = bridgedSource;
      if (bridgedTarget) target = bridgedTarget;
      return classifyDynamicCast(function, source, target, false, isWholeModuleOpts);
    }

    // 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: 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());
    // ObjC-to-Swift casts may fail. And in most cases it is impossible to
    // statically predict the outcome. So, let's be conservative here.
    return DynamicCastFeasibility::MaySucceed;
  }
  
  if (target->isBridgeableObjectType() && mayBridgeToObjectiveC(M, source)) {
    // Try to get the ObjC type which is bridged to source type.
    assert(!source.isAnyExistentialType());
    if (Type ObjCTy = M->getASTContext().getBridgedToObjC(M, source)) {
      // If the bridged ObjC type is known, check if
      // this type can be cast into target type.
      return classifyDynamicCast(function,
          ObjCTy->getCanonicalType(),
          target,
          /* isSourceTypeExact */ false, isWholeModuleOpts);
    }
    return DynamicCastFeasibility::MaySucceed;
  }

  // Check if it is a cast between bridged error types.
  if (isError(source) && isError(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.
      if (sourceStruct->getDecl() == 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 (sourceStruct->isArray() || sourceStruct->isSet()) {
          auto valueFeasibility =
            classifyDynamicCast(function, sourceArgs[0], targetArgs[0]);
          return atWorst(valueFeasibility,
                         DynamicCastFeasibility::MaySucceed);

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

  return DynamicCastFeasibility::WillFail;
}

bool swift::matchesActorIsolation(ProtocolConformanceRef conformance, SILFunction *inFunction) {
  return !conformance.forEachIsolatedConformance([&](ProtocolConformanceRef isolatedConf) -> bool {
    if (!isolatedConf.isConcrete())
      return false;

    ActorIsolation isolation = isolatedConf.getConcrete()->getIsolation();
    if (isolation.isNonisolated())
      return false;

    if (isolation.isGlobalActor()) {
      if (auto functionIsolation = inFunction->getActorIsolation()) {
        if (isolation == functionIsolation.value())
          return false;
      }
    }
    return true;
  });
}

static unsigned getOptionalDepth(CanType type) {
  unsigned depth = 0;
  while (CanType objectType = type.getOptionalObjectType()) {
    ++depth;
    type = objectType;
  }
  return depth;
}

namespace {
  struct Source {
    SILValue Value;
    CanType FormalType;

    bool isAddress() const { return Value->getType().isAddress(); }

    SILType getSILType() const { return Value->getType(); }

    Source() = default;
    Source(SILValue value, CanType formalType)
      : Value(value), FormalType(formalType) {}
  };

  struct Target {
    SILValue Address;
    SILType LoweredType;
    CanType FormalType;

    bool isAddress() const { return (bool) Address; }

    Source asAddressSource() const {
      assert(isAddress());
      return { Address, FormalType };
    }
    Source asScalarSource(SILValue value) const {
      assert(!isAddress());
      assert(!value->getType().isAddress());
      return { value, FormalType };
    }
    SILType getSILType() const {
      if (isAddress())
        return Address->getType();
      else
        return LoweredType;
    }

    Target() = default;
    Target(SILValue address, CanType formalType)
      : Address(address), LoweredType(address->getType()),
        FormalType(formalType) {
      assert(LoweredType.isAddress());
    }
    Target(SILType loweredType, CanType formalType)
      : Address(), LoweredType(loweredType), FormalType(formalType) {
      assert(!loweredType.isAddress());
    }
  };

  class CastEmitter {
    SILBuilder &B;
    SILModule &M;
    ASTContext &Ctx;
    SILLocation Loc;

  public:
    CastEmitter(SILBuilder &B, SILLocation loc)
      : B(B), M(B.getModule()), Ctx(M.getASTContext()), Loc(loc) {}

    Source emitTopLevel(Source source, Target target) {
      unsigned sourceOptDepth = getOptionalDepth(source.FormalType);
      unsigned targetOptDepth = getOptionalDepth(target.FormalType);      

      assert(sourceOptDepth <= targetOptDepth);
      return emitAndInjectIntoOptionals(source, target,
                                        targetOptDepth - sourceOptDepth);
    }

  private:
    const TypeLowering &getTypeLowering(SILType type) {
      return B.getFunction().getTypeLowering(type);
    }

    SILValue getOwnedScalar(Source source, const TypeLowering &srcTL) {
      assert(!source.isAddress());
      auto value = source.Value;
      if (value->getOwnershipKind() == OwnershipKind::Guaranteed)
        value = B.emitCopyValueOperation(Loc, value);
      return value;
    }

    Source emitSameType(Source source, Target target) {
      assert(source.FormalType == target.FormalType ||
             source.getSILType() == target.getSILType());

      auto &srcTL = getTypeLowering(source.Value->getType());

      // The destination always wants a +1 value, so make the source
      // +1 if it's a scalar.
      if (!source.isAddress()) {
        source.Value = getOwnedScalar(source, srcTL);
      }

      // If we've got a scalar and want a scalar, the source is
      // exactly right.
      if (!target.isAddress() && !source.isAddress())
        return source;

      // If the destination wants a non-address value, load
      if (!target.isAddress()) {
        SILValue value = srcTL.emitLoadOfCopy(B, Loc, source.Value, IsTake);
        return target.asScalarSource(value);
      }

      if (source.isAddress()) {
        srcTL.emitCopyInto(B, Loc, source.Value, target.Address,
                           IsTake, IsInitialization);
      } else {
        srcTL.emitStoreOfCopy(B, Loc, source.Value, target.Address,
                              IsInitialization);
      }
      return target.asAddressSource();
    }

    Source emit(Source source, Target target) {
      if (source.FormalType == target.FormalType ||
          source.getSILType() == target.getSILType())
        return emitSameType(source, target);

      // Handle subtype conversions involving optionals.
      if (auto sourceObjectType = source.FormalType.getOptionalObjectType()) {
        return emitOptionalToOptional(source, sourceObjectType, target);
      }
      assert(!target.FormalType.getOptionalObjectType());

      // The only other things we return WillSucceed for currently is
      // an upcast or CF/NS toll-free-bridging conversion.
      // FIXME: Upcasts between existential metatypes are not handled yet.
      // We should generate for it:
      // %openedSrcMetatype = open_existential srcMetatype
      // init_existential dstMetatype, %openedSrcMetatype
      auto &srcTL = getTypeLowering(source.Value->getType());
      SILValue value;
      if (source.isAddress()) {
        value = srcTL.emitLoadOfCopy(B, Loc, source.Value, IsTake);
      } else {
        // May have any valid ownership.
        value = source.Value;
      }
      auto targetFormalTy = target.FormalType;
      auto targetLoweredTy =
          SILType::getPrimitiveObjectType(target.LoweredType.getASTType());
      if (isCFBridgingConversion(source.FormalType,
                                 targetFormalTy)) {
        value = B.createUncheckedRefCast(Loc, value, targetLoweredTy);
      } else {
        value = B.createUpcast(Loc, value, targetLoweredTy);
      }
      // If the target is an address, then scalar must be Owned. Otherwise, it
      // may be Guaranteed.
      assert(value->getType() == target.LoweredType.getObjectType());
      if (!target.isAddress())
        return target.asScalarSource(value);

      auto &targetTL = getTypeLowering(target.LoweredType);
      targetTL.emitStoreOfCopy(B, Loc, value, target.Address, IsInitialization);
      return target.asAddressSource();
    }

    Source emitAndInjectIntoOptionals(Source source, Target target,
                                      unsigned depth) {
      if (depth == 0)
        return emit(source, target);

      // Recurse.
      EmitSomeState state;
      Target objectTarget = prepareForEmitSome(target, state);
      Source objectSource =
        emitAndInjectIntoOptionals(source, objectTarget, depth - 1);
      return emitSome(objectSource, target, state);
    }

    Source emitOptionalToOptional(Source source,
                                  CanType sourceObjectType,
                                  Target target) {
      // Switch on the incoming value.
      SILBasicBlock *contBB = B.splitBlockForFallthrough();
      SILBasicBlock *noneBB = B.splitBlockForFallthrough();
      SILBasicBlock *someBB = B.splitBlockForFallthrough();

      // Emit the switch.
      std::pair<EnumElementDecl*, SILBasicBlock*> cases[] = {
        { Ctx.getOptionalSomeDecl(), someBB },
        { Ctx.getOptionalNoneDecl(), noneBB },
      };
      if (source.isAddress()) {
        B.createSwitchEnumAddr(Loc, source.Value, /*default*/ nullptr, cases);
      } else {
        auto *switchEnum =
            B.createSwitchEnum(Loc, source.Value, /*default*/ nullptr, cases);
        switchEnum->createOptionalSomeResult();
      }

      // Create the Some block, which recurses.
      B.setInsertionPoint(someBB);
      {
        auto sourceSomeDecl = Ctx.getOptionalSomeDecl();

        SILType loweredSourceObjectType =
            source.Value->getType().getEnumElementType(
                sourceSomeDecl, M, B.getTypeExpansionContext());

        // Form the target for the optional object.
        EmitSomeState state;
        Target objectTarget = prepareForEmitSome(target, state);

        // Form the source value.
        AllocStackInst *sourceTemp = nullptr;
        Source objectSource;
        if (source.isAddress()) {
          // TODO: add an instruction for non-destructively getting a
          // specific element's data.
          SILValue sourceAddr = source.Value;
          sourceAddr = B.createUncheckedTakeEnumDataAddr(Loc, sourceAddr,
                                    sourceSomeDecl, loweredSourceObjectType);
          objectSource = Source(sourceAddr, sourceObjectType);
        } else {
          objectSource = Source(someBB->getArgument(0), sourceObjectType);
        }

        Source resultObject = emit(objectSource, objectTarget);

        // Deallocate the source temporary if we needed one.
        if (sourceTemp) {
          B.createDeallocStack(Loc, sourceTemp);
        }

        Source result = emitSome(resultObject, target, state);
        assert(result.isAddress() == target.isAddress());
        if (target.isAddress()) {
          B.createBranch(Loc, contBB);
        } else {
          auto &resultTL = getTypeLowering(result.Value->getType());
          SILValue resultVal = getOwnedScalar(source, resultTL);
          B.createBranch(Loc, contBB, {resultVal});
        }
      }

      // Create the None block.
      B.setInsertionPoint(noneBB);
      {
        Source result = emitNone(target);
        assert(result.isAddress() == target.isAddress());
        if (target.isAddress()) {
          B.createBranch(Loc, contBB);
        } else {
          B.createBranch(Loc, contBB, { result.Value });
        }
      }

      // Continuation block.
      B.setInsertionPoint(contBB);
      if (target.isAddress()) {
        return target.asAddressSource();
      } else {
        SILValue result =
            contBB->createPhiArgument(target.LoweredType, OwnershipKind::Owned);
        return target.asScalarSource(result);
      }
    }

    struct EmitSomeState {
      EnumElementDecl *SomeDecl;
    };

    Target prepareForEmitSome(Target target, EmitSomeState &state) {
      auto objectType = target.FormalType.getOptionalObjectType();
      assert(objectType && "emitting Some into non-optional type");

      auto someDecl = Ctx.getOptionalSomeDecl();
      state.SomeDecl = someDecl;

      SILType loweredObjectType = target.LoweredType.getEnumElementType(
          someDecl, M, B.getTypeExpansionContext());

      if (target.isAddress()) {
        SILValue objectAddr =
          B.createInitEnumDataAddr(Loc, target.Address, someDecl,
                                   loweredObjectType);
        return { objectAddr, objectType };
      } else {
        return { loweredObjectType, objectType };
      }
    }

    // May return an Owned or Guaranteed result. If source is has ownership
    // None, then the result may still be Guaranteed for nontrivial types.
    Source emitSome(Source source, Target target, EmitSomeState &state) {
      // If our target is an address, prepareForEmitSome should have set this
      // up so that we emitted directly into 
      if (target.isAddress()) {
        B.createInjectEnumAddr(Loc, target.Address, state.SomeDecl);
        return target.asAddressSource();
      } else {
        auto someEnum =
            B.createEnum(Loc, source.Value, state.SomeDecl, target.LoweredType);
        return target.asScalarSource(someEnum);
      }
    }

    Source emitNone(Target target) {
      auto noneDecl = Ctx.getOptionalNoneDecl();
      
      if (target.isAddress()) {
        B.createInjectEnumAddr(Loc, target.Address, noneDecl);
        return target.asAddressSource();
      } else {
        SILValue res = B.createEnum(Loc, nullptr, noneDecl, target.LoweredType);
        return target.asScalarSource(res);
      }
    }
  };
} // end anonymous namespace

SILValue
swift::emitSuccessfulScalarUnconditionalCast(SILBuilder &B, SILLocation loc,
                                             SILDynamicCastInst dynamicCast) {
  return emitSuccessfulScalarUnconditionalCast(
      B, B.getModule().getSwiftModule(), loc, dynamicCast.getSource(),
      dynamicCast.getTargetLoweredType(), dynamicCast.getSourceFormalType(),
      dynamicCast.getTargetFormalType(), dynamicCast.getInstruction());
}

/// Emit an unconditional scalar cast that's known to succeed.
SILValue
swift::emitSuccessfulScalarUnconditionalCast(SILBuilder &B, ModuleDecl *M,
                                             SILLocation loc, SILValue value,
                                             SILType targetLoweredType,
                                             CanType sourceFormalType,
                                             CanType targetFormalType,
                                             SILInstruction *existingCast) {
  assert(classifyDynamicCast(&B.getFunction(), sourceFormalType, targetFormalType)
           == DynamicCastFeasibility::WillSucceed);

  // Casts to/from existential types cannot be further improved.
  if (sourceFormalType.isAnyExistentialType() ||
      targetFormalType.isAnyExistentialType()) {
    if (existingCast)
      // Indicate that the existing cast cannot be further improved.
      return SILValue();

    llvm_unreachable("Casts to/from existentials are not supported yet");
  }

  // Fast path changes that don't change the type.
  if (sourceFormalType == targetFormalType)
    return value;

  Source source(value, sourceFormalType);
  Target target(targetLoweredType, targetFormalType);
  Source result = CastEmitter(B, loc).emitTopLevel(source, target);
  assert(!result.isAddress());
  assert(result.Value->getType() == targetLoweredType);
  return result.Value;
}

bool swift::emitSuccessfulIndirectUnconditionalCast(
    SILBuilder &B, SILLocation loc, SILDynamicCastInst dynamicCast) {
  return emitSuccessfulIndirectUnconditionalCast(
      B, B.getModule().getSwiftModule(), loc, dynamicCast.getSource(),
      dynamicCast.getSourceFormalType(), dynamicCast.getDest(),
      dynamicCast.getTargetFormalType(), dynamicCast.getInstruction());
}

bool swift::emitSuccessfulIndirectUnconditionalCast(
    SILBuilder &B, ModuleDecl *M, SILLocation loc, SILValue src,
    CanType sourceFormalType, SILValue dest, CanType targetFormalType,
    SILInstruction *existingCast) {
  assert(classifyDynamicCast(&B.getFunction(), sourceFormalType, targetFormalType)
           == DynamicCastFeasibility::WillSucceed);

  assert(src->getType().isAddress());
  assert(dest->getType().isAddress());

  // Casts between the same types can be always handled here.
  // Casts from non-existentials into existentials and
  // vice-versa cannot be improved yet.
  // Casts between a value type and a class cannot be optimized.
  // Therefore generate a simple unconditional_checked_cast_aadr.

  if (src->getType() != dest->getType())
  if (src->getType().isAnyExistentialType() !=
      dest->getType().isAnyExistentialType() ||
      !(src->getType().getClassOrBoundGenericClass() &&
       dest->getType().getClassOrBoundGenericClass())) {

    // If there is an existing cast with the same arguments,
    // indicate we cannot improve it.
    if (existingCast) {
      auto *UCCAI = dyn_cast<UnconditionalCheckedCastAddrInst>(existingCast);
      if (UCCAI && UCCAI->getSrc() == src && UCCAI->getDest() == dest
          && UCCAI->getSourceFormalType() == sourceFormalType
          && UCCAI->getTargetFormalType() == targetFormalType) {
        // Indicate that the existing cast cannot be further improved.
        return false;
      }
    }

    B.createUnconditionalCheckedCastAddr(loc,
                                         CheckedCastInstOptions(),
                                         src, sourceFormalType,
                                         dest, targetFormalType);
    return true;
  }

  Source source(src, sourceFormalType);
  Target target(dest, targetFormalType);
  Source result = CastEmitter(B, loc).emitTopLevel(source, target);
  assert(result.isAddress());
  assert(result.Value == dest);
  (void) result;
  return true;
}

/// Can the given cast be performed by the scalar checked-cast
/// instructions at the current SIL stage?
///
/// Always returns true for !useLoweredAddresses. Scalar casts are always
/// valid for owned values. If the operand is +1, the case will always destroy
/// or forward it. The result is always either +1 or trivial. The cast never
/// hides a copy. doesCastPreserveOwnershipForTypes determines whether the
/// scalar cast is also compatible with guaranteed values.
bool swift::canSILUseScalarCheckedCastInstructions(SILModule &M,
                                                   CanType sourceFormalType,
                                                   CanType targetFormalType) {
  if (!M.useLoweredAddresses())
    return true;

  return canIRGenUseScalarCheckedCastInstructions(M, sourceFormalType,
                                                  targetFormalType);
}

bool swift::canOptimizeToScalarCheckedCastInstructions(
    SILFunction *func, CanType sourceType, CanType targetType,
    CastConsumptionKind consumption) {
  if (!canSILUseScalarCheckedCastInstructions(func->getModule(), sourceType,
                                              targetType)) {
    return false;
  }

  if (consumption == CastConsumptionKind::CopyOnSuccess) {
    // If it's a copy-on-success cast, check whether the cast preserves
    // ownership in ossa. This is needed because the optimization creates a
    // scalar cast which is a guaranteed forwarding instruction and is valid
    // only when ownership can be preserved.
    return !func->hasOwnership() ||
           doesCastPreserveOwnershipForTypes(func->getModule(), sourceType,
                                             targetType);
  }

  return true;
}

/// Can the given cast be performed by the scalar checked-cast
/// instructions?
bool swift::canIRGenUseScalarCheckedCastInstructions(SILModule &M,
                                                     CanType sourceFormalType,
                                                     CanType targetFormalType) {
  // If the cast involves any kind of generalized existential we
  // need to use the indirect-cast path to handle checking the extra
  // constraints there as the scalar path does not (yet) know how to do it.
  if (sourceFormalType->hasParameterizedExistential() ||
      targetFormalType->hasParameterizedExistential()) {
    return false;
  }

  // Look through one level of optionality on the source.
  auto objectType = sourceFormalType;
  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.
  
  if (auto archetype = targetFormalType->getAs<ArchetypeType>()) {
    // Only ever permit this if the source type is a reference type.
    if (!objectType.isAnyClassReferenceType())
      return false;

    if (M.getASTContext().LangOpts.EnableObjCInterop) {
      // 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.
      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 (auto nserror = M.Types.getNSErrorType())
         return !super->isEqual(nserror);
    } else {
      if (!archetype->requiresClass())
        return false;
    }
    
    // If NSError wasn't loaded, any base class constraint must not be NSError.
    return true;
  }
  
  if (M.getASTContext().LangOpts.EnableObjCInterop
      && targetFormalType == 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 targetFormalType->isExactSuperclassOf(objectType);
  }
  
  // Three supported cases:
  // - metatype to metatype
  // - metatype to object
  // - object to object
  if ((objectType.isAnyClassReferenceType() || isa<AnyMetatypeType>(objectType))
      && targetFormalType.isAnyClassReferenceType())
    return true;

  if (isa<AnyMetatypeType>(objectType) && isa<AnyMetatypeType>(targetFormalType))
    return true;
  
  // Otherwise, we need to use the general indirect-cast functions.
  return false;
}

/// Carry out the operations required for an indirect conditional cast
/// using a scalar cast operation.
void swift::emitIndirectConditionalCastWithScalar(
    SILBuilder &B, ModuleDecl *M, SILLocation loc,
    CheckedCastInstOptions options,
    CastConsumptionKind consumption,
    SILValue srcAddr, CanType sourceFormalType,
    SILValue destAddr, CanType targetFormalType,
    SILBasicBlock *indirectSuccBB, SILBasicBlock *indirectFailBB,
    ProfileCounter TrueCount, ProfileCounter FalseCount) {
  assert(canSILUseScalarCheckedCastInstructions(B.getModule(),
                                                sourceFormalType,
                                                targetFormalType));

  // Create our successor and fail blocks.
  SILBasicBlock *scalarFailBB = B.splitBlockForFallthrough();
  SILBasicBlock *scalarSuccBB = B.splitBlockForFallthrough();

  // Always take; this works under an assumption that retaining the result is
  // equivalent to retaining the source. That means that these casts would not
  // be appropriate for bridging-like conversions.
  //
  // Our plan is:
  //
  // 1. If the original cast was a take_always cast, then we take from our
  // memory location in the caller, store the value into dest in the success
  // block, and perform a destroy of our default argument in the failure block.
  //
  // 2. If the original cast was copy_on_success, then with ownership we borrow,
  // copy in the success path and store back into the source slot after copying.
  //
  // 3. If the original cast was take_on_success, then on success we place the
  // casted value into dest and on failure, store the original value back into
  // src.
  SILType targetLoweredType = destAddr->getType().getObjectType();
  // Inline constructor
  auto srcValue = ([&]() -> SILValue {
    if (consumption == CastConsumptionKind::CopyOnSuccess)
      return B.emitLoadBorrowOperation(loc, srcAddr);
    return B.emitLoadValueOperation(loc, srcAddr, LoadOwnershipQualifier::Take);
  })();

  auto *ccb = B.createCheckedCastBranch(
      loc, /*exact*/ false, options, srcValue, sourceFormalType,
      targetLoweredType, targetFormalType, scalarSuccBB, scalarFailBB,
      TrueCount, FalseCount);

  // Emit the success block.
  B.setInsertionPoint(scalarSuccBB); {
    SILValue succValue = scalarSuccBB->createPhiArgument(
        targetLoweredType, ccb->getForwardingOwnershipKind());

    switch (consumption) {
    // On success, we take with both take_always and take_on_success.
    case CastConsumptionKind::TakeAlways:
    case CastConsumptionKind::TakeOnSuccess:
      break;
    case CastConsumptionKind::CopyOnSuccess: {
      succValue = B.emitCopyValueOperation(loc, succValue);
      B.emitEndBorrowOperation(loc, srcValue);
      break;
    }
    case CastConsumptionKind::BorrowAlways:
      llvm_unreachable("should never see a borrow_always here");
    }

    // And then store the succValue into dest.
    B.emitStoreValueOperation(loc, succValue, destAddr,
                              StoreOwnershipQualifier::Init);
    B.createBranch(loc, indirectSuccBB);
  }

  // Emit the failure block.
  B.setInsertionPoint(scalarFailBB);
  {
    SILValue failValue = srcValue;

    // If we have ownership, we need to create something for the default
    // argument. Otherwise, we just use the input argument to the
    // checked_cast_br.
    if (B.hasOwnership()) {
      failValue = scalarFailBB->createPhiArgument(srcValue->getType(),
                                                  srcValue->getOwnershipKind());
    }

    switch (consumption) {
    case CastConsumptionKind::TakeAlways:
      // We need to destroy the fail value if we have take_always.
      B.emitDestroyValueOperation(loc, failValue);
      break;
    case CastConsumptionKind::TakeOnSuccess:
      // If we have take_on_success, since we failed, just store the value back
      // into the src location that we originally took from.
      B.emitStoreValueOperation(loc, failValue, srcAddr,
                                StoreOwnershipQualifier::Init);
      break;
    case CastConsumptionKind::CopyOnSuccess:
      B.emitEndBorrowOperation(loc, srcValue);
      break;
    case CastConsumptionKind::BorrowAlways:
      llvm_unreachable("borrow_on_success should never appear here");
    }

    B.createBranch(loc, indirectFailBB);
  }
}
