//===--- 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 "TupleGenerators.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::emitSelfDeclForClassDeinit(VarDecl *selfDecl) {
  SILFunctionConventions conventions = F.getConventionsInContext();

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

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

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

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

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

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

  //Mark the value with  mark_unresolved_non_copyable_value so we can't escape
  // it.
  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;
  SILArgumentConvention currentConvention
    = SILArgumentConvention::Direct_Guaranteed; // will be overwritten

  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.mapTypeIntoEnvironment(SGF.getSILType(parameterInfo, fnTy));
    ManagedValue mv = SGF.B.createInputFunctionArgument(
        paramType, paramDecl, isNoImplicitCopy, lifetimeAnnotation,
        /*isClosureCapture*/ false, isFormalParameterPack, isImplicitParameter);
    currentConvention =
      cast<SILFunctionArgument>(SGF.F.begin()->getArguments().back())
        ->getArgumentConvention();
    return mv;
  }

  SILArgumentConvention getCurrentConvention() const {
    // This is only actually called after building a pack parameter.
    assert(currentConvention.isPackParameter());
    return currentConvention;
  }
  void finishCurrent(ManagedValue packAddr) {
    // Ignore: we don't care about residual cleanups on the pack.
  }

  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:
  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);
    }
    // The client handles pack expansions when there's a meaningful
    // abstraction pattern, but for now, at least, we can also end up
    // here when there's an unabstracted pack expansion type. Fortunately,
    // the lack of abstraction means we can just bind the parameter pack
    // directly as the argument value.
    if (isa<PackExpansionType>(substType)) {
      return handleScalar(origType,
                          CanPackType::get(SGF.getASTContext(), {substType}),
                          /*emitInto*/ nullptr);
    }

    return handleValue(origType, substType, /*emitInto*/ nullptr);
  }

  ManagedValue handleValue(AbstractionPattern origType, CanType substType,
                           Initialization *emitInto) {
    if (origType.isTuple()) {
      return handleExpansion(origType, substType, emitInto);
    }
    return handleScalar(origType, substType, emitInto);
  }

  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 handleScalar(AbstractionPattern orig, CanType t,
                            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);
        }
      }

      // Inverse of the @c/@_extern(c) epilog bridging: the C entry point
      // receives an ObjC metatype ('Class') but the native body expects a thick
      // metatype (e.g. AnyClass). No-op for every other parameter.
      if (auto argMetaTy = argType.getAs<AnyMetatypeType>()) {
        if (auto mvMetaTy = mv.getType().getAs<AnyMetatypeType>()) {
          if (argMetaTy->hasRepresentation() && mvMetaTy->hasRepresentation() &&
              argMetaTy->getRepresentation() == MetatypeRepresentation::Thick &&
              mvMetaTy->getRepresentation() == MetatypeRepresentation::ObjC) {
            mv = ManagedValue::forObjectRValueWithoutOwnership(
                SGF.B.createObjCToThickMetatype(loc, mv.getValue(), 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;
  }

  static bool shouldForceIntoTemporary(const TypeLowering &tl) {
    // If the type is loadable, we never need this.
    if (tl.isLoadable()) return false;


    // Otherwise, we have an address-only type.
    return false;
  }

  ManagedValue handleExpansion(AbstractionPattern origType, CanType substType,
                               Initialization *init) {
    assert(origType.isTuple());

    auto &substTL =
      SGF.SGM.Types.getTypeLowering(substType, SGF.getTypeExpansionContext());

    ExpandedTupleInputGenerator expansion(SGF.getASTContext(), parameters,
                                          origType, substType);

    // If the original tuple vanishes, the type lowering and initialization
    // are actually for its surviving element.
    bool tupleVanishes = expansion.doesOrigTupleVanish();

    // If we're not already emitting into an initialization, there are
    // several situations where we want to do so.
    bool emitIntoTemporary = [&] {
      if (init) return false;

      // We don't need to do this if the orig tuple vanishes. (It may end up
      // being a tuple in the substituted type, but we should defer the decision
      // to create a temporary; the tuple might end up being passed as a single
      // pack element.)
      if (tupleVanishes) return false;

      // All of the situations that want a temporary are exclusive with
      // having a loadable tuple.
      if (substTL.isLoadable()) return false;

      // If the substituted type contains pack expansions, we always need a
      // a temporary (even with SIL opaque values) because we currently have
      // no way of directly generating scalar tuples that contain expansions.
      if (auto tupleTy = substTL.getLoweredType().getAs<TupleType>()) {
        if (tupleTy.containsPackExpansionType())
          return true;
      }

      // If SIL opaque values are disabled, we always need a temporary to
      // reconstitute an address-only tuple.
      if (SGF.silConv.useLoweredAddresses())
        return true;

      // Otherwise, we don't need a temporary.
      return false;
    }();

    TemporaryInitializationPtr temporary;
    if (emitIntoTemporary) {
      temporary = SGF.emitTemporary(loc, substTL);
      init = temporary.get();
    }

    // If we're emitting into an initialization, and the tuple doesn't
    // vanish, split the tuple initialization. We're only ever given
    // specific kinds of initialization that can always be split.
    SmallVector<InitializationPtr, 8> eltInitsBuffer;
    MutableArrayRef<InitializationPtr> eltInits;
    if (init && !tupleVanishes) {
      assert(init->canSplitIntoTupleElements());
      eltInits = init->splitIntoTupleElements(SGF, loc, substType,
                                              eltInitsBuffer);
    }

    // Iterate over the elements in order, either emitting them into the
    // initialization or collecting scalar values from them.
    auto expectedNumElts = (init ? 0 :
                            tupleVanishes ? 1 :
                            cast<TupleType>(substType)->getNumElements());
    SmallVector<ManagedValue, 4> eltMVs;
    eltMVs.reserve(expectedNumElts);

    // Remember if we saw anything that would force us to copy in order to
    // form the tuple:
    // - if any of the scalar components have cleanups, since we can't mix
    //   owned and guaranteed values in the tuple, or
    // - if the tuple overall is address-only, since that's a representation
    //   change that the address lowering can't reliably eliminate without
    //   ownership.
    // This is only used in the path where we construct a tuple.
    bool tupleValueCanBeGuaranteed = substTL.isLoadable();

    for (; !expansion.isFinished(); expansion.advance()) {
      Initialization *componentInit = nullptr;
      if (init) {
        if (tupleVanishes) {
          componentInit = init;
        } else {
          componentInit = eltInits[expansion.getSubstElementIndex()].get();
        }
      }

      // If the original element is not from a pack expansion, just
      // recursively collect it.
      if (!expansion.isOrigPackExpansion()) {
        auto eltResult =
          handleValue(expansion.getOrigType(), expansion.getSubstType(),
                      componentInit);
        assert((init != nullptr) == (eltResult.isInContext()));

        if (!eltResult.isInContext()) {
          eltMVs.push_back(eltResult);
          if (eltResult.hasCleanup())
            tupleValueCanBeGuaranteed = false;
        }

        continue;
      }

      // Otherwise, we're starting with a pack parameter. Project out
      // the substituted component.
      auto componentMV = expansion.projectPackComponent(SGF, loc);

      // If the substituted element type is not a pack expansion,
      // we can just use the substituted component in the scalar path.
      if (!expansion.isSubstPackExpansion()) {
        auto eltResult = handleScalar(componentMV, expansion.getOrigType(),
                                      expansion.getSubstType(), componentInit,
                                      /*inout*/ false,
                                      /*addressable*/ false);
        assert((init != nullptr) == (eltResult.isInContext()));

        if (!eltResult.isInContext()) {
          eltMVs.push_back(eltResult);
          if (eltResult.hasCleanup())
            tupleValueCanBeGuaranteed = false;
        }

        continue;
      }

      // Otherwise, we need to emit a pack loop over the expansion
      // component. Since the substituted type always contains a pack
      // expansion, we should've forced the existence of an init earlier.
      assert(componentInit);
      assert(componentInit->canPerformPackExpansionInitialization());

      auto packComponentIndex = expansion.getPackComponentIndex();
      auto packComponentTy = expansion.getPackComponentType();
      auto substComponentType = expansion.getSubstType();

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

      auto inducedPackType = expansion.getFormalPackType();

      SGF.emitPackForEach(loc, componentMV, inducedPackType,
                          packComponentIndex, openedEnv, eltTy,
                          [&](SILValue indexWithinComponent,
                              SILValue expansionPackIndex,
                              ManagedValue eltAddrMV) {
        componentInit->performPackExpansionInitialization(SGF, loc,
                                            indexWithinComponent,
                                            [&](Initialization *eltInit) {
          auto eltResult = handleScalar(eltAddrMV, expansion.getOrigType(),
                                        substEltType, eltInit,
                                        /*inout*/ false,
                                        /*addressable*/ false);
          assert(eltResult.isInContext()); (void) eltResult;
        });
      });
      componentInit->finishInitialization(SGF);
    }
    expansion.finish();

    // If we emitted into an initialization, we're done.
    if (init) {
      // Make sure we finish the tuple initialization if we split it before.
      if (!tupleVanishes)
        init->finishInitialization(SGF);

      // If we created the initialization ourselves, return the managed
      // address.
      if (emitIntoTemporary)
        return temporary->getManagedAddress();

      // Otherwise, signal that we emitted into the provided intialization.
      return ManagedValue::forInContext();
    }

    // Okay, there's no initialization, so we have to return a scalar value.

    // If the tuple pattern vanishes, the loop above should have produced a
    // single "element".
    if (tupleVanishes) {
      assert(eltMVs.size() == 1);
      return eltMVs[0];
    }

    // If the tuple doesn't vanish, we need to build the scalar element
    // values into a scalar tuple values.
    SmallVector<SILValue, 4> eltValues;
    eltValues.reserve(eltMVs.size());

    // If all of the elements were guaranteed, we can form a guaranteed tuple.
    if (tupleValueCanBeGuaranteed) {
      for (auto element : eltMVs) {
        eltValues.push_back(element.getUnmanagedValue());
      }

    // Otherwise, we need to move or copy values into a +1 tuple.
    } else {
      for (auto element : eltMVs) {
        SILValue value = element.hasCleanup()
          ? element.forward(SGF)
          : element.copyUnmanaged(SGF, loc).forward(SGF);
        eltValues.push_back(value);
      }
    }
    auto tupleValue = SGF.B.createTuple(loc, substTL.getLoweredType(), eltValues);
    if (tupleValue->getOwnershipKind() == OwnershipKind::None)
      return ManagedValue::forObjectRValueWithoutOwnership(tupleValue);
    return tupleValueCanBeGuaranteed
             ? ManagedValue::forBorrowedObjectRValue(tupleValue)
             : SGF.emitManagedRValueWithCleanup(tupleValue);
  }
};

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

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 or a borrow
      // dependency.
      bool isAddressable =
          pd->isAddressable() ||
          (ScopedDependencies.contains(pd) &&
           SGF.getTypeProperties(origType, substType)
               .isAddressableForDependencies()) ||
          SGF.getFunction().getConventions().hasAddressResult() ||
          SGF.getFunction().getConventions().hasGuaranteedResult();
      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->visitAuxiliaryVars(/*forNameLookup*/ false, [&](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()->mapTypeOutOfEnvironment();

    auto type = SGF.F.mapTypeIntoEnvironment(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::Consuming:
    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.mapTypeIntoEnvironment(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(Loc, 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(Loc, val.forward(SGF), addr->getAddress(),
                                    StoreOwnershipQualifier::Init);
      addr->finishInitialization(SGF);
      val = addr->getManagedAddress();
    }
    
    if (isNoImplicitCopy && !val.getType().isMoveOnly()) {
      val = SGF.B.createGuaranteedCopyableToMoveOnlyWrapperValue(Loc, 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::Consuming: {
    assert(!isPack);

    auto *fArg = SGF.F.begin()->createFunctionArgument(ty, VD);
    fArg->setClosureCapture(true);
    ManagedValue val = SGF.emitManagedRValueWithCleanup(fArg);

    // Sema treats the captured decl as an lvalue since it's `Var`-introduced;
    // materialize an address for it, moving (not copying) the incoming
    // owned value in, since it can't be copied.
    auto addr = SGF.emitTemporary(Loc, lowering);
    SGF.B.emitStoreValueOperation(Loc, val.forward(SGF), addr->getAddress(),
                                  StoreOwnershipQualifier::Init);
    addr->finishInitialization(SGF);
    val = addr->getManagedAddress();

    val = val.ensurePlusOne(SGF, Loc);
    val = SGF.B.createMarkUnresolvedNonCopyableValueInst(
        Loc, val,
        MarkUnresolvedNonCopyableValueInst::CheckKind::ConsumableAndAssignable);

    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(Loc, box, 0);
    if (isNoImplicitCopy && !arg->getType().isMoveOnly()) {
      arg = SGF.B.createCopyableToMoveOnlyWrapperAddr(Loc, 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 fnConv = SGF.F.getConventions();
    auto paramInfo = fnConv.getParamInfoForSILArg(argIndex);
    if (fnConv.isSILIndirect(paramInfo)) {
      ty = ty.getAddressType();
    }
    auto *fArg = SGF.F.begin()->createFunctionArgument(ty, VD);
    fArg->setClosureCapture(true);
    arg = SILValue(fArg);
    
    if (isNoImplicitCopy && !arg->getType().isMoveOnly()) {
      if (fnConv.isSILIndirect(paramInfo)) {
        arg = SGF.B.createCopyableToMoveOnlyWrapperAddr(Loc, arg);

      } else if (paramInfo.isGuaranteedInCallee()) {
        arg = SGF.B.createGuaranteedCopyableToMoveOnlyWrapperValue(Loc, arg);

      } else {
        arg = SGF.B.createOwnedCopyableToMoveOnlyWrapperValue(Loc, arg);
      }
    }

    // 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.
    bool isInOut = paramInfo.isIndirectMutating();
    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()->mapTypeOutOfEnvironment();
      type = F.mapTypeIntoEnvironment(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->mapTypeIntoEnvironment(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 (!SILAddressConventions::isReturnedIndirectlyInSIL(resultConvType,
                                                       SGF.F)) {
    if (SGF.SGM.M.usesOpaqueValues()
        || 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->mapTypeIntoEnvironment(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 (!SILAddressConventions::isThrownIndirectlyInSIL(errorConvType,
                                                     SGF.F)) {
    if (SGF.SGM.M.usesOpaqueValues()
        || 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() &&
      !origClosureType->isClangType()) {
    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.
  //
  // The abstraction pattern's `errorType` can describe a throwing closure
  // while the emitted SIL function is non-throwing — this happens when a
  // non-throwing literal is stored into a `throws(E)` position and the
  // outer conversion adds the error result externally. The indirect error
  // parameter emission above already guards on the lowered SIL function's
  // conventions; do the same for the `$error` debug placeholder, which
  // must only appear in a function whose SIL type has an error result
  // (SIL verifier enforces this invariant).
  if (errorType && !(*errorType)->isNever() && IndirectErrorResult == nullptr &&
      F.getLoweredFunctionType()->hasErrorResult()) {
    CanType errorTypeInContext =
      DC->mapTypeIntoEnvironment(*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;
}
