//===--- SILGenProlog.cpp - Function prologue emission --------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#include "ArgumentSource.h"
#include "ExecutorBreadcrumb.h"
#include "FunctionInputGenerator.h"
#include "Initialization.h"
#include "ManagedValue.h"
#include "SILGenFunction.h"
#include "Scope.h"

#include "swift/AST/CanTypeVisitor.h"
#include "swift/AST/DiagnosticsSIL.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/PropertyWrappers.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/Defer.h"
#include "swift/Basic/Generators.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILArgumentConvention.h"
#include "swift/SIL/SILInstruction.h"

using namespace swift;
using namespace Lowering;

template <typename... T, typename... U>
static void diagnose(ASTContext &Context, SourceLoc loc, Diag<T...> diag,
                     U &&...args) {
  Context.Diags.diagnose(loc, diag, std::forward<U>(args)...);
}

SILValue SILGenFunction::emitSelfDeclForDestructor(VarDecl *selfDecl) {
  SILFunctionConventions conventions = F.getConventionsInContext();

  // Emit the implicit 'self' argument.
  SILType selfType = conventions.getSILArgumentType(
      conventions.getNumSILArguments() - 1, F.getTypeExpansionContext());
  selfType = F.mapTypeIntoContext(selfType);
  SILValue selfValue = F.begin()->createFunctionArgument(selfType, selfDecl);

  uint16_t ArgNo = 1; // Hardcoded for destructors.
  auto dv = SILDebugVariable(selfDecl->isLet(), ArgNo);

  // If we have a move only type, then mark it with
  // mark_unresolved_non_copyable_value so we can't escape it.
  //
  // For now, we do not handle move only class deinits. This is because we need
  // to do a bit more refactoring to handle the weird way that it deals with
  // ownership. But for simple move only deinits (like struct/enum), that are
  // owned, lets mark them as needing to be no implicit copy checked so they
  // cannot escape.
  if (selfType.isMoveOnly() && !selfType.isAnyClassReferenceType()) {
    SILValue addr = B.createAllocStack(selfDecl, selfValue->getType(), dv);
    addr = B.createMarkUnresolvedNonCopyableValueInst(
        selfDecl, addr,
        MarkUnresolvedNonCopyableValueInst::CheckKind::ConsumableAndAssignable);
    if (selfValue->getType().isObject()) {
      B.createStore(selfDecl, selfValue, addr, StoreOwnershipQualifier::Init);
    } else {
      B.createCopyAddr(selfDecl, selfValue, addr, IsTake, IsInitialization);
    }
    // drop_deinit invalidates any user-defined struct/enum deinit
    // before the individual members are destroyed.
    addr = B.createDropDeinit(selfDecl, addr);
    selfValue = addr;
  }

  VarLocs[selfDecl] = VarLoc(selfValue, SILAccessEnforcement::Unknown);
  SILLocation PrologueLoc(selfDecl);
  PrologueLoc.markAsPrologue();
  B.emitDebugDescription(PrologueLoc, selfValue, dv);
  return selfValue;
}

namespace {
struct LoweredParamGenerator {
  SILGenFunction &SGF;
  CanSILFunctionType fnTy;
  ArrayRefGenerator<ArrayRef<SILParameterInfo>> parameterTypes;

  LoweredParamGenerator(SILGenFunction &SGF,
                        unsigned numIgnoredTrailingParameters)
    : SGF(SGF), fnTy(SGF.F.getLoweredFunctionType()),
      parameterTypes(
          SGF.F.getLoweredFunctionTypeInContext()
              ->getParameters().drop_back(numIgnoredTrailingParameters)) {}

  ParamDecl *paramDecl = nullptr;
  bool isNoImplicitCopy = false;
  LifetimeAnnotation lifetimeAnnotation = LifetimeAnnotation::None;
  bool isImplicitParameter = false;

  void configureParamData(ParamDecl *paramDecl, bool isNoImplicitCopy,
                          LifetimeAnnotation lifetimeAnnotation) {
    this->paramDecl = paramDecl;
    this->isNoImplicitCopy = isNoImplicitCopy;
    this->lifetimeAnnotation = lifetimeAnnotation;
    this->isImplicitParameter = false;
  }

  void configureParamDataForImplicitParam() { isImplicitParameter = true; }

  void resetParamData() {
    configureParamData(nullptr, false, LifetimeAnnotation::None);
  }

  ManagedValue claimNext() {
    auto parameterInfo = parameterTypes.claimNext();

    // We should only be called without a param decl when pulling pack
    // parameters out for multiple formal parameters (or a single formal
    // parameter pack) or if we have an implicit parameter.
    //
    // TODO: preserve the parameters captured by the pack into the SIL
    // representation.
    bool isFormalParameterPack = (paramDecl == nullptr) && !isImplicitParameter;
    assert(!isFormalParameterPack || parameterInfo.isPack());

    auto paramType =
        SGF.F.mapTypeIntoContext(SGF.getSILType(parameterInfo, fnTy));
    ManagedValue mv = SGF.B.createInputFunctionArgument(
        paramType, paramDecl, isNoImplicitCopy, lifetimeAnnotation,
        /*isClosureCapture*/ false, isFormalParameterPack, isImplicitParameter);
    return mv;
  }

  std::optional<SILParameterInfo> peek() const {
    if (isFinished())
      return {};
    return parameterTypes.get();
  }

  bool isFinished() const {
    return parameterTypes.isFinished();
  }

  void advance() {
    (void) claimNext();
  }

  void finish() {
    parameterTypes.finish();
  }
};

struct WritebackReabstractedInoutCleanup final : Cleanup {
  SILValue OrigAddress, SubstAddress;
  AbstractionPattern OrigTy;
  CanType SubstTy;
  WritebackReabstractedInoutCleanup(SILValue origAddress, SILValue substAddress,
                                    AbstractionPattern origTy,
                                    CanType substTy)
      : OrigAddress(origAddress), SubstAddress(substAddress),
        OrigTy(origTy), SubstTy(substTy)
  {}
  
  void emit(SILGenFunction &SGF, CleanupLocation l, ForUnwind_t forUnwind)
  override {
    Scope s(SGF.Cleanups, l);
    // Load the final local value coming in.
    auto mv = SGF.emitLoad(l, SubstAddress,
                           SGF.getTypeLowering(SubstAddress->getType()),
                           SGFContext(), IsTake);
    // Reabstract the value back to the original representation.
    mv = SGF.emitSubstToOrigValue(l, mv.ensurePlusOne(SGF, l),
                                  OrigTy, SubstTy);
    // Write it back to the original inout parameter.
    SGF.B.createStore(l, mv.forward(SGF), OrigAddress,
                      StoreOwnershipQualifier::Init);
  }
  
  void dump(SILGenFunction&) const override {
    llvm::errs() << "WritebackReabstractedInoutCleanup\n";
    OrigAddress->print(llvm::errs());
    SubstAddress->print(llvm::errs());
  }
};

class EmitBBArguments : public CanTypeVisitor<EmitBBArguments,
                                              /*RetTy*/ ManagedValue,
                                              /*ArgTys...*/ AbstractionPattern,
                                              Initialization *>
{
public:
  SILGenFunction &SGF;
  SILLocation loc;
  LoweredParamGenerator &parameters;

  EmitBBArguments(SILLocation l, LoweredParamGenerator &parameters)
      : SGF(parameters.SGF), loc(l), parameters(parameters) {}

  ManagedValue claimNextParameter() {
    return parameters.claimNext();
  }

  ManagedValue handleParam(AbstractionPattern origType, CanType substType,
                           ParamDecl *pd, bool isAddressable) {
    // Note: inouts of tuples are not exploded, so we bypass visit().
    if (pd->isInOut()) {
      return handleInOut(origType, substType, pd->isAddressable());
    }
    // Addressability also suppresses exploding the parameter.
    if (isAddressable) {
      return handleScalar(claimNextParameter(),
                          AbstractionPattern::getOpaque(), substType,
                          /*emitInto*/ nullptr,
                          /*inout*/ false, /*addressable*/ true);
    }
    return visit(substType, origType, /*emitInto*/ nullptr);
  }

  ManagedValue handlePackComponent(FunctionInputGenerator &formalParam) {
    auto origPatternType =
      formalParam.getOrigType().getPackExpansionPatternType();

    auto substParam = formalParam.getSubstParam();
    CanType substType = substParam.getParameterType();

    // Forward the pack cleanup and enter a new cleanup for the
    // remaining components.
    auto componentValue = formalParam.projectPackComponent(SGF, loc);

    // Handle scalar components.
    if (!isa<PackExpansionType>(substType)) {
      return handleScalar(componentValue, origPatternType, substType,
                          /*emit into*/ nullptr,
                          substParam.isInOut(),
                          /*is addressable*/ false);
    }

    auto componentPackTy = componentValue.getType().castTo<SILPackType>();

    // Handle pack expansion components.
    auto formalPackType = formalParam.getFormalPackType();
    auto componentIndex = formalParam.getPackComponentIndex();

    auto expectedExpansionTy = SGF.getLoweredRValueType(substType);
    auto expectedPackTy =
        SILPackType::get(SGF.getASTContext(), componentPackTy->getExtInfo(),
                         {expectedExpansionTy});

    // If we don't need a pack transformation, this is simple.
    // This is simultaneously testing that we don't need a transformation
    // and that we don't have other components in the pack.
    if (componentPackTy == expectedPackTy) {
      return componentValue;
    }

    // FIXME: perform this forwarding by just slicing the original pack.
    bool canForward =
      (expectedExpansionTy == componentPackTy->getElementType(componentIndex));

    auto rawOutputPackAddr =
      SGF.emitTemporaryPackAllocation(loc,
        SILType::getPrimitiveObjectType(expectedPackTy));
    auto outputFormalPackType =
      CanPackType::get(SGF.getASTContext(), {substType});
    return SGF.emitPackTransform(loc, componentValue,
                                 formalPackType, componentIndex,
                                 rawOutputPackAddr, outputFormalPackType, 0,
                                 canForward, /*plus one*/ !canForward,
                                 [&](ManagedValue input, SILType outputTy,
                                     SGFContext context) {
      if (canForward) return input;

      auto substEltType =
        cast<PackExpansionType>(substType).getPatternType();
      if (auto openedEnv = SGF.getInnermostPackExpansion()->OpenedElementEnv) {
        substEltType =
          openedEnv->mapContextualPackTypeIntoElementContext(substEltType);
      }

      return handleScalar(input, origPatternType, substEltType,
                          context.getEmitInto(),
                          /*inout*/ false,
                          /*addressable*/ false);
    });
  }

  ManagedValue visitType(CanType t, AbstractionPattern orig,
                         Initialization *emitInto) {
    auto mv = claimNextParameter();
    return handleScalar(mv, orig, t, emitInto,
                        /*inout*/ false,
                        /*addressable*/ false);
  }

  ManagedValue handleInOut(AbstractionPattern orig, CanType t,
                           bool isAddressable) {
    auto mv = claimNextParameter();
    return handleScalar(mv, orig, t, /*emitInto*/ nullptr,
                        /*inout*/ true,
                        isAddressable);
  }

  ManagedValue handleScalar(ManagedValue mv,
                            AbstractionPattern orig, CanType t,
                            Initialization *emitInto,
                            bool isInOut,
                            bool isAddressable) {
    assert(!(isInOut && emitInto != nullptr));

    auto argType = SGF.getLoweredType(t, mv.getType().getCategory());

    // This is a hack to deal with the fact that Self.Type comes in as a static
    // metatype, but we have to downcast it to a dynamic Self metatype to get
    // the right semantics.
    if (argType != mv.getType()) {
      if (auto argMetaTy = argType.getAs<MetatypeType>()) {
        if (auto argSelfTy = dyn_cast<DynamicSelfType>(argMetaTy.getInstanceType())) {
          assert(argSelfTy.getSelfType()
                   == mv.getType().castTo<MetatypeType>().getInstanceType());
          mv = SGF.B.createUncheckedBitCast(loc, mv, argType);
        }
      }
    }
    if (isInOut) {
      // If we are inout and are move only, insert a note to the move checker to
      // check ownership.
      if (mv.getType().isMoveOnly() && !mv.getType().isMoveOnlyWrapped())
        mv = SGF.B.createMarkUnresolvedNonCopyableValueInst(
            loc, mv,
            MarkUnresolvedNonCopyableValueInst::CheckKind::
                ConsumableAndAssignable);

      // If the value needs to be reabstracted, set up a shadow copy with
      // writeback here.
      if (argType.getASTType() != mv.getType().getASTType()) {
        // Load the value coming in.
        auto origBuf = mv.getValue();
        mv = SGF.emitLoad(loc, origBuf, SGF.getTypeLowering(mv.getType()), SGFContext(), IsTake);
        // Reabstract the value if necessary.
        mv = SGF.emitOrigToSubstValue(loc, mv.ensurePlusOne(SGF, loc), orig, t);
        // Store the value to a local buffer.
        auto substBuf = SGF.emitTemporaryAllocation(loc, argType);
        SGF.B.createStore(loc, mv.forward(SGF), substBuf, StoreOwnershipQualifier::Init);
        // Introduce a writeback to put the final value back in the inout.
        SGF.Cleanups.pushCleanup<WritebackReabstractedInoutCleanup>(origBuf, substBuf, orig, t);
        mv = ManagedValue::forLValue(substBuf);
      }

      return mv;
    }

    // If the parameter is marked `@_addressable`, then we want to defer any
    // reabstraction of the parameter as received, so that we can use the
    // original value at its stable address when possible.
    bool argIsLoadable = false;
    if (!isAddressable) {
      argIsLoadable = argType.isLoadable(SGF.F);
      // This can happen if the value is resilient in the calling convention
      // but not resilient locally.
      if (argIsLoadable) {
        if (argType.isAddress()) {
          mv = SGF.B.createLoadWithSameOwnership(loc, mv);
          argType = argType.getObjectType();
        }
      }

      assert(argType.getCategory() == mv.getType().getCategory());
      if (argType.getASTType() != mv.getType().getASTType()) {
        // Reabstract the value if necessary.
        mv = SGF.emitOrigToSubstValue(loc, mv.ensurePlusOne(SGF, loc), orig, t);
      }
    }

    if (parameters.isNoImplicitCopy && !argIsLoadable) {
      // We do not support no implicit copy address only types. Emit an error.
      auto diag = diag::noimplicitcopy_used_on_generic_or_existential;
      diagnose(SGF.getASTContext(), mv.getValue().getLoc().getSourceLoc(),
               diag);
    }

    // If the value is a (possibly optional) ObjC block passed into the entry
    // point of the function, then copy it so we can treat the value reliably
    // as a heap object. Escape analysis can eliminate this copy if it's
    // unneeded during optimization.
    CanType objectType = t;
    if (auto theObjTy = t.getOptionalObjectType())
      objectType = theObjTy;
    if (isa<FunctionType>(objectType) &&
        cast<FunctionType>(objectType)->getRepresentation()
              == FunctionType::Representation::Block) {
      SILValue blockCopy = SGF.B.createCopyBlock(loc, mv.getValue());
      mv = SGF.emitManagedRValueWithCleanup(blockCopy);
    }

    if (emitInto) {
      if (mv.isPlusOneOrTrivial(SGF))
        mv.forwardInto(SGF, loc, emitInto);
      else
        mv.copyInto(SGF, loc, emitInto);
      return ManagedValue::forInContext();
    }

    return mv;
  }

  ManagedValue visitPackExpansionType(CanPackExpansionType t,
                                      AbstractionPattern orig,
                                      Initialization *emitInto) {
    // Pack expansions in the formal parameter list are made
    // concrete as packs.
    return visitType(PackType::get(SGF.getASTContext(), {t})
                       ->getCanonicalType(),
                     orig, emitInto);
  }

  ManagedValue visitTupleType(CanTupleType t, AbstractionPattern orig,
                              Initialization *emitInto) {
    // Only destructure if the abstraction pattern is also a tuple.
    if (!orig.isTuple())
      return visitType(t, orig, emitInto);

    auto &tl = SGF.SGM.Types.getTypeLowering(t, SGF.getTypeExpansionContext());

    // If the tuple contains pack expansions, and we're not emitting
    // into an initialization already, create a temporary so that we're
    // always emitting into an initialization.
    if (t.containsPackExpansionType() && !emitInto) {
      auto temporary = SGF.emitTemporary(loc, tl);

      auto result = expandTuple(orig, t, tl, temporary.get());
      assert(result.isInContext()); (void) result;

      return temporary->getManagedAddress();
    }

    return expandTuple(orig, t, tl, emitInto);
  }

  ManagedValue expandTuple(AbstractionPattern orig, CanTupleType t,
                           const TypeLowering &tl, Initialization *init) {
    assert((!t.containsPackExpansionType() || init) &&
           "should always have an emission context when expanding "
           "a tuple containing pack expansions");

    bool canBeGuaranteed = tl.isLoadable();

    // We only use specific initializations here that can always be split.
    SmallVector<InitializationPtr, 8> eltInitsBuffer;
    MutableArrayRef<InitializationPtr> eltInits;
    if (init) {
      assert(init->canSplitIntoTupleElements());
      eltInits = init->splitIntoTupleElements(SGF, loc, t, eltInitsBuffer);
    }

    // Collect the exploded elements.
    //
    // Reabstraction can give us original types that are pack
    // expansions without having pack expansions in the result.
    // In this case, we do not need to force emission into a pack
    // expansion.
    SmallVector<ManagedValue, 4> elements;
    orig.forEachTupleElement(t, [&](TupleElementGenerator &elt) {
      auto origEltType = elt.getOrigType();
      auto substEltTypes = elt.getSubstTypes();
      if (!elt.isOrigPackExpansion()) {
        auto eltValue =
          visit(substEltTypes[0], origEltType,
                init ? eltInits[elt.getSubstIndex()].get() : nullptr);
        assert((init != nullptr) == (eltValue.isInContext()));
        if (!eltValue.isInContext())
          elements.push_back(eltValue);

        if (eltValue.hasCleanup())
          canBeGuaranteed = false;
      } else {
        assert(init);
        expandPack(origEltType, substEltTypes, elt.getSubstIndex(),
                   eltInits.slice(elt.getSubstIndex(), substEltTypes.size()),
                   elements);
      }
    });

    // If we emitted into a context, we're done.
    if (init) {
      init->finishInitialization(SGF);
      return ManagedValue::forInContext();
    }

    if (tl.isLoadable() || !SGF.silConv.useLoweredAddresses()) {
      SmallVector<SILValue, 4> elementValues;
      if (canBeGuaranteed) {
        // If all of the elements were guaranteed, we can form a guaranteed tuple.
        for (auto element : elements)
          elementValues.push_back(element.getUnmanagedValue());
      } else {
        // Otherwise, we need to move or copy values into a +1 tuple.
        for (auto element : elements) {
          SILValue value = element.hasCleanup()
            ? element.forward(SGF)
            : element.copyUnmanaged(SGF, loc).forward(SGF);
          elementValues.push_back(value);
        }
      }
      auto tupleValue = SGF.B.createTuple(loc, tl.getLoweredType(),
                                          elementValues);
      if (tupleValue->getOwnershipKind() == OwnershipKind::None)
        return ManagedValue::forObjectRValueWithoutOwnership(tupleValue);
      return canBeGuaranteed ? ManagedValue::forBorrowedObjectRValue(tupleValue)
                             : SGF.emitManagedRValueWithCleanup(tupleValue);
    } else {
      // If the type is address-only, we need to move or copy the elements into
      // a tuple in memory.
      // TODO: It would be a bit more efficient to use a preallocated buffer
      // in this case.
      auto buffer = SGF.emitTemporaryAllocation(loc, tl.getLoweredType());
      for (auto i : indices(elements)) {
        auto element = elements[i];
        auto elementBuffer = SGF.B.createTupleElementAddr(loc, buffer,
                                        i, element.getType().getAddressType());
        if (element.hasCleanup())
          element.forwardInto(SGF, loc, elementBuffer);
        else
          element.copyInto(SGF, loc, elementBuffer);
      }
      return SGF.emitManagedRValueWithCleanup(buffer);
    }
  }

  void expandPack(AbstractionPattern origExpansionType,
                  CanTupleEltTypeArrayRef substEltTypes,
                  size_t firstSubstEltIndex,
                  MutableArrayRef<InitializationPtr> eltInits,
                  SmallVectorImpl<ManagedValue> &eltMVs) {
    assert(substEltTypes.size() == eltInits.size());

    // The next parameter is a pack which corresponds to some number of
    // components in the tuple.  Some of them may be pack expansions.
    // Either copy/move them into the tuple (necessary if there are any
    // pack expansions) or collect them in eltMVs.

    // Claim the next parameter, remember whether it was +1, and forward
    // the cleanup.  We can get away with just forwarding the cleanup
    // up front, not destructuring it, because we assume that the work
    // we're doing here won't ever unwind.
    ManagedValue packAddrMV = claimNextParameter();
    CleanupCloner cloner(SGF, packAddrMV);
    SILValue packAddr = packAddrMV.forward(SGF);
    auto packTy = packAddr->getType().castTo<SILPackType>();

    auto origPatternType = origExpansionType.getPackExpansionPatternType();

    auto inducedPackType =
      CanPackType::get(SGF.getASTContext(), substEltTypes);

    for (auto packComponentIndex : indices(substEltTypes)) {
      CanType substComponentType = substEltTypes[packComponentIndex];
      Initialization *componentInit =
        eltInits.empty() ? nullptr : eltInits[packComponentIndex].get();
      auto packComponentTy = packTy->getSILElementType(packComponentIndex);

      auto substExpansionType =
        dyn_cast<PackExpansionType>(substComponentType);

      // In the scalar case, project out the element address from the
      // pack and use the normal scalar path to trigger initialization.
      if (!substExpansionType) {
        auto packIndex =
          SGF.B.createScalarPackIndex(loc, packComponentIndex, inducedPackType);
        auto eltAddr =
          SGF.B.createPackElementGet(loc, packIndex, packAddr,
                                     packComponentTy);
        auto eltAddrMV = cloner.clone(eltAddr);
        auto result = handleScalar(eltAddrMV, origPatternType,
                                   substComponentType, componentInit,
                                   /*inout*/ false,
                                   /*addressable*/ false);
        assert(result.isInContext() == (componentInit != nullptr));
        if (!result.isInContext())
          eltMVs.push_back(result);
        continue;
      }

      // In the pack-expansion case, do the exact same thing,
      // but in a pack loop.
      assert(componentInit);
      assert(componentInit->canPerformPackExpansionInitialization());

      SILType eltTy;
      CanType substEltType;
      auto openedEnv =
        SGF.createOpenedElementValueEnvironment({packComponentTy},
                                                {&eltTy},
                                                {substExpansionType},
                                                {&substEltType});

      SGF.emitDynamicPackLoop(loc, inducedPackType, packComponentIndex,
                              openedEnv, [&](SILValue indexWithinComponent,
                                             SILValue expansionPackIndex,
                                             SILValue packIndex) {
        componentInit->performPackExpansionInitialization(SGF, loc,
                                            indexWithinComponent,
                                            [&](Initialization *eltInit) {
          // Project out the pack element and enter a managed value for it.
          auto eltAddr =
            SGF.B.createPackElementGet(loc, packIndex, packAddr, eltTy);
          auto eltAddrMV = cloner.clone(eltAddr);

          auto result = handleScalar(eltAddrMV, origPatternType, substEltType,
                                     eltInit,
                                     /*inout*/ false,
                                     /*addressable*/ false);
          assert(result.isInContext()); (void) result;
        });
      });
      componentInit->finishInitialization(SGF);
    }
  }
};

/// A helper for creating SILArguments and binding variables to the argument
/// names.
class ArgumentInitHelper {
  SILGenFunction &SGF;

  LoweredParamGenerator loweredParams;
  uint16_t ArgNo = 0;

  std::optional<FunctionInputGenerator> FormalParamTypes;

  SmallPtrSet<ParamDecl *, 2> ScopedDependencies;
  SmallPtrSet<ParamDecl *, 2> AddressableParams;

public:
  ArgumentInitHelper(SILGenFunction &SGF,
                     unsigned numIgnoredTrailingParameters,
                     llvm::SmallPtrSet<ParamDecl*, 2> &&scopedDependencies)
      : SGF(SGF), loweredParams(SGF, numIgnoredTrailingParameters),
        ScopedDependencies(std::move(scopedDependencies)) {}

  /// Emit the given list of parameters.
  unsigned emitParams(std::optional<AbstractionPattern> origFnType,
                      ParameterList *paramList, ParamDecl *selfParam) {
    // If have an orig function type, initialize FormalParamTypes.
    SmallVector<AnyFunctionType::Param, 8> substFormalParams;
    if (origFnType) {
      // Start by constructing an array of subst params that we can use
      // for the generator.  This array needs to stay in scope across
      // the loop below, while we're potentially using FormalParamTypes.

      auto addParamDecl = [&](ParamDecl *pd) {
        if (pd->hasExternalPropertyWrapper())
          pd = cast<ParamDecl>(pd->getPropertyWrapperBackingProperty());
        substFormalParams.push_back(
          pd->toFunctionParam(pd->getTypeInContext()).getCanonical(nullptr));
      };
      for (auto paramDecl : *paramList) {
        addParamDecl(paramDecl);
      }
      if (selfParam) {
        addParamDecl(selfParam);
      }

      // Initialize the formal parameter generator.  Note that this can
      // immediately claim lowered parameters.
      // Some of the callers to emitBasicProlog do ask it to ignore the
      // formal self parameter, but they do not pass an origFnType down,
      // so we can ignore that possibility.
      FormalParamTypes.emplace(SGF.getASTContext(), loweredParams, *origFnType,
                               llvm::ArrayRef(substFormalParams),
                               /*ignore final*/ false);
    }

    // Go through all of our implicit SIL parameters and emit them. These do not
    // exist in the AST and always appear in between the results and the
    // explicit parameters.
    while (auto param = loweredParams.peek()) {
      if (!param->hasOption(SILParameterInfo::ImplicitLeading))
        break;
      loweredParams.configureParamDataForImplicitParam();
      loweredParams.advance();
      loweredParams.resetParamData();
    }

    // Emit each of the function's explicit parameters in order.
    if (paramList) {
      for (auto *param : *paramList)
        emitParam(param);
    }

    // The self parameter follows the formal parameters.
    if (selfParam) {
      emitParam(selfParam);
    }

    if (FormalParamTypes) FormalParamTypes->finish();
    loweredParams.finish();
    return ArgNo;
  }

private:
  ManagedValue makeArgument(SILLocation loc, ParamDecl *pd) {
    LifetimeAnnotation lifetimeAnnotation = LifetimeAnnotation::None;
    bool isNoImplicitCopy = false;
    if (pd->isSelfParameter()) {
      if (auto *afd = dyn_cast<AbstractFunctionDecl>(pd->getDeclContext())) {
        lifetimeAnnotation = afd->getLifetimeAnnotation();
        isNoImplicitCopy = afd->isNoImplicitCopy();
      }
    } else {
      lifetimeAnnotation = pd->getLifetimeAnnotation();
      isNoImplicitCopy = pd->isNoImplicitCopy();
    }

    // Configure the lowered parameter generator for this formal parameter.
    loweredParams.configureParamData(pd, isNoImplicitCopy, lifetimeAnnotation);

    ManagedValue paramValue;
    EmitBBArguments argEmitter(loc, loweredParams);
    if (FormalParamTypes && FormalParamTypes->isOrigPackExpansion()) {
      paramValue = argEmitter.handlePackComponent(*FormalParamTypes);
    } else {
      auto substType = pd->getTypeInContext()->getCanonicalType();
      assert(!FormalParamTypes ||
             FormalParamTypes->getSubstParam().getParameterType() == substType);
      auto origType = (FormalParamTypes ? FormalParamTypes->getOrigType()
                                        : AbstractionPattern(substType));

      // A parameter can be directly marked as addressable, or its
      // addressability can be implied by a scoped dependency.
      bool isAddressable = false;
      
      isAddressable = pd->isAddressable()
        || (ScopedDependencies.contains(pd)
            && SGF.getTypeProperties(origType, substType)
                  .isAddressableForDependencies());
      if (isAddressable) {
        AddressableParams.insert(pd);
      }
      paramValue = argEmitter.handleParam(origType, substType, pd,
                                          isAddressable);
    }

    // Reset the parameter data on the lowered parameter generator.
    loweredParams.resetParamData();

    // Advance the formal parameter types generator.  This must happen
    // after resetting parameter data because it can claim lowered
    // parameters.
    if (FormalParamTypes) {
      FormalParamTypes->advance();
    }

    return paramValue;
  }

  void updateArgumentValueForBinding(ManagedValue argrv, SILLocation loc,
                                     ParamDecl *pd,
                                     const SILDebugVariable &varinfo) {
    bool calledCompletedUpdate = false;
    SWIFT_DEFER {
      assert(calledCompletedUpdate && "Forgot to call completed update along "
                                      "all paths or manually turn it off");
    };
    auto completeUpdate = [&](ManagedValue value) -> void {
      SGF.B.emitDebugDescription(loc, value.getValue(), varinfo);
      SGF.VarLocs[pd] = SILGenFunction::VarLoc(value.getValue(),
                                               SILAccessEnforcement::Unknown);
      calledCompletedUpdate = true;
    };

    // If we do not need to support lexical lifetimes, just return value as the
    // updated value.
    if (!SGF.getASTContext().SILOpts.supportsLexicalLifetimes(SGF.getModule()))
      return completeUpdate(argrv);

    // Look for the following annotations on the function argument:
    // - @noImplicitCopy
    // - @_eagerMove
    // - @_noEagerMove
    bool isNoImplicitCopy = pd->isNoImplicitCopy();
    if (!argrv.getType().isMoveOnly(/*orWrapped=*/false)) {
      isNoImplicitCopy |= pd->getSpecifier() == ParamSpecifier::Borrowing;
      isNoImplicitCopy |= pd->getSpecifier() == ParamSpecifier::Consuming;
      if (pd->isSelfParameter()) {
        auto *dc = pd->getDeclContext();
        if (auto *fn = dyn_cast<FuncDecl>(dc)) {
          auto accessKind = fn->getSelfAccessKind();
          isNoImplicitCopy |= accessKind == SelfAccessKind::Borrowing;
          isNoImplicitCopy |= accessKind == SelfAccessKind::Consuming;
        }
      }
    }
    // If we're relying on ManualOwnership for explicit-copies enforcement,
    // we don't need @noImplicitCopy / MoveOnlyWrapper.
    if (SGF.B.hasManualOwnershipAttr())
      isNoImplicitCopy = false;

    // If we have a no implicit copy argument and the argument is trivial,
    // we need to use copyable to move only to convert it to its move only
    // form.
    if (!isNoImplicitCopy) {
      if (!argrv.getType().isMoveOnly()) {
        // Follow the normal path.  The value's lifetime will be enforced based
        // on its ownership.
        return completeUpdate(argrv);
      }

      // At this point, we have a noncopyable type. If it is owned, create an
      // alloc_box for it.
      if (argrv.getOwnershipKind() == OwnershipKind::Owned) {
        // TODO: Once owned values are mutable, this needs to become mutable.
        auto boxType = SGF.SGM.Types.getContextBoxTypeForCapture(
            pd,
            SGF.SGM.Types.getLoweredRValueType(TypeExpansionContext::minimal(),
                                               pd->getTypeInContext()),
            SGF.F.getGenericEnvironment(),
            /*mutable*/ false);

        auto *box = SGF.B.createAllocBox(loc, boxType, varinfo);
        SILValue destAddr = SGF.B.createProjectBox(loc, box, 0);
        SGF.B.emitStoreValueOperation(loc, argrv.forward(SGF), destAddr,
                                      StoreOwnershipQualifier::Init);
        SGF.emitManagedRValueWithCleanup(box);

        // We manually set calledCompletedUpdate to true since we want to use
        // the debug info from the box rather than insert a custom debug_value.
        calledCompletedUpdate = true;
        SGF.VarLocs[pd] = SILGenFunction::VarLoc(destAddr,
           pd->isImmutableInFunctionBody() ? SILAccessEnforcement::Unknown
                                           : SILAccessEnforcement::Dynamic,
           box);
        return;
      }

      // If we have a guaranteed noncopyable argument, we do something a little
      // different. Specifically, we emit it as normal and do a non-consume or
      // assign. The reason why we do this is that a guaranteed argument cannot
      // be used in an escaping closure. So today, we leave it with the
      // misleading consuming message. We still are able to pass it to
      // non-escaping closures though since the onstack partial_apply does not
      // consume the value.
      assert(argrv.getOwnershipKind() == OwnershipKind::Guaranteed);
      argrv = argrv.copy(SGF, loc);
      argrv = SGF.B.createMarkUnresolvedNonCopyableValueInst(
          loc, argrv,
          MarkUnresolvedNonCopyableValueInst::CheckKind::NoConsumeOrAssign);
      return completeUpdate(argrv);
    }

    if (argrv.getType().isTrivial(SGF.F)) {
      SILValue value = SGF.B.createOwnedCopyableToMoveOnlyWrapperValue(
          loc, argrv.getValue());
      argrv = SGF.emitManagedRValueWithCleanup(value);
      argrv = SGF.B.createMoveValue(loc, argrv, IsLexical);

      // If our argument was owned, we use no implicit copy. Otherwise, we
      // use no copy.
      MarkUnresolvedNonCopyableValueInst::CheckKind kind;
      switch (pd->getValueOwnership()) {
      case ValueOwnership::Default:
      case ValueOwnership::Shared:
      case ValueOwnership::InOut:
        kind = MarkUnresolvedNonCopyableValueInst::CheckKind::NoConsumeOrAssign;
        break;

      case ValueOwnership::Owned:
        kind = MarkUnresolvedNonCopyableValueInst::CheckKind::
            ConsumableAndAssignable;
        break;
      }

      argrv = SGF.B.createMarkUnresolvedNonCopyableValueInst(loc, argrv, kind);
      return completeUpdate(argrv);
    }

    if (argrv.getOwnershipKind() == OwnershipKind::Guaranteed) {
      argrv = SGF.B.createGuaranteedCopyableToMoveOnlyWrapperValue(loc, argrv);
      argrv = argrv.copy(SGF, loc);
      argrv = SGF.B.createMarkUnresolvedNonCopyableValueInst(
          loc, argrv,
          MarkUnresolvedNonCopyableValueInst::CheckKind::NoConsumeOrAssign);
      return completeUpdate(argrv);
    }

    if (argrv.getOwnershipKind() == OwnershipKind::Owned) {
      // If we have an owned value, forward it into the
      // mark_unresolved_non_copyable_value to avoid an extra destroy_value.
      argrv = SGF.B.createOwnedCopyableToMoveOnlyWrapperValue(loc, argrv);
      argrv = SGF.B.createMoveValue(loc, argrv, IsLexical);
      argrv = SGF.B.createMarkUnresolvedNonCopyableValueInst(
          loc, argrv,
          MarkUnresolvedNonCopyableValueInst::CheckKind::
              ConsumableAndAssignable);
      return completeUpdate(argrv);
    }

    return completeUpdate(argrv);
  }

  /// Create a SILArgument and store its value into the given Initialization,
  /// if not null.
  void makeArgumentIntoBinding(SILLocation loc, ParamDecl *pd) {
    ManagedValue argrv = makeArgument(loc, pd);
    if (pd->isInOut()) {
      assert(argrv.getType().isAddress() && "expected inout to be address");
    } else if (argrv.getType().is<SILPackType>()) {
      assert(argrv.getType().isAddress() && "expected pack to be address");
    } else if (!pd->isImmutableInFunctionBody()) {
      // If it's a locally mutable parameter, then we need to move the argument
      // value into a local box to hold the mutated value.
      // We don't need to mark_uninitialized since we immediately initialize.
      auto mutableBox =
          SGF.emitLocalVariableWithCleanup(pd,
                                           /*uninitialized kind*/ std::nullopt);
      argrv.ensurePlusOne(SGF, loc).forwardInto(SGF, loc, mutableBox.get());
      return;
    }
    
    // If the variable is immutable, we can bind the value as is.
    // Leave the cleanup on the argument, if any, in place to consume the
    // argument if we're responsible for it.
    SILDebugVariable varinfo(pd->isImmutableInFunctionBody(), ArgNo);
    if (!argrv.getType().isAddress()) {
      // NOTE: We setup SGF.VarLocs[pd] in updateArgumentValueForBinding.
      updateArgumentValueForBinding(argrv, loc, pd, varinfo);
      SGF.enterLocalVariableAddressableBufferScope(pd);
      return;
    }

    if (auto *allocStack = dyn_cast<AllocStackInst>(argrv.getValue())) {
      allocStack->setArgNo(ArgNo);
      allocStack->setIsFromVarDecl();
      if (SGF.getASTContext().SILOpts.supportsLexicalLifetimes(
              SGF.getModule()) &&
          SGF.F.getLifetime(pd, allocStack->getType()).isLexical()) {
        allocStack->setIsLexical();
      }
      SGF.VarLocs[pd] = SILGenFunction::VarLoc(allocStack,
        SILAccessEnforcement::Unknown);
      SGF.enterLocalVariableAddressableBufferScope(pd);
      return;
    }

    if (auto *arg = dyn_cast<SILFunctionArgument>(argrv.getValue())) {
      if (arg->isNoImplicitCopy()) {
        switch (pd->getSpecifier()) {
        case swift::ParamSpecifier::Borrowing:
          // Shouldn't have any cleanups on this.
          assert(!argrv.hasCleanup());
          argrv = ManagedValue::forBorrowedAddressRValue(
              SGF.B.createCopyableToMoveOnlyWrapperAddr(pd, argrv.getValue()));
          break;
        case swift::ParamSpecifier::ImplicitlyCopyableConsuming:
        case swift::ParamSpecifier::Consuming:
        case swift::ParamSpecifier::Default:
        case swift::ParamSpecifier::InOut:
        case swift::ParamSpecifier::LegacyOwned:
        case swift::ParamSpecifier::LegacyShared:
          break;
        }
      }
    }

    SILValue debugOperand = argrv.getValue();

    if (argrv.getType().isMoveOnly()) {
      switch (pd->getValueOwnership()) {
      case ValueOwnership::Default:
        if (pd->isSelfParameter()) {
          assert(!isa<MarkUnresolvedNonCopyableValueInst>(argrv.getValue()) &&
                 "Should not have inserted mark must check inst in EmitBBArgs");
          if (!pd->isInOut()) {
            argrv = SGF.B.createMarkUnresolvedNonCopyableValueInst(
                loc, argrv,
                MarkUnresolvedNonCopyableValueInst::CheckKind::
                    NoConsumeOrAssign);
          }
        } else {
          if (auto *fArg = dyn_cast<SILFunctionArgument>(argrv.getValue())) {
            switch (fArg->getArgumentConvention()) {
            case SILArgumentConvention::Direct_Guaranteed:
            case SILArgumentConvention::Direct_Owned:
            case SILArgumentConvention::Direct_Unowned:
            case SILArgumentConvention::Indirect_Inout:
            case SILArgumentConvention::Indirect_Out:
            case SILArgumentConvention::Indirect_InoutAliasable:
            case SILArgumentConvention::Pack_Inout:
            case SILArgumentConvention::Pack_Guaranteed:
            case SILArgumentConvention::Pack_Owned:
            case SILArgumentConvention::Pack_Out:
              llvm_unreachable("Should have been handled elsewhere");
            case SILArgumentConvention::Indirect_In:
              argrv = SGF.B.createMarkUnresolvedNonCopyableValueInst(
                  loc, argrv,
                  MarkUnresolvedNonCopyableValueInst::CheckKind::
                      ConsumableAndAssignable);
              break;
            case SILArgumentConvention::Indirect_In_CXX:
            case SILArgumentConvention::Indirect_In_Guaranteed:
              argrv = SGF.B.createMarkUnresolvedNonCopyableValueInst(
                  loc, argrv,
                  MarkUnresolvedNonCopyableValueInst::CheckKind::
                      NoConsumeOrAssign);
            }
          } else {
            assert(isa<MarkUnresolvedNonCopyableValueInst>(argrv.getValue()) &&
                   "Should have inserted mark must check inst in EmitBBArgs");
          }
        }
        break;
      case ValueOwnership::InOut: {
        assert(isa<MarkUnresolvedNonCopyableValueInst>(argrv.getValue()) &&
               "Expected mark must check inst with inout to be handled in "
               "emitBBArgs earlier");
        auto mark = cast<MarkUnresolvedNonCopyableValueInst>(argrv.getValue());
        debugOperand = mark->getOperand();
        break;
      }
      case ValueOwnership::Owned:
        argrv = SGF.B.createMarkUnresolvedNonCopyableValueInst(
            loc, argrv,
            MarkUnresolvedNonCopyableValueInst::CheckKind::
                ConsumableAndAssignable);
        break;
      case ValueOwnership::Shared:
        argrv = SGF.B.createMarkUnresolvedNonCopyableValueInst(
            loc, argrv,
            MarkUnresolvedNonCopyableValueInst::CheckKind::NoConsumeOrAssign);
        break;
      }
    }

    DebugValueInst *debugInst
      = SGF.B.emitDebugDescription(loc, debugOperand, varinfo);

    if (argrv.getValue() != debugOperand) {
      if (auto valueInst =
              dyn_cast<MarkUnresolvedNonCopyableValueInst>(argrv.getValue())) {
        // Move the debug instruction outside of any marker instruction that might
        // have been applied to the value, so that analysis doesn't move the
        // debug_value anywhere it shouldn't be.
        debugInst->moveBefore(valueInst);
      }
    }
    
    SILAccessEnforcement access;
    switch (pd->getValueOwnership()) {
    case ValueOwnership::Shared:
    case ValueOwnership::Owned:
    case ValueOwnership::Default:
      access = SILAccessEnforcement::Unknown;
      break;
    
    case ValueOwnership::InOut:
      access = SILAccessEnforcement::Static;
      break;
    }
    
    SGF.VarLocs[pd] = SILGenFunction::VarLoc(argrv.getValue(), access);
    SGF.enterLocalVariableAddressableBufferScope(pd);
  }

  void emitParam(ParamDecl *PD) {
    // Register any auxiliary declarations for the parameter to be
    // visited later.
    PD->visitAuxiliaryDecls([&](VarDecl *localVar) {
      SGF.LocalAuxiliaryDecls.push_back(localVar);
    });

    // If the parameter has an external property wrapper, then the
    // wrapper is the actual parameter.  Use that for everything
    // except the auxiliary decls collection above.
    if (PD->hasExternalPropertyWrapper()) {
      PD = cast<ParamDecl>(PD->getPropertyWrapperBackingProperty());
    }

    SILLocation loc(PD);
    loc.markAsPrologue();

    assert(PD->getTypeInContext()->isMaterializable());

    ++ArgNo;
    if (PD->hasName() || PD->isIsolated()) {
      makeArgumentIntoBinding(loc, PD);
    } else {
      emitAnonymousParam(loc, PD);
    }
  }

  void emitAnonymousParam(SILLocation loc, ParamDecl *PD) {
    // A value bound to _ is unused and can be immediately released.
    Scope discardScope(SGF.Cleanups, CleanupLocation(PD));

    // Manage the parameter.
    auto argrv = makeArgument(loc, PD);

    // Emit debug information for the argument.
    SILDebugVariable DebugVar(PD->isLet(), ArgNo);
    if (argrv.getType().isAddress())
      SGF.B.emitDebugDescription(loc, argrv.getValue(), DebugVar);
    else
      SGF.B.emitDebugDescription(loc, argrv.getValue(), DebugVar);
  }
};
} // end anonymous namespace

  
static void makeArgument(Type ty, ParamDecl *decl,
                         SmallVectorImpl<SILValue> &args, SILGenFunction &SGF) {
  assert(ty && "no type?!");
  
  if (ty->is<PackExpansionType>()) {
    ty = PackType::get(SGF.getASTContext(), {ty});
  }

  // Destructure tuple value arguments.
  if (!decl->isInOut()) {
    if (TupleType *tupleTy = ty->getAs<TupleType>()) {
      for (auto fieldType : tupleTy->getElementTypes())
        makeArgument(fieldType, decl, args, SGF);
      return;
    }
  }

  auto loweredTy = SGF.getLoweredTypeForFunctionArgument(ty);
  if (decl->isInOut())
    loweredTy = SILType::getPrimitiveAddressType(loweredTy.getASTType());
  auto arg = SGF.F.begin()->createFunctionArgument(loweredTy, decl);
  args.push_back(arg);
}

void SILGenFunction::bindParameterForForwarding(ParamDecl *param,
                                     SmallVectorImpl<SILValue> &parameters) {
  if (param->hasExternalPropertyWrapper()) {
    param = cast<ParamDecl>(param->getPropertyWrapperBackingProperty());
  }

  makeArgument(param->getTypeInContext(), param, parameters, *this);
}

void SILGenFunction::bindParametersForForwarding(const ParameterList *params,
                                     SmallVectorImpl<SILValue> &parameters) {
  for (auto param : *params)
    bindParameterForForwarding(param, parameters);
}

static void emitCaptureArguments(SILGenFunction &SGF,
                                 GenericSignature origGenericSig,
                                 CapturedValue capture,
                                 uint16_t ArgNo) {
  if (auto *expr = capture.getPackElement()) {
    SILLocation Loc(expr);
    Loc.markAsPrologue();

    auto interfaceType = expr->getType()->mapTypeOutOfContext();

    auto type = SGF.F.mapTypeIntoContext(interfaceType);
    auto &lowering = SGF.getTypeLowering(type);
    SILType ty = lowering.getLoweredType();

    SILValue arg;

    auto expansion = SGF.getTypeExpansionContext();
    auto captureKind = SGF.SGM.Types.getDeclCaptureKind(capture, expansion);
    switch (captureKind) {
    case CaptureKind::Constant:
    case CaptureKind::StorageAddress:
    case CaptureKind::Immutable: {
      auto argIndex = SGF.F.begin()->getNumArguments();
      // Non-escaping stored decls are captured as the address of the value.
      auto param = SGF.F.getConventions().getParamInfoForSILArg(argIndex);
      if (SGF.F.getConventions().isSILIndirect(param))
        ty = ty.getAddressType();

      auto *fArg = SGF.F.begin()->createFunctionArgument(ty, nullptr);
      fArg->setClosureCapture(true);

      arg = fArg;
      break;
    }

    case CaptureKind::ImmutableBox:
    case CaptureKind::Box:
      llvm_unreachable("should be impossible");
    }

    ManagedValue mv = ManagedValue::forBorrowedRValue(arg);
    auto inserted = SGF.OpaqueValues.insert(std::make_pair(expr, mv));
    assert(inserted.second);
    (void) inserted;

    return;
  }

  auto *VD = cast<VarDecl>(capture.getDecl());
  
  SILLocation Loc(VD);
  Loc.markAsPrologue();

  auto interfaceType = VD->getInterfaceType()->getReducedType(
      origGenericSig);

  // If we're capturing a parameter pack, wrap it in a tuple.
  bool isPack = false;
  if (isa<PackExpansionType>(interfaceType)) {
    assert(!VD->supportsMutation() &&
           "Cannot capture a pack as an lvalue");

    SmallVector<TupleTypeElt, 1> elts;
    elts.push_back(interfaceType);
    interfaceType = CanTupleType(TupleType::get(elts, SGF.getASTContext()));
    isPack = true;
  }

  auto type = SGF.F.mapTypeIntoContext(interfaceType);
  auto &lowering = SGF.getTypeLowering(type);
  SILType ty = lowering.getLoweredType();

  bool isNoImplicitCopy;

  if (ty.isTrivial(SGF.F) || ty.isMoveOnly() ||
      SGF.B.hasManualOwnershipAttr()) {
    isNoImplicitCopy = false;
  } else if (VD->isNoImplicitCopy()) {
    isNoImplicitCopy = true;
  } else if (auto pd = dyn_cast<ParamDecl>(VD)) {
    switch (pd->getSpecifier()) {
    case ParamSpecifier::Borrowing:
    case ParamSpecifier::Consuming:
      isNoImplicitCopy = true;
      break;
    case ParamSpecifier::ImplicitlyCopyableConsuming:
    case ParamSpecifier::Default:
    case ParamSpecifier::InOut:
    case ParamSpecifier::LegacyOwned:
    case ParamSpecifier::LegacyShared:
      isNoImplicitCopy = false;
      break;
    }
  } else {
    isNoImplicitCopy = false;
  }
    
  SILValue arg;
  SILFunctionArgument *box = nullptr;

  auto expansion = SGF.getTypeExpansionContext();
  auto captureKind = SGF.SGM.Types.getDeclCaptureKind(capture, expansion);
  SILAccessEnforcement enforcement;
  switch (captureKind) {
  case CaptureKind::Constant: {
    assert(!isPack);

    // Constant decls are captured by value.
    auto *fArg = SGF.F.begin()->createFunctionArgument(ty, VD);
    fArg->setClosureCapture(true);

    ManagedValue val = ManagedValue::forBorrowedRValue(fArg);

    // If the original variable was settable, then Sema will have treated the
    // VarDecl as an lvalue, even in the closure's use.  As such, we need to
    // allow formation of the address for this captured value.  Create a
    // temporary within the closure to provide this address.
    if (VD->isSettable(VD->getDeclContext())) {
      auto addr = SGF.emitTemporary(VD, lowering);
      // We have created a copy that needs to be destroyed.
      val = SGF.B.emitCopyValueOperation(Loc, val);
      // We use the SILValue version of this because the SILGenBuilder version
      // will create a cloned cleanup, which we do not want since our temporary
      // already has a cleanup.
      //
      // MG: Is this the right semantics for createStore? Seems like that
      // should be potentially a different API.
      SGF.B.emitStoreValueOperation(VD, val.forward(SGF), addr->getAddress(),
                                    StoreOwnershipQualifier::Init);
      addr->finishInitialization(SGF);
      val = addr->getManagedAddress();
    }
    
    if (isNoImplicitCopy && !val.getType().isMoveOnly()) {
      val = SGF.B.createGuaranteedCopyableToMoveOnlyWrapperValue(VD, val);
    }

    // If this constant is a move only type, we need to add no_consume_or_assign checking to
    // ensure that we do not consume this captured value in the function. This
    // is because closures can be invoked multiple times which is inconsistent
    // with consuming the move only type.
    if (val.getType().isMoveOnly()) {
      val = val.ensurePlusOne(SGF, Loc);
      val = SGF.B.createMarkUnresolvedNonCopyableValueInst(
          Loc, val,
          MarkUnresolvedNonCopyableValueInst::CheckKind::NoConsumeOrAssign);
    }

    arg = val.getValue();
    enforcement = SILAccessEnforcement::Unknown;
    break;
  }

  case CaptureKind::ImmutableBox:
  case CaptureKind::Box: {
    assert(!isPack);

    // LValues are captured as a retained @box that owns
    // the captured value.
    bool isMutable = captureKind == CaptureKind::Box;
    // Get the content for the box in the minimal  resilience domain because we
    // are declaring a type.
    ty = SGF.SGM.Types.getLoweredType(type, TypeExpansionContext::minimal());
    auto boxTy = SGF.SGM.Types.getContextBoxTypeForCapture(
        VD, ty.getASTType(), SGF.F.getGenericEnvironment(),
        /*mutable*/ isMutable);
    box = SGF.F.begin()->createFunctionArgument(
        SILType::getPrimitiveObjectType(boxTy), VD);
    box->setClosureCapture(true);
    arg = SGF.B.createProjectBox(VD, box, 0);
    if (isNoImplicitCopy && !arg->getType().isMoveOnly()) {
      arg = SGF.B.createCopyableToMoveOnlyWrapperAddr(VD, arg);
    }
    enforcement = isMutable
      ? SILAccessEnforcement::Dynamic
      : SILAccessEnforcement::Unknown;
    break;
  }
  case CaptureKind::StorageAddress:
    assert(!isPack);

    LLVM_FALLTHROUGH;

  case CaptureKind::Immutable: {
    auto argIndex = SGF.F.begin()->getNumArguments();
    // Non-escaping stored decls are captured as the address of the value.
    auto argConv = SGF.F.getConventions().getSILArgumentConvention(argIndex);
    bool isInOut = (argConv == SILArgumentConvention::Indirect_Inout ||
                    argConv == SILArgumentConvention::Indirect_InoutAliasable);
    auto param = SGF.F.getConventions().getParamInfoForSILArg(argIndex);
    if (SGF.F.getConventions().isSILIndirect(param)) {
      ty = ty.getAddressType();
    }
    auto *fArg = SGF.F.begin()->createFunctionArgument(ty, VD);
    fArg->setClosureCapture(true);
    arg = SILValue(fArg);
    
    if (isNoImplicitCopy && !arg->getType().isMoveOnly()) {
      switch (argConv) {
      case SILArgumentConvention::Indirect_Inout:
      case SILArgumentConvention::Indirect_InoutAliasable:
      case SILArgumentConvention::Indirect_In:
      case SILArgumentConvention::Indirect_In_Guaranteed:
      case SILArgumentConvention::Indirect_In_CXX:
      case SILArgumentConvention::Pack_Inout:
      case SILArgumentConvention::Pack_Owned:
      case SILArgumentConvention::Pack_Guaranteed:
        arg = SGF.B.createCopyableToMoveOnlyWrapperAddr(VD, arg);
        break;
        
      case SILArgumentConvention::Direct_Owned:
        arg = SGF.B.createOwnedCopyableToMoveOnlyWrapperValue(VD, arg);
        break;
      
      case SILArgumentConvention::Direct_Guaranteed:
        arg = SGF.B.createGuaranteedCopyableToMoveOnlyWrapperValue(VD, arg);
        break;
      
      case SILArgumentConvention::Direct_Unowned:
      case SILArgumentConvention::Indirect_Out:
      case SILArgumentConvention::Pack_Out:
        llvm_unreachable("should be impossible");
      }
    }

    // If we have an inout noncopyable parameter, insert a consumable and
    // assignable.
    //
    // NOTE: If we have an escaping closure, we are going to emit an error later
    // in SIL since it is illegal to capture an inout value in an escaping
    // closure. The later code knows how to handle that we have the
    // mark_unresolved_non_copyable_value here.
    if (isInOut && arg->getType().isMoveOnly()) {
      arg = SGF.B.createMarkUnresolvedNonCopyableValueInst(
          Loc, arg,
          MarkUnresolvedNonCopyableValueInst::CheckKind::
              ConsumableAndAssignable);
    }
    enforcement = isInOut
      ? SILAccessEnforcement::Static
      : SILAccessEnforcement::Unknown;
    break;
  }
  }

  // If we captured a pack as a tuple, create a pack from the elements
  // of the tuple.
  if (isPack) {
    auto tupleType = ty.castTo<TupleType>();
    assert(tupleType->getNumElements() == 1);

    auto packType =
        SILPackType::get(SGF.getASTContext(),
                         SILPackType::ExtInfo(/*indirect=*/true),
                         {tupleType.getElementType(0)});
    auto packValue = SGF.emitTemporaryPackAllocation(
        Loc, SILType::getPrimitiveObjectType(packType));

    auto formalPackType = cast<TupleType>(type->getCanonicalType())
        .getInducedPackType();
    SGF.projectTupleElementsToPack(Loc, arg, packValue, formalPackType);

    arg = packValue;
  }

  SGF.VarLocs[VD] = SILGenFunction::VarLoc(arg, enforcement, box);
  SGF.enterLocalVariableAddressableBufferScope(VD);
  SILDebugVariable DbgVar(VD->isLet(), ArgNo);
  if (auto *AllocStack = dyn_cast<AllocStackInst>(arg)) {
    AllocStack->setArgNo(ArgNo);
  } else if (box || ty.isAddress()) {
    SGF.B.emitDebugDescription(Loc, arg, DbgVar);
  } else {
    SGF.B.emitDebugDescription(Loc, arg, DbgVar);
  }
}

void SILGenFunction::emitProlog(
    DeclContext *DC, CaptureInfo captureInfo, ParameterList *paramList,
    ParamDecl *selfParam, Type resultType, std::optional<Type> errorType,
    SourceLoc throwsLoc) {
  // Emit the capture argument variables. These are placed last because they
  // become the first curry level of the SIL function.
  bool hasErasedIsolation =
    (TypeContext && TypeContext->ExpectedLoweredType->hasErasedIsolation());

  uint16_t ArgNo = emitBasicProlog(DC, paramList, selfParam, resultType,
                                   errorType, throwsLoc,
                                   /*ignored parameters*/
                                     (hasErasedIsolation ? 1 : 0) +
                                     captureInfo.getCaptures().size());

  // If we're emitting into a type context that expects erased isolation,
  // add (and ignore) the isolation parameter.
  if (hasErasedIsolation) {
    SILType ty = SILType::getOpaqueIsolationType(getASTContext());
    SILValue val = F.begin()->createFunctionArgument(ty);
    (void) val;
  }

  for (auto capture : captureInfo.getCaptures()) {
    if (capture.isDynamicSelfMetadata()) {
      auto selfMetatype = MetatypeType::get(
        captureInfo.getDynamicSelfType());
      SILType ty = getLoweredType(selfMetatype);
      SILValue val = F.begin()->createFunctionArgument(ty);
      (void) val;

      continue;
    }

    if (capture.isOpaqueValue()) {
      OpaqueValueExpr *opaqueValue = capture.getOpaqueValue();
      Type type = opaqueValue->getType()->mapTypeOutOfContext();
      type = F.mapTypeIntoContext(type);
      auto &lowering = getTypeLowering(type);
      SILType ty = lowering.getLoweredType();
      SILValue val = F.begin()->createFunctionArgument(ty);

      // Opaque values are always passed 'owned', so add a clean up if needed.
      //
      // TODO: Should this be tied to the mv?
      if (!lowering.isTrivial())
        enterDestroyCleanup(val);

      ManagedValue mv;
      if (lowering.isTrivial())
        mv = ManagedValue::forObjectRValueWithoutOwnership(val);
      else
        mv = ManagedValue::forUnmanagedOwnedValue(val);

      OpaqueValues[opaqueValue] = mv;

      continue;
    }

    emitCaptureArguments(*this, DC->getGenericSignatureOfContext(),
                         capture, ++ArgNo);
  }

  emitExpectedExecutorProlog();

  // IMPORTANT: This block should be the last one in `emitProlog`,
  // since it terminates BB and no instructions should be insterted after it.
  // Emit an unreachable instruction if a parameter type is
  // uninhabited
  if (paramList) {
    for (auto *param : *paramList) {
      if (param->getTypeInContext()->isStructurallyUninhabited()) {
        SILLocation unreachableLoc(param);
        unreachableLoc.markAsPrologue();
        B.createUnreachable(unreachableLoc);
        break;
      }
    }
  }
}

static void emitIndirectPackParameter(SILGenFunction &SGF,
                                      PackType *resultType,
                                      CanTupleEltTypeArrayRef
                                        resultTypesInContext,
                                      AbstractionPattern origExpansionType,
                                      DeclContext *DC) {
  auto &ctx = SGF.getASTContext();

  bool indirect =
    origExpansionType.arePackElementsPassedIndirectly(SGF.SGM.Types);
  SmallVector<CanType, 4> packElts;
  for (auto substEltType : resultTypesInContext) {
    auto origComponentType
      = origExpansionType.getPackExpansionComponentType(substEltType);
    CanType loweredEltTy =
      SGF.getLoweredRValueType(origComponentType, substEltType);
    packElts.push_back(loweredEltTy);
  }

  SILPackType::ExtInfo extInfo(indirect);
  auto packType = SILPackType::get(ctx, extInfo, packElts);
  auto resultSILType = SILType::getPrimitiveAddressType(packType);

  auto var = new (ctx) ParamDecl(SourceLoc(), SourceLoc(),
                                 ctx.getIdentifier("$return_value"), SourceLoc(),
                                 ctx.getIdentifier("$return_value"),
                                 DC);
  var->setSpecifier(ParamSpecifier::InOut);
  var->setInterfaceType(resultType);
  auto *arg = SGF.F.begin()->createFunctionArgument(resultSILType, var);
  (void)arg;
}

static void emitIndirectResultParameters(SILGenFunction &SGF,
                                         Type resultType,
                                         AbstractionPattern origResultType,
                                         DeclContext *DC) {
  // Borrow and mutate accessors do not return indirectly.
  if (auto *accessor = dyn_cast<AccessorDecl>(DC)) {
    if (accessor->isBorrowAccessor() || accessor->isMutateAccessor()) {
      return;
    }
  }

  CanType resultTypeInContext =
    DC->mapTypeIntoContext(resultType)->getCanonicalType();

  // Tuples in the original result type are expanded.
  if (origResultType.isTuple()) {
    origResultType.forEachTupleElement(resultTypeInContext,
                                       [&](TupleElementGenerator &elt) {
      auto origEltType = elt.getOrigType();
      auto substEltTypes = elt.getSubstTypes(resultType);

      // If the original element isn't a pack expansion, pull out the
      // corresponding substituted tuple element and recurse.
      if (!elt.isOrigPackExpansion()) {
        emitIndirectResultParameters(SGF, substEltTypes[0], origEltType, DC);
        return;
      }

      // Otherwise, bind a pack parameter.
      PackType *resultPackType = [&] {
        SmallVector<Type, 4> packElts(substEltTypes.begin(),
                                      substEltTypes.end());
        return PackType::get(SGF.getASTContext(), packElts);
      }();
      emitIndirectPackParameter(SGF, resultPackType, elt.getSubstTypes(),
                                origEltType, DC);
    });
    return;
  }

  assert(!resultType->is<PackExpansionType>());

  // If the return type is address-only, emit the indirect return argument.

  // The calling convention always uses minimal resilience expansion.
  auto resultConvType = SGF.SGM.Types.getLoweredType(
      resultTypeInContext, TypeExpansionContext::minimal());

  // And the abstraction pattern may force an indirect return even if the
  // concrete type wouldn't normally be returned indirectly.
  if (!SILModuleConventions::isReturnedIndirectlyInSIL(resultConvType,
                                                       SGF.SGM.M)) {
    if (!SILModuleConventions(SGF.SGM.M).useLoweredAddresses()
        || origResultType.getResultConvention(SGF.SGM.Types) != AbstractionPattern::Indirect)
      return;
  }

  auto &ctx = SGF.getASTContext();
  auto var = new (ctx) ParamDecl(SourceLoc(), SourceLoc(),
                                 ctx.getIdentifier("$return_value"), SourceLoc(),
                                 ctx.getIdentifier("$return_value"),
                                 DC);
  var->setSpecifier(ParamSpecifier::InOut);
  var->setInterfaceType(resultType);
  auto &resultTI =
    SGF.SGM.Types.getTypeLowering(origResultType, resultTypeInContext,
                                  SGF.getTypeExpansionContext());
  SILType resultSILType = resultTI.getLoweredType().getAddressType();
  auto *arg = SGF.F.begin()->createFunctionArgument(resultSILType, var);
  (void)arg;
}

static void emitIndirectErrorParameter(SILGenFunction &SGF,
                                       Type errorType,
                                       AbstractionPattern origErrorType,
                                       DeclContext *DC) {
  CanType errorTypeInContext =
    DC->mapTypeIntoContext(errorType)->getCanonicalType();

  // If the error type is address-only, emit the indirect error argument.

  // The calling convention always uses minimal resilience expansion.
  auto errorConvType = SGF.SGM.Types.getLoweredType(
      origErrorType, errorTypeInContext, TypeExpansionContext::minimal());

  // And the abstraction pattern may force an indirect return even if the
  // concrete type wouldn't normally be returned indirectly.
  if (!SILModuleConventions::isThrownIndirectlyInSIL(errorConvType,
                                                     SGF.SGM.M)) {
    if (!SILModuleConventions(SGF.SGM.M).useLoweredAddresses()
        || origErrorType.getErrorConvention(SGF.SGM.Types)
            != AbstractionPattern::Indirect)
      return;
  }

  auto &ctx = SGF.getASTContext();
  auto var = new (ctx) ParamDecl(SourceLoc(), SourceLoc(),
                                 ctx.getIdentifier("$error"), SourceLoc(),
                                 ctx.getIdentifier("$error"),
                                 DC);
  var->setSpecifier(ParamSpecifier::InOut);
  var->setInterfaceType(errorType);

  auto &errorTI =
    SGF.SGM.Types.getTypeLowering(origErrorType, errorTypeInContext,
                                  SGF.getTypeExpansionContext());
  SILType errorSILType = errorTI.getLoweredType().getAddressType();
  assert(SGF.IndirectErrorResult == nullptr);
  SGF.IndirectErrorResult = SGF.F.begin()->createFunctionArgument(errorSILType, var);
}

uint16_t SILGenFunction::emitBasicProlog(
    DeclContext *DC, ParameterList *paramList, ParamDecl *selfParam,
    Type resultType, std::optional<Type> errorType, SourceLoc throwsLoc,
    unsigned numIgnoredTrailingParameters) {
  // Create the indirect result parameters.
  auto genericSig = DC->getGenericSignatureOfContext();
  resultType = resultType->getReducedType(genericSig);
  if (errorType)
    errorType = (*errorType)->getReducedType(genericSig);

  std::optional<AbstractionPattern> origClosureType;
  if (TypeContext) origClosureType = TypeContext->OrigType;

  AbstractionPattern origResultType = origClosureType
    ? origClosureType->getFunctionResultType()
    : AbstractionPattern(genericSig.getCanonicalSignature(),
                         resultType->getCanonicalType());
  
  emitIndirectResultParameters(*this, resultType, origResultType, DC);

  std::optional<AbstractionPattern> origErrorType;
  if (origClosureType && !origClosureType->isTypeParameterOrOpaqueArchetype()) {
    origErrorType = origClosureType->getFunctionThrownErrorType();
    if (origErrorType && !errorType)
      errorType = origErrorType->getEffectiveThrownErrorType();
  } else if (errorType) {
    origErrorType = AbstractionPattern(genericSig.getCanonicalSignature(),
                                       (*errorType)->getCanonicalType());
  }

  if (origErrorType && errorType &&
      F.getConventions().hasIndirectSILErrorResults()) {
    emitIndirectErrorParameter(*this, *errorType, *origErrorType, DC);
  }
  
  // Parameters with scoped dependencies may lower differently. Parameters are
  // relative to the current SILGenFunction, not the passed in DeclContext. For
  // example, the an argument initializer's DeclContext is the enclosing
  // function definition rather that the initializer's generator function.
  llvm::SmallPtrSet<ParamDecl *, 2> scopedDependencyParams;
  if (auto afd = dyn_cast<AbstractFunctionDecl>(FunctionDC)) {
    if (auto deps = afd->getLifetimeDependencies()) {
      for (auto &dep : *deps) {
        auto scoped = dep.getScopeIndices();
        if (!scoped) {
          continue;
        }
        for (unsigned i = 0, e = paramList->size(); i < e; ++i) {
          if (scoped->contains(i)) {
            scopedDependencyParams.insert((*paramList)[i]);
          }
        }
        if (scoped->contains(paramList->size())) {
          scopedDependencyParams.insert(selfParam);
        }
      }
    }
  }

  // Emit the argument variables in calling convention order.
  unsigned ArgNo =
    ArgumentInitHelper(*this, numIgnoredTrailingParameters,
                       std::move(scopedDependencyParams))
      .emitParams(origClosureType, paramList, selfParam);

  // Record the ArgNo of the artificial $error inout argument. 
  if (errorType && IndirectErrorResult == nullptr) {
    CanType errorTypeInContext =
      DC->mapTypeIntoContext(*errorType)->getCanonicalType();
    auto loweredErrorTy = getLoweredType(*origErrorType, errorTypeInContext);
    ManagedValue undef = emitUndef(loweredErrorTy);
    SILDebugVariable dbgVar("$error", /*Constant*/ false, ++ArgNo);
    RegularLocation loc = RegularLocation::getAutoGeneratedLocation();
    if (throwsLoc.isValid())
      loc = throwsLoc;
    B.emitDebugDescription(loc, undef.getValue(), dbgVar);
  }

  for (auto &bb : B.getFunction())
    for (auto &i : bb) {
      auto *alloc = dyn_cast<AllocStackInst>(&i);
      if (!alloc)
        continue;
      auto varInfo = alloc->getVarInfo();
      if (!varInfo || varInfo->ArgNo)
        continue;
      // The allocation has a varinfo but no argument number, which should not
      // happen in the prolog. Unfortunately, some copies can generate wrong
      // debug info, so we have to fix it here, by invalidating it.
      alloc->invalidateVarInfo();
    }

  return ArgNo;
}
