text: //===--- Local.h - Local SIL transformations. -------------------*- C++ -*-===//
  //
  // This source file is part of the Swift.org open source project
  //
  // Copyright (c) 2014 - 2017 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
  //
  //===----------------------------------------------------------------------===//
  
  #ifndef SWIFT_SILOPTIMIZER_UTILS_LOCAL_H
  #define SWIFT_SILOPTIMIZER_UTILS_LOCAL_H
  
  #include "swift/SILOptimizer/Analysis/ARCAnalysis.h"
  #include "swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h"
  #include "swift/SILOptimizer/Analysis/ClassHierarchyAnalysis.h"
  #include "swift/SILOptimizer/Analysis/SimplifyInstruction.h"
  #include "swift/SIL/SILInstruction.h"
  #include "swift/SIL/SILBuilder.h"
  #include "swift/SIL/SILCloner.h"
  #include "llvm/ADT/SmallPtrSet.h"
  #include "llvm/Support/Allocator.h"
  #include <functional>
  #include <utility>
  
  namespace swift {
  
  class DominanceInfo;
  template <class T> class NullablePtr;
  
  /// Transform a Use Range (Operand*) into a User Range (SILInstruction*)
  using UserTransform = std::function<SILInstruction *(Operand *)>;
  using ValueBaseUserRange =
    TransformRange<IteratorRange<ValueBase::use_iterator>, UserTransform>;
  
  inline ValueBaseUserRange makeUserRange(
      iterator_range<ValueBase::use_iterator> R) {
    auto toUser = [](Operand *O) { return O->getUser(); };
    return makeTransformRange(makeIteratorRange(R.begin(), R.end()),
                              UserTransform(toUser));
  }
  
  using DeadInstructionSet = llvm::SmallSetVector<SILInstruction *, 8>;
  
  /// Create a retain of \p Ptr before the \p InsertPt.
  NullablePtr<SILInstruction> createIncrementBefore(SILValue Ptr,
                                                    SILInstruction *InsertPt);
  
  /// Create a release of \p Ptr before the \p InsertPt.
  NullablePtr<SILInstruction> createDecrementBefore(SILValue Ptr,
                                                    SILInstruction *InsertPt);
  
  /// For each of the given instructions, if they are dead delete them
  /// along with their dead operands.
  ///
  /// \param I The ArrayRef of instructions to be deleted.
  /// \param Force If Force is set, don't check if the top level instructions
  ///        are considered dead - delete them regardless.
  /// \param C a callback called whenever an instruction is deleted.
  void
  recursivelyDeleteTriviallyDeadInstructions(
    ArrayRef<SILInstruction*> I, bool Force = false,
    llvm::function_ref<void(SILInstruction *)> C = [](SILInstruction *){});
  
  /// For each of the given instructions, if they are dead delete them
  /// along with their dead operands.
  ///
  /// \param I The ArrayRef of instructions to be deleted.
  /// \param InstIter is updated to the next valid instruction if it points to any
  /// deleted instruction, including debug values.
  /// \param Force If Force is set, don't check if the top level instructions
  ///        are considered dead - delete them regardless.
  /// \param C a callback called whenever an instruction is deleted.
  void recursivelyDeleteTriviallyDeadInstructions(
      ArrayRef<SILInstruction *> I, SILBasicBlock::iterator &InstIter,
      bool Force = false,
      llvm::function_ref<void(SILInstruction *)> C = [](SILInstruction *) {});
  
  /// If the given instruction is dead, delete it along with its dead
  /// operands.
  ///
  /// \param I The instruction to be deleted.
  /// \param Force If Force is set, don't check if the top level instruction is
  ///        considered dead - delete it regardless.
  /// \param C a callback called whenever an instruction is deleted.
  ///
  /// Returns a valid instruction iterator to the next nondeleted instruction
  /// after `I`.
  SILBasicBlock::iterator recursivelyDeleteTriviallyDeadInstructions(
      SILInstruction *I, bool Force = false,
      llvm::function_ref<void(SILInstruction *)> C = [](SILInstruction *) {});
  
  /// Perform a fast local check to see if the instruction is dead.
  ///
  /// This routine only examines the state of the instruction at hand.
  bool isInstructionTriviallyDead(SILInstruction *I);
  
  /// Return true if this is a release instruction that's not going to
  /// free the object.
  bool isIntermediateRelease(SILInstruction *I, EpilogueARCFunctionInfo *ERFI);
  
  /// Recursively collect all the uses and transitive uses of the
  /// instruction.
  void
  collectUsesOfValue(SILValue V, llvm::SmallPtrSetImpl<SILInstruction *> &Insts);
  
  /// Recursively erase all of the uses of the instruction (but not the
  /// instruction itself)
  void eraseUsesOfInstruction(
      SILInstruction *Inst,
      llvm::function_ref<void(SILInstruction *)> C = [](SILInstruction *){});
  
  /// Recursively erase all of the uses of the value (but not the
  /// value itself)
  void eraseUsesOfValue(SILValue V);
  
  FullApplySite findApplyFromDevirtualizedResult(SILValue value);
  
  /// Cast a value into the expected, ABI compatible type if necessary.
  /// This may happen e.g. when:
  /// - a type of the return value is a subclass of the expected return type.
  /// - actual return type and expected return type differ in optionality.
  /// - both types are tuple-types and some of the elements need to be casted.
  SILValue castValueToABICompatibleType(SILBuilder *B, SILLocation Loc,
                                        SILValue Value,
                                        SILType SrcTy,
                                        SILType DestTy);
  /// Peek through trivial Enum initialization, typically for pointless
  /// Optionals.
  ///
  /// The returned InitEnumDataAddr dominates the given
  /// UncheckedTakeEnumDataAddrInst.
  InitEnumDataAddrInst *
  findInitAddressForTrivialEnum(UncheckedTakeEnumDataAddrInst *UTEDAI);
  
  /// Returns a project_box if it is the next instruction after \p ABI and
  /// and has \p ABI as operand. Otherwise it creates a new project_box right
  /// after \p ABI and returns it.
  ProjectBoxInst *getOrCreateProjectBox(AllocBoxInst *ABI, unsigned Index);
  
  /// Return true if any call inside the given function may bind dynamic
  /// 'Self' to a generic argument of the callee.
  bool mayBindDynamicSelf(SILFunction *F);
  
  /// Check whether the \p addr is an address of a tail-allocated array element.
  bool isAddressOfArrayElement(SILValue addr);
  
  /// Move an ApplyInst's FuncRef so that it dominates the call site.
  void placeFuncRef(ApplyInst *AI, DominanceInfo *DT);
  
  /// Add an argument, \p val, to the branch-edge that is pointing into
  /// block \p Dest. Return a new instruction and do not erase the old
  /// instruction.
  TermInst *addArgumentToBranch(SILValue Val, SILBasicBlock *Dest,
                                TermInst *Branch);
  
  /// Handle the mechanical aspects of removing an unreachable block.
  void removeDeadBlock(SILBasicBlock *BB);
  
  /// Remove all instructions in the body of \p BB in safe manner by using
  /// undef.
  void clearBlockBody(SILBasicBlock *BB);
  
  /// Get the linkage to be used for specializations of a function with
  /// the given linkage.
  SILLinkage getSpecializedLinkage(SILFunction *F, SILLinkage L);
  
  /// Tries to optimize a given apply instruction if it is a concatenation of
  /// string literals. Returns a new instruction if optimization was possible.
  SingleValueInstruction *tryToConcatenateStrings(ApplyInst *AI, SILBuilder &B);
  
  /// Tries to perform jump-threading on all checked_cast_br instruction in
  /// function \p Fn.
  bool tryCheckedCastBrJumpThreading(SILFunction *Fn, DominanceInfo *DT,
                            SmallVectorImpl<SILBasicBlock *> &BlocksForWorklist);
  
  /// A structure containing callbacks that are called when an instruction is
  /// removed or added.
  struct InstModCallbacks {
    using CallbackTy = std::function<void (SILInstruction *)>;
    CallbackTy DeleteInst = [](SILInstruction *I) {
      I->eraseFromParent();
    };
    CallbackTy CreatedNewInst = [](SILInstruction *){};
  
    InstModCallbacks(CallbackTy DeleteInst, CallbackTy CreatedNewInst)
      : DeleteInst(DeleteInst), CreatedNewInst(CreatedNewInst) {}
    InstModCallbacks() = default;
    ~InstModCallbacks() = default;
    InstModCallbacks(const InstModCallbacks &) = default;
    InstModCallbacks(InstModCallbacks &&) = default;
  };
  
  /// If Closure is a partial_apply or thin_to_thick_function with only local
  /// ref count users and a set of post-dominating releases:
  ///
  /// 1. Remove all ref count operations and the closure.
  /// 2. Add each one of the last release locations insert releases for the
  ///    captured args if we have a partial_apply.
  ///
  /// In the future this should be extended to be less conservative with users.
  bool
  tryDeleteDeadClosure(SingleValueInstruction *Closure,
                       InstModCallbacks Callbacks = InstModCallbacks());
  
  /// Given a SILValue argument to a partial apply \p Arg and the associated
  /// parameter info for that argument, perform the necessary cleanups to Arg when
  /// one is attempting to delete the partial apply.
  void releasePartialApplyCapturedArg(
      SILBuilder &Builder, SILLocation Loc, SILValue Arg, SILParameterInfo PInfo,
      InstModCallbacks Callbacks = InstModCallbacks());
  
  /// This computes the lifetime of a single SILValue.
  ///
  /// This does not compute a set of jointly postdominating use points. Instead it
  /// assumes that the value's existing uses already jointly postdominate the
  /// definition. This makes sense for values that are returned +1 from an
  /// instruction, like partial_apply, and therefore must be released on all paths
  /// via strong_release or apply.
  class ValueLifetimeAnalysis {
  public:
  
    /// The lifetime frontier for the value. It is the list of instructions
    /// following the last uses of the value. All the frontier instructions
    /// end the value's lifetime.
    typedef llvm::SmallVector<SILInstruction *, 4> Frontier;
  
    /// Constructor for the value \p Def with a specific set of users of Def's
    /// users.
    ValueLifetimeAnalysis(SILInstruction *Def, ArrayRef<SILInstruction*> UserList) :
        DefValue(Def), UserSet(UserList.begin(), UserList.end()) {
      propagateLiveness();
    }
  
    /// Constructor for the value \p Def considering all the value's uses.
    ValueLifetimeAnalysis(SILInstruction *Def) : DefValue(Def) {
      for (auto result : Def->getResults()) {
        for (Operand *op : result->getUses()) {
          UserSet.insert(op->getUser());
        }
      }
      propagateLiveness();
    }
  
    enum Mode {
      /// Don't split critical edges if the frontier instructions are located on
      /// a critical edges. Instead fail.
      DontModifyCFG,
      
      /// Split critical edges if the frontier instructions are located on
      /// a critical edges.
      AllowToModifyCFG,
      
      /// Require that all users must commonly post-dominate the definition. In
      /// other words: All paths from the definition to the function exit must
      /// contain at least one use. Fail if this is not the case.
      UsersMustPostDomDef
    };
  
    /// Computes and returns the lifetime frontier for the value in \p Fr.
    ///
    /// Returns true if all instructions in the frontier could be found in
    /// non-critical edges.
    /// Returns false if some frontier instructions are located on critical edges.
    /// In this case, if \p mode is AllowToModifyCFG, those critical edges are
    /// split, otherwise nothing is done and the returned \p Fr is not valid.
    ///
    /// If \p deadEndBlocks is provided, all dead-end blocks are ignored. This
    /// prevents unreachable-blocks to be included in the frontier.
    bool computeFrontier(Frontier &Fr, Mode mode,
                         DeadEndBlocks *DEBlocks = nullptr);
  
    /// Returns true if the instruction \p Inst is located within the value's
    /// lifetime.
    /// It is assumed that \p Inst is located after the value's definition.
    bool isWithinLifetime(SILInstruction *Inst);
  
    /// Returns true if the value is alive at the begin of block \p BB.
    bool isAliveAtBeginOfBlock(SILBasicBlock *BB) {
      return LiveBlocks.count(BB) && BB != DefValue->getParent();
    }
  
    /// For debug dumping.
    void dump() const;
  
  private:
  
    /// The value.
    SILInstruction *DefValue;
  
    /// The set of blocks where the value is live.
    llvm::SmallSetVector<SILBasicBlock *, 16> LiveBlocks;
  
    /// The set of instructions where the value is used, or the users-list
    /// provided with the constructor.
    llvm::SmallPtrSet<SILInstruction*, 16> UserSet;
  
    /// Propagates the liveness information up the control flow graph.
    void propagateLiveness();
  
    /// Returns the last use of the value in the live block \p BB.
    SILInstruction *findLastUserInBlock(SILBasicBlock *BB);
  };
  
  /// Clone a single basic block and any required successor edges within the same
  /// function.
  class BasicBlockCloner : public SILCloner<BasicBlockCloner> {
    using SuperTy = SILCloner<BasicBlockCloner>;
    friend class SILCloner<BasicBlockCloner>;
  
  protected:
    /// The original block to be cloned.
    SILBasicBlock *origBB;
  
  public:
    /// An ordered list of old to new available value pairs.
    ///
    /// updateSSAAfterCloning() expects this public field to hold values that may
    /// be remapped in the cloned block and live out.
    SmallVector<std::pair<SILValue, SILValue>, 16> AvailVals;
  
    // Clone blocks starting at `origBB`, within the same function.
    BasicBlockCloner(SILBasicBlock *origBB)
        : SILCloner(*origBB->getParent()), origBB(origBB) {}
  
    void cloneBlock(SILBasicBlock *insertAfterBB = nullptr) {
      SmallVector<SILBasicBlock *, 4> successorBBs;
      successorBBs.reserve(origBB->getSuccessors().size());
      llvm::copy(origBB->getSuccessors(), std::back_inserter(successorBBs));
      cloneReachableBlocks(origBB, successorBBs, insertAfterBB);
    }
  
    /// Clone the given branch instruction's destination block, splitting
    /// its successors, and rewrite the branch instruction.
    void cloneBranchTarget(BranchInst *BI) {
      assert(origBB == BI->getDestBB());
  
      cloneBlock(/*insertAfter*/BI->getParent());
  
      SILBuilderWithScope(BI).createBranch(BI->getLoc(), getNewBB(),
                                           BI->getArgs());
      BI->eraseFromParent();
    }
  
    /// Get the newly cloned block corresponding to `origBB`.
    SILBasicBlock *getNewBB() {
      return remapBasicBlock(origBB);
    }
  
    /// Call this after processing all instructions to fix the control flow
    /// graph. The branch cloner may have left critical edges.
    bool splitCriticalEdges(DominanceInfo *DT, SILLoopInfo *LI);
  
  protected:
    // MARK: CRTP overrides.
  
    /// Override getMappedValue to allow values defined outside the block to be
    /// cloned to be reused in the newly cloned block.
    SILValue getMappedValue(SILValue Value) {
      if (auto SI = Value->getDefiningInstruction()) {
        if (!isBlockCloned(SI->getParent()))
          return Value;
      } else if (auto BBArg = dyn_cast<SILArgument>(Value)) {
        if (!isBlockCloned(BBArg->getParent()))
          return Value;
      } else {
        assert(isa<SILUndef>(Value) && "Unexpected Value kind");
        return Value;
      }
      // `value` is not defined outside the cloned block, so consult the cloner's
      // map of cloned values.
      return SuperTy::getMappedValue(Value);
    }
  
    void mapValue(SILValue origValue, SILValue mappedValue) {
      SuperTy::mapValue(origValue, mappedValue);
      AvailVals.emplace_back(origValue, mappedValue);
    }
  };
  
  /// Helper function to perform SSA updates in case of jump threading.
  void updateSSAAfterCloning(BasicBlockCloner &Cloner, SILBasicBlock *SrcBB,
                             SILBasicBlock *DestBB);
  
  // Helper class that provides a callback that can be used in
  // inliners/cloners for collecting new call sites.
  class CloneCollector {
  public:
    typedef std::pair<SILInstruction *, SILInstruction *> value_type;
    typedef std::function<void(SILInstruction *, SILInstruction *)> CallbackType;
    typedef std::function<bool (SILInstruction *)> FilterType;
  
  private:
    FilterType Filter;
  
    // Pairs of collected instructions; (new, old)
    llvm::SmallVector<value_type, 4> InstructionPairs;
  
    void collect(SILInstruction *Old, SILInstruction *New) {
      if (Filter(New))
        InstructionPairs.push_back(std::make_pair(New, Old));
    }
  
  public:
    CloneCollector(FilterType Filter) : Filter(Filter) {}
  
    CallbackType getCallback() {
      return std::bind(&CloneCollector::collect, this, std::placeholders::_1,
                       std::placeholders::_2);
    }
  
    llvm::SmallVectorImpl<value_type> &getInstructionPairs() {
      return InstructionPairs;
    }
  };
  
  /// This iterator 'looks through' one level of builtin expect users exposing all
  /// users of the looked through builtin expect instruction i.e it presents a
  /// view that shows all users as if there were no builtin expect instructions
  /// interposed.
  class IgnoreExpectUseIterator
      : public std::iterator<std::forward_iterator_tag, Operand *, ptrdiff_t> {
    ValueBaseUseIterator OrigUseChain;
    ValueBaseUseIterator CurrentIter;
  
    static BuiltinInst *isExpect(Operand *Use) {
      if (auto *BI = dyn_cast<BuiltinInst>(Use->getUser()))
        if (BI->getIntrinsicInfo().ID == llvm::Intrinsic::expect)
          return BI;
      return nullptr;
    }
  
    // Advance through expect users to their users until we encounter a user that
    // is not an expect.
    void advanceThroughExpects() {
      while (CurrentIter == OrigUseChain &&
             CurrentIter != ValueBaseUseIterator(nullptr)) {
        auto *Expect = isExpect(*CurrentIter);
        if (!Expect) return;
        CurrentIter = Expect->use_begin();
        // Expect with no users advance to next item in original use chain.
        if (CurrentIter == Expect->use_end())
          CurrentIter = ++OrigUseChain;
      }
    }
  
  public:
    IgnoreExpectUseIterator(ValueBase *V)
        : OrigUseChain(V->use_begin()), CurrentIter(V->use_begin()) {
      advanceThroughExpects();
    }
  
    IgnoreExpectUseIterator() = default;
  
    Operand *operator*() const { return *CurrentIter; }
    Operand *operator->() const { return *CurrentIter; }
    SILInstruction *getUser() const { return CurrentIter->getUser(); }
  
    IgnoreExpectUseIterator &operator++() {
      assert(**this && "increment past end()!");
      if (OrigUseChain == CurrentIter) {
        // Use chain of the original value.
        ++OrigUseChain;
        ++CurrentIter;
        // Ignore expects.
        advanceThroughExpects();
        } else {
        // Use chain of an expect.
        ++CurrentIter;
        if (CurrentIter == ValueBaseUseIterator(nullptr)) {
          // At the end of the use chain of an expect.
          CurrentIter = ++OrigUseChain;
          advanceThroughExpects();
        }
      }
      return *this;
    }
  
    IgnoreExpectUseIterator operator++(int unused) {
      IgnoreExpectUseIterator Copy = *this;
      ++*this;
      return Copy;
    }
    friend bool operator==(IgnoreExpectUseIterator lhs,
                           IgnoreExpectUseIterator rhs) {
      return lhs.CurrentIter == rhs.CurrentIter;
    }
    friend bool operator!=(IgnoreExpectUseIterator lhs,
                           IgnoreExpectUseIterator rhs) {
      return !(lhs == rhs);
    }
  };
  
  inline iterator_range<IgnoreExpectUseIterator>
  ignore_expect_uses(ValueBase *V) {
    return make_range(IgnoreExpectUseIterator(V),
                      IgnoreExpectUseIterator());
  }
  
  /// Run simplifyInstruction() on all of the instruction I's users if they only
  /// have one result (since simplifyInstruction assumes that). Replace all uses
  /// of the user with its simplification of we succeed. Returns true if we
  /// succeed and false otherwise.
  ///
  /// An example of how this is useful is in cases where one is splitting up an
  /// aggregate and reforming it, the reformed aggregate may have extract
  /// operations from it. These can be simplified and removed.
  bool simplifyUsers(SingleValueInstruction *I);
  
  ///  True if a type can be expanded
  /// without a significant increase to code size.
  bool shouldExpand(SILModule &Module, SILType Ty);
  
  /// Check if a given type is a simple type, i.e. a builtin
  /// integer or floating point type or a struct/tuple whose members
  /// are of simple types.
  bool isSimpleType(SILType SILTy, SILModule& Module);
  
  /// Check if the value of V is computed by means of a simple initialization.
  /// Store the actual SILValue into \p Val and the reversed list of instructions
  /// initializing it in \p Insns.
  /// The check is performed by recursively walking the computation of the
  /// SIL value being analyzed.
  bool analyzeStaticInitializer(SILValue V,
                                SmallVectorImpl<SILInstruction *> &Insns);
  
  /// Replace load sequence which may contain
  /// a chain of struct_element_addr followed by a load.
  /// The sequence is traversed inside out, i.e.
  /// starting with the innermost struct_element_addr
  void replaceLoadSequence(SILInstruction *I,
                           SILValue Value,
                           SILBuilder &B);
  
  
  /// Do we have enough information to determine all callees that could
  /// be reached by calling the function represented by Decl?
  bool calleesAreStaticallyKnowable(SILModule &M, SILDeclRef Decl);
  
  // Attempt to get the instance for S, whose static type is the same as
  // its exact dynamic type, returning a null SILValue() if we cannot find it.
  // The information that a static type is the same as the exact dynamic,
  // can be derived e.g.:
  // - from a constructor or
  // - from a successful outcome of a checked_cast_br [exact] instruction.
  SILValue getInstanceWithExactDynamicType(SILValue S, SILModule &M,
                                           ClassHierarchyAnalysis *CHA);
  
  /// Try to determine the exact dynamic type of an object.
  /// returns the exact dynamic type of the object, or an empty type if the exact
  /// type could not be determined.
  SILType getExactDynamicType(SILValue S, SILModule &M,
                              ClassHierarchyAnalysis *CHA,
                              bool ForUnderlyingObject = false);
  
  /// Try to statically determine the exact dynamic type of the underlying object.
  /// returns the exact dynamic type of the underlying object, or an empty SILType
  /// if the exact type could not be determined.
  SILType getExactDynamicTypeOfUnderlyingObject(SILValue S, SILModule &M,
                                                ClassHierarchyAnalysis *CHA);
  
  /// Utility class for cloning init values into the static initializer of a
  /// SILGlobalVariable.
  class StaticInitCloner : public SILCloner<StaticInitCloner> {
    friend class SILInstructionVisitor<StaticInitCloner>;
    friend class SILCloner<StaticInitCloner>;
  
    /// The number of not yet cloned operands for each instruction.
    llvm::DenseMap<SILInstruction *, int> NumOpsToClone;
  
    /// List of instructions for which all operands are already cloned (or which
    /// don't have any operands).
    llvm::SmallVector<SILInstruction *, 8> ReadyToClone;
  
  public:
    StaticInitCloner(SILGlobalVariable *GVar)
        : SILCloner<StaticInitCloner>(GVar) { }
  
    /// Add \p InitVal and all its operands (transitively) for cloning.
    ///
    /// Note: all init values must are added, before calling clone().
    void add(SILInstruction *InitVal);
  
    /// Clone \p InitVal and all its operands into the initializer of the
    /// SILGlobalVariable.
    ///
    /// \return Returns the cloned instruction in the SILGlobalVariable.
    SingleValueInstruction *clone(SingleValueInstruction *InitVal);
  
    /// Convenience function to clone a single \p InitVal.
    static void appendToInitializer(SILGlobalVariable *GVar,
                                    SingleValueInstruction *InitVal) {
      StaticInitCloner Cloner(GVar);
      Cloner.add(InitVal);
      Cloner.clone(InitVal);
    }
  
  protected:
    SILLocation remapLocation(SILLocation Loc) {
      return ArtificialUnreachableLocation();
    }
  };
  
  /// Move only data structure that is the result of findLocalApplySite.
  ///
  /// NOTE: Generally it is not suggested to have move only types that contain
  /// small vectors. Since our small vectors contain one element or a std::vector
  /// like data structure , this is ok since we will either just copy the single
  /// element when we do the move or perform a move of the vector type.
  struct LLVM_LIBRARY_VISIBILITY FindLocalApplySitesResult {
    /// Contains the list of local non fully applied partial apply sites that we
    /// found.
    SmallVector<ApplySite, 1> partialApplySites;
  
    /// Contains the list of full apply sites that we found.
    SmallVector<FullApplySite, 1> fullApplySites;
  
    /// Set to true if the function_ref escapes into a use that our analysis does
    /// not understand. Set to false if we found a use that had an actual
    /// escape. Set to None if we did not find any call sites, but also didn't
    /// find any "escaping uses" as well.
    ///
    /// The none case is so that we can distinguish in between saying that a value
    /// did escape and saying that we did not find any conservative information.
    bool escapes;
  
    FindLocalApplySitesResult() = default;
    FindLocalApplySitesResult(const FindLocalApplySitesResult &) = delete;
    FindLocalApplySitesResult &
    operator=(const FindLocalApplySitesResult &) = delete;
    FindLocalApplySitesResult(FindLocalApplySitesResult &&) = default;
    FindLocalApplySitesResult &operator=(FindLocalApplySitesResult &&) = default;
    ~FindLocalApplySitesResult() = default;
  
    /// Treat this function ref as escaping only if we found an actual user we
    /// didn't understand. Do not treat it as escaping if we did not find any
    /// users at all.
    bool isEscaping() const { return escapes; }
  };
  
  /// Returns .some(FindLocalApplySitesResult) if we found any interesting
  /// information for the given function_ref. Otherwise, returns None.
  ///
  /// We consider "interesting information" to mean inclusively that:
  ///
  /// 1. We discovered that the function_ref never escapes.
  /// 2. We were able to find either a partial apply or a full apply site.
  Optional<FindLocalApplySitesResult>
  findLocalApplySites(FunctionRefBaseInst *FRI);
  
  } // end namespace swift
  
  #endif
  