//===--- ApplySite.h -------------------------------------*- mode: c++ -*--===//
//
// 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
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// This file defines utilities for working with "call-site like" SIL
/// instructions. We use the term "call-site" like since we handle partial
/// applications in our utilities.
///
//===----------------------------------------------------------------------===//

#ifndef SWIFT_SIL_APPLYSITE_H
#define SWIFT_SIL_APPLYSITE_H

#include "swift/AST/ExtInfo.h"
#include "swift/Basic/STLExtras.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILBasicBlock.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "llvm/ADT/ArrayRef.h"

namespace swift {

class FullApplySite;

//===----------------------------------------------------------------------===//
//                                 ApplySite
//===----------------------------------------------------------------------===//

struct ApplySiteKind {
  enum innerty : std::underlying_type<SILInstructionKind>::type {
    ApplyInst = unsigned(SILInstructionKind::ApplyInst),
    PartialApplyInst = unsigned(SILInstructionKind::PartialApplyInst),
    TryApplyInst = unsigned(SILInstructionKind::TryApplyInst),
    BeginApplyInst = unsigned(SILInstructionKind::BeginApplyInst),
  } value;

  explicit ApplySiteKind(SILInstructionKind kind) {
    auto newValue = ApplySiteKind::fromNodeKindHelper(kind);
    assert(newValue && "Non apply site passed into ApplySiteKind");
    value = newValue.value();
  }

  ApplySiteKind(innerty value) : value(value) {}
  operator innerty() const { return value; }

  static std::optional<ApplySiteKind> fromNodeKind(SILInstructionKind kind) {
    if (auto innerTyOpt = ApplySiteKind::fromNodeKindHelper(kind))
      return ApplySiteKind(*innerTyOpt);
    return std::nullopt;
  }

private:
  static std::optional<innerty> fromNodeKindHelper(SILInstructionKind kind) {
    switch (kind) {
    case SILInstructionKind::ApplyInst:
      return ApplySiteKind::ApplyInst;
    case SILInstructionKind::PartialApplyInst:
      return ApplySiteKind::PartialApplyInst;
    case SILInstructionKind::TryApplyInst:
      return ApplySiteKind::TryApplyInst;
    case SILInstructionKind::BeginApplyInst:
      return ApplySiteKind::BeginApplyInst;
    default:
      return std::nullopt;
    }
  }
};

/// An apply instruction.
class ApplySite {
  SILInstruction *Inst;

protected:
  explicit ApplySite(void *p) : Inst(static_cast<SILInstruction *>(p)) {}

public:
  ApplySite() : Inst(nullptr) {}
  explicit ApplySite(SILInstruction *inst)
      : Inst(static_cast<SILInstruction *>(inst)) {
    assert(classof(inst) && "not an apply instruction?");
  }
  ApplySite(ApplyInst *inst) : Inst(inst) {}
  ApplySite(PartialApplyInst *inst) : Inst(inst) {}
  ApplySite(TryApplyInst *inst) : Inst(inst) {}
  ApplySite(BeginApplyInst *inst) : Inst(inst) {}

  SILModule &getModule() const { return Inst->getModule(); }

  static ApplySite isa(SILInstruction *inst) {
    auto kind = ApplySiteKind::fromNodeKind(inst->getKind());
    if (!kind)
      return ApplySite();

    switch (kind.value()) {
    case ApplySiteKind::ApplyInst:
      return ApplySite(cast<ApplyInst>(inst));
    case ApplySiteKind::BeginApplyInst:
      return ApplySite(cast<BeginApplyInst>(inst));
    case ApplySiteKind::TryApplyInst:
      return ApplySite(cast<TryApplyInst>(inst));
    case ApplySiteKind::PartialApplyInst:
      return ApplySite(cast<PartialApplyInst>(inst));
    }
    llvm_unreachable("covered switch");
  }

  static ApplySite isa(SILValue value) {
    if (auto *inst = value->getDefiningInstruction())
      return ApplySite::isa(inst);
    return ApplySite();
  }

  ApplySiteKind getKind() const { return ApplySiteKind(Inst->getKind()); }

  SILInstruction *operator*() const { return Inst; }
  SILInstruction *operator->() const { return Inst; }

  explicit operator bool() const { return Inst != nullptr; }

  SILInstruction *getInstruction() const { return Inst; }
  SILLocation getLoc() const { return Inst->getLoc(); }
  const SILDebugScope *getDebugScope() const { return Inst->getDebugScope(); }
  SILFunction *getFunction() const { return Inst->getFunction(); }
  SILBasicBlock *getParent() const { return Inst->getParent(); }

#define FOREACH_IMPL_RETURN(OPERATION)                                         \
  do {                                                                         \
    switch (ApplySiteKind(Inst->getKind())) {                                  \
    case ApplySiteKind::ApplyInst:                                             \
      return cast<ApplyInst>(Inst)->OPERATION;                                 \
    case ApplySiteKind::BeginApplyInst:                                        \
      return cast<BeginApplyInst>(Inst)->OPERATION;                            \
    case ApplySiteKind::PartialApplyInst:                                      \
      return cast<PartialApplyInst>(Inst)->OPERATION;                          \
    case ApplySiteKind::TryApplyInst:                                          \
      return cast<TryApplyInst>(Inst)->OPERATION;                              \
    }                                                                          \
    llvm_unreachable("covered switch");                                        \
  } while (0)

  /// Return the callee operand as a value.
  SILValue getCallee() const { return getCalleeOperand()->get(); }

  /// Return the callee operand.
  Operand *getCalleeOperand() { FOREACH_IMPL_RETURN(getCalleeOperand()); }

  /// Return the callee operand.
  const Operand *getCalleeOperand() const {
    FOREACH_IMPL_RETURN(getCalleeOperand());
  }

  /// Return the callee value by looking through function conversions until we
  /// find a function_ref, partial_apply, or unrecognized callee value.
  SILValue getCalleeOrigin() const { FOREACH_IMPL_RETURN(getCalleeOrigin()); }

  /// Gets the referenced function by looking through partial apply,
  /// convert_function, and thin to thick function until we find a function_ref.
  SILFunction *getCalleeFunction() const {
    FOREACH_IMPL_RETURN(getCalleeFunction());
  }

  /// Gets the referenced function by looking through partial apply,
  /// convert_function, and thin to thick function until we find a function_ref.
  SILDeclRef getCalleeDeclRef() const {
    FOREACH_IMPL_RETURN(getCalleeDeclRef());
  }

  bool isCalleeDynamicallyReplaceable() const {
    FOREACH_IMPL_RETURN(isCalleeDynamicallyReplaceable());
  }

  /// Return the referenced function if the callee is a function_ref
  /// instruction.
  SILFunction *getReferencedFunctionOrNull() const {
    FOREACH_IMPL_RETURN(getReferencedFunctionOrNull());
  }

  /// Return the referenced function if the callee is a function_ref like
  /// instruction.
  ///
  /// WARNING: This not necessarily the function that will be called at runtime.
  /// If the callee is a (prev_)dynamic_function_ref the actual function called
  /// might be different because it could be dynamically replaced at runtime.
  ///
  /// If the client of this API wants to look at the content of the returned SIL
  /// function it should call getReferencedFunctionOrNull() instead.
  SILFunction *getInitiallyReferencedFunction() const {
    FOREACH_IMPL_RETURN(getInitiallyReferencedFunction());
  }

  /// Should we optimize this call.
  /// Calls to (previous_)dynamic_function_ref have a dynamic target function so
  /// we should not optimize them.
  bool canOptimize() const {
    return !swift::isa<DynamicFunctionRefInst>(getCallee()) &&
      !swift::isa<PreviousDynamicFunctionRefInst>(getCallee());
  }

  /// Return the type.
  SILType getType() const {
    return getSubstCalleeConv().getSILResultType(
        getFunction()->getTypeExpansionContext());
  }

  /// Get the type of the callee without the applied substitutions.
  CanSILFunctionType getOrigCalleeType() const {
    return getCallee()->getType().castTo<SILFunctionType>();
  }
  /// Get the conventions of the callee without the applied substitutions.
  SILFunctionConventions getOrigCalleeConv() const {
    return SILFunctionConventions(
        getOrigCalleeType(),
        SILAddressConventions::forFunction(*getFunction()));
  }

  /// Get the type of the callee with the applied substitutions.
  CanSILFunctionType getSubstCalleeType() const {
    return getSubstCalleeSILType().castTo<SILFunctionType>();
  }
  SILType getSubstCalleeSILType() const {
    FOREACH_IMPL_RETURN(getSubstCalleeSILType());
  }
  void setSubstCalleeType(CanSILFunctionType t) {
    FOREACH_IMPL_RETURN(setSubstCalleeType(t));
  }

  /// Get the conventions of the callee with the applied substitutions.
  SILFunctionConventions getSubstCalleeConv() const {
    return SILFunctionConventions(
        getSubstCalleeType(),
        SILAddressConventions::forFunction(*getFunction()));
  }

  bool isAsync() const {
    return getOrigCalleeType()->isAsync();
  }

  /// Returns true if the callee function is annotated with
  /// @_semantics("programtermination_point")
  bool isCalleeKnownProgramTerminationPoint() const {
    FOREACH_IMPL_RETURN(isCalleeKnownProgramTerminationPoint());
  }

  /// Returns true if the callee function is annotated with
  /// @_semantics("unavailable_code_reached")
  bool isCalleeUnavailableCodeReached() const {
    FOREACH_IMPL_RETURN(isCalleeUnavailableCodeReached());
  }

  /// Check if this is a call of a never-returning function.
  bool isCalleeNoReturn() const { FOREACH_IMPL_RETURN(isCalleeNoReturn()); }

  bool isCalleeThin() const {
    switch (getSubstCalleeType()->getRepresentation()) {
    case SILFunctionTypeRepresentation::CFunctionPointer:
    case SILFunctionTypeRepresentation::CXXMethod:
    case SILFunctionTypeRepresentation::Thin:
    case SILFunctionTypeRepresentation::Method:
    case SILFunctionTypeRepresentation::ObjCMethod:
    case SILFunctionTypeRepresentation::WitnessMethod:
    case SILFunctionTypeRepresentation::Closure:
    case SILFunctionTypeRepresentation::KeyPathAccessorGetter:
    case SILFunctionTypeRepresentation::KeyPathAccessorSetter:
    case SILFunctionTypeRepresentation::KeyPathAccessorEquals:
    case SILFunctionTypeRepresentation::KeyPathAccessorHash:
      return true;
    case SILFunctionTypeRepresentation::Block:
    case SILFunctionTypeRepresentation::Thick:
      return false;
    }
  }

  /// True if this application has generic substitutions.
  bool hasSubstitutions() const { FOREACH_IMPL_RETURN(hasSubstitutions()); }

  /// The substitutions used to bind the generic arguments of this function.
  SubstitutionMap getSubstitutionMap() const {
    FOREACH_IMPL_RETURN(getSubstitutionMap());
  }

  /// Return the associated specialization information.
  const GenericSpecializationInformation *getSpecializationInfo() const {
    FOREACH_IMPL_RETURN(getSpecializationInfo());
  }

  /// Return an operand list corresponding to the applied arguments.
  MutableArrayRef<Operand> getArgumentOperands() const {
    FOREACH_IMPL_RETURN(getArgumentOperands());
  }

  /// Return a list of applied argument values.
  OperandValueArrayRef getArguments() const {
    FOREACH_IMPL_RETURN(getArguments());
  }

  /// Return the number of applied arguments.
  unsigned getNumArguments() const { FOREACH_IMPL_RETURN(getNumArguments()); }

  /// Return the apply operand for the given applied argument index.
  Operand &getArgumentRef(unsigned i) const { return getArgumentOperands()[i]; }

  // The apply operand at the given index into the callee's function's
  // arguments.
  Operand &getArgumentRefAtCalleeArgIndex(unsigned i) const {
    return getArgumentRef(i - getSubstCalleeArgIndexOfFirstAppliedArg());
  }

  /// Return the ith applied argument.
  SILValue getArgument(unsigned i) const { return getArguments()[i]; }

  // The argument at the given index into the callee's function's arguments.
  SILValue getArgumentAtCalleeArgIndex(unsigned i) const {
    return getArgument(i - getSubstCalleeArgIndexOfFirstAppliedArg());
  }

  /// Set the ith applied argument.
  void setArgument(unsigned i, SILValue V) const {
    getArgumentOperands()[i].set(V);
  }

  void setCallee(SILValue V) const {
    unsigned calleeIndex = getCalleeOperand()->getOperandNumber();
    getInstruction()->getAllOperands()[calleeIndex].set(V);
  }

  /// Return the operand index of the first applied argument.
  unsigned getOperandIndexOfFirstArgument() const {
    FOREACH_IMPL_RETURN(getArgumentOperandNumber());
  }

  /// Returns the SILLocation associated with the argument at \p argIdx,
  /// falling back to the apply's anchor location when no per-argument
  /// override has been stored.
  SILLocation getArgumentLoc(unsigned argIdx) const {
    FOREACH_IMPL_RETURN(getArgumentLoc(argIdx));
  }

  /// Set the per-argument location for the argument at \p argIdx. Requires
  /// that the apply was constructed with per-argument location storage;
  /// \p loc must be a valid (non-null) SILLocation.
  void setArgumentLoc(unsigned argIdx, SILLocation loc) const {
    FOREACH_IMPL_RETURN(setArgumentLoc(argIdx, loc));
  }

  /// Returns the per-argument SILLocations stored on this apply, parallel
  /// to `getArguments()`. `std::nullopt` when no per-argument storage was
  /// reserved at construction time.
  std::optional<ArrayRef<SILLocation>> getArgumentLocs() const {
    FOREACH_IMPL_RETURN(getArgumentLocs());
  }
#undef FOREACH_IMPL_RETURN

  /// Returns the SILLocation associated with the apply argument referred to
  /// by \p op, with anchor fallback. \p op must be an argument operand of
  /// this apply (not the callee or a type-dependent operand).
  SILLocation getArgumentLoc(const Operand *op) const {
    assert(op->getUser() == Inst && "operand is not from this apply");
    assert(isArgumentOperand(*op) && "operand is not an argument operand");
    return getArgumentLoc(getAppliedArgIndex(*op));
  }

  /// Returns true if \p oper is an argument operand and not the callee
  /// operand.
  bool isArgumentOperand(const Operand &oper) const {
    return oper.getOperandNumber() >= getOperandIndexOfFirstArgument() &&
      oper.getOperandNumber() < getOperandIndexOfFirstArgument() + getNumArguments();
  }

  /// Return the applied argument index for the given operand.
  ///
  /// This corresponds to the index of the operand in the actual
  /// SILFunctionType.
  ///
  /// As a result this does not include indirect parameters, but it does include
  /// implicit parameters (e.x.: the implicit isolated(any) parameter) that are
  /// in the SIL type but are not in the AST type. To ignore such implicit
  /// parameters, use getASTAppliedArgIndex.
  unsigned getAppliedArgIndex(const Operand &oper) const {
    assert(oper.getUser() == Inst);
    assert(isArgumentOperand(oper));

    return oper.getOperandNumber() - getOperandIndexOfFirstArgument();
  }

  /// Returns the applied AST argument index for the given operand.
  ///
  /// This is guaranteed to return an index that can be used with AST function
  /// types. This means it looks past the callee, indirect function types, as
  /// well as any other implicit parameters like isolated(any).
  ///
  /// NOTE: If we add more implicit parameters to SILFunction types that do not
  /// appear at the AST level, this code needs to be updated.
  unsigned getASTAppliedArgIndex(const Operand &oper) const {
    // We rely on the assertions in getAppliedArgIndex to allow for us to assume
    // that we have an argument operand. We check later that we do not have an
    // isolated(any) parameter.
    unsigned appliedArgIndex = getAppliedArgIndex(oper);
    if (auto *pai = dyn_cast<PartialApplyInst>(Inst)) {
      if (pai->getFunctionType()->getIsolation() ==
          SILFunctionTypeIsolation::forErased()) {
        assert(appliedArgIndex != 0 &&
               "isolation(any) does not correspond to an AST argument");
        appliedArgIndex -= 1;
      }
    }
    return appliedArgIndex;
  }

private:
  /// The argument index of the first applied argument in \p calleeConv: 0 for
  /// full applies, >= 0 for partial applies.
  unsigned argIndexOfFirstAppliedArg(SILFunctionConventions calleeConv) const {
    switch (ApplySiteKind(Inst->getKind())) {
    case ApplySiteKind::ApplyInst:
    case ApplySiteKind::BeginApplyInst:
    case ApplySiteKind::TryApplyInst:
      return 0;
    case ApplySiteKind::PartialApplyInst:
      // The arguments to partial_apply are a suffix of the partial_apply's
      // callee.
      //
      // pa1 = partial_apply f(c) : $(a, b, c)
      // pa2 = partial_apply pa1(b) : $(a, b)
      // apply pa2(a)
      return calleeConv.getNumSILArguments() - getNumArguments();
    }
    llvm_unreachable("covered switch");
  }

public:
  /// Return the callee function's argument that applied operand \p op maps to,
  /// or nullptr if the callee is not a concrete function with a body (indirect
  /// call, closure value, witness/vtable dispatch, or an external declaration).
  ///
  /// The index is resolved against the callee's conventions, so it is correct
  /// even when the callee function's lowered-address form differs from that of
  /// the function containing this apply (the caller). This is the safe way to
  /// reach the argument in the callee function's definition. This is in
  /// contrast to getSubstCalleeArgIndex, which indexes into the space of
  /// arguments in *this* particular apply of the callee, from the perspective
  /// of *this* caller.
  ///
  /// The raw callee-space index, if needed, is the returned argument's
  /// getIndex().
  SILArgument *getCalleeArgument(const Operand &op) const {
    auto *callee = getCalleeFunction();
    if (!callee || callee->empty())
      return nullptr;
    return getCalleeArgument(callee, op);
  }

  /// Return \p callee's argument that applied operand \p op maps to.
  ///
  /// Use this overload only when \p callee was obtained other than through
  /// getCalleeFunction() (e.g. getReferencedFunctionOrNull, or a devirtualized
  /// target); otherwise prefer getCalleeArgument(op). \p callee must be a
  /// function this apply can call, i.e. with at least getNumArguments()
  /// arguments.
  SILArgument *getCalleeArgument(SILFunction *callee, const Operand &op) const {
    ASSERT(callee->getArguments().size() >= getNumArguments() &&
           "applying more arguments than the callee has");
    unsigned idx = argIndexOfFirstAppliedArg(callee->getConventions()) +
                   getAppliedArgIndex(op);
    return callee->getArgument(idx);
  }

  /// The index of the first applied argument in this call's substituted-callee
  /// conventions: 0 for full applies; >= 0 for partial applies.
  unsigned getSubstCalleeArgIndexOfFirstAppliedArg() const {
    return argIndexOfFirstAppliedArg(getSubstCalleeConv());
  }

  /// The argument index of applied operand \p oper in this call's
  /// *substituted-callee* conventions, the call site's own view of its
  /// arguments.
  ///
  /// This is NOT an index into the callee function's arguments. The two
  /// disagree on two independent axes:
  ///   - per-function address lowering can leave caller and callee in different
  ///     lowered-address forms mid-pipeline
  ///   - getSubstCalleeType (the callee value's type here) can have a different
  ///     SIL-argument structure than the callee function's own signature
  ///     (generic substitution, reabstraction thunks).
  ///
  /// To reach the SILFunctionArgument declared in the callee function's
  /// definition, use getCalleeArgument. Never pass a substituted callee
  /// argument index into SILFunction::getArgument.
  unsigned getSubstCalleeArgIndex(const Operand &oper) const {
    return getSubstCalleeArgIndexOfFirstAppliedArg() + getAppliedArgIndex(oper);
  }

  /// Return the SILArgumentConvention for the given applied argument operand.
  SILArgumentConvention getArgumentConvention(const Operand &oper) const {
    unsigned calleeArgIdx =
        getSubstCalleeArgIndexOfFirstAppliedArg() + getAppliedArgIndex(oper);
    return getSubstCalleeConv().getSILArgumentConvention(calleeArgIdx);
  }

  /// Return the SILParameterInfo for the given applied parameter operand, from
  /// this call site's substituted-callee conventions. \p oper must be a
  /// parameter operand (not an indirect result).
  SILParameterInfo getParamInfoForOperand(const Operand &oper) const {
    return getSubstCalleeConv().getParamInfoForSILArg(
        getSubstCalleeArgIndex(oper));
  }

  /// Return the SILArgumentConvention for the given applied argument operand at
  /// the apply instruction.
  ///
  /// For full applies, this is equivalent to `getArgumentConvention`. But for
  /// a partial_apply, the argument ownership convention at the partial_apply
  /// instruction itself is different from the argument convention of the
  /// callee.
  /// 
  /// For details see the partial_apply documentation in SIL.rst.
  SILArgumentConvention getCaptureConvention(const Operand &oper) const {
    SILArgumentConvention conv = getArgumentConvention(oper);
    auto *pai = dyn_cast<PartialApplyInst>(Inst);
    if (!pai)
      return conv;
    switch (conv) {
    case SILArgumentConvention::Indirect_Inout:
    case SILArgumentConvention::Indirect_InoutAliasable:
    case SILArgumentConvention::Pack_Inout:
      return conv;
    case SILArgumentConvention::Direct_Owned:
    case SILArgumentConvention::Direct_Unowned:
    case SILArgumentConvention::Direct_Guaranteed:
      return pai->isOnStack() ? SILArgumentConvention::Direct_Guaranteed
                              : SILArgumentConvention::Direct_Owned;
    case SILArgumentConvention::Indirect_In:
    case SILArgumentConvention::Indirect_In_Guaranteed:
    case SILArgumentConvention::Indirect_In_CXX:
      return pai->isOnStack() ? SILArgumentConvention::Indirect_In_Guaranteed
                              : SILArgumentConvention::Indirect_In;
    case SILArgumentConvention::Pack_Guaranteed:
    case SILArgumentConvention::Pack_Owned:
      return pai->isOnStack() ? SILArgumentConvention::Pack_Guaranteed
                              : SILArgumentConvention::Pack_Owned;
    case SILArgumentConvention::Indirect_Out:
    case SILArgumentConvention::Pack_Out:
      llvm_unreachable("partial_apply cannot have an @out operand");
    }
    llvm_unreachable("covered switch");
  }

  /// Return true if 'self' is an applied argument.
  bool hasSelfArgument() const {
    switch (ApplySiteKind(Inst->getKind())) {
    case ApplySiteKind::ApplyInst:
      return cast<ApplyInst>(Inst)->hasSelfArgument();
    case ApplySiteKind::BeginApplyInst:
      return cast<BeginApplyInst>(Inst)->hasSelfArgument();
    case ApplySiteKind::TryApplyInst:
      return cast<TryApplyInst>(Inst)->hasSelfArgument();
    case ApplySiteKind::PartialApplyInst:
      llvm_unreachable("unhandled case");
    }
    llvm_unreachable("covered switch");
  }

  /// Return the applied 'self' argument value.
  SILValue getSelfArgument() const {
    switch (ApplySiteKind(Inst->getKind())) {
    case ApplySiteKind::ApplyInst:
      return cast<ApplyInst>(Inst)->getSelfArgument();
    case ApplySiteKind::BeginApplyInst:
      return cast<BeginApplyInst>(Inst)->getSelfArgument();
    case ApplySiteKind::TryApplyInst:
      return cast<TryApplyInst>(Inst)->getSelfArgument();
    case ApplySiteKind::PartialApplyInst:
      llvm_unreachable("unhandled case");
    }
    llvm_unreachable("covered switch");
  }

  /// Return the 'self' apply operand.
  Operand &getSelfArgumentOperand() {
    switch (ApplySiteKind(Inst->getKind())) {
    case ApplySiteKind::ApplyInst:
      return cast<ApplyInst>(Inst)->getSelfArgumentOperand();
    case ApplySiteKind::BeginApplyInst:
      return cast<BeginApplyInst>(Inst)->getSelfArgumentOperand();
    case ApplySiteKind::TryApplyInst:
      return cast<TryApplyInst>(Inst)->getSelfArgumentOperand();
    case ApplySiteKind::PartialApplyInst:
      llvm_unreachable("Unhandled cast");
    }
    llvm_unreachable("covered switch");
  }

  /// Return the sil_isolated operand if we have one.
  Operand *getIsolatedArgumentOperandOrNullPtr() {
    switch (ApplySiteKind(Inst->getKind())) {
    case ApplySiteKind::ApplyInst:
      return cast<ApplyInst>(Inst)->getIsolatedArgumentOperandOrNullPtr();
    case ApplySiteKind::BeginApplyInst:
      return cast<BeginApplyInst>(Inst)->getIsolatedArgumentOperandOrNullPtr();
    case ApplySiteKind::TryApplyInst:
      return cast<TryApplyInst>(Inst)->getIsolatedArgumentOperandOrNullPtr();
    case ApplySiteKind::PartialApplyInst:
      llvm_unreachable("Unhandled case");
    }
    llvm_unreachable("covered switch");
  }

  /// Return a list of applied arguments without self.
  OperandValueArrayRef getArgumentsWithoutSelf() const {
    switch (ApplySiteKind(Inst->getKind())) {
    case ApplySiteKind::ApplyInst:
      return cast<ApplyInst>(Inst)->getArgumentsWithoutSelf();
    case ApplySiteKind::BeginApplyInst:
      return cast<BeginApplyInst>(Inst)->getArgumentsWithoutSelf();
    case ApplySiteKind::TryApplyInst:
      return cast<TryApplyInst>(Inst)->getArgumentsWithoutSelf();
    case ApplySiteKind::PartialApplyInst:
      llvm_unreachable("Unhandled case");
    }
    llvm_unreachable("covered switch");
  }

  /// Return a list of applied operands of the apply without self.
  ArrayRef<Operand> getOperandsWithoutSelf() const {
    switch (ApplySiteKind(Inst->getKind())) {
    case ApplySiteKind::ApplyInst:
      return cast<ApplyInst>(Inst)->getOperandsWithoutSelf();
    case ApplySiteKind::BeginApplyInst:
      return cast<BeginApplyInst>(Inst)->getOperandsWithoutSelf();
    case ApplySiteKind::TryApplyInst:
      return cast<TryApplyInst>(Inst)->getOperandsWithoutSelf();
    case ApplySiteKind::PartialApplyInst:
      llvm_unreachable("Unhandled case");
    }
    llvm_unreachable("covered switch");
  }

  MutableArrayRef<Operand> getOperandsWithoutSelf() {
    switch (ApplySiteKind(Inst->getKind())) {
    case ApplySiteKind::ApplyInst:
      return cast<ApplyInst>(Inst)->getOperandsWithoutSelf();
    case ApplySiteKind::BeginApplyInst:
      return cast<BeginApplyInst>(Inst)->getOperandsWithoutSelf();
    case ApplySiteKind::TryApplyInst:
      return cast<TryApplyInst>(Inst)->getOperandsWithoutSelf();
    case ApplySiteKind::PartialApplyInst:
      llvm_unreachable("Unhandled case");
    }
    llvm_unreachable("covered switch");
  }

  bool hasGuaranteedResult() const {
    switch (ApplySiteKind(Inst->getKind())) {
    case ApplySiteKind::ApplyInst:
      return cast<ApplyInst>(Inst)->hasGuaranteedResult();
    case ApplySiteKind::BeginApplyInst:
    case ApplySiteKind::TryApplyInst:
    case ApplySiteKind::PartialApplyInst:
      return false;
    }
    llvm_unreachable("covered switch");
  }

  bool hasAddressResult() const {
    switch (ApplySiteKind(Inst->getKind())) {
    case ApplySiteKind::ApplyInst:
      return cast<ApplyInst>(Inst)->hasAddressResult();
    case ApplySiteKind::BeginApplyInst:
    case ApplySiteKind::TryApplyInst:
    case ApplySiteKind::PartialApplyInst:
      return false;
    }
    llvm_unreachable("covered switch");
  }

  /// Returns true if \p op is an operand that passes an indirect
  /// result argument to the apply site.
  bool isIndirectResultOperand(const Operand &op) const;

  /// Returns true if \p op is an operand that is passed as an indirect error
  /// result.
  bool isIndirectErrorResultOperand(const Operand &op) const;

  ApplyOptions getApplyOptions() const {
    switch (ApplySiteKind(getInstruction()->getKind())) {
    case ApplySiteKind::ApplyInst:
      return cast<ApplyInst>(Inst)->getApplyOptions();
    case ApplySiteKind::BeginApplyInst:
      return cast<BeginApplyInst>(Inst)->getApplyOptions();
    case ApplySiteKind::TryApplyInst:
      return cast<TryApplyInst>(Inst)->getApplyOptions();
    case ApplySiteKind::PartialApplyInst:
      return ApplyOptions();
    }
    llvm_unreachable("covered switch");
  }

  /// If this is a terminator apply site, then pass a builder to insert at the
  /// first instruction of each successor to \p func. Otherwise, pass a builder
  /// to insert at std::next(Inst).
  ///
  /// The intention is that this abstraction will enable the compiler writer to
  /// ignore whether or not an apply site is a terminator when inserting
  /// instructions after an apply site. This results in eliminating unnecessary
  /// if-else code otherwise required to handle such situations.
  ///
  /// NOTE: We pass std::next() for begin_apply. If one wishes to insert code
  /// /after/ the end_apply/abort_apply, please use instead
  /// insertAfterApplication.
  void insertAfterInvocation(function_ref<void(SILBuilder &)> func) const;

  /// Pass a builder with insertion points that are guaranteed to be immediately
  /// after this apply site has been applied.
  ///
  /// For apply and try_apply, that means after the apply.  For partial_apply,
  /// that means after the partial_apply.  For begin_apply, that means after its
  /// end_apply and abort_apply instructions.
  ///
  /// This is just like insertAfterInvocation except that if the full apply site
  /// is a begin_apply, we pass the insertion points after the end_apply,
  /// abort_apply rather than an insertion point right after the
  /// begin_apply. For such functionality, please invoke insertAfterInvocation.
  void insertAfterApplication(function_ref<void(SILBuilder &)> func) const;

  /// Return whether the given apply is of a formally-throwing function
  /// which is statically known not to throw.
  bool isNonThrowing() const {
    return getApplyOptions().contains(ApplyFlags::DoesNotThrow);
  }

  /// Return whether the given apply is of a formally-async function
  /// which is statically known not to await.
  bool isNonAsync() const {
    return getApplyOptions().contains(ApplyFlags::DoesNotAwait);
  }

  /// Return the SILParameterInfo for this operand in the callee function.
  SILParameterInfo getArgumentParameterInfo(const Operand &oper) const {
    assert(!getArgumentConvention(oper).isIndirectOutParameter() &&
           "Can only be applied to non-out parameters");

    // The ParameterInfo is going to be the parameter in the caller.
    unsigned calleeArgIndex = getSubstCalleeArgIndex(oper);
    return getSubstCalleeConv().getParamInfoForSILArg(calleeArgIndex);
  }

  /// Returns true if \p op is the callee operand of this apply site
  /// and not an argument operand.
  ///
  /// If this instruction is not a full apply site, this always returns false.
  bool isCalleeOperand(const Operand &op) const;

  /// Returns true if this is an 'out' parameter.
  bool isSending(const Operand &oper) const {
    if (isIndirectErrorResultOperand(oper) || oper.isTypeDependent() ||
        isCalleeOperand(oper))
      return false;
    if (isIndirectResultOperand(oper))
      return getSubstCalleeType()->hasSendingResult();
    return getArgumentParameterInfo(oper).hasOption(SILParameterInfo::Sending);
  }

  /// Returns true if this operand is an 'inout sending' parameter.
  bool isInOutSending(const Operand &oper) const {
    return isSending(oper) && getArgumentConvention(oper).isInoutConvention();
  }

  /// Return true if 'operand' is addressable after type substitution in the
  /// caller's context.
  bool isAddressable(const Operand &operand) const;

  static ApplySite getFromOpaqueValue(void *p) { return ApplySite(p); }

  friend bool operator==(ApplySite lhs, ApplySite rhs) {
    return lhs.getInstruction() == rhs.getInstruction();
  }
  friend bool operator!=(ApplySite lhs, ApplySite rhs) {
    return lhs.getInstruction() != rhs.getInstruction();
  }

  static bool classof(const SILInstruction *inst) {
    return bool(ApplySiteKind::fromNodeKind(inst->getKind()));
  }

  void dump() const LLVM_ATTRIBUTE_USED { getInstruction()->dump(); }

  /// Form a FullApplySite.  Note that it will be null if this apply site is not
  /// in fact a FullApplySite.
  FullApplySite asFullApplySite() const;
};

//===----------------------------------------------------------------------===//
//                               FullApplySite
//===----------------------------------------------------------------------===//

struct FullApplySiteKind {
  enum innerty : std::underlying_type<SILInstructionKind>::type {
    ApplyInst = unsigned(SILInstructionKind::ApplyInst),
    TryApplyInst = unsigned(SILInstructionKind::TryApplyInst),
    BeginApplyInst = unsigned(SILInstructionKind::BeginApplyInst),
  } value;

  explicit FullApplySiteKind(SILInstructionKind kind) {
    auto fullApplySiteKind = FullApplySiteKind::fromNodeKindHelper(kind);
    assert(fullApplySiteKind && "SILNodeKind is not a FullApplySiteKind?!");
    value = fullApplySiteKind.value();
  }

  FullApplySiteKind(innerty value) : value(value) {}
  operator innerty() const { return value; }

  static std::optional<FullApplySiteKind>
  fromNodeKind(SILInstructionKind kind) {
    if (auto innerOpt = FullApplySiteKind::fromNodeKindHelper(kind))
      return FullApplySiteKind(*innerOpt);
    return std::nullopt;
  }

private:
  static std::optional<innerty> fromNodeKindHelper(SILInstructionKind kind) {
    switch (kind) {
    case SILInstructionKind::ApplyInst:
      return FullApplySiteKind::ApplyInst;
    case SILInstructionKind::TryApplyInst:
      return FullApplySiteKind::TryApplyInst;
    case SILInstructionKind::BeginApplyInst:
      return FullApplySiteKind::BeginApplyInst;
    default:
      return std::nullopt;
    }
  }
};

/// A full function application.
class FullApplySite : public ApplySite {
  explicit FullApplySite(void *p) : ApplySite(p) {}

public:
  FullApplySite() : ApplySite() {}
  explicit FullApplySite(SILInstruction *inst) : ApplySite(inst) {
    assert(classof(inst) && "not an apply instruction?");
  }
  FullApplySite(ApplyInst *inst) : ApplySite(inst) {}
  FullApplySite(BeginApplyInst *inst) : ApplySite(inst) {}
  FullApplySite(TryApplyInst *inst) : ApplySite(inst) {}

  static FullApplySite isa(SILInstruction *inst) {
    auto kind = FullApplySiteKind::fromNodeKind(inst->getKind());
    if (!kind)
      return FullApplySite();
    switch (kind.value()) {
    case FullApplySiteKind::ApplyInst:
      return FullApplySite(cast<ApplyInst>(inst));
    case FullApplySiteKind::BeginApplyInst:
      return FullApplySite(cast<BeginApplyInst>(inst));
    case FullApplySiteKind::TryApplyInst:
      return FullApplySite(cast<TryApplyInst>(inst));
    }
    llvm_unreachable("covered switch");
  }

  static FullApplySite isa(SILValue value) {
    if (auto *inst = value->getDefiningInstruction())
      return FullApplySite::isa(inst);
    return FullApplySite();
  }

  FullApplySiteKind getKind() const {
    return FullApplySiteKind(getInstruction()->getKind());
  }

  bool hasIndirectSILResults() const {
    return getSubstCalleeConv().hasIndirectSILResults();
  }

  /// Get the SIL value that represents all of the given call's results. For a
  /// single direct result, returns the actual result. For multiple results,
  /// returns a pseudo-result tuple. The tuple has no storage of its own. The
  /// real results must be extracted from it.
  ///
  /// For ApplyInst, returns the single-value instruction itself.
  ///
  /// For TryApplyInst returns the continuation block argument.
  ///
  /// For BeginApplyInst, returns an invalid value. For coroutines, there is no
  /// single value representing all results. Yielded values are generally
  /// handled differently since they have the convention of incoming arguments.
  SILValue getResult() const {
    switch (getKind()) {
    case FullApplySiteKind::ApplyInst:
      return SILValue(cast<ApplyInst>(getInstruction()));
    case FullApplySiteKind::BeginApplyInst: {
      return SILValue();
    }
    case FullApplySiteKind::TryApplyInst: {
      auto *normalBlock = cast<TryApplyInst>(getInstruction())->getNormalBB();
      assert(normalBlock->getNumArguments() == 1 &&
             "Expected try apply to have a single result");
      return normalBlock->getArgument(0);
    }
    }
    llvm_unreachable("Covered switch isn't covered?!");
  }

  // For a direct error result, as a result of an @error convention, if any.
  SILValue getDirectErrorResult() const {
    switch (getKind()) {
    case FullApplySiteKind::ApplyInst:
    case FullApplySiteKind::BeginApplyInst:
      return SILValue();
    case FullApplySiteKind::TryApplyInst: {
      if (getNumIndirectSILErrorResults())
        return SILValue(); // Not a direct @error convention.

      auto *errBlock = cast<TryApplyInst>(getInstruction())->getErrorBB();
      assert(errBlock->getNumArguments() == 1 &&
             "Expected this try_apply to have a single direct error result");
      return errBlock->getArgument(0);
    }
    }
    llvm_unreachable("Covered switch isn't covered?!");
  }

  unsigned getNumIndirectSILResults() const {
    return getSubstCalleeConv().getNumIndirectSILResults();
  }

  unsigned getNumIndirectSILErrorResults() const {
    return getSubstCalleeConv().getNumIndirectSILErrorResults();
  }

  OperandValueArrayRef getIndirectSILResults() const {
    return getArguments().slice(0, getNumIndirectSILResults());
  }

  OperandValueArrayRef getIndirectSILErrorResults() const {
    return getArguments().slice(getNumIndirectSILResults(),
                                getNumIndirectSILErrorResults());
  }

  OperandValueArrayRef getArgumentsWithoutIndirectResults() const {
    return getArguments().slice(getNumIndirectSILResults() +
                                getNumIndirectSILErrorResults());
  }

  MutableArrayRef<Operand> getOperandsWithoutIndirectResults() const {
    return getArgumentOperands().slice(getNumIndirectSILResults() +
                                       getNumIndirectSILErrorResults());
  }

  MutableArrayRef<Operand> getOperandsWithoutIndirectResultsOrSelf() const {
    auto ops = getOperandsWithoutIndirectResults();
    if (!hasSelfArgument())
      return ops;
    return ops.drop_back();
  }

  InoutArgumentRange getInoutArguments() const {
    switch (getKind()) {
    case FullApplySiteKind::ApplyInst:
      return cast<ApplyInst>(getInstruction())->getInoutArguments();
    case FullApplySiteKind::TryApplyInst:
      return cast<TryApplyInst>(getInstruction())->getInoutArguments();
    case FullApplySiteKind::BeginApplyInst:
      return cast<BeginApplyInst>(getInstruction())->getInoutArguments();
    }
    llvm_unreachable("invalid apply kind");
  }

  AutoDiffSemanticResultArgumentRange getAutoDiffSemanticResultArguments() const {
    switch (getKind()) {
    case FullApplySiteKind::ApplyInst:
      return cast<ApplyInst>(getInstruction())->getAutoDiffSemanticResultArguments();
    case FullApplySiteKind::TryApplyInst:
      return cast<TryApplyInst>(getInstruction())->getAutoDiffSemanticResultArguments();
    case FullApplySiteKind::BeginApplyInst:
      return cast<BeginApplyInst>(getInstruction())->getAutoDiffSemanticResultArguments();
    }
    llvm_unreachable("invalid apply kind");
  }

  /// Returns true if \p op is the callee operand of this apply site
  /// and not an argument operand.
  bool isCalleeOperand(const Operand &op) const {
    return op.getOperandNumber() < getOperandIndexOfFirstArgument();
  }

  /// Is this an ApplySite that begins the evaluation of a coroutine.
  bool beginsCoroutineEvaluation() const {
    switch (getKind()) {
    case FullApplySiteKind::ApplyInst:
    case FullApplySiteKind::TryApplyInst:
      return false;
    case FullApplySiteKind::BeginApplyInst:
      return true;
    }
    llvm_unreachable("Covered switch isn't covered?!");
  }

  /// Returns true if \p op is an operand that passes an indirect
  /// result argument to the apply site.
  bool isIndirectResultOperand(const Operand &op) const {
    return isArgumentOperand(op)
      && (getSubstCalleeArgIndex(op) < getNumIndirectSILResults());
  }

  /// Returns true if \p op is an operand that passes an indirect
  /// result argument to the apply site.
  bool isIndirectErrorResultOperand(const Operand &op) const {
    return isArgumentOperand(op)
      && (getSubstCalleeArgIndex(op) >= getNumIndirectSILResults())
      && (getSubstCalleeArgIndex(op) < getNumIndirectSILResults() + getNumIndirectSILErrorResults());
  }

  std::optional<ApplyIsolationCrossing> getIsolationCrossing() const {
    switch (getKind()) {
    case FullApplySiteKind::ApplyInst:
      return cast<ApplyInst>(**this)->getIsolationCrossing();
    case FullApplySiteKind::TryApplyInst:
      return cast<TryApplyInst>(**this)->getIsolationCrossing();
    case FullApplySiteKind::BeginApplyInst:
      return cast<BeginApplyInst>(**this)->getIsolationCrossing();
    }
  }

  /// Return the applied argument index for the given operand ignoring indirect
  /// results.
  ///
  /// So for instance:
  ///
  /// apply %f(%result, %0, %1, %2, ...).
  unsigned getAppliedArgIndexWithoutIndirectResults(const Operand &oper) const {
    assert(oper.getUser() == **this);
    assert(isArgumentOperand(oper));

    return getAppliedArgIndex(oper) - getNumIndirectSILResults() -
           getNumIndirectSILErrorResults();
  }

  bool isNonisolatedNonsending() const {
    return getSubstCalleeType()->hasNonisolatedNonsendingIsolation();
  }

  static FullApplySite getFromOpaqueValue(void *p) { return FullApplySite(p); }

  static bool classof(const SILInstruction *inst) {
    return bool(FullApplySiteKind::fromNodeKind(inst->getKind()));
  }
};

} // namespace swift

namespace llvm {

template<>
struct PointerLikeTypeTraits<swift::ApplySite> {
public:
  static inline void *getAsVoidPointer(swift::ApplySite apply) {
    return (void*)apply.getInstruction();
  }
  static inline swift::ApplySite getFromVoidPointer(void *pointer) {
    return swift::ApplySite((swift::SILInstruction*)pointer);
  }
  enum { NumLowBitsAvailable =
         PointerLikeTypeTraits<swift::SILNode *>::NumLowBitsAvailable };
};

template<>
struct PointerLikeTypeTraits<swift::FullApplySite> {
public:
  static inline void *getAsVoidPointer(swift::FullApplySite apply) {
    return (void*)apply.getInstruction();
  }
  static inline swift::FullApplySite getFromVoidPointer(void *pointer) {
    return swift::FullApplySite((swift::SILInstruction*)pointer);
  }
  enum { NumLowBitsAvailable =
         PointerLikeTypeTraits<swift::SILNode *>::NumLowBitsAvailable };
};

// An ApplySite casts like a SILInstruction*.
template <> struct simplify_type<const ::swift::ApplySite> {
  using SimpleType = ::swift::SILInstruction *;
  static SimpleType getSimplifiedValue(const ::swift::ApplySite &Val) {
    return Val.getInstruction();
  }
};
template <>
struct simplify_type<::swift::ApplySite>
    : public simplify_type<const ::swift::ApplySite> {};
template <>
struct simplify_type<::swift::FullApplySite>
    : public simplify_type<const ::swift::ApplySite> {};
template <>
struct simplify_type<const ::swift::FullApplySite>
    : public simplify_type<const ::swift::ApplySite> {};

template <> struct DenseMapInfo<::swift::ApplySite> {
  static ::swift::ApplySite getEmptyKey() {
    return ::swift::ApplySite::getFromOpaqueValue(
        llvm::DenseMapInfo<void *>::getEmptyKey());
  }
  static ::swift::ApplySite getTombstoneKey() {
    return ::swift::ApplySite::getFromOpaqueValue(
        llvm::DenseMapInfo<void *>::getTombstoneKey());
  }
  static unsigned getHashValue(::swift::ApplySite AS) {
    auto *I = AS.getInstruction();
    return DenseMapInfo<::swift::SILInstruction *>::getHashValue(I);
  }
  static bool isEqual(::swift::ApplySite LHS, ::swift::ApplySite RHS) {
    return LHS == RHS;
  }
};

template <> struct DenseMapInfo<::swift::FullApplySite> {
  static ::swift::FullApplySite getEmptyKey() {
    return ::swift::FullApplySite::getFromOpaqueValue(
        llvm::DenseMapInfo<void *>::getEmptyKey());
  }
  static ::swift::FullApplySite getTombstoneKey() {
    return ::swift::FullApplySite::getFromOpaqueValue(
        llvm::DenseMapInfo<void *>::getTombstoneKey());
  }
  static unsigned getHashValue(::swift::FullApplySite AS) {
    auto *I = AS.getInstruction();
    return DenseMapInfo<::swift::SILInstruction *>::getHashValue(I);
  }
  static bool isEqual(::swift::FullApplySite LHS, ::swift::FullApplySite RHS) {
    return LHS == RHS;
  }
};

} // namespace llvm

//===----------------------------------------------------------------------===//
//           Inline Definitions to work around Forward Declaration
//===----------------------------------------------------------------------===//

namespace swift {

inline FullApplySite ApplySite::asFullApplySite() const {
  return FullApplySite::isa(getInstruction());
}

inline bool ApplySite::isIndirectResultOperand(const Operand &op) const {
  auto fas = asFullApplySite();
  if (!fas)
    return false;
  return fas.isIndirectResultOperand(op);
}

inline bool ApplySite::isIndirectErrorResultOperand(const Operand &op) const {
  auto fas = asFullApplySite();
  if (!fas)
    return false;
  return fas.isIndirectErrorResultOperand(op);
}

inline bool ApplySite::isCalleeOperand(const Operand &op) const {
  auto fas = asFullApplySite();
  if (!fas)
    return false;
  return fas.isCalleeOperand(op);
}

} // namespace swift

#endif
