//===--- SILGenFunction.cpp - Top-level lowering for functions ------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file defines the primary routines for creating and emitting
//  functions.
//
//===----------------------------------------------------------------------===//

#include "SILGenFunction.h"
#include "RValue.h"
#include "Scope.h"
#include "swift/Basic/Fallthrough.h"
#include "swift/AST/AST.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILUndef.h"

#include "swift/AST/DiagnosticsSIL.h"

using namespace swift;
using namespace Lowering;

//===--------------------------------------------------------------------===//
// SILGenFunction Class implementation
//===--------------------------------------------------------------------===//

SILGenFunction::SILGenFunction(SILGenModule &SGM, SILFunction &F)
  : SGM(SGM), F(F),
    B(*this, createBasicBlock()),
    CurrentSILLoc(F.getLocation()),
    Cleanups(*this)
{
  B.setCurrentDebugScope(F.getDebugScope());
}

/// SILGenFunction destructor - called after the entire function's AST has been
/// visited.  This handles "falling off the end of the function" logic.
SILGenFunction::~SILGenFunction() {
  // If the end of the function isn't terminated, we screwed up somewhere.
  assert(!B.hasValidInsertionPoint() &&
         "SILGenFunction did not terminate function?!");

  // If we didn't clean up the rethrow destination, we screwed up somewhere.
  assert(!ThrowDest.isValid() &&
         "SILGenFunction did not emit throw destination");

  freeWritebackStack();
}

//===--------------------------------------------------------------------===//
// Function emission
//===--------------------------------------------------------------------===//

// Get the __FUNCTION__ name for a declaration.
DeclName SILGenModule::getMagicFunctionName(DeclContext *dc) {
  // For closures, use the parent name.
  if (auto closure = dyn_cast<AbstractClosureExpr>(dc)) {
    return getMagicFunctionName(closure->getParent());
  }
  if (auto absFunc = dyn_cast<AbstractFunctionDecl>(dc)) {
    // If this is an accessor, use the name of the storage.
    if (auto func = dyn_cast<FuncDecl>(absFunc)) {
      if (auto storage = func->getAccessorStorageDecl())
        return storage->getFullName();
    }

    return absFunc->getFullName();
  }
  if (auto init = dyn_cast<Initializer>(dc)) {
    return getMagicFunctionName(init->getParent());
  }
  if (auto nominal = dyn_cast<NominalTypeDecl>(dc)) {
    return nominal->getName();
  }
  if (auto tl = dyn_cast<TopLevelCodeDecl>(dc)) {
    return tl->getModuleContext()->getName();
  }
  if (auto fu = dyn_cast<FileUnit>(dc)) {
    return fu->getParentModule()->getName();
  }
  if (auto m = dyn_cast<Module>(dc)) {
    return m->getName();
  }
  if (auto e = dyn_cast<ExtensionDecl>(dc)) {
    assert(e->getExtendedType()->getAnyNominal() && "extension for nonnominal");
    return e->getExtendedType()->getAnyNominal()->getName();
  }
  llvm_unreachable("unexpected __FUNCTION__ context");
}

DeclName SILGenModule::getMagicFunctionName(SILDeclRef ref) {
  switch (ref.kind) {
  case SILDeclRef::Kind::Func:
    if (auto closure = ref.getAbstractClosureExpr())
      return getMagicFunctionName(closure);
    return getMagicFunctionName(cast<FuncDecl>(ref.getDecl()));
  case SILDeclRef::Kind::Initializer:
  case SILDeclRef::Kind::Allocator:
    return getMagicFunctionName(cast<ConstructorDecl>(ref.getDecl()));
  case SILDeclRef::Kind::Deallocator:
  case SILDeclRef::Kind::Destroyer:
    return getMagicFunctionName(cast<DestructorDecl>(ref.getDecl()));
  case SILDeclRef::Kind::GlobalAccessor:
  case SILDeclRef::Kind::GlobalGetter:
    return getMagicFunctionName(cast<VarDecl>(ref.getDecl())->getDeclContext());
  case SILDeclRef::Kind::DefaultArgGenerator:
    return getMagicFunctionName(cast<AbstractFunctionDecl>(ref.getDecl()));
  case SILDeclRef::Kind::IVarInitializer:
    return getMagicFunctionName(cast<ClassDecl>(ref.getDecl()));
  case SILDeclRef::Kind::IVarDestroyer:
    return getMagicFunctionName(cast<ClassDecl>(ref.getDecl()));
  case SILDeclRef::Kind::EnumElement:
    return getMagicFunctionName(cast<EnumElementDecl>(ref.getDecl())
                                  ->getDeclContext());
  }
}

SILValue SILGenFunction::emitGlobalFunctionRef(SILLocation loc,
                                               SILDeclRef constant,
                                               SILConstantInfo constantInfo) {
  assert(constantInfo == getConstantInfo(constant));

  // Builtins must be fully applied at the point of reference.
  if (constant.hasDecl() &&
      isa<BuiltinUnit>(constant.getDecl()->getDeclContext())) {
    SGM.diagnose(loc.getSourceLoc(), diag::not_implemented,
                 "delayed application of builtin");
    return SILUndef::get(constantInfo.getSILType(), SGM.M);
  }
  
  // If the constant is a thunk we haven't emitted yet, emit it.
  if (!SGM.hasFunction(constant)) {
    if (constant.isCurried) {
      auto vd = constant.getDecl();
      // Reference the next uncurrying level of the function.
      SILDeclRef next = SILDeclRef(vd, constant.kind,
                                 SILDeclRef::ConstructAtBestResilienceExpansion,
                                 constant.uncurryLevel + 1);
      // If the function is fully uncurried and natively foreign, reference its
      // foreign entry point.
      if (!next.isCurried && vd->hasClangNode())
        next = next.asForeign();
      
      // Preserve whether the curry thunks lead to a direct reference to the
      // method implementation.
      next = next.asDirectReference(constant.isDirectReference);

      SGM.emitCurryThunk(vd, constant, next);
    }
    // Otherwise, if this is a calling convention thunk we haven't emitted yet,
    // emit it.
    else if (constant.isForeignToNativeThunk()) {
      SGM.emitForeignToNativeThunk(constant);
    } else if (constant.isNativeToForeignThunk()) {
      SGM.emitNativeToForeignThunk(constant);
    }
  }

  auto f = SGM.getFunction(constant, NotForDefinition);
  assert(f->getLoweredFunctionType() == constantInfo.SILFnType);
  return B.createFunctionRef(loc, f);
}

std::tuple<ManagedValue, SILType, ArrayRef<Substitution>>
SILGenFunction::emitSiblingMethodRef(SILLocation loc,
                                     SILValue selfValue,
                                     SILDeclRef methodConstant,
                                     ArrayRef<Substitution> subs) {
  SILValue methodValue;

  // If the method is dynamic, access it through runtime-hookable virtual
  // dispatch (viz. objc_msgSend for now).
  if (methodConstant.hasDecl()
      && methodConstant.getDecl()->getAttrs().hasAttribute<DynamicAttr>())
    methodValue = emitDynamicMethodRef(loc, methodConstant,
                                     SGM.Types.getConstantInfo(methodConstant));
  else
    methodValue = emitGlobalFunctionRef(loc, methodConstant);

  SILType methodTy = methodValue.getType();

  if (!subs.empty()) {
    // Specialize the generic method.
    methodTy = getLoweredLoadableType(
                    methodTy.castTo<SILFunctionType>()
                      ->substGenericArgs(SGM.M, SGM.SwiftModule, subs));
  }

  return std::make_tuple(ManagedValue::forUnmanaged(methodValue),
                         methodTy, subs);
}

ManagedValue SILGenFunction::emitFunctionRef(SILLocation loc,
                                             SILDeclRef constant) {
  return emitFunctionRef(loc, constant, getConstantInfo(constant));
}

ManagedValue SILGenFunction::emitFunctionRef(SILLocation loc,
                                             SILDeclRef constant,
                                             SILConstantInfo constantInfo) {
  // If the function has captures, apply them.
  if (auto fn = constant.getAnyFunctionRef()) {
    if (fn->getCaptureInfo().hasLocalCaptures() ||
        fn->getCaptureInfo().hasGenericParamCaptures()) {
      return emitClosureValue(loc, constant, *fn);
    }
  }

  // Otherwise, use a global FunctionRefInst.
  SILValue c = emitGlobalFunctionRef(loc, constant, constantInfo);
  return ManagedValue::forUnmanaged(c);
}

void SILGenFunction::emitCaptures(SILLocation loc,
                                  AnyFunctionRef TheClosure,
                                  SmallVectorImpl<ManagedValue> &capturedArgs) {
  auto captureInfo = SGM.Types.getLoweredLocalCaptures(TheClosure);
  for (auto capture : captureInfo.getCaptures()) {
    auto *vd = capture.getDecl();

    switch (SGM.Types.getDeclCaptureKind(capture)) {
    case CaptureKind::None:
      break;

    case CaptureKind::Constant: {
      // let declarations.
      auto Entry = VarLocs[vd];

      // Non-address-only constants are passed at +1.
      auto &tl = getTypeLowering(vd->getType()->getReferenceStorageReferent());
      SILValue Val = Entry.value;

      if (!Val.getType().isAddress()) {
        // Just retain a by-val let.
        B.emitRetainValueOperation(loc, Val);
      } else {
        // If we have a mutable binding for a 'let', such as 'self' in an
        // 'init' method, load it.
        Val = emitLoad(loc, Val, tl, SGFContext(), IsNotTake).forward(*this);
      }

      // Use an RValue to explode Val if it is a tuple.
      RValue RV(*this, loc, vd->getType()->getCanonicalType(),
                ManagedValue::forUnmanaged(Val));

      // If we're capturing an unowned pointer by value, we will have just
      // loaded it into a normal retained class pointer, but we capture it as
      // an unowned pointer.  Convert back now.
      if (vd->getType()->is<ReferenceStorageType>()) {
        auto type = getTypeLowering(vd->getType()).getLoweredType();
        auto val = std::move(RV).forwardAsSingleStorageValue(*this, type,loc);
        capturedArgs.push_back(emitManagedRValueWithCleanup(val));
      } else {
        std::move(RV).getAll(capturedArgs);
      }
      break;
    }

    case CaptureKind::StorageAddress: {
      // No-escaping stored declarations are captured as the
      // address of the value.
      assert(VarLocs.count(vd) && "no location for captured var!");
      VarLoc vl = VarLocs[vd];
      assert(vl.value.getType().isAddress() && "no address for captured var!");
      capturedArgs.push_back(ManagedValue::forLValue(vl.value));
      break;
    }

    case CaptureKind::Box: {
      // LValues are captured as both the box owning the value and the
      // address of the value.
      assert(VarLocs.count(vd) && "no location for captured var!");
      VarLoc vl = VarLocs[vd];
      assert(vl.value.getType().isAddress() && "no address for captured var!");

      // If this is a boxed variable, we can use it directly.
      if (vl.box) {
        B.createStrongRetain(loc, vl.box);
        capturedArgs.push_back(emitManagedRValueWithCleanup(vl.box));
        capturedArgs.push_back(ManagedValue::forLValue(vl.value));
      } else {
        // Address only 'let' values are passed by box.  This isn't great, in
        // that a variable captured by multiple closures will be boxed for each
        // one.  This could be improved by doing an "isCaptured" analysis when
        // emitting address-only let constants, and emit them into a alloc_box
        // like a variable instead of into an alloc_stack.
        AllocBoxInst *allocBox =
          B.createAllocBox(loc, vl.value.getType().getObjectType());
        auto boxAddress = SILValue(allocBox, 1);
        B.createCopyAddr(loc, vl.value, boxAddress, IsNotTake,IsInitialization);
        capturedArgs.push_back(emitManagedRValueWithCleanup(SILValue(allocBox, 0)));
        capturedArgs.push_back(ManagedValue::forLValue(boxAddress));
      }

      break;
    }
    }
  }
}

ManagedValue
SILGenFunction::emitClosureValue(SILLocation loc, SILDeclRef constant,
                                 AnyFunctionRef TheClosure) {
  assert(((constant.uncurryLevel == 1 &&
           TheClosure.getCaptureInfo().hasLocalCaptures()) ||
          (constant.uncurryLevel == 0 &&
           !TheClosure.getCaptureInfo().hasLocalCaptures())) &&
         "curried local functions not yet supported");

  auto constantInfo = getConstantInfo(constant);
  SILValue functionRef = emitGlobalFunctionRef(loc, constant, constantInfo);
  SILType functionTy = functionRef.getType();

  auto expectedType =
    cast<FunctionType>(TheClosure.getType()->getCanonicalType());

  // Forward substitutions from the outer scope.

  auto pft = constantInfo.SILFnType;

  auto forwardSubs = constantInfo.getForwardingSubstitutions(getASTContext());

  bool wasSpecialized = false;
  if (pft->isPolymorphic() && !forwardSubs.empty()) {
    auto specialized = pft->substGenericArgs(F.getModule(),
                                             F.getModule().getSwiftModule(),
                                             forwardSubs);
    functionTy = SILType::getPrimitiveObjectType(specialized);
    wasSpecialized = true;
  }

  if (!TheClosure.getCaptureInfo().hasLocalCaptures() && !wasSpecialized) {
    auto result = ManagedValue::forUnmanaged(functionRef);
    return emitOrigToSubstValue(loc, result,
                                AbstractionPattern(expectedType),
                                expectedType);
  }

  SmallVector<ManagedValue, 4> capturedArgs;
  emitCaptures(loc, TheClosure, capturedArgs);

  // Currently all capture arguments are captured at +1.
  // TODO: Ideally this would be +0.
  SmallVector<SILValue, 4> forwardedArgs;
  for (auto capture : capturedArgs)
    forwardedArgs.push_back(capture.forward(*this));

  SILType closureTy =
    SILGenBuilder::getPartialApplyResultType(functionRef.getType(),
                                             capturedArgs.size(), SGM.M,
                                             forwardSubs);
  auto toClosure =
    B.createPartialApply(loc, functionRef, functionTy,
                         forwardSubs, forwardedArgs, closureTy);
  auto result = emitManagedRValueWithCleanup(toClosure);

  return emitOrigToSubstValue(loc, result,
                              AbstractionPattern(expectedType),
                              expectedType);
}

void SILGenFunction::emitFunction(FuncDecl *fd) {
  MagicFunctionName = SILGenModule::getMagicFunctionName(fd);

  Type resultTy = fd->getResultType();
  emitProlog(fd, fd->getBodyParamPatterns(), resultTy);
  prepareEpilog(resultTy, fd->isBodyThrowing(), CleanupLocation(fd));

  emitProfilerIncrement(fd->getBody());
  emitStmt(fd->getBody());

  emitEpilog(fd);
}

void SILGenFunction::emitClosure(AbstractClosureExpr *ace) {
  MagicFunctionName = SILGenModule::getMagicFunctionName(ace);

  emitProlog(ace, ace->getParams(), ace->getResultType());
  prepareEpilog(ace->getResultType(), ace->isBodyThrowing(),
                CleanupLocation(ace));
  if (auto *ce = dyn_cast<ClosureExpr>(ace)) {
    emitProfilerIncrement(ce);
    emitStmt(ce->getBody());
  } else {
    auto *autoclosure = cast<AutoClosureExpr>(ace);
    // Closure expressions implicitly return the result of their body
    // expression.
    emitProfilerIncrement(autoclosure);
    emitReturnExpr(ImplicitReturnLocation(ace),
                   autoclosure->getSingleExpressionBody());
  }
  emitEpilog(ace);
}

void SILGenFunction::emitArtificialTopLevel(ClassDecl *mainClass) {
  // Load argc and argv from the entry point arguments.
  SILValue argc = F.begin()->getBBArg(0);
  SILValue argv = F.begin()->getBBArg(1);

  switch (mainClass->getArtificialMainKind()) {
  case ArtificialMainKind::UIApplicationMain: {
    // Emit a UIKit main.
    // return UIApplicationMain(C_ARGC, C_ARGV, nil, ClassName);

    CanType NSStringTy = SGM.Types.getNSStringType();
    CanType OptNSStringTy
      = OptionalType::get(NSStringTy)->getCanonicalType();
    CanType IUOptNSStringTy
      = ImplicitlyUnwrappedOptionalType::get(NSStringTy)->getCanonicalType();

    // Get the class name as a string using NSStringFromClass.
    CanType mainClassTy = mainClass->getDeclaredTypeInContext()->getCanonicalType();
    CanType mainClassMetaty = CanMetatypeType::get(mainClassTy,
                                                   MetatypeRepresentation::ObjC);
    ProtocolDecl *anyObjectProtocol =
      getASTContext().getProtocol(KnownProtocolKind::AnyObject);
    auto mainClassAnyObjectConformance =
      SGM.M.getSwiftModule()->lookupConformance(mainClassTy, anyObjectProtocol,
                                                nullptr)
        .getPointer();
    CanType anyObjectTy = anyObjectProtocol
      ->getDeclaredTypeInContext()
      ->getCanonicalType();
    CanType anyObjectMetaTy = CanExistentialMetatypeType::get(anyObjectTy,
                                                  MetatypeRepresentation::ObjC);

    auto NSStringFromClassType = SILFunctionType::get(nullptr,
                  SILFunctionType::ExtInfo()
                    .withRepresentation(SILFunctionType::Representation::
                                        CFunctionPointer),
                  ParameterConvention::Direct_Unowned,
                  SILParameterInfo(anyObjectMetaTy,
                                   ParameterConvention::Direct_Unowned),
                  SILResultInfo(OptNSStringTy,
                                ResultConvention::Autoreleased),
                  /*error result*/ None,
                  getASTContext());
    auto NSStringFromClassFn
      = SGM.M.getOrCreateFunction(mainClass, "NSStringFromClass",
                                  SILLinkage::PublicExternal,
                                  NSStringFromClassType,
                                  IsBare, IsTransparent, IsNotFragile);
    auto NSStringFromClass = B.createFunctionRef(mainClass, NSStringFromClassFn);
    SILValue metaTy = B.createMetatype(mainClass,
                             SILType::getPrimitiveObjectType(mainClassMetaty));
    metaTy = B.createInitExistentialMetatype(mainClass, metaTy,
                          SILType::getPrimitiveObjectType(anyObjectMetaTy),
                          getASTContext().AllocateCopy(
                            llvm::makeArrayRef(mainClassAnyObjectConformance)));
    SILValue optName = B.createApply(mainClass,
                               NSStringFromClass,
                               NSStringFromClass->getType(),
                               SILType::getPrimitiveObjectType(OptNSStringTy),
                               {}, metaTy);
    SILValue iuoptName = B.createUncheckedBitCast(mainClass, optName,
                              SILType::getPrimitiveObjectType(IUOptNSStringTy));

    // Call UIApplicationMain.
    SILParameterInfo argTypes[] = {
      SILParameterInfo(argc.getType().getSwiftRValueType(),
                       ParameterConvention::Direct_Unowned),
      SILParameterInfo(argv.getType().getSwiftRValueType(),
                       ParameterConvention::Direct_Unowned),
      SILParameterInfo(IUOptNSStringTy, ParameterConvention::Direct_Unowned),
      SILParameterInfo(IUOptNSStringTy, ParameterConvention::Direct_Unowned),
    };
    auto UIApplicationMainType = SILFunctionType::get(nullptr,
                  SILFunctionType::ExtInfo()
                    .withRepresentation(SILFunctionType::Representation::
                                        CFunctionPointer),
                  ParameterConvention::Direct_Unowned,
                  argTypes,
                  SILResultInfo(argc.getType().getSwiftRValueType(),
                                ResultConvention::Unowned),
                  /*error result*/ None,
                  getASTContext());

    auto UIApplicationMainFn
      = SGM.M.getOrCreateFunction(mainClass, "UIApplicationMain",
                                  SILLinkage::PublicExternal,
                                  UIApplicationMainType,
                                  IsBare, IsTransparent, IsNotFragile);

    auto UIApplicationMain = B.createFunctionRef(mainClass, UIApplicationMainFn);
    auto nil = B.createEnum(mainClass, SILValue(),
                      getASTContext().getImplicitlyUnwrappedOptionalNoneDecl(),
                      SILType::getPrimitiveObjectType(IUOptNSStringTy));

    SILValue args[] = { argc, argv, nil, iuoptName };

    B.createApply(mainClass, UIApplicationMain,
                  UIApplicationMain->getType(),
                  argc.getType(), {}, args);
    SILValue r = B.createIntegerLiteral(mainClass,
                        SILType::getBuiltinIntegerType(32, getASTContext()), 0);
    if (r.getType() != F.getLoweredFunctionType()->getResult().getSILType())
      r = B.createStruct(mainClass,
                       F.getLoweredFunctionType()->getResult().getSILType(), r);

    B.createReturn(mainClass, r);
    return;
  }

  case ArtificialMainKind::NSApplicationMain: {
    // Emit an AppKit main.
    // return NSApplicationMain(C_ARGC, C_ARGV);

    SILParameterInfo argTypes[] = {
      SILParameterInfo(argc.getType().getSwiftRValueType(),
                       ParameterConvention::Direct_Unowned),
      SILParameterInfo(argv.getType().getSwiftRValueType(),
                       ParameterConvention::Direct_Unowned),
    };
    auto NSApplicationMainType = SILFunctionType::get(nullptr,
                  SILFunctionType::ExtInfo()
                    // Should be C calling convention, but NSApplicationMain
                    // has an overlay to fix the type of argv.
                    .withRepresentation(SILFunctionType::Representation::Thin),
                  ParameterConvention::Direct_Unowned,
                  argTypes,
                  SILResultInfo(argc.getType().getSwiftRValueType(),
                                ResultConvention::Unowned),
                  /*error result*/ None,
                  getASTContext());

    auto NSApplicationMainFn
      = SGM.M.getOrCreateFunction(mainClass, "NSApplicationMain",
                                  SILLinkage::PublicExternal,
                                  NSApplicationMainType,
                                  IsBare, IsTransparent, IsNotFragile);

    auto NSApplicationMain = B.createFunctionRef(mainClass, NSApplicationMainFn);
    SILValue args[] = { argc, argv };

    B.createApply(mainClass, NSApplicationMain,
                  NSApplicationMain->getType(),
                  argc.getType(), {}, args);
    SILValue r = B.createIntegerLiteral(mainClass,
                        SILType::getBuiltinIntegerType(32, getASTContext()), 0);
    if (r.getType() != F.getLoweredFunctionType()->getResult().getSILType())
      r = B.createStruct(mainClass,
                       F.getLoweredFunctionType()->getResult().getSILType(), r);
    B.createReturn(mainClass, r);
    return;
  }
  }
}

static void forwardCaptureArgs(SILGenFunction &gen,
                               SmallVectorImpl<SILValue> &args,
                               CapturedValue capture) {
  auto addSILArgument = [&](SILType t, ValueDecl *d) {
    args.push_back(new (gen.SGM.M) SILArgument(gen.F.begin(), t, d));
  };

  auto *vd = capture.getDecl();

  switch (gen.SGM.Types.getDeclCaptureKind(capture)) {
  case CaptureKind::None:
    break;

  case CaptureKind::Constant:
    addSILArgument(gen.getLoweredType(vd->getType()), vd);
    break;

  case CaptureKind::Box: {
    SILType ty = gen.getLoweredType(vd->getType()->getRValueType())
      .getAddressType();
    // Forward the captured owning box.
    SILType boxTy = SILType::getPrimitiveObjectType(
        SILBoxType::get(ty.getSwiftRValueType()));
    addSILArgument(boxTy, vd);
    // Forward the captured value address.
    addSILArgument(ty, vd);
    break;
  }

  case CaptureKind::StorageAddress: {
    SILType ty = gen.getLoweredType(vd->getType()->getRValueType())
      .getAddressType();
    // Forward the captured value address.
    addSILArgument(ty, vd);
    break;
  }
  }
}

static SILValue getNextUncurryLevelRef(SILGenFunction &gen,
                                       SILLocation loc,
                                       SILDeclRef next,
                                       bool direct,
                                       ArrayRef<SILValue> curriedArgs,
                                       ArrayRef<Substitution> curriedSubs) {
  // For a foreign function, reference the native thunk.
  if (next.isForeign)
    return gen.emitGlobalFunctionRef(loc, next.asForeign(false));

  // If the fully-uncurried reference is to a native dynamic class method, emit
  // the dynamic dispatch.
  auto fullyAppliedMethod = !next.isCurried && !next.isForeign && !direct &&
    next.hasDecl();

  auto constantInfo = gen.SGM.Types.getConstantInfo(next);
  SILValue thisArg;
  if (!curriedArgs.empty())
      thisArg = curriedArgs.back();

  if (fullyAppliedMethod &&
      isa<AbstractFunctionDecl>(next.getDecl()) &&
      gen.getMethodDispatch(cast<AbstractFunctionDecl>(next.getDecl()))
        == MethodDispatch::Class) {
    SILValue thisArg = curriedArgs.back();

    // Use the dynamic thunk if dynamic.
    if (next.getDecl()->isDynamic()) {
      auto dynamicThunk = gen.SGM.getDynamicThunk(next, constantInfo);
      return gen.B.createFunctionRef(loc, dynamicThunk);
    }

    return gen.B.createClassMethod(loc, thisArg, next);
  }

  // If the fully-uncurried reference is to a generic method, look up the
  // witness.
  if (fullyAppliedMethod &&
      constantInfo.SILFnType->getRepresentation()
        == SILFunctionTypeRepresentation::WitnessMethod) {
    auto thisType = curriedSubs[0].getReplacement()->getCanonicalType();
    assert(isa<ArchetypeType>(thisType) && "no archetype for witness?!");
    SILValue OpenedExistential;
    if (!cast<ArchetypeType>(thisType)->getOpenedExistentialType().isNull())
      OpenedExistential = thisArg;
    return gen.B.createWitnessMethod(loc, thisType, nullptr, next,
                                     constantInfo.getSILType(),
                                     OpenedExistential);
  }

  // Otherwise, emit a direct call.
  return gen.emitGlobalFunctionRef(loc, next);
}

void SILGenFunction::emitCurryThunk(ValueDecl *vd,
                                    SILDeclRef from, SILDeclRef to) {
  SmallVector<SILValue, 8> curriedArgs;

  unsigned paramCount = from.uncurryLevel + 1;

  if (isa<ConstructorDecl>(vd) || isa<EnumElementDecl>(vd)) {
    // The first body parameter pattern for a constructor specifies the
    // "self" instance, but the constructor is invoked from outside on a
    // metatype.
    assert(from.uncurryLevel == 0 && to.uncurryLevel == 1
           && "currying constructor at level other than one?!");
    F.setBare(IsBare);
    auto selfMetaTy = vd->getType()->getAs<AnyFunctionType>()->getInput();
    auto metatypeVal = new (F.getModule()) SILArgument(F.begin(),
                                            getLoweredLoadableType(selfMetaTy));
    curriedArgs.push_back(metatypeVal);

  } else if (auto fd = dyn_cast<AbstractFunctionDecl>(vd)) {
    // Forward implicit closure context arguments.
    bool hasCaptures = fd->getCaptureInfo().hasLocalCaptures();
    if (hasCaptures)
      --paramCount;

    // Forward the curried formal arguments.
    auto forwardedPatterns = fd->getBodyParamPatterns().slice(0, paramCount);
    for (auto *paramPattern : reversed(forwardedPatterns))
      bindParametersForForwarding(paramPattern, curriedArgs);

    // Forward captures.
    if (hasCaptures) {
      auto captureInfo = SGM.Types.getLoweredLocalCaptures(fd);
      for (auto capture : captureInfo.getCaptures())
        forwardCaptureArgs(*this, curriedArgs, capture);
    }
  } else {
    llvm_unreachable("don't know how to curry this decl");
  }

  // Forward substitutions.
  ArrayRef<Substitution> subs;
  if (auto gp = getConstantInfo(to).ContextGenericParams) {
    subs = gp->getForwardingSubstitutions(getASTContext());
  }

  SILValue toFn = getNextUncurryLevelRef(*this, vd, to, from.isDirectReference,
                                         curriedArgs, subs);
  SILType resultTy
    = SGM.getConstantType(from).castTo<SILFunctionType>()
         ->getResult().getSILType();
  resultTy = F.mapTypeIntoContext(resultTy);
  auto toTy = toFn.getType();

  // Forward archetypes and specialize if the function is generic.
  if (!subs.empty()) {
    auto toFnTy = toFn.getType().castTo<SILFunctionType>();
    toTy = getLoweredLoadableType(
              toFnTy->substGenericArgs(SGM.M, SGM.SwiftModule, subs));
  }

  // Partially apply the next uncurry level and return the result closure.
  auto closureTy =
    SILGenBuilder::getPartialApplyResultType(toFn.getType(), curriedArgs.size(),
                                             SGM.M, subs);
  SILInstruction *toClosure =
    B.createPartialApply(vd, toFn, toTy, subs, curriedArgs, closureTy);
  if (resultTy != closureTy)
    toClosure = B.createConvertFunction(vd, toClosure, resultTy);
  B.createReturn(ImplicitReturnLocation::getImplicitReturnLoc(vd), toClosure);
}

void SILGenFunction::emitGeneratorFunction(SILDeclRef function, Expr *value) {
  MagicFunctionName = SILGenModule::getMagicFunctionName(function);

  RegularLocation Loc(value);
  Loc.markAutoGenerated();

  // Override location for __FILE__ __LINE__ etc. to an invalid one so that we
  // don't put extra strings into the defaut argument generator function that
  // is not going to be ever used anyway.
  overrideLocationForMagicIdentifiers = SourceLoc();

  emitProlog({ }, value->getType(), function.getDecl()->getDeclContext());
  prepareEpilog(value->getType(), false, CleanupLocation::get(Loc));
  emitReturnExpr(Loc, value);
  emitEpilog(Loc);
}

SILGenBuilder::SILGenBuilder(SILGenFunction &gen)
  : SILBuilder(gen.F), SGM(gen.SGM) {}
SILGenBuilder::SILGenBuilder(SILGenFunction &gen, SILBasicBlock *insertBB)
  : SILBuilder(insertBB), SGM(gen.SGM) {}
SILGenBuilder::SILGenBuilder(SILGenFunction &gen, SILBasicBlock *insertBB,
                             SmallVectorImpl<SILInstruction *> *insertedInsts)
  : SILBuilder(insertBB, insertedInsts), SGM(gen.SGM) {}
SILGenBuilder::SILGenBuilder(SILGenFunction &gen, SILBasicBlock *insertBB,
                             SILInstruction *insertInst)
    : SILBuilder(insertBB, insertInst->getIterator()), SGM(gen.SGM) {}

MetatypeInst *SILGenBuilder::createMetatype(SILLocation loc, SILType metatype) {
  auto theMetatype = metatype.castTo<MetatypeType>();
  // Getting a nontrivial metatype requires forcing any conformances necessary
  // to instantiate the type.
  switch (theMetatype->getRepresentation()) {
  case MetatypeRepresentation::Thin:
    break;
  case MetatypeRepresentation::Thick:
  case MetatypeRepresentation::ObjC: {
    // Walk the type recursively to look for substitutions we may need.
    SmallVector<Substitution, 2> subsBuf;
    theMetatype.getInstanceType().findIf([&](Type t) -> bool {
      if (!t->getAnyNominal())
        return false;

      auto subs = t->gatherAllSubstitutions(SGM.SwiftModule, subsBuf, nullptr);
      SGM.useConformancesFromSubstitutions(subs);
      return false;
    });

    break;
  }
  }

  return SILBuilder::createMetatype(loc, metatype);
}

ApplyInst *SILGenBuilder::createApply(SILLocation Loc, SILValue Fn,
                                      SILType SubstFnTy,
                                      SILType Result,
                                      ArrayRef<Substitution> Subs,
                                      ArrayRef<SILValue> Args) {
  SGM.useConformancesFromSubstitutions(Subs);
  return SILBuilder::createApply(Loc, Fn, SubstFnTy, Result, Subs, Args, false);
}

TryApplyInst *SILGenBuilder::createTryApply(SILLocation loc, SILValue Fn,
                                            SILType substFnTy,
                                            ArrayRef<Substitution> subs,
                                            ArrayRef<SILValue> args,
                                            SILBasicBlock *normalBB,
                                            SILBasicBlock *errorBB) {
  SGM.useConformancesFromSubstitutions(subs);
  return SILBuilder::createTryApply(loc, Fn, substFnTy, subs, args, normalBB,
                                    errorBB);
}

PartialApplyInst *SILGenBuilder::createPartialApply(SILLocation Loc,
                                                    SILValue Fn,
                                                    SILType SubstFnTy,
                                                    ArrayRef<Substitution> Subs,
                                                    ArrayRef<SILValue> Args,
                                                    SILType ClosureTy) {
  SGM.useConformancesFromSubstitutions(Subs);
  return SILBuilder::createPartialApply(Loc, Fn, SubstFnTy, Subs, Args,
                                        ClosureTy);
}

BuiltinInst *SILGenBuilder::createBuiltin(SILLocation Loc, Identifier Name,
                                          SILType ResultTy,
                                          ArrayRef<Substitution> Subs,
                                          ArrayRef<SILValue> Args) {
  SGM.useConformancesFromSubstitutions(Subs);
  return SILBuilder::createBuiltin(Loc, Name, ResultTy, Subs, Args);
}

InitExistentialAddrInst *
SILGenBuilder::createInitExistentialAddr(SILLocation Loc,
                                   SILValue Existential,
                                   CanType FormalConcreteType,
                                   SILType LoweredConcreteType,
                                   ArrayRef<ProtocolConformance*> Conformances){
  for (auto *conformance : Conformances)
    SGM.useConformance(conformance);

  return SILBuilder::createInitExistentialAddr(Loc, Existential,
                                               FormalConcreteType,
                                               LoweredConcreteType,
                                               Conformances);
}

InitExistentialMetatypeInst *
SILGenBuilder::createInitExistentialMetatype(SILLocation loc,
                                             SILValue metatype,
                                             SILType existentialType,
                                   ArrayRef<ProtocolConformance*> conformances){
  for (auto *conformance : conformances)
    SGM.useConformance(conformance);

  return SILBuilder::createInitExistentialMetatype(loc, metatype,
                                                   existentialType,
                                                   conformances);
}

InitExistentialRefInst *
SILGenBuilder::createInitExistentialRef(SILLocation Loc,
                                        SILType ExistentialType,
                                        CanType FormalConcreteType,
                                        SILValue Concrete,
                                  ArrayRef<ProtocolConformance*> Conformances) {
  for (auto *conformance : Conformances)
    SGM.useConformance(conformance);

  return SILBuilder::createInitExistentialRef(Loc, ExistentialType,
                                              FormalConcreteType, Concrete,
                                              Conformances);
}

AllocExistentialBoxInst *
SILGenBuilder::createAllocExistentialBox(SILLocation Loc,
                                         SILType ExistentialType,
                                         CanType ConcreteType,
                                         SILType ConcreteLoweredType,
                                 ArrayRef<ProtocolConformance *> Conformances) {
  for (auto *conformance : Conformances)
    SGM.useConformance(conformance);

  return SILBuilder::createAllocExistentialBox(Loc, ExistentialType,
                                               ConcreteType,
                                               ConcreteLoweredType,
                                               Conformances);
}
