//===--- CSApply.cpp - Constraint Application -----------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements application of a solution to a constraint
// system to a particular expression, resulting in a
// fully-type-checked expression.
//
//===----------------------------------------------------------------------===//

#include "CSDiagnostics.h"
#include "CodeSynthesis.h"
#include "MiscDiagnostics.h"
#include "OpenedExistentials.h"
#include "TypeCheckConcurrency.h"
#include "TypeCheckEmbedded.h"
#include "TypeCheckMacros.h"
#include "TypeCheckProtocol.h"
#include "TypeCheckType.h"
#include "swift/AST/ASTVisitor.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/ClangModuleLoader.h"
#include "swift/AST/ConcreteDeclRef.h"
#include "swift/AST/ConformanceLookup.h"
#include "swift/AST/Effects.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/GenericSignature.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/OperatorNameLookup.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/SubstitutionMap.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/Defer.h"
#include "swift/Basic/StringExtras.h"
#include "swift/Sema/ConstraintLocator.h"
#include "swift/Sema/ConstraintSystem.h"
#include "swift/Sema/SolutionResult.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Mangle.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/Template.h"
#include "clang/Sema/TemplateDeduction.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/SaveAndRestore.h"

using namespace swift;
using namespace constraints;

static bool isClosureLiteralExpr(Expr *expr) {
  expr = expr->getSemanticsProvidingExpr();
  return (isa<CaptureListExpr>(expr) || isa<ClosureExpr>(expr));
}

bool Solution::hasFixedType(TypeVariableType *typeVar) const {
  auto knownBinding = typeBindings.find(typeVar);
  return knownBinding != typeBindings.end();
}

/// Retrieve the fixed type for the given type variable.
Type Solution::getFixedType(TypeVariableType *typeVar) const {
  auto knownBinding = typeBindings.find(typeVar);
  assert(knownBinding != typeBindings.end());
  return knownBinding->second;
}

/// Determine whether the given type is an opened AnyObject.
///
/// This comes up in computeSubstitutions() when accessing
/// members via dynamic lookup.
static bool isOpenedAnyObject(Type type) {
  auto archetype = type->getAs<ExistentialArchetypeType>();
  if (!archetype || !archetype->isRoot())
    return false;

  return archetype->getExistentialType()->isAnyObject();
}

SubstitutionMap
Solution::computeSubstitutions(NullablePtr<ValueDecl> decl,
                               GenericSignature sig,
                               ConstraintLocator *locator) const {
  if (sig.isNull())
    return SubstitutionMap();

  // Gather the substitutions from dependent types to concrete types.
  auto openedTypes = OpenedTypes.find(locator);

  // If we have a member reference on an existential, there are no
  // opened types or substitutions.
  if (openedTypes == OpenedTypes.end())
    return SubstitutionMap();

  auto &ctx = getConstraintSystem().getASTContext();

  SmallVector<Type, 4> replacementTypes;
  for (const auto &opened : openedTypes->second) {
    auto type = getFixedType(opened.second);
    if (opened.first->isParameterPack()) {
      if (type->is<PlaceholderType>()) {
        type = PackType::get(
            ctx,
            {PackExpansionType::get(ErrorType::get(ctx), ErrorType::get(ctx))});
      } else if (!type->is<PackType>())
        type = PackType::getSingletonPackExpansion(type);
    }
    replacementTypes.push_back(simplifyType(type));
  }

  auto lookupConformanceFn =
      [&](InFlightSubstitution &IFS, Type original,
          ProtocolDecl *protoType) -> ProtocolConformanceRef {
    auto replacement = original.subst(IFS);
    assert(!replacement->is<GenericTypeParamType>());

    if (replacement->hasError()) {
      return ProtocolConformanceRef::forAbstract(ErrorType::get(replacement),
                                                 protoType);
    }
    if (isOpenedAnyObject(replacement) ||
        replacement->is<GenericTypeParamType>()) {
      return ProtocolConformanceRef::forAbstract(replacement, protoType);
    }

    // FIXME: Retrieve the conformance from the solution itself.
    auto conformance = lookupConformance(
            replacement, protoType, /*allowMissing=*/true);

    if (conformance.isInvalid()) {
      auto synthesized = SynthesizedConformances.find(locator);
      if (synthesized != SynthesizedConformances.end()) {
        return ProtocolConformanceRef::forAbstract(
            replacement, synthesized->second);
      }
    }

    return conformance;
  };

  auto subs = SubstitutionMap::get(sig, replacementTypes, lookupConformanceFn);
  ASSERT(!subs.getRecursiveProperties().isSolverAllocated());
  return subs;
}

// Lazily instantiate function definitions for class template specializations.
// Members of a class template specialization will be instantiated here (not
// when imported). If this method has already be instantiated, then this is a
// no-op.
static void maybeInstantiateCXXMethodDefinition(ValueDecl *decl) {
  if (const auto *constMethod =
          dyn_cast_or_null<clang::CXXMethodDecl>(decl->getClangDecl())) {
    auto method = const_cast<clang::CXXMethodDecl *>(constMethod);
    // Make sure that this method is part of a class template specialization.
    if (method->getTemplateInstantiationPattern())
      decl->getASTContext()
          .getClangModuleLoader()
          ->getClangSema()
          .InstantiateFunctionDefinition(method->getLocation(), method);
  }
}

ConcreteDeclRef
Solution::resolveConcreteDeclRef(ValueDecl *decl,
                                 ConstraintLocator *locator) const {
  if (!decl)
    return ConcreteDeclRef();

  // Get the generic signature of the decl and compute the substitutions.
  auto sig = decl->getInnermostDeclContext()->getGenericSignatureOfContext();
  auto subst = computeSubstitutions(decl, sig, locator);

  maybeInstantiateCXXMethodDefinition(decl);

  // If this is a C++ function template, get it's specialization for the given
  // substitution map and update the decl accordingly.
  if (isa_and_nonnull<clang::FunctionTemplateDecl>(decl->getClangDecl())) {
    auto moduleLoader = decl->getASTContext().getClangModuleLoader();
    return moduleLoader->getCXXFunctionTemplateSpecialization(subst, decl);
  }

  return ConcreteDeclRef(decl, subst);
}

SelectedOverload
Solution::getCalleeOverloadChoice(ConstraintLocator *locator) const {
  return getOverloadChoice(getCalleeLocator(locator));
}

std::optional<SelectedOverload>
Solution::getCalleeOverloadChoiceIfAvailable(ConstraintLocator *locator) const {
  return getOverloadChoiceIfAvailable(getCalleeLocator(locator));
}

ConstraintLocator *Solution::getCalleeLocator(ConstraintLocator *locator,
                                              bool lookThroughApply) const {
  auto &cs = getConstraintSystem();
  return cs.getCalleeLocator(
      locator, lookThroughApply,
      [&](Expr *expr) -> Type { return getType(expr); },
      [&](Type type) -> Type { return simplifyType(type)->getRValueType(); },
      [&](ConstraintLocator *locator) -> std::optional<SelectedOverload> {
        return getOverloadChoiceIfAvailable(locator);
      });
}

ConstraintLocator *
Solution::getConstraintLocator(ASTNode anchor,
                               ArrayRef<LocatorPathElt> path) const {
  auto &cs = getConstraintSystem();
  return cs.getConstraintLocator(anchor, path);
}

ConstraintLocator *
Solution::getConstraintLocator(ConstraintLocator *base,
                               ArrayRef<LocatorPathElt> path) const {
  auto &cs = getConstraintSystem();
  return cs.getConstraintLocator(base, path);
}

/// Return the implicit access kind for a MemberRefExpr with the
/// specified base and member in the specified DeclContext.
static AccessSemantics
getImplicitMemberReferenceAccessSemantics(Expr *base, VarDecl *member,
                                          DeclContext *DC) {
  // Check whether this is a member access on 'self'.
  bool isAccessOnSelf = false;
  if (auto *baseDRE = dyn_cast<DeclRefExpr>(base->getValueProvidingExpr()))
    if (auto *baseVar = dyn_cast<VarDecl>(baseDRE->getDecl()))
      isAccessOnSelf = baseVar->isSelfParameter();

  // If the value is always directly accessed from this context, do it.
  return member->getAccessSemanticsFromContext(DC, isAccessOnSelf);
}

/// This extends functionality of `Expr::isTypeReference` with
/// support for `UnresolvedDotExpr` and `UnresolvedMemberExpr`.
/// This method could be used on not yet fully type-checked AST.
bool ConstraintSystem::isTypeReference(Expr *E) {
  return E->isTypeReference(
      [&](Expr *E) -> Type { return simplifyType(getType(E)); },
      [&](Expr *E) -> Decl * {
        ConstraintLocator *locator = nullptr;
        if (auto *UDE = dyn_cast<UnresolvedDotExpr>(E))
          locator = getConstraintLocator(UDE, ConstraintLocator::Member);
        else if (auto *UME = dyn_cast<UnresolvedMemberExpr>(E))
          locator = getConstraintLocator(UME, ConstraintLocator::UnresolvedMember);
        else if (isa<OverloadSetRefExpr>(E))
          locator = getConstraintLocator(const_cast<Expr *>(E));
        else
          return nullptr;

        // See if we have a resolution for this member.
        auto overload = findSelectedOverloadFor(locator);
        if (!overload)
          return nullptr;

        // We only want to handle the simplest decl binding.
        auto choice = overload->choice;
        if (choice.getKind() != OverloadChoiceKind::Decl)
          return nullptr;

        return choice.getDecl();
      });
}

bool Solution::isTypeReference(Expr *E) const {
  return E->isTypeReference(
      [&](Expr *expr) -> Type { return simplifyType(getType(expr)); },
      [&](Expr *expr) -> Decl * {
        ConstraintLocator *locator = nullptr;
        if (auto *UDE = dyn_cast<UnresolvedDotExpr>(E)) {
          locator = getConstraintLocator(UDE, {ConstraintLocator::Member});
        }

        if (auto *UME = dyn_cast<UnresolvedMemberExpr>(E)) {
          locator =
              getConstraintLocator(UME, {ConstraintLocator::UnresolvedMember});
        }

        if (isa<OverloadSetRefExpr>(E))
          locator = getConstraintLocator(const_cast<Expr *>(E));

        if (locator) {
          if (auto selectedOverload = getOverloadChoiceIfAvailable(locator)) {
            const auto &choice = selectedOverload->choice;
            return choice.getDeclOrNull();
          }
        }

        return nullptr;
      });
}

bool ConstraintSystem::isStaticallyDerivedMetatype(Expr *E) {
  return E->isStaticallyDerivedMetatype(
      [&](Expr *E) -> Type { return simplifyType(getType(E)); },
      [&](Expr *E) -> bool { return isTypeReference(E); });
}

bool Solution::isStaticallyDerivedMetatype(Expr *E) const {
  return E->isStaticallyDerivedMetatype(
      [&](Expr *E) -> Type { return simplifyType(getType(E)); },
      [&](Expr *E) -> bool { return isTypeReference(E); });
}

Type ConstraintSystem::getInstanceType(TypeExpr *E) {
  if (!hasType(E))
    return Type();

  if (auto metaType = getType(E)->getAs<MetatypeType>())
    return metaType->getInstanceType();

  return ErrorType::get(getType(E)->getASTContext());
}

Type ConstraintSystem::getResultType(const AbstractClosureExpr *E) {
  return E->getResultType([&](Expr *E) -> Type { return getType(E); });
}

static bool buildObjCKeyPathString(KeyPathExpr *E,
                                   llvm::SmallVectorImpl<char> &buf) {
  for (auto &component : E->getComponents()) {
    switch (component.getKind()) {
    case KeyPathExpr::Component::Kind::OptionalChain:
    case KeyPathExpr::Component::Kind::OptionalForce:
    case KeyPathExpr::Component::Kind::OptionalWrap:
      // KVC propagates nulls, so these don't affect the key path string.
      continue;
    case KeyPathExpr::Component::Kind::Identity:
      // The identity component can be elided from the KVC string (unless it's
      // the only component, in which case we use @"self").
      continue;

    case KeyPathExpr::Component::Kind::Member: {
      // Property references must be to @objc properties.
      // TODO: If we added special properties matching KVC operators like
      // '@sum', '@count', etc. those could be mapped too.
      if (auto property = dyn_cast<VarDecl>(component.getDeclRef().getDecl())) {
        if (!property->isObjC())
          return false;
        if (!buf.empty()) {
          buf.push_back('.');
        }
        auto objcName = property->getObjCPropertyName().str();
        buf.append(objcName.begin(), objcName.end());
      }
        continue;
    }

    case KeyPathExpr::Component::Kind::Apply:
    case KeyPathExpr::Component::Kind::TupleElement:
    case KeyPathExpr::Component::Kind::Subscript:
      // Subscripts, methods and tuples aren't generally represented in KVC.
      // TODO: There are some subscript forms we could map to KVC, such as
      // when indexing a Dictionary or NSDictionary by string, or when applying
      // a mapping subscript operation to Array/Set or NSArray/NSSet.
      return false;
    case KeyPathExpr::Component::Kind::Invalid:
    case KeyPathExpr::Component::Kind::UnresolvedMember:
    case KeyPathExpr::Component::Kind::UnresolvedSubscript:
    case KeyPathExpr::Component::Kind::UnresolvedApply:
    case KeyPathExpr::Component::Kind::CodeCompletion:
      // Don't bother building the key path string if the key path didn't even
      // resolve.
      return false;
    case KeyPathExpr::Component::Kind::DictionaryKey:
      llvm_unreachable("DictionaryKey only valid in #keyPath expressions.");
      return false;
    }
  }
  
  // If there are no non-identity components, this is the "self" key.
  if (buf.empty()) {
    auto self = StringRef("self");
    buf.append(self.begin(), self.end());
  }
  
  return true;
}

/// Since a cast to an optional will consume a noncopyable type, check to see
/// if injecting the value into an optional here will potentially be confusing.
static bool willHaveConfusingConsumption(Type type,
                                         ConstraintLocatorBuilder locator,
                                         ConstraintSystem &cs) {
  assert(type);
  if (type->isCopyable())
    return false; /// If it's a copyable type, there's no confusion.

  auto loc = cs.getConstraintLocator(locator);
  if (!loc)
    return true;

  auto path = loc->getPath();
  if (path.empty())
    return true;

  switch (loc->getPath().back().getKind()) {
  case ConstraintLocator::FunctionResult:
  case ConstraintLocator::ClosureResult:
  case ConstraintLocator::ClosureBody:
  case ConstraintLocator::ContextualType:
  case ConstraintLocator::CoercionOperand:
    return false; // These last-uses won't be confused for borrowing.

  case ConstraintLocator::ApplyArgToParam: {
    auto argLoc = loc->castLastElementTo<LocatorPathElt::ApplyArgToParam>();
    auto paramFlags = argLoc.getParameterFlags();
    // If the param declares borrowing, then this implicit consumption
    // due to the conversion to pass the argument is indeed confusing.
    if (paramFlags.getOwnershipSpecifier() == ParamSpecifier::Borrowing)
      return true;

    return false;
  }

  default:
    return true;
  }
}

/// Check if this is an attempted downcast between two C++ foreign reference
/// types, and diagnose if it is. This kind of downcasts is not supported yet.
static bool diagnoseUnsupportedFRTDowncast(ASTContext &ctx, SourceLoc loc,
                                           Type fromType, Type toType) {
  auto fromUnderlying = fromType->lookThroughAllOptionalTypes();
  auto toUnderlying = toType->lookThroughAllOptionalTypes();
  if (fromUnderlying->isForeignReferenceType() &&
      toUnderlying->isForeignReferenceType() &&
      fromUnderlying->isBindableToSuperclassOf(toUnderlying)) {
    ctx.Diags.diagnose(loc, diag::downcast_to_foreign_reference_type, fromType,
                       toType);
    return true;
  }
  return false;
}

namespace {

  /// Rewrites an expression by applying the solution of a constraint
  /// system to that expression.
  class ExprRewriter : public ExprVisitor<ExprRewriter, Expr *> {
    // Delayed items to type-check.
    SmallVector<Decl *, 4> LocalDeclsToTypeCheck;
    SmallVector<MacroExpansionExpr *, 4> MacrosToExpand;

  public:
    ASTContext &ctx;
    ConstraintSystem &cs;
    DeclContext *dc;
    Solution &solution;
    std::optional<SyntacticElementTarget> target;
    bool SuppressDiagnostics;

    ExprRewriter(ConstraintSystem &cs, Solution &solution,
                 std::optional<SyntacticElementTarget> target,
                 bool suppressDiagnostics)
        : ctx(cs.getASTContext()), cs(cs),
          dc(target ? target->getDeclContext() : cs.DC),
          solution(solution), target(target),
          SuppressDiagnostics(suppressDiagnostics) {}

    ConstraintSystem &getConstraintSystem() const { return cs; }

    void addLocalDeclToTypeCheck(Decl *D) {
      // If we're doing code completion, avoid doing any further type-checking,
      // that should instead be handled by TypeCheckASTNodeAtLocRequest.
      if (ctx.SourceMgr.hasIDEInspectionTargetBuffer())
        return;

      LocalDeclsToTypeCheck.push_back(D);
    }

    void addMacroToExpand(MacroExpansionExpr *E) {
      // If we're doing code completion, avoid doing any further type-checking,
      // that should instead be handled by TypeCheckASTNodeAtLocRequest.
      if (ctx.SourceMgr.hasIDEInspectionTargetBuffer())
        return;

      MacrosToExpand.push_back(E);
    }

    /// Coerce the given tuple to another tuple type.
    ///
    /// \param expr The expression we're converting.
    ///
    /// \param fromTuple The tuple type we're converting from, which is the same
    /// as \c expr->getType().
    ///
    /// \param toTuple The tuple type we're converting to.
    ///
    /// \param locator Locator describing where this tuple conversion occurs.
    ///
    /// \param sources The sources of each of the elements to be used in the
    /// resulting tuple, as provided by \c computeTupleShuffle.
    Expr *coerceTupleToTuple(Expr *expr, TupleType *fromTuple,
                             TupleType *toTuple,
                             ConstraintLocatorBuilder locator,
                             ArrayRef<unsigned> sources);

    /// Coerce a subclass, class-constrained archetype, class-constrained
    /// existential or to a superclass type.
    ///
    /// Also supports metatypes of the above.
    ///
    /// \param expr The expression to be coerced.
    /// \param toType The type to which the expression will be coerced.
    ///
    /// \return The coerced expression, whose type will be equivalent to
    /// \c toType.
    Expr *coerceSuperclass(Expr *expr, Type toType);

    /// Coerce the given value to existential type.
    ///
    /// The following conversions are supported:
    /// - concrete to existential
    /// - existential to existential
    /// - concrete metatype to existential metatype
    /// - existential metatype to existential metatype
    ///
    /// \param expr The expression to be coerced.
    /// \param toType The type to which the expression will be coerced.
    /// \param locator Locator describing where this existential conversion occurs.
    ///
    /// \return The coerced expression, whose type will be equivalent to
    /// \c toType.
    Expr *coerceExistential(Expr *expr, Type toType,
                            ConstraintLocatorBuilder locator);

    /// Coerce an expression of (possibly unchecked) optional
    /// type to have a different (possibly unchecked) optional type.
    Expr *coerceOptionalToOptional(Expr *expr, Type toType,
                                   ConstraintLocatorBuilder locator);

    /// Peephole an array upcast.
    void peepholeArrayUpcast(ArrayExpr *expr, Type toType, bool bridged,
                             Type elementType,
                             ConstraintLocatorBuilder locator);

    /// Peephole a dictionary upcast.
    void peepholeDictionaryUpcast(DictionaryExpr *expr, Type toType,
                                  bool bridged, Type keyType,
                                  Type valueType,
                                  ConstraintLocatorBuilder locator);

    /// Try to peephole the collection upcast, eliminating the need for
    /// a separate collection-upcast expression.
    ///
    /// \returns true if the peephole operation succeeded, in which case
    /// \c expr already subsumes the upcast.
    bool peepholeCollectionUpcast(Expr *expr, Type toType,  bool bridged,
                                  ConstraintLocatorBuilder locator);

    /// Build a collection upcast expression.
    ///
    /// \param bridged Whether this is a bridging conversion, meaning that the
    /// element types themselves are bridged (vs. simply coerced).
    Expr *buildCollectionUpcastExpr(Expr *expr, Type toType,
                                    bool bridged,
                                    ConstraintLocatorBuilder locator);

    /// Build the expression that performs a bridging operation from the
    /// given expression to the given \c toType.
    Expr *buildObjCBridgeExpr(Expr *expr, Type toType,
                              ConstraintLocatorBuilder locator);

    static Type getBaseType(AnyFunctionType *fnType,
                            bool wantsRValueInstanceType = true) {
      auto params = fnType->getParams();
      assert(params.size() == 1);

      const auto &base = params.front();
      if (wantsRValueInstanceType)
        return base.getPlainType()->getMetatypeInstanceType();

      return base.getOldType();
    }

    /// Check whether it is possible to have an ObjC key path string for the keypath expression
    /// and set the key path string, if yes
    void checkAndSetObjCKeyPathString(KeyPathExpr *keyPath) {
      if (ctx.LangOpts.EnableObjCInterop) {
        SmallString<64> compatStringBuf;
        if (buildObjCKeyPathString(keyPath, compatStringBuf)) {
            auto stringCopy = ctx.AllocateCopy<char>(compatStringBuf.begin(),
                                                     compatStringBuf.end());
            auto stringExpr = new (ctx) StringLiteralExpr(
                                  StringRef(stringCopy, compatStringBuf.size()),
                                  SourceRange(),
                                  /*implicit*/ true);
            cs.setType(
                stringExpr,
                ctx.getStringType());
            keyPath->setObjCStringLiteralExpr(stringExpr);
        }
      }
    }

    /// Determine whether the given reference is to a method on
    /// a remote distributed actor in the given context.
    bool isDistributedThunk(ConcreteDeclRef ref, Expr *context);

  public:
    /// Build a reference to the given declaration.
    Expr *buildDeclRef(SelectedOverload overload, DeclNameLoc loc,
                       ConstraintLocatorBuilder locator, bool implicit) {
      auto choice = overload.choice;
      assert(choice.getKind() != OverloadChoiceKind::DeclViaDynamic);
      auto *decl = choice.getDecl();
      Type fullType = simplifyType(overload.openedFullType);
      Type adjustedFullType = simplifyType(overload.adjustedOpenedFullType);

      // Determine the declaration selected for this overloaded reference.

      auto semantics = decl->getAccessSemanticsFromContext(dc,
                                                           /*isAccessOnSelf*/false);

      // If this is a member of a nominal type, build a reference to the
      // member with an implied base type.
      if (decl->getDeclContext()->isTypeContext() && isa<FuncDecl>(decl)) {
        assert(cast<FuncDecl>(decl)->isOperator() && "Must be an operator");

        auto baseTy = getBaseType(adjustedFullType->castTo<FunctionType>());

        // Build a reference to the member.
        Expr *base =
          TypeExpr::createImplicitHack(loc.getBaseNameLoc(), baseTy, ctx);
        cs.cacheExprTypes(base);

        return buildMemberRef(base, SourceLoc(), overload, loc, locator,
                              locator, implicit, semantics);
      }

      if (auto *typeDecl = dyn_cast<TypeDecl>(decl)) {
        if (!isa<ModuleDecl>(decl)) {
          TypeExpr *typeExpr = nullptr;
          if (implicit) {
            typeExpr = TypeExpr::createImplicitHack(
                loc.getBaseNameLoc(), adjustedFullType->getMetatypeInstanceType(), ctx);
          } else {
            typeExpr = TypeExpr::createForDecl(loc, typeDecl, dc);
            typeExpr->setType(adjustedFullType);
          }
          cs.cacheType(typeExpr);
          return typeExpr;
        }
      }

      auto ref = resolveConcreteDeclRef(decl, locator);

      // If we have a variable that's treated as an rvalue but allows
      // assignment (for initialization) in the current context,
      // treat it as an rvalue that we immediately load. This is
      // the AST that's expected by SILGen.
      bool loadImmediately = false;
      if (auto var = dyn_cast_or_null<VarDecl>(ref.getDecl())) {
        if (!fullType->hasLValueType()) {
          if (var->mutability(dc) == StorageMutability::Initializable) {
            fullType = LValueType::get(fullType);
            adjustedFullType = LValueType::get(adjustedFullType);
            loadImmediately = true;
          }
        }
      }


      auto declRefExpr =
          new (ctx) DeclRefExpr(ref, loc, implicit, semantics, fullType);
      cs.cacheType(declRefExpr);
      declRefExpr->setFunctionRefInfo(choice.getFunctionRefInfo());

      Expr *result = forceUnwrapIfExpected(declRefExpr, locator);

      if (auto *fnDecl = dyn_cast<AbstractFunctionDecl>(decl)) {
        if (AnyFunctionRef(fnDecl).hasExternalPropertyWrapperParameters() &&
            declRefExpr->getFunctionRefInfo().isUnapplied()) {
          // We don't need to do any further adjustment once we've built the
          // curry thunk.
          return buildSingleCurryThunk(result, fnDecl,
                                       adjustedFullType->castTo<FunctionType>(),
                                       adjustedFullType->castTo<FunctionType>(),
                                       locator);
        }
      }

      result = adjustTypeForDeclReference(result, fullType, adjustedFullType,
                                          locator);
      // If we have to load, do so now.
      if (loadImmediately)
        result = cs.addImplicitLoadExpr(result);

      return result;
    }

    /// Describes an opened existential that has not yet been closed.
    struct OpenedExistential {
      /// The archetype describing this opened existential.
      ExistentialArchetypeType *Archetype;

      /// The existential value being opened.
      Expr *ExistentialValue;

      /// The opaque value (of archetype type) stored within the
      /// existential.
      OpaqueValueExpr *OpaqueValue;

      /// The depth of this currently-opened existential. Once the
      /// depth of the expression stack is equal to this value, the
      /// existential can be closed.
      unsigned Depth;
    };

    /// A stack of opened existentials that have not yet been closed.
    /// Ordered by decreasing depth.
    llvm::SmallVector<OpenedExistential, 2> OpenedExistentials;

    /// A stack of expressions being walked, used to compute existential depth.
    llvm::SmallVector<Expr *, 8> ExprStack;

    struct CallLocatorInfo {
      ConstraintLocator *callLoc;
      ConstraintLocator *calleeLoc;
    };

    /// A map of apply exprs to their call locators. This is necessary
    /// because after rewriting an apply's function expr, its locators may no
    /// longer be equivalent to the ones stored in the solution.
    llvm::DenseMap<ApplyExpr *, CallLocatorInfo> CallLocators;

    /// A cache of decl references with their contextual substitutions for a
    /// given callee locator.
    llvm::DenseMap<ConstraintLocator *, ConcreteDeclRef> CachedConcreteRefs;

    /// Resolves the contextual substitutions for a reference to a declaration
    /// at a given locator. This should be preferred to
    /// Solution::resolveConcreteDeclRef as it caches the result.
    ConcreteDeclRef
    resolveConcreteDeclRef(ValueDecl *decl, ConstraintLocatorBuilder locator) {
      if (!decl)
        return ConcreteDeclRef();

      // Cache the resulting concrete reference. Ideally this would be done on
      // Solution, however unfortunately that would require a const_cast which
      // would be undefined behaviour if we ever had a `const Solution`.
      auto *loc = getConstraintSystem().getConstraintLocator(locator);
      auto &ref = CachedConcreteRefs[loc];
      if (!ref)
        ref = solution.resolveConcreteDeclRef(decl, loc);

      return ref;
    }

    /// Members which are AbstractFunctionDecls but not FuncDecls cannot
    /// mutate self.
    bool isNonMutatingMember(ValueDecl *member) {
      if (!isa<AbstractFunctionDecl>(member))
        return false;
      return !isa<FuncDecl>(member) || !cast<FuncDecl>(member)->isMutating();
    }

    /// If the expression might be a dynamic method call, return the base
    /// value for the call.
    Expr *getBaseExpr(Expr *expr) {
      // Keep going up as long as this expression is the parent's base.
      if (auto unresolvedDot = dyn_cast<UnresolvedDotExpr>(expr)) {
        return unresolvedDot->getBase();
      // Remaining cases should only come up when we're re-typechecking.
      // FIXME: really it would be much better if Sema had stricter phase
      // separation.
      } else if (auto selfApply = dyn_cast<SelfApplyExpr>(expr)) {
        return selfApply->getBase();
      } else if (auto apply = dyn_cast<ApplyExpr>(expr)) {
        return apply->getFn();
      } else if (auto lookupRef = dyn_cast<LookupExpr>(expr)) {
        return lookupRef->getBase();
      } else if (auto load = dyn_cast<LoadExpr>(expr)) {
        return load->getSubExpr();
      } else if (auto inout = dyn_cast<InOutExpr>(expr)) {
        return inout->getSubExpr();
      } else if (auto force = dyn_cast<ForceValueExpr>(expr)) {
        return force->getSubExpr();
      } else {
        return nullptr;
      }
    }

    /// Calculates the nesting depth of the current application.
    unsigned getArgCount(unsigned maxArgCount) {
      // FIXME: Walking over the ExprStack to figure out the number of argument
      // lists being applied is brittle. We should instead be checking
      // hasAppliedSelf to figure out if the self param is applied, and looking
      // at the FunctionRefInfo to see if the parameter list is applied.
      unsigned e = ExprStack.size();
      unsigned argCount;

      // Starting from the current expression, count up if the expression is
      // equal to its parent expression's base.
      Expr *prev = ExprStack.back();

      for (argCount = 1; argCount < maxArgCount && argCount < e; ++argCount) {
        Expr *result = ExprStack[e - argCount - 1];
        Expr *base = getBaseExpr(result);
        if (base != prev)
          break;
        prev = result;
      }

      return argCount;
    }

    /// Open an existential value into a new, opaque value of
    /// archetype type.
    ///
    /// \param base An expression of existential type whose value will
    /// be opened.
    ///
    /// \param archetype The archetype that describes the opened existential
    /// type.
    ///
    /// \param member The member that is being referenced on the existential
    /// type.
    ///
    /// \returns An OpaqueValueExpr that provides a reference to the value
    /// stored within the expression or its metatype (if the base was a
    /// metatype).
    Expr *openExistentialReference(Expr *base,
                                   ExistentialArchetypeType *archetype,
                                   ValueDecl *member, SourceLoc memberLoc) {
      assert(archetype && "archetype not already opened?");

      // Dig out the base type.
      Type baseTy = cs.getType(base);

      // Look through inout.
      bool isLValue = false;
      InOutExpr *origInOutBase = dyn_cast<InOutExpr>(base);
      if (origInOutBase) {
        base = origInOutBase->getSubExpr();
        baseTy = baseTy->getInOutObjectType();
        isLValue = true;
      }

      // Look through lvalues.
      if (auto lvalueTy = baseTy->getAs<LValueType>()) {
        isLValue = true;
        baseTy = lvalueTy->getObjectType();
      }

      // Look through metatypes.
      bool isMetatype = false;
      if (auto metaTy = baseTy->getAs<AnyMetatypeType>()) {
        isMetatype = true;
        baseTy = metaTy->getInstanceType();
      }

      assert(baseTy->isAnyExistentialType() && "Type must be existential");

      // Embedded Swift has limitations on the use of generic members of
      // existentials. Diagnose them here.
      diagnoseGenericMemberOfExistentialInEmbedded(
          dc, memberLoc, baseTy, member);

      // If the base was an lvalue but it will only be treated as an
      // rvalue, turn the base into an rvalue now. This results in
      // better SILGen.
      if (isLValue && !origInOutBase &&
          (isNonMutatingMember(member) ||
           member->getDeclContext()->getDeclaredInterfaceType()
             ->hasReferenceSemantics())) {
        base = cs.coerceToRValue(base);
        isLValue = false;
      }

      // Determine the number of applications that need to occur before
      // we can close this existential, and record it.
      unsigned maxArgCount = member->getNumCurryLevels();
      unsigned depth = ExprStack.size() - getArgCount(maxArgCount);

      // Invalid case -- direct call of a metatype. Has one less argument
      // application because there's no ".init".
      if (isa<ApplyExpr>(ExprStack.back()))
        depth++;

      // Create the opaque opened value. If we started with a
      // metatype, it's a metatype.
      Type opaqueType = archetype;
      if (isMetatype)
        opaqueType = MetatypeType::get(opaqueType);
      if (isLValue)
        opaqueType = LValueType::get(opaqueType);

      auto archetypeVal =
          new (ctx) OpaqueValueExpr(base->getSourceRange(), opaqueType);
      cs.cacheType(archetypeVal);

      // Record the opened existential.
      OpenedExistentials.push_back({archetype, base, archetypeVal, depth});

      // Re-apply inout if needed.
      Expr *resultExpr = archetypeVal;
      if (origInOutBase) {
        resultExpr = new (ctx) InOutExpr(
            origInOutBase->getLoc(), resultExpr, opaqueType->getRValueType());
        cs.cacheType(resultExpr);
      }

      return resultExpr;
    }

    /// Try to close the innermost active existential, if there is one.
    bool closeExistential(Expr *&result, ConstraintLocatorBuilder locator,
                          bool force) {
      if (OpenedExistentials.empty())
        return false;

      auto &record = OpenedExistentials.back();
      assert(record.Depth <= ExprStack.size());

      if (!force && record.Depth < ExprStack.size() - 1)
        return false;

      // If we had a return type of 'Self', erase it.
      Type resultTy;
      resultTy = cs.getType(result);

      auto *env = record.Archetype->getGenericEnvironment();

      if (resultTy->hasLocalArchetypeFromEnvironment(env)) {
        Type erasedTy = typeEraseOpenedArchetypesFromEnvironment(resultTy, env);
        ASSERT(erasedTy.getPointer() != resultTy.getPointer());

        // We currently cannot keep lvalueness if the object type changed.
        if (auto *lvalueTy = dyn_cast<LValueType>(erasedTy.getPointer())) {
          erasedTy = lvalueTy->getObjectType();
        }

        auto range = result->getSourceRange();
        result = coerceToType(result, erasedTy, locator);
        // FIXME: Implement missing tuple-to-tuple conversion
        if (result == nullptr) {
          result = new (ctx) ErrorExpr(range);
          cs.setType(result, erasedTy);
          // The opaque value is no longer reachable in an AST walk as
          // a result of the result above being replaced with an
          // ErrorExpr, but there is code expecting to have a type set
          // on it. Since we no longer have a reachable reference,
          // we'll null this out.
          record.OpaqueValue = nullptr;
        }
      }

      // Form the open-existential expression.
      result = new (ctx) OpenExistentialExpr(
                                  record.ExistentialValue,
                                  record.OpaqueValue,
                                  result, cs.getType(result));
      cs.cacheType(result);

      OpenedExistentials.pop_back();
      return true;
    }

    /// Close any active existentials.
    bool closeExistentials(Expr *&result, ConstraintLocatorBuilder locator,
                           bool force=false) {
      bool closedAny = false;
      while (closeExistential(result, locator, force)) {
        force = false;
        closedAny = true;
      }

      return closedAny;
    }

    /// When we have a reference to a declaration whose type in context is
    /// different from its normal interface type, introduce the appropriate
    /// conversions. This can happen due to `@preconcurrency`.
    Expr *adjustTypeForDeclReference(
        Expr *expr, Type openedType, Type adjustedOpenedType,
        ConstraintLocatorBuilder locator,
        llvm::function_ref<Type(Type)> getNewType = [](Type type) {
          return type;
        }) {
      // If the types are the same, do nothing.
      if (openedType->isEqual(adjustedOpenedType))
        return expr;

      // For an RValue function type, use a standard function conversion.
      if (openedType->is<AnyFunctionType>()) {
        expr = new (ctx) FunctionConversionExpr(
            expr, getNewType(adjustedOpenedType));
        cs.cacheType(expr);
        return expr;
      }

      // For any kind of LValue, use an ABISafeConversion.
      if (openedType->hasLValueType()) {
        assert(adjustedOpenedType->hasLValueType() && "lvalueness mismatch?");

        expr = new (ctx) ABISafeConversionExpr(
                                expr, getNewType(adjustedOpenedType));
        cs.cacheType(expr);
        return expr;
      }

      // If we have an optional type, wrap it up in a monadic '?' and recurse.
      if (Type objectType = openedType->getOptionalObjectType()) {
        Type adjustedRefType = getNewType(adjustedOpenedType);
        Type adjustedObjectType = adjustedRefType->getOptionalObjectType();
        assert(adjustedObjectType && "Not an optional?");

        expr = new (ctx) BindOptionalExpr(expr, SourceLoc(), 0, objectType);
        cs.cacheType(expr);
        expr = adjustTypeForDeclReference(
            expr, objectType, adjustedObjectType, locator);
        expr = new (ctx) InjectIntoOptionalExpr(expr, adjustedRefType);
        cs.cacheType(expr);
        expr = new (ctx) OptionalEvaluationExpr(expr, adjustedRefType);
        cs.cacheType(expr);
        return expr;
      }

      return coerceToType(expr, adjustedOpenedType, locator);
    }

    /// Determines if a partially-applied member reference should be
    /// converted into a fully-applied member reference with a pair of
    /// closures.
    bool shouldBuildCurryThunk(OverloadChoice choice,
                               bool baseIsInstance) {
      ValueDecl *member = choice.getDecl();

      // If we're inside a selector expression, don't build the thunk.
      // Were not actually going to emit the member reference, just
      // look at the AST.
      for (auto expr : ExprStack)
        if (isa<ObjCSelectorExpr>(expr))
          return false;

      // Unbound instance method references always build a thunk, even if
      // we apply the arguments (eg, SomeClass.method(self)(a)), to avoid
      // representational issues.  Metatype extension instance members are
      // bound directly since the metatype value is the instance.
      if (!baseIsInstance && member->isInstanceMember()) {
        if (member->getDeclContext()->isMetatypeExtension())
          return false;
        return true;
      }

      // Bound member references that are '@objc optional' or found via dynamic
      // lookup are always represented via DynamicMemberRefExpr instead of a
      // curry thunk.
      if (member->getAttrs().hasAttribute<OptionalAttr>() ||
          choice.getKind() == OverloadChoiceKind::DeclViaDynamic)
        return false;

      // Figure out how many argument lists we need.
      unsigned maxArgCount = member->getNumCurryLevels();

      unsigned argCount = [&]() -> unsigned {
        Expr *prev = ExprStack.back();

        // FIXME: Representational gunk because "T(...)" is really
        // "T.init(...)" -- pretend it has two argument lists like
        // a real '.' call.
        if (isa<ConstructorDecl>(member) &&
            isa<CallExpr>(prev)) {
          assert(maxArgCount == 2);
          return 2;
        }

        // Similarly, ".foo(...)" really applies two argument lists.
        if (isa<CallExpr>(prev) &&
            isa<UnresolvedMemberExpr>(cast<CallExpr>(prev)->getFn()))
          return 2;

        return getArgCount(maxArgCount);
      }();

      // If we have fewer argument lists than expected, build a thunk.
      if (argCount < maxArgCount)
        return true;

      return false;
    }

    /// Build the call inside the body of a single curry thunk
    /// "{ args in base.fn(args) }".
    ///
    /// \param baseExpr The captured base expression, if warranted.
    /// \param fnExpr The expression to be called by consecutively applying
    /// the optional \p baseExpr and thunk parameters.
    /// \param declOrClosure The underlying function-like declaration or
    /// closure we're going to call.
    /// \param thunkParamList The enclosing thunk's parameter list.
    /// \param locator The locator pinned on the function reference carried
    /// by \p fnExpr. If the function has associated applied property wrappers,
    /// the locator is used to pull them in.
    ApplyExpr *buildSingleCurryThunkBodyCall(Expr *baseExpr, Expr *fnExpr,
                                             DeclContext *declOrClosure,
                                             ParameterList *thunkParamList,
                                             ConstraintLocatorBuilder locator) {
      auto *const fnTy = cs.getType(fnExpr)->castTo<FunctionType>();
      auto *calleeFnTy = fnTy;

      if (baseExpr) {
        // Coerce the base expression to the container type.
        const auto calleeSelfParam = calleeFnTy->getParams().front();
        baseExpr =
            coerceToType(baseExpr, calleeSelfParam.getOldType(), locator);

        // Uncurry the callee type in the presence of a base expression; we
        // want '(args) -> result' vs. '(self) -> (args) -> result'.
        calleeFnTy = calleeFnTy->getResult()->castTo<FunctionType>();
      }

      auto appliedPropertyWrappers =
          solution.getAppliedPropertyWrappers(locator.getAnchor());
      const auto calleeDeclRef = resolveConcreteDeclRef(
          dyn_cast<AbstractFunctionDecl>(declOrClosure), locator);

      auto *const calleeParamList = getParameterList(declOrClosure);
      const auto calleeParams = calleeFnTy->getParams();

      // Rebuild the callee params: SILGen knows how to emit property-wrapped
      // parameters, but the corresponding parameter types need to match the
      // backing wrapper types.
      SmallVector<AnyFunctionType::Param, 4> newCalleeParams;
      newCalleeParams.reserve(calleeParams.size());

      // Build the argument list for the call.
      SmallVector<Argument, 4> args;
      unsigned appliedWrapperIndex = 0;
      for (const auto idx : indices(*thunkParamList)) {
        auto *const thunkParamDecl = thunkParamList->get(idx);

        const auto calleeParam = calleeParams[idx];
        const auto calleeParamType = calleeParam.getParameterType();
        const auto thunkParamType = thunkParamDecl->getTypeInContext();

        Expr *paramRef = new (ctx)
            DeclRefExpr(thunkParamDecl, DeclNameLoc(), /*implicit*/ true);
        paramRef->setType(thunkParamDecl->isInOut()
                              ? LValueType::get(thunkParamType)
                              : thunkParamType);
        cs.cacheType(paramRef);

        paramRef = coerceToType(paramRef,
                                thunkParamDecl->isInOut()
                                    ? LValueType::get(calleeParamType)
                                    : calleeParamType,
                                locator);

        auto *const calleeParamDecl = calleeParamList->get(idx);
        if (calleeParamDecl->hasExternalPropertyWrapper()) {
          // Rewrite the parameter ref to the backing wrapper initialization
          // expression.
          auto &appliedWrapper = appliedPropertyWrappers[appliedWrapperIndex++];

          using ValueKind = AppliedPropertyWrapperExpr::ValueKind;
          ValueKind valueKind = (appliedWrapper.initKind ==
                                         PropertyWrapperInitKind::ProjectedValue
                                     ? ValueKind::ProjectedValue
                                     : ValueKind::WrappedValue);

          paramRef = AppliedPropertyWrapperExpr::create(
              ctx, calleeDeclRef, calleeParamDecl, SourceLoc(),
              appliedWrapper.wrapperType, paramRef, valueKind);
          cs.cacheExprTypes(paramRef);

          newCalleeParams.push_back(calleeParam.withType(paramRef->getType()));

          // TODO: inout
          // FIXME: vararg
        } else {
          if (thunkParamDecl->isInOut()) {
            paramRef =
                new (ctx) InOutExpr(SourceLoc(), paramRef, calleeParamType,
                                    /*implicit=*/true);
          } else if (thunkParamDecl->isVariadic()) {
            assert(calleeParamType->isEqual(paramRef->getType()));
            paramRef = VarargExpansionExpr::createParamExpansion(ctx, paramRef);
          }
          cs.cacheType(paramRef);

          newCalleeParams.push_back(calleeParam);
        }

        args.emplace_back(SourceLoc(), calleeParam.getLabel(), paramRef);
      }

      ASSERT(appliedWrapperIndex == appliedPropertyWrappers.size());

      // SILGen knows how to emit property-wrapped parameters, but the
      // corresponding parameter types need to match the backing wrapper types.
      // To handle this, build a new callee function type out of the adjusted
      // callee params, hand it over to the conditional 'self' call, and use it
      // to update the type of the called expression with respect to whether
      // it's 'self'-curried.
      auto *const newCalleeFnTy = FunctionType::get(
          newCalleeParams, calleeFnTy->getResult(), calleeFnTy->getExtInfo());

      // If given, apply the base expression to the curried 'self'
      // parameter first.
      if (baseExpr) {
        fnExpr->setType(FunctionType::get(fnTy->getParams(), newCalleeFnTy,
                                          fnTy->getExtInfo()));
        cs.cacheType(fnExpr);

        fnExpr = DotSyntaxCallExpr::create(ctx, fnExpr, SourceLoc(),
                                           Argument::unlabeled(baseExpr));
      }
      fnExpr->setType(newCalleeFnTy);
      cs.cacheType(fnExpr);

      // Finally, apply the argument list to the callee.
      ApplyExpr *callExpr = CallExpr::createImplicit(
          ctx, fnExpr, ArgumentList::createImplicit(ctx, args));
      callExpr->setType(calleeFnTy->getResult());
      cs.cacheType(callExpr);

      return callExpr;
    }

    std::optional<ActorIsolation>
    getIsolationFromFunctionType(FunctionType *thunkTy) {
      switch (thunkTy->getIsolation().getKind()) {
      case FunctionTypeIsolation::Kind::NonIsolated:
      case FunctionTypeIsolation::Kind::Parameter:
      case FunctionTypeIsolation::Kind::Erased:
        return {};
      case FunctionTypeIsolation::Kind::GlobalActor:
        return ActorIsolation::forGlobalActor(
            thunkTy->getIsolation().getGlobalActorType());
      case FunctionTypeIsolation::Kind::NonIsolatedNonsending:
        return ActorIsolation::forNonisolatedNonsending();
      }
    }

    /// Build a "{ args in base.fn(args) }" single-expression curry thunk.
    ///
    /// \param baseExpr The base expression to be captured, if warranted.
    /// \param fnExpr The expression to be called by consecutively applying
    /// the optional \p baseExpr and thunk parameters.
    /// \param declOrClosure The underlying function-like declaration or
    /// closure we're going to call.
    /// \param thunkTy The type of the thunk.
    /// \param locator The locator pinned on the function reference carried
    /// by \p fnExpr. If the function has associated applied property wrappers,
    /// the locator is used to pull them in.
    AutoClosureExpr *buildSingleCurryThunk(Expr *baseExpr, Expr *fnExpr,
                                           DeclContext *declOrClosure,
                                           FunctionType *thunkTy,
                                           FunctionType *refTy,
                                           ConstraintLocatorBuilder locator) {
      const OptionSet<ParameterList::CloneFlags> options =
          (ParameterList::Implicit | ParameterList::NamedArguments);
      auto *const thunkParamList =
          getParameterList(declOrClosure)->clone(ctx, options);

      for (const auto idx : indices(*thunkParamList)) {
        auto *param = thunkParamList->get(idx);
        auto arg = thunkTy->getParams()[idx];

        // Propagate isolation from function type to the parameter declaration,
        // this is important for subsequent verification.
        param->setIsolated(arg.isIsolated());

        param->setInterfaceType(arg.getParameterType()->mapTypeOutOfEnvironment());
        param->setSpecifier(ParamDecl::getParameterSpecifierForValueOwnership(
            arg.getValueOwnership()));
      }

      auto *const thunk =
          new (ctx) AutoClosureExpr(/*set body later*/ nullptr, thunkTy, dc);
      thunk->setParameterList(thunkParamList);
      thunk->setThunkKind(AutoClosureExpr::Kind::SingleCurryThunk);

      // TODO: We should do this in the ActorIsolation checker but for now it is
      // too risky. This is a narrow fix for 6.2.
      //
      // The problem that this is working around is given an autoclosure that
      // returns an autoclosure that partially applies over an instance method,
      // we do not visit the inner autoclosure in the ActorIsolation checker
      // meaning that we do not properly call setActorIsolation on the
      // AbstractClosureExpr that we produce here.
      if (auto isolation = getIsolationFromFunctionType(thunkTy)) {
        thunk->setActorIsolation(*isolation);
      }

      cs.cacheType(thunk);

      // If the `self` type is existential, it must be opened.
      OpaqueValueExpr *baseOpened = nullptr;
      Expr *origBaseExpr = baseExpr;
      if (baseExpr) {
        auto baseTy = cs.getType(baseExpr);
        if (baseTy->isAnyExistentialType()) {
          Type openedTy = solution.OpenedExistentialTypes.lookup(
              cs.getConstraintLocator(locator));
          assert(openedTy);

          Type opaqueValueTy = openedTy;
          if (baseTy->is<ExistentialMetatypeType>())
            opaqueValueTy = MetatypeType::get(opaqueValueTy);

          baseOpened = new (ctx) OpaqueValueExpr(SourceLoc(), opaqueValueTy);
          cs.cacheType(baseOpened);
          baseExpr = baseOpened;
        }
      }

      Expr *thunkBody = buildSingleCurryThunkBodyCall(
          baseExpr, fnExpr, declOrClosure, thunkParamList, locator);
          
      // If we called a function with a dynamic 'Self' result, we may need some
      // special handling.
      if (baseExpr) {
        if (auto *fnDecl = dyn_cast<AbstractFunctionDecl>(declOrClosure)) {
          if (fnDecl->getDeclContext()->getSelfClassDecl()) {
            auto convTy = refTy->getResult();
            if (!thunkBody->getType()->isEqual(convTy)) {
              thunkBody = cs.cacheType(
                  new (ctx) CovariantReturnConversionExpr(thunkBody, convTy));
            }
          }
        }
      }

      // Now, coerce to the result type of the thunk.
      thunkBody = coerceToType(thunkBody, thunkTy->getResult(), locator);

      // Close up the existential if necessary.
      if (baseOpened) {
        thunkBody = new (ctx) OpenExistentialExpr(origBaseExpr,
                                                  baseOpened,
                                                  thunkBody,
                                                  thunkBody->getType());
        cs.cacheType(thunkBody);
      }

      if (thunkTy->getExtInfo().isThrowing()) {
        thunkBody = new (ctx)
            TryExpr(thunkBody->getStartLoc(), thunkBody, cs.getType(thunkBody),
                    /*implicit=*/true);
        cs.cacheType(thunkBody);
      }

      thunk->setBody(thunkBody);

      return thunk;
    }

    /// Build a "{ args in fn(args) }" single-expression curry thunk.
    ///
    /// \param fnExpr The expression to be called by applying the thunk
    /// parameters.
    /// \param declOrClosure The underlying function-like declaration or
    /// closure we're going to call.
    /// \param thunkTy The type of the resulting thunk. This should be the
    /// type of the \c fnExpr, with any potential adjustments for things like
    /// concurrency.
    /// \param refTy The type of the declaration reference inside the thunk.
    ///              This might involve opened existentials or a covariant
    ///              Self result.
    /// \param locator The locator pinned on the function reference carried
    /// by \p fnExpr. If the function has associated applied property wrappers,
    /// the locator is used to pull them in.
    AutoClosureExpr *buildSingleCurryThunk(Expr *fnExpr,
                                           DeclContext *declOrClosure,
                                           FunctionType *thunkTy,
                                           FunctionType *refTy,
                                           ConstraintLocatorBuilder locator) {
      return buildSingleCurryThunk(/*baseExpr=*/nullptr, fnExpr, declOrClosure,
                                   thunkTy, refTy, locator);
    }

    /// Build a "{ self in { args in self.fn(args) } }" nested curry thunk.
    ///
    /// \param memberRef The expression to be called in the inner thunk by
    /// consecutively applying the captured outer thunk's 'self' parameter and
    /// the parameters of the inner thunk.
    /// \param member The underlying function declaration to be called.
    /// \param outerThunkTy The type of the outer thunk.
    /// \param outerRefTy The type of the declaration reference inside the thunk.
    ///                   This might involve opened existentials or a covariant
    ///                   Self result.
    /// \param memberLocator The locator pinned on the member reference. If the
    /// function has associated applied property wrappers, the locator is used
    /// to pull them in.
    AutoClosureExpr *
    buildDoubleCurryThunk(DeclRefExpr *memberRef, ValueDecl *member,
                          FunctionType *outerThunkTy,
                          FunctionType *outerRefTy,
                          ConstraintLocatorBuilder memberLocator,
                          DeclNameLoc memberLoc, bool isDynamicLookup) {
      const auto selfThunkParam = outerThunkTy->getParams().front();
      const auto selfThunkParamTy = selfThunkParam.getPlainType();

      // Build the 'self' param for the outer thunk, "{ self in ... }".
      auto *const selfParamDecl =
          new (ctx) ParamDecl(SourceLoc(),
                              /*argument label*/ SourceLoc(), Identifier(),
                              /*parameter name*/ SourceLoc(), ctx.Id_self, dc);
      selfParamDecl->setInterfaceType(selfThunkParamTy->mapTypeOutOfEnvironment());
      selfParamDecl->setSpecifier(
          ParamDecl::getParameterSpecifierForValueOwnership(
              selfThunkParam.getValueOwnership()));
      selfParamDecl->setImplicit();

      // Propagate isolation from function type to the parameter declaration,
      // this is important for subsequent verification.
      selfParamDecl->setIsolated(selfThunkParam.isIsolated());

      // Build a reference to the 'self' parameter.
      Expr *selfParamRef = new (ctx) DeclRefExpr(selfParamDecl, DeclNameLoc(),
                                                 /*implicit=*/true);
      selfParamRef->setType(selfThunkParam.isInOut()
                                ? LValueType::get(selfThunkParamTy)
                                : selfThunkParamTy);
      cs.cacheType(selfParamRef);

      if (selfThunkParam.isInOut()) {
        selfParamRef =
            new (ctx) InOutExpr(SourceLoc(), selfParamRef, selfThunkParamTy,
                                /*implicit=*/true);
        cs.cacheType(selfParamRef);
      }

      bool hasOpenedExistential = false;
      Expr *selfOpenedRef = selfParamRef;

      // If the 'self' parameter type is existential, it must be opened.
      if (selfThunkParamTy->isAnyExistentialType()) {
        Type openedTy = solution.OpenedExistentialTypes.lookup(
            cs.getConstraintLocator(memberLocator));
        assert(openedTy);

        hasOpenedExistential = true;

        // If we're opening an existential:
        // - The type of 'memberRef' inside the thunk is written in terms of the
        //   opened existential archetype.
        // - The type of the thunk is written in terms of the
        //   erased existential bounds.
        Type opaqueValueTy = openedTy;
        if (selfThunkParamTy->is<ExistentialMetatypeType>())
          opaqueValueTy = MetatypeType::get(opaqueValueTy);

        if (selfThunkParam.isInOut())
          opaqueValueTy = LValueType::get(opaqueValueTy);

        selfOpenedRef = new (ctx) OpaqueValueExpr(SourceLoc(), opaqueValueTy);
        cs.cacheType(selfOpenedRef);
      }

      auto outerActorIsolation = [&]() -> std::optional<ActorIsolation> {
        auto resultType = outerThunkTy->getResult();
        // Look through all optionals.
        while (auto opt = resultType->getOptionalObjectType())
          resultType = opt;
        auto f =
            getIsolationFromFunctionType(resultType->castTo<FunctionType>());
        if (!f)
          return {};

        // If we have a non-async function and our "inferred" isolation is
        // caller isolation inheriting, then we do not infer isolation and just
        // use the default. The reason why we are doing this is:
        //
        // 1. nonisolated for synchronous functions is equivalent to
        // nonisolated(nonsending).
        //
        // 2. There is a strong invariant in the compiler today that caller
        // isolation inheriting is always async. By using nonisolated here, we
        // avoid breaking that invariant.
        if (!outerThunkTy->isAsync() && f->isNonisolatedNonsending())
          return {};
        return f;
      }();

      Expr *outerThunkBody = nullptr;

      // For an @objc optional member or a member found via dynamic lookup,
      // build a dynamic member reference. Otherwise, build a nested
      // "{ args... in self.member(args...) }" thunk that calls the member.
      if (isDynamicLookup || member->getAttrs().hasAttribute<OptionalAttr>()) {
        auto *const selfCalleeTy =
            cs.getType(memberRef)->castTo<FunctionType>();

        outerThunkBody = new (ctx) DynamicMemberRefExpr(
            selfOpenedRef, SourceLoc(),
            resolveConcreteDeclRef(member, memberLocator), memberLoc);
        outerThunkBody->setImplicit(true);
        outerThunkBody->setType(selfCalleeTy->getResult());
        cs.cacheType(outerThunkBody);

        outerThunkBody = coerceToType(outerThunkBody, outerThunkTy->getResult(),
                                      memberLocator);

        // Close the existential if warranted.
        if (hasOpenedExistential) {
          outerThunkBody = new (ctx) OpenExistentialExpr(
              selfParamRef, cast<OpaqueValueExpr>(selfOpenedRef),
              outerThunkBody, outerThunkBody->getType());
          cs.cacheType(outerThunkBody);
        }
      } else {
        auto *innerThunk = buildSingleCurryThunk(
            selfOpenedRef, memberRef, cast<DeclContext>(member),
            outerThunkTy->getResult()->castTo<FunctionType>(),
            outerRefTy->getResult()->castTo<FunctionType>(),
            memberLocator);
        assert((!outerActorIsolation ||
                innerThunk->getActorIsolation().getKind() ==
                    outerActorIsolation->getKind()) &&
               "If we have an isolation for our double curry thunk it should "
               "match our single curry thunk");

        // Rewrite the body to close the existential if warranted.
        if (hasOpenedExistential) {
          auto *body = innerThunk->getSingleExpressionBody();
          body = new (ctx) OpenExistentialExpr(
              selfParamRef, cast<OpaqueValueExpr>(selfOpenedRef), body,
              body->getType());
          cs.cacheType(body);

          innerThunk->setBody(body);
        }

        outerThunkBody = innerThunk;
      }

      // Finally, construct the outer thunk.
      auto *outerThunk =
          new (ctx) AutoClosureExpr(outerThunkBody, outerThunkTy, dc);
      outerThunk->setThunkKind(AutoClosureExpr::Kind::DoubleCurryThunk);
      outerThunk->setParameterList(
          ParameterList::create(ctx, SourceLoc(), selfParamDecl, SourceLoc()));

      if (outerActorIsolation) {
        outerThunk->setActorIsolation(*outerActorIsolation);
      }

      cs.cacheType(outerThunk);

      return outerThunk;
    }

    Expr *buildStaticCurryThunk(Expr *base, Expr *declRefExpr,
                                AbstractFunctionDecl *member,
                                FunctionType *curryThunkTy,
                                FunctionType *curryRefTy,
                                ConstraintLocatorBuilder locator,
                                ConstraintLocatorBuilder memberLocator,
                                bool openedExistential) {
      if (cs.isStaticallyDerivedMetatype(base)) {
        // Add a useless ".self" to avoid downstream diagnostics.
        base = new (ctx) DotSelfExpr(base, SourceLoc(), base->getEndLoc(),
                                     cs.getType(base));
        cs.setType(base, base->getType());

        // Skip the code below -- we're not building an extra level of
        // call by applying the metatype; instead, the closure we just
        // built is the curried reference.
        return buildSingleCurryThunk(
            base, declRefExpr, member,
            curryThunkTy, curryRefTy,
            memberLocator);
      } else {
        // Add a useless ".self" to avoid downstream diagnostics, in case
        // the type ref is still a TypeExpr.
        base = new (ctx) DotSelfExpr(base, SourceLoc(), base->getEndLoc(),
                                     cs.getType(base));
        // Introduce a capture variable.
        cs.cacheType(base);
        solution.setExprTypes(base);
        auto capture = new (ctx) VarDecl(/*static*/ false,
                                         VarDecl::Introducer::Let,
                                         base->getEndLoc(),
                                         ctx.getIdentifier("$base$"),
                                         dc);
        capture->setImplicit();
        capture->setInterfaceType(base->getType()->mapTypeOutOfEnvironment());

        auto *capturePat =
            NamedPattern::createImplicit(ctx, capture, base->getType());

        auto *captureDecl = PatternBindingDecl::createImplicit(
            ctx, StaticSpellingKind::None, capturePat, base, dc);

        // Write the closure in terms of the capture.
        auto baseRef = new (ctx)
          DeclRefExpr(capture, DeclNameLoc(base->getLoc()), /*implicit*/ true);
        baseRef->setType(base->getType());
        cs.cacheType(baseRef);

        auto *closure = buildSingleCurryThunk(
          baseRef, declRefExpr, member,
          curryThunkTy, curryRefTy,
          memberLocator);

        // Wrap the closure in a capture list.
        auto captureEntry = CaptureListEntry(captureDecl);
        auto captureExpr = CaptureListExpr::create(ctx, captureEntry,
                                                   closure);
        captureExpr->setImplicit();
        captureExpr->setType(cs.getType(closure));
        cs.cacheType(captureExpr);

        Expr *finalExpr = captureExpr;
        closeExistentials(finalExpr, locator,
                          /*force*/ openedExistential);
        return finalExpr;
      }
    }

    Expr *buildDynamicMemberRef(Expr *base, Type baseTy,
                                SourceLoc dotLoc,
                                ConcreteDeclRef memberRef,
                                Type openedType,
                                Type adjustedOpenedType,
                                DeclNameLoc memberLoc,
                                ConstraintLocatorBuilder locator,
                                ConstraintLocatorBuilder memberLocator,
                                bool openedExistential,
                                bool Implicit) {
      base = cs.coerceToRValue(base);
      Expr *ref = new (ctx) DynamicMemberRefExpr(base, dotLoc, memberRef,
                                                 memberLoc);
      ref->setImplicit(Implicit);
      // FIXME: FunctionRefInfo

      // Compute the type of the reference.
      auto computeRefType = [&](Type refType) {
        // If the base was an opened existential, erase the opened
        // existential.
        if (openedExistential) {
          refType = typeEraseOpenedArchetypesFromEnvironment(
              refType, baseTy->castTo<ExistentialArchetypeType>()->getGenericEnvironment());
        }

        return refType;
      };

      Type refType = computeRefType(openedType);
      cs.setType(ref, refType);

      // Adjust the declaration reference type, if required.
      ref = adjustTypeForDeclReference(
          ref, openedType, adjustedOpenedType, locator, computeRefType);

      closeExistentials(ref, locator, /*force=*/openedExistential);

      // We also need to handle the implicitly unwrap of the result
      // of the called function if that's the type checking solution
      // we ended up with.
      return forceUnwrapIfExpected(ref, memberLocator);
    }

    /// FIXME: Simplify this horrible parameter list.
    Expr *buildVarMemberRef(Expr *base, SourceLoc dotLoc,
                            bool baseIsInstance,
                            ConcreteDeclRef memberRef,
                            DeclNameLoc memberLoc,
                            Type containerTy,
                            Type refTy,
                            Type refTySelf,
                            Type adjustedRefTy,
                            Type adjustedRefTySelf,
                            AccessSemantics semantics,
                            ConstraintLocatorBuilder locator,
                            ConstraintLocatorBuilder memberLocator,
                            bool isSuper,
                            bool isUnboundInstanceMember,
                            bool Implicit) {
      auto *varDecl = cast<VarDecl>(memberRef.getDecl());

      bool loadImmediately = false;
      auto resultType = [&loadImmediately](Type fnTy) -> Type {
        Type resultTy = fnTy->castTo<FunctionType>()->getResult();
        if (loadImmediately)
          return LValueType::get(resultTy);
        return resultTy;
      };

      // If we have an instance property that's treated as an rvalue
      // but allows assignment (for initialization) in the current
      // context, treat it as an rvalue that we immediately load.
      // This is the AST that's expected by SILGen.
      if (baseIsInstance && !resultType(refTy)->hasLValueType() &&
          varDecl->mutability(dc, dyn_cast<DeclRefExpr>(base))
              == StorageMutability::Initializable) {
        loadImmediately = true;
      }

      if (isUnboundInstanceMember) {
        assert(memberLocator.getBaseLocator() &&
               cs.UnevaluatedRootExprs.count(
                   getAsExpr(memberLocator.getBaseLocator()->getAnchor())) &&
               "Attempt to reference an instance member of a metatype");
        auto baseInstanceTy = cs.getType(base)
            ->getInOutObjectType()->getMetatypeInstanceType();
        base = new (ctx) UnevaluatedInstanceExpr(base, baseInstanceTy);
        cs.cacheType(base);
        base->setImplicit();
      }

      auto memberRefExpr
        = new (ctx) MemberRefExpr(base, dotLoc, memberRef,
                                  memberLoc, Implicit, semantics);
      memberRefExpr->setIsSuper(isSuper);

      auto resultTySelf = resultType(refTySelf);
      cs.setType(memberRefExpr, resultTySelf);

      Expr *result = memberRefExpr;
      result = adjustTypeForDeclReference(result, resultTySelf,
                                          resultType(adjustedRefTySelf),
                                          locator);

      // If the property is of dynamic 'Self' type, wrap an implicit
      // conversion around the resulting expression, with the destination
      // type having 'Self' swapped for the appropriate replacement
      // type -- usually the base object type.
      auto resultTy = resultType(refTy);
      if (!resultTy->isEqual(resultTySelf)) {
        result = cs.cacheType(new (ctx) CovariantReturnConversionExpr(
            result, resultTy));
      }

      closeExistentials(result, locator);

      // If we need to load, do so now.
      if (loadImmediately) {
        result = cs.addImplicitLoadExpr(result);
      }

      return forceUnwrapIfExpected(result, memberLocator);
    }

    /// Build a new member reference with the given base and member.
    Expr *buildMemberRef(Expr *base, SourceLoc dotLoc,
                         SelectedOverload overload, DeclNameLoc memberLoc,
                         ConstraintLocatorBuilder locator,
                         ConstraintLocatorBuilder memberLocator, bool Implicit,
                         AccessSemantics semantics) {
      const auto &choice = overload.choice;
      const auto openedType = simplifyType(overload.openedType);
      const auto adjustedOpenedType = simplifyType(overload.adjustedOpenedType);

      ValueDecl *member = choice.getDecl();

      bool isSuper = base->isSuperExpr();

      // The formal type of the 'self' value for the call.
      Type baseTy = cs.getType(base)->getRValueType();

      // Figure out the actual base type, and whether we have an instance of
      // that type or its metatype.
      bool baseIsInstance = true;
      bool isExistentialMetatype = false;
      if (auto baseMeta = baseTy->getAs<AnyMetatypeType>()) {
        baseIsInstance = false;
        isExistentialMetatype = baseMeta->is<ExistentialMetatypeType>();
        baseTy = baseMeta->getInstanceType();
        if (auto existential = baseTy->getAs<ExistentialType>())
          baseTy = existential->getConstraintType();

        // A valid reference to a static member (computed property or a method)
        // declared on a protocol is only possible if result type conforms to
        // that protocol, otherwise it would be impossible to find a witness to
        // use.
        // Such means that (for valid references) base expression here could be
        // adjusted to  point to a type conforming to a protocol as-if reference
        // has originated directly from it e.g.
        //
        // \code
        // protocol P {}
        // struct S : P {}
        //
        // extension P {
        //   static var foo: S { S() }
        // }
        //
        // _ = P.foo
        // \endcode
        //
        // Here `P.foo` would be replaced with `S.foo`
        if (!isExistentialMetatype && baseTy->isConstraintType() &&
            member->isStatic()) {
          auto selfParam =
              overload.adjustedOpenedFullType->castTo<FunctionType>()->getParams()[0];

          Type baseTy =
              simplifyType(selfParam.getPlainType())->getMetatypeInstanceType();

          base = TypeExpr::createImplicitHack(base->getLoc(), baseTy, ctx);
          cs.cacheType(base);
        }
      }

      // Build a member reference.
      auto memberRef = resolveConcreteDeclRef(member, memberLocator);

      // If our member reference is a value generic, then the resulting
      // expression is the type value one to access the underlying parameter's
      // value.
      //
      // This can occur in code that does something like: 'type(of: x).a' where
      // 'a' is the static value generic member.
      if (auto gp = dyn_cast<GenericTypeParamDecl>(member)) {
        ASSERT(gp->isValue());

        auto refType = adjustedOpenedType;
        auto ref = TypeValueExpr::createForDecl(memberLoc, gp, dc);
        cs.setType(ref, refType);

        auto gpTy = gp->getDeclaredInterfaceType();
        auto subs = baseTy->getContextSubstitutionMap();
        ref->setParamType(gpTy.subst(subs));

        auto result = new (ctx) DotSyntaxBaseIgnoredExpr(base, dotLoc, ref,
                                                         refType);
        cs.setType(result, refType);
        return result;
      }

      // If we're referring to a member type, it's just a type
      // reference.
      if (auto *TD = dyn_cast<TypeDecl>(member)) {
        Type refType = adjustedOpenedType;
        auto ref = TypeExpr::createForDecl(memberLoc, TD, dc);
        cs.setType(ref, refType);
        auto *result = new (ctx) DotSyntaxBaseIgnoredExpr(
            base, dotLoc, ref, refType);
        cs.setType(result, refType);
        return result;
      }

      Type refTy = simplifyType(overload.openedFullType);
      Type adjustedRefTy = simplifyType(overload.adjustedOpenedFullType);

      // If we're referring to the member of a module, it's just a simple
      // reference.
      if (baseTy->is<ModuleType>()) {
        assert(semantics == AccessSemantics::Ordinary &&
               "Direct property access doesn't make sense for this");
        auto *dre = new (ctx) DeclRefExpr(memberRef, memberLoc, Implicit);
        cs.setType(dre, refTy);
        dre->setFunctionRefInfo(choice.getFunctionRefInfo());
        Expr *ref = cs.cacheType(new (ctx) DotSyntaxBaseIgnoredExpr(
            base, dotLoc, dre, refTy));

        ref = adjustTypeForDeclReference(ref, refTy, adjustedRefTy,
                                         locator);
        return forceUnwrapIfExpected(ref, memberLocator);
      }

      const bool isMetatypeExtMember =
          member->getDeclContext()->isMetatypeExtension();
      const bool isUnboundInstanceMember =
          (!baseIsInstance && member->isInstanceMember() &&
           !isMetatypeExtMember);
      const bool needsCurryThunk =
          shouldBuildCurryThunk(choice, baseIsInstance);

      // The formal type of the 'self' value for the member's declaration.
      Type containerTy = getBaseType(refTy->castTo<FunctionType>());

      // If we have an opened existential, selfTy and baseTy will both be
      // the same opened existential type.
      Type selfTy = containerTy;

      // If we opened up an existential when referencing this member, update
      // the base accordingly.
      Type baseOpenedTy = baseTy;
      bool openedExistential = false;

      auto knownOpened = solution.OpenedExistentialTypes.find(
                           getConstraintSystem().getConstraintLocator(
                             memberLocator));
      if (knownOpened != solution.OpenedExistentialTypes.end()) {
        baseOpenedTy = knownOpened->second;

        // Determine if we're going to have an OpenExistentialExpr around
        // this member reference.
        //
        // For an unbound reference to a method, always open the existential
        // inside the curry thunk, because we won't have a 'self' value until
        // the curry thunk is applied.
        //
        // For a partial application of a protocol method, open the existential
        // inside the curry thunk as well. This reduces abstraction and
        // post-factum function type conversions, and results in better SILGen.
        //
        // For a partial application of a class instance method, however, we
        // always want the thunk to accept a class to avoid potential
        // abstraction, so the existential base must be opened eagerly in order
        // to be upcast to the appropriate class reference type before it is
        //  passed to the thunk.
        if (!needsCurryThunk ||
            (!member->getDeclContext()->getSelfProtocolDecl() &&
             baseIsInstance && member->isInstanceMember())) {
          // Open the existential before performing the member reference.
          base = openExistentialReference(base, knownOpened->second, member,
                                          memberLoc.getBaseNameLoc());
          baseTy = baseOpenedTy;
          selfTy = baseTy;
          openedExistential = true;
        } else {
          // Erase opened existentials from the type of the thunk; we're
          // going to open the existential inside the thunk's body.
          containerTy = typeEraseOpenedArchetypesFromEnvironment(
              containerTy, knownOpened->second->getGenericEnvironment());
          selfTy = containerTy;
        }
      }

      // References to properties with accessors and storage usually go
      // through the accessors, but sometimes are direct.
      if (auto *VD = dyn_cast<VarDecl>(member)) {
        if (semantics == AccessSemantics::Ordinary)
          semantics = getImplicitMemberReferenceAccessSemantics(base, VD, dc);
      }

      auto isDynamic = choice.getKind() == OverloadChoiceKind::DeclViaDynamic;
      if (isMetatypeExtMember) {
        // For metatype extension members, the metatype value IS the instance.
        // The base is already the right type; just coerce to an rvalue.
        base = cs.coerceToRValue(base);
      } else if (baseIsInstance) {
        // Convert the base to the appropriate container type, turning it
        // into an lvalue if required.

        // If the base is already an lvalue with the right base type, we can
        // pass it as an inout qualified type.
        auto selfParamTy = isDynamic ? selfTy : containerTy;

        // If type equality check fails we need to check whether the types
        // are the same with deep equality restriction since `any Sendable`
        // to `Any` conversion is now supported in generic argument positions
        // of @preconcurrency declarations. i.e. referencing a member on
        // `[any Sendable]` if member declared in an extension that expects
        // `Element` to be equal to `Any`.
        if (selfTy->isEqual(baseTy) ||
            solution.getConversionRestriction(baseTy->getCanonicalType(),
                                              selfTy->getCanonicalType()) ==
                ConversionRestrictionKind::DeepEquality)
          if (cs.getType(base)->is<LValueType>())
            selfParamTy = InOutType::get(selfTy);

        base = coerceSelfArgumentToType(
                 base, selfParamTy, member,
                 locator.withPathElement(ConstraintLocator::MemberRefBase));
      } else {
        // The base of an unbound reference is unused, and thus a conversion
        // is not necessary.
        if (!isUnboundInstanceMember) {
          if (!isExistentialMetatype || openedExistential) {
            // Convert the base to an rvalue of the appropriate metatype.
            base = coerceToType(
                base, MetatypeType::get(isDynamic ? selfTy : containerTy),
                locator.withPathElement(ConstraintLocator::MemberRefBase));
          }
        }

        if (!base)
          return nullptr;

        base = cs.coerceToRValue(base);
      }
      assert(base && "Unable to convert base?");

      // This is the type of the DeclRefExpr, where DynamicSelfType has
      // been replaced with the static Self type of the member, ie the
      // base class it is declared in.
      Type refTySelf = refTy, adjustedRefTySelf = adjustedRefTy;

      // Now, deal with DynamicSelfType.
      if (overload.openedFullType->hasDynamicSelfType()) {
        // We look at the original opened type with unsimplified type
        // variables, because we only want to erase DynamicSelfType
        // that appears in the original type of the member, and not
        // one introduced by substitution.
        refTySelf = simplifyType(
            overload.openedFullType->eraseDynamicSelfType());
        adjustedRefTySelf = simplifyType(
            overload.adjustedOpenedFullType->eraseDynamicSelfType());

        // Now replace DynamicSelfType with the actual base type
        // of the call.
        auto replacementTy = getDynamicSelfReplacementType(
              baseOpenedTy, member, memberLocator.getBaseLocator());
        refTy = simplifyType(
              overload.openedFullType
                  ->replaceDynamicSelfType(replacementTy));
        adjustedRefTy = simplifyType(
              overload.adjustedOpenedFullType
                  ->replaceDynamicSelfType(replacementTy));
      }

      // Handle dynamic references.
      if (!needsCurryThunk &&
          (isDynamic || member->getAttrs().hasAttribute<OptionalAttr>())) {
        return buildDynamicMemberRef(base, baseTy, dotLoc, memberRef,
                                     openedType, adjustedOpenedType,
                                     memberLoc, locator, memberLocator,
                                     openedExistential, Implicit);
      }

      // For properties, build member references.
      if (isa<VarDecl>(member)) {
        return buildVarMemberRef(base, dotLoc, baseIsInstance,
                                 memberRef, memberLoc, containerTy,
                                 refTy, refTySelf, adjustedRefTy, adjustedRefTySelf,
                                 semantics, locator, memberLocator,
                                 isSuper, isUnboundInstanceMember, Implicit);
      }

      ASSERT(isa<AbstractFunctionDecl>(member) ||
             isa<EnumElementDecl>(member));

      // Handle all other references.
      auto declRefExpr = new (ctx) DeclRefExpr(memberRef, memberLoc,
                                               Implicit, semantics);
      declRefExpr->setFunctionRefInfo(choice.getFunctionRefInfo());
      declRefExpr->setType(refTySelf);
      cs.setType(declRefExpr, refTySelf);
      Expr *ref = declRefExpr;

      ref = adjustTypeForDeclReference(ref, refTySelf, adjustedRefTySelf, locator);

      // A partial application thunk consists of two nested closures:
      //
      // { self in { args... in self.method(args...) } }
      //
      // If the reference has an applied 'self', eg 'let fn = foo.method',
      // the outermost closure is wrapped inside a single ApplyExpr:
      //
      // { self in { args... in self.method(args...) } }(foo)
      //
      // This is done instead of just hoisting the expression 'foo' up
      // into the closure, which would change evaluation order.
      //
      // However, for a super method reference, eg, 'let fn = super.foo',
      // the base expression is always a SuperRefExpr, possibly wrapped
      // by an upcast. Since SILGen expects super method calls to have a
      // very specific shape, we only emit a single closure here and
      // capture the original SuperRefExpr, since its evaluation does not
      // have side effects, instead of abstracting out a 'self' parameter.
      if (isUnboundInstanceMember) {
        if (needsCurryThunk) {
          // For an unbound reference to a method, all conversions, including
          // dynamic 'Self' handling, are done within the thunk to support
          // the edge case of an unbound reference to a 'Self'-returning class
          // method on a protocol metatype. The result of calling the method
          // must be downcast to the opened archetype before being erased to the
          // subclass existential to cope with the expectations placed
          // on 'CovariantReturnConversionExpr'.
          auto *curryThunkTy = adjustedOpenedType->castTo<FunctionType>();
          auto *curryRefTy = adjustedRefTy->castTo<FunctionType>();

          // Replace the DeclRefExpr with a closure expression which SILGen
          // knows how to emit.
          ref = buildDoubleCurryThunk(declRefExpr, member,
                                      curryThunkTy, curryRefTy,
                                      memberLocator, memberLoc,
                                      isDynamic);
        }

        ref = adjustTypeForDeclReference(
            ref, cs.getType(ref), adjustedOpenedType,
            locator);

        // Reference to an unbound instance method.
        Expr *result = new (ctx) DotSyntaxBaseIgnoredExpr(base, dotLoc,
                                                              ref,
                                                              cs.getType(ref));
        cs.cacheType(result);
        closeExistentials(result, locator, /*force=*/openedExistential);
        return forceUnwrapIfExpected(result, memberLocator);

      } else if (needsCurryThunk && isSuper) {
        ref = buildSingleCurryThunk(
            base, declRefExpr, cast<AbstractFunctionDecl>(member),
            adjustedOpenedType->castTo<FunctionType>(),
            adjustedRefTy->castTo<FunctionType>()
                ->getResult()->castTo<FunctionType>(),
            memberLocator);

        // Handle DynamicSelfType.
        if (!adjustedRefTy->isEqual(adjustedRefTySelf)) {
          auto conversionTy = adjustedRefTy->castTo<FunctionType>()->getResult();
          ref = cs.cacheType(new (ctx) CovariantFunctionConversionExpr(
              ref, conversionTy));
        }

        // The thunk that is built for a 'super' method reference does not
        // require application.
        return forceUnwrapIfExpected(ref, memberLocator);

      } else if (needsCurryThunk) {
        // Another case where we want to build a single closure is when
        // we have a partial application of a static member. It is better
        // to either push the base reference down into the closure (if it's
        // just a literal type reference, in which case there are no order of
        // operation concerns with capturing vs. evaluating it in the closure),
        // or to evaluate the base as a capture and hand it down via the
        // capture list.
        if (isa<ConstructorDecl>(member) || member->isStatic()) {
          return buildStaticCurryThunk(
              base, declRefExpr, cast<AbstractFunctionDecl>(member),
              adjustedOpenedType->castTo<FunctionType>(),
              adjustedRefTy->castTo<FunctionType>()
                  ->getResult()->castTo<FunctionType>(),
              locator, memberLocator, openedExistential);
        }

        auto *curryThunkTy = adjustedRefTySelf->castTo<FunctionType>();
        auto *curryRefTy = curryThunkTy;

        // Check if we need to open an existential stored inside 'self'.
        if (knownOpened != solution.OpenedExistentialTypes.end()) {
          curryThunkTy =
              typeEraseOpenedArchetypesFromEnvironment(
                curryThunkTy, knownOpened->second->getGenericEnvironment())
                  ->castTo<FunctionType>();
        }

        // Replace the DeclRefExpr with a closure expression which SILGen
        // knows how to emit.
        ref = buildDoubleCurryThunk(declRefExpr, member,
                                    curryThunkTy, curryRefTy,
                                    memberLocator, memberLoc,
                                    isDynamic);

        // Fall through, but with 'ref' now an AutoClosureExpr instead of
        // a DeclRefExpr.
      }

      // If the member is a method with a dynamic 'Self' result type, wrap an
      // implicit function type conversion around the resulting expression,
      // with the destination type having 'Self' swapped for the appropriate
      // replacement type -- usually the base object type.
      //
      // Note: For unbound references this is handled inside the thunk.
      if (member->getDeclContext()->getSelfClassDecl()) {
        if (!adjustedRefTy->isEqual(adjustedRefTySelf)) {
          ref = cs.cacheType(new (ctx) CovariantFunctionConversionExpr(
              ref, adjustedRefTy));
        }
      }

      ApplyExpr *apply;
      if (isa<ConstructorDecl>(member)) {
        // FIXME: Provide type annotation.
        ref = forceUnwrapIfExpected(ref, memberLocator);
        apply = ConstructorRefCallExpr::create(ctx, ref, base);
      } else {
        assert((!baseIsInstance || member->isInstanceMember()) &&
               "can't call a static method on an instance");
        ref = forceUnwrapIfExpected(ref, memberLocator);
        apply = DotSyntaxCallExpr::create(ctx, ref, dotLoc, Argument::unlabeled(base));
        if (Implicit) {
          apply->setImplicit();
        }
      }

      return finishApply(apply, adjustedOpenedType, locator, memberLocator);
    }

    /// Convert the given literal expression via a protocol pair.
    ///
    /// This routine handles the two-step literal conversion process used
    /// by integer, float, character, extended grapheme cluster, and string
    /// literals. The first step uses \c builtinProtocol while the second
    /// step uses \c protocol.
    ///
    /// \param literal The literal expression.
    ///
    /// \param type The literal type. This type conforms to \c protocol,
    /// and may also conform to \c builtinProtocol.
    ///
    /// \param protocol The protocol that describes the literal requirement.
    ///
    /// \param literalType The name of the associated type in \c protocol that
    /// describes the argument type of the conversion function (\c
    /// literalFuncName).
    ///
    /// \param literalFuncName The name of the conversion function requirement
    /// in \c protocol.
    ///
    /// \param builtinProtocol The "builtin" form of the protocol, which
    /// always takes builtin types and can only be properly implemented
    /// by standard library types. If \c type does not conform to this
    /// protocol, it's literal type will.
    ///
    /// \param builtinLiteralFuncName The name of the conversion function
    /// requirement in \c builtinProtocol.
    ///
    /// \param brokenProtocolDiag The diagnostic to emit if the protocol
    /// is broken.
    ///
    /// \param brokenBuiltinProtocolDiag The diagnostic to emit if the builtin
    /// protocol is broken.
    ///
    /// \returns the converted literal expression.
    Expr *convertLiteralInPlace(LiteralExpr *literal, Type type,
                                ProtocolDecl *protocol, Identifier literalType,
                                DeclName literalFuncName,
                                ProtocolDecl *builtinProtocol,
                                DeclName builtinLiteralFuncName,
                                Diag<> brokenProtocolDiag,
                                Diag<> brokenBuiltinProtocolDiag);

    /// Finish a function application by performing the appropriate
    /// conversions on the function and argument expressions and setting
    /// the resulting type.
    ///
    /// \param apply The function application to finish type-checking, which
    /// may be a newly-built expression.
    ///
    /// \param openedType The "opened" type this expression had during
    /// type checking, which will be used to specialize the resulting,
    /// type-checked expression appropriately.
    ///
    /// \param locator The locator for the original expression.
    ///
    /// \param calleeLocator The locator that identifies the apply's callee.
    Expr *finishApply(ApplyExpr *apply, Type openedType,
                      ConstraintLocatorBuilder locator,
                      ConstraintLocatorBuilder calleeLocator);

    /// Build the function and argument list for a `@dynamicCallable`
    /// application.
    std::pair</*fn*/ Expr *, ArgumentList *>
    buildDynamicCallable(ApplyExpr *apply, SelectedOverload selected,
                         FuncDecl *method, AnyFunctionType *methodType,
                         ConstraintLocatorBuilder applyFunctionLoc);

  private:
    /// Simplify the given type by substituting all occurrences of
    /// type variables for their fixed types.
    Type simplifyType(Type type) {
      return solution.simplifyType(type);
    }

  public:
    /// Coerce the given expression to the given type.
    ///
    /// This operation cannot fail.
    ///
    /// \param expr The expression to coerce.
    /// \param toType The type to coerce the expression to.
    /// \param locator Locator used to describe where in this expression we are.
    ///
    /// \returns the coerced expression, which will have type \c ToType.
    Expr *coerceToType(Expr *expr, Type toType,
                       ConstraintLocatorBuilder locator);

    /// Coerce the arguments in the provided argument list to their matching
    /// parameter types.
    ///
    /// This operation cannot fail.
    ///
    /// \param args The argument list.
    /// \param funcType The function type.
    /// \param callee The callee for the function being applied.
    /// \param apply The ApplyExpr that forms the call.
    /// \param locator Locator used to describe where in this expression we are.
    ///
    /// \returns The resulting ArgumentList.
    ArgumentList *coerceCallArguments(
        ArgumentList *args, AnyFunctionType *funcType, ConcreteDeclRef callee,
        ApplyExpr *apply, ConstraintLocatorBuilder locator,
        ArrayRef<AppliedPropertyWrapper> appliedPropertyWrappers);

    /// Coerce the given 'self' argument (e.g., for the base of a
    /// member expression) to the given type.
    ///
    /// \param expr The expression to coerce.
    ///
    /// \param baseTy The base type
    ///
    /// \param member The member being accessed.
    ///
    /// \param locator Locator used to describe where in this expression we are.
    Expr *coerceSelfArgumentToType(Expr *expr,
                                   Type baseTy, ValueDecl *member,
                                   ConstraintLocatorBuilder locator);

  private:
    /// Build a new subscript.
    ///
    /// \param base The base of the subscript.
    /// \param args The argument list of the subscript.
    /// \param locator The locator used to refer to the subscript.
    /// \param isImplicit Whether this is an implicit subscript.
    /// \param isDynamicMemberLookup Whether this is an implicit
    /// subscript inserted as a result of dynamic member access. This
    /// changes how we fish out the call argument match for the call.
    Expr *buildSubscript(Expr *base, ArgumentList *args,
                         ConstraintLocatorBuilder locator,
                         ConstraintLocatorBuilder memberLocator,
                         bool isImplicit, bool isDynamicMemberLookup,
                         AccessSemantics semantics,
                         const SelectedOverload &selected) {
      // Build the new subscript.
      auto newSubscript = buildSubscriptHelper(base, args, selected,
                                               locator, isImplicit,
                                               isDynamicMemberLookup,
                                               semantics);
      return forceUnwrapIfExpected(newSubscript, memberLocator,
                                   IUOReferenceKind::ReturnValue);
    }

    Expr *buildSubscriptHelper(Expr *base, ArgumentList *args,
                               const SelectedOverload &selected,
                               ConstraintLocatorBuilder locator,
                               bool isImplicit, bool isDynamicMemberLookup,
                               AccessSemantics semantics) {
      auto choice = selected.choice;

      // Apply a key path if we have one.
      if (choice.getKind() == OverloadChoiceKind::KeyPathApplication) {
        auto applicationTy =
            simplifyType(selected.adjustedOpenedType)->castTo<FunctionType>();

        // The index argument should be (keyPath: KeyPath<Root, Value>).
        // Dig the key path expression out of the arguments.
        auto *indexKP = args->getUnaryExpr();
        assert(indexKP);
        indexKP = cs.coerceToRValue(indexKP);
        auto keyPathExprTy = cs.getType(indexKP);
        auto keyPathTy = applicationTy->getParams().front().getOldType();

        Type valueTy;
        Type baseTy;
        bool resultIsLValue;
        
        if (auto nom = keyPathTy->getAs<NominalType>()) {
          // AnyKeyPath is <T> rvalue T -> rvalue Any?
          if (nom->isAnyKeyPath()) {
            valueTy = ctx.getAnyExistentialType();
            valueTy = OptionalType::get(valueTy);
            resultIsLValue = false;
            base = cs.coerceToRValue(base);
            baseTy = cs.getType(base);
            // We don't really want to attempt AnyKeyPath application
            // if we know a more specific key path type is being applied.
            if (!keyPathTy->isEqual(keyPathExprTy)) {
              ctx.Diags
                  .diagnose(base->getLoc(),
                            diag::expr_smart_keypath_application_type_mismatch,
                            keyPathExprTy, baseTy)
                  .highlight(args->getSourceRange());
            }
          } else {
            llvm_unreachable("unknown key path class!");
          }
        } else {
          if (keyPathTy->is<ArchetypeType>()) {
            keyPathTy = keyPathTy->getSuperclass();
            ASSERT(keyPathTy);
          }

          // Situations like `any KeyPath<...> & Sendable`.
          if (keyPathTy->isExistentialType()) {
            keyPathTy = keyPathTy->getSuperclass();
            ASSERT(keyPathTy);
          }

          auto keyPathBGT = keyPathTy->castTo<BoundGenericType>();
          baseTy = keyPathBGT->getGenericArgs()[0];

          // Coerce the index to the key path's type
          indexKP = coerceToType(indexKP, keyPathTy, locator);

          // Coerce the base to the key path's expected base type.
          if (!baseTy->isEqual(cs.getType(base)->getRValueType()))
            base = coerceToType(base, baseTy, locator);

          if (keyPathBGT->isPartialKeyPath()) {
            // PartialKeyPath<T> is rvalue T -> rvalue Any
            valueTy = ctx.getAnyExistentialType();
            resultIsLValue = false;
            base = cs.coerceToRValue(base);
          } else {
            // *KeyPath<T, U> is T -> U, with rvalueness based on mutability
            // of base and keypath
            valueTy = keyPathBGT->getGenericArgs()[1];
        
            // The result may be an lvalue based on the base and key path kind.
            if (keyPathBGT->isKeyPath()) {
              resultIsLValue = false;
              base = cs.coerceToRValue(base);
            } else if (keyPathBGT->isWritableKeyPath()) {
              resultIsLValue = cs.getType(base)->hasLValueType();
            } else if (keyPathBGT->isReferenceWritableKeyPath()) {
              resultIsLValue = true;
              base = cs.coerceToRValue(base);
            } else {
              llvm_unreachable("unknown key path class!");
            }
          }
        }
        if (resultIsLValue)
          valueTy = LValueType::get(valueTy);

        auto keyPathAp = new (ctx) KeyPathApplicationExpr(
            base, args->getStartLoc(), indexKP, args->getEndLoc(), valueTy,
            base->isImplicit() && args->isImplicit());
        cs.setType(keyPathAp, valueTy);
        return keyPathAp;
      }
      
      auto subscript = cast<SubscriptDecl>(choice.getDecl());

      auto baseTy = cs.getType(base)->getRValueType();
      
      bool baseIsInstance = true;
      if (auto baseMeta = baseTy->getAs<AnyMetatypeType>()) {
        baseIsInstance = false;
        baseTy = baseMeta->getInstanceType();
      }

      // Check whether the base is 'super'.
      bool isSuper = base->isSuperExpr();

      // If we opened up an existential when performing the subscript, open
      // the base accordingly.
      auto memberLoc = cs.getCalleeLocator(cs.getConstraintLocator(locator));
      auto knownOpened = solution.OpenedExistentialTypes.find(memberLoc);
      if (knownOpened != solution.OpenedExistentialTypes.end()) {
        base = openExistentialReference(base, knownOpened->second, subscript,
                                        args->getLoc());
        baseTy = knownOpened->second;
      }

      // Compute the concrete reference to the subscript.
      auto subscriptRef = resolveConcreteDeclRef(subscript, memberLoc);

      // Coerce the index argument.
      auto openedFullFnType = simplifyType(selected.adjustedOpenedFullType)
                                  ->castTo<FunctionType>();

      auto openedFullFnTypeSelf = openedFullFnType;

      // Now, deal with DynamicSelfType.
      if (selected.adjustedOpenedFullType->hasDynamicSelfType()) {
        openedFullFnTypeSelf = simplifyType(
            selected.adjustedOpenedFullType->eraseDynamicSelfType())
                  ->castTo<FunctionType>();
        auto replacementTy = getDynamicSelfReplacementType(
            baseTy, subscript, memberLoc);
        openedFullFnType = simplifyType(
              selected.adjustedOpenedFullType
                  ->replaceDynamicSelfType(replacementTy))
                  ->castTo<FunctionType>();
      }

      auto fullSubscriptTy = openedFullFnType->getResult()
                                  ->castTo<FunctionType>();
      auto fullSubscriptTySelf = openedFullFnTypeSelf->getResult()
                                  ->castTo<FunctionType>();

      auto appliedWrappers =
          solution.getAppliedPropertyWrappers(memberLoc->getAnchor());

      auto tempLoc = ConstraintLocatorBuilder(memberLoc);
      auto callArgLoc =
          (isDynamicMemberLookup
           ? tempLoc.withPathElement(
                ConstraintLocator::ImplicitDynamicMemberSubscript)
           : locator);

      args = coerceCallArguments(
          args, fullSubscriptTy, subscriptRef, nullptr,
          callArgLoc.withPathElement(ConstraintLocator::ApplyArgument),
          appliedWrappers);
      if (!args)
        return nullptr;

      // Handle dynamic lookup.
      if (choice.getKind() == OverloadChoiceKind::DeclViaDynamic ||
          subscript->getAttrs().hasAttribute<OptionalAttr>()) {
        base = coerceSelfArgumentToType(base, baseTy, subscript, locator);
        if (!base)
          return nullptr;

        // TODO: diagnose if semantics != AccessSemantics::Ordinary?
        auto subscriptExpr = DynamicSubscriptExpr::create(
            ctx, base, args, subscriptRef, isImplicit);
        auto resultTy = simplifyType(selected.adjustedOpenedType)
                            ->castTo<FunctionType>()
                            ->getResult();
        assert(!selected.adjustedOpenedFullType->hasOpenedExistential()
               && "open existential archetype in AnyObject subscript type?");
        cs.setType(subscriptExpr, resultTy);
        Expr *result = subscriptExpr;
        closeExistentials(result, locator);
        return result;
      }

      // Convert the base.
      auto openedBaseType =
          getBaseType(openedFullFnType, /*wantsRValue*/ false);
      auto containerTy = solution.simplifyType(openedBaseType);
      
      if (baseIsInstance) {
        base = coerceSelfArgumentToType(
          base, containerTy, subscript,
          locator.withPathElement(ConstraintLocator::MemberRefBase));
      } else {
        base = coerceToType(base,
                            MetatypeType::get(containerTy),
                            locator.withPathElement(
                              ConstraintLocator::MemberRefBase));
        
        if (!base)
          return nullptr;
        
        base = cs.coerceToRValue(base);
      }
      if (!base)
        return nullptr;

      // Form the subscript expression.
      auto subscriptExpr = SubscriptExpr::create(
          ctx, base, args, subscriptRef, isImplicit, semantics);
      subscriptExpr->setIsSuper(isSuper);

      cs.setType(subscriptExpr, fullSubscriptTySelf->getResult());

      Expr *result = subscriptExpr;

      // If the element is of dynamic 'Self' type, wrap an implicit conversion
      // around the resulting expression, with the destination type having
      // 'Self' swapped for the appropriate replacement type -- usually the
      // base object type.
      if (!fullSubscriptTy->getResult()->isEqual(
            fullSubscriptTySelf->getResult())) {
        result = cs.cacheType(
            new (ctx) CovariantReturnConversionExpr(
                result, fullSubscriptTy->getResult()));
      }

      closeExistentials(result, locator);

      return result;
    }

    /// Build a new reference to another constructor.
    Expr *buildOtherConstructorRef(Type openedFullType,
                                   ConcreteDeclRef ref, Expr *base,
                                   DeclNameLoc loc,
                                   ConstraintLocatorBuilder locator,
                                   bool implicit) {
      // The constructor was opened with the allocating type, not the
      // initializer type. Map the former into the latter.
      auto getOpenedInitializerType = [&](Type ty) -> FunctionType * {
        auto *resultTy = solution.simplifyType(ty)->castTo<FunctionType>();
        auto selfTy = getBaseType(resultTy);

        ParameterTypeFlags flags;
        if (!selfTy->hasReferenceSemantics())
          flags = flags.withInOut(true);

        auto selfParam = AnyFunctionType::Param(selfTy, Identifier(), flags);
        return FunctionType::get({selfParam},
                                 resultTy->getResult(),
                                 resultTy->getExtInfo());
      };

      auto *resultTySelf = getOpenedInitializerType(
            openedFullType->eraseDynamicSelfType());

      // Build the constructor reference.
      Expr *ctorRef = cs.cacheType(
          new (ctx) OtherConstructorDeclRefExpr(ref, loc, implicit, resultTySelf));

      auto *resultTy = getOpenedInitializerType(
            openedFullType->replaceDynamicSelfType(
              cs.getType(base)->getWithoutSpecifierType()));

      // Wrap in covariant `Self` return if needed.
      if (!resultTy->isEqual(resultTySelf)) {
        ASSERT(ref.getDecl()->getDeclContext()->getSelfClassDecl());
        ctorRef = cs.cacheType(
             new (ctx) CovariantFunctionConversionExpr(ctorRef, resultTy));
      }

      return ctorRef;
    }

    /// Build an implicit argument for keypath based dynamic lookup,
    /// which consists of KeyPath expression and a single component.
    ///
    /// \param paramType The type of the keypath subscript parameter
    ///                  this argument is passed to.
    /// \param dotLoc The location of the '.' preceding member name.
    /// \param memberLoc The locator to be associated with new argument.
    Expr *buildKeyPathDynamicMemberArgExpr(Type paramType, SourceLoc dotLoc,
                                           ConstraintLocator *memberLoc) {
      using Component = KeyPathExpr::Component;
      auto *anchor = getAsExpr(memberLoc->getAnchor());

      auto makeKeyPath = [&](ArrayRef<Component> components) -> Expr * {
        Type keyPathTy = paramType;

        // If parameter of a dynamic member lookup is `& Sendable` type
        // we need to check key path captures to determine whether the
        // argument could be `& Sendable` as well or not.
        if (paramType->isExistentialType() && paramType->isSendableType()) {
          auto allCapturesAreSendable = [&](const Component &component) {
            auto *argList = component.getArgs();
            if (!argList)
              return true;

            return llvm::all_of(*argList, [&](const auto &arg) {
              return solution.getResolvedType(arg.getExpr())->isSendableType();
            });
          };

          if (!llvm::all_of(components, allCapturesAreSendable))
            keyPathTy = paramType->getSuperclass();
        }

        auto *kp = KeyPathExpr::createImplicit(ctx, /*backslashLoc*/ dotLoc,
                                               components, anchor->getEndLoc());
        kp->setType(keyPathTy);
        cs.cacheExprTypes(kp);

        // See whether there's an equivalent ObjC key path string we can produce
        // for interop purposes.
        checkAndSetObjCKeyPathString(kp);
        return kp;
      };

      Type keyPathTy = paramType;
      if (auto *existential = keyPathTy->getAs<ExistentialType>()) {
        keyPathTy = existential->getSuperclass();
        assert(keyPathTy->isKnownKeyPathType());
      }

      SmallVector<Component, 2> components;
      auto *componentLoc = cs.getConstraintLocator(
          memberLoc,
          LocatorPathElt::KeyPathDynamicMember(keyPathTy->getAnyNominal()));
      auto overload = solution.getOverloadChoice(componentLoc);

      // Looks like there is a chain of implicit `subscript(dynamicMember:...)`
      // calls necessary to resolve a member reference.
      switch (overload.choice.getKind()) {
      case OverloadChoiceKind::DynamicMemberLookup:
      case OverloadChoiceKind::KeyPathDynamicMemberLookup: {
        buildKeyPathSubscriptComponent(overload, dotLoc, /*args=*/nullptr,
                                       componentLoc, components);
        return makeKeyPath(components);
      }

      default:
        break;
      }

      if (auto *KPE = dyn_cast<KeyPathExpr>(anchor)) {
        // Looks like keypath dynamic member lookup was used inside
        // of a keypath expression e.g. `\Lens<[Int]>.count` where
        // `count` is referenced using dynamic lookup.
        auto kpElt = memberLoc->findFirst<LocatorPathElt::KeyPathComponent>();
        assert(kpElt && "no keypath component node");
        auto &comp = KPE->getComponents()[kpElt->getIndex()];

        if (comp.getKind() == Component::Kind::UnresolvedMember) {
          buildKeyPathMemberComponent(overload, comp.getLoc(), componentLoc,
                                      components);
        } else if (comp.getKind() == Component::Kind::UnresolvedSubscript) {
          buildKeyPathSubscriptComponent(overload, comp.getLoc(),
                                         comp.getArgs(), componentLoc,
                                         components);
        } else {
          return nullptr;
        }
        return makeKeyPath(components);
      }

      if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor)) {
        buildKeyPathMemberComponent(overload, UDE->getLoc(), componentLoc,
                                    components);
      } else if (auto *SE = dyn_cast<SubscriptExpr>(anchor)) {
        buildKeyPathSubscriptComponent(overload, SE->getLoc(), SE->getArgs(),
                                       componentLoc, components);
      } else {
        return nullptr;
      }
      return makeKeyPath(components);
    }

    /// Bridge the given value (which is an error type) to NSError.
    Expr *bridgeErrorToObjectiveC(Expr *value) {
      auto nsErrorType = ctx.getNSErrorType();
      assert(nsErrorType && "Missing NSError?");

      auto result = new (ctx) BridgeToObjCExpr(value, nsErrorType);
      return cs.cacheType(result);
    }

    /// Bridge the given value to its corresponding Objective-C object
    /// type.
    ///
    /// This routine should only be used for bridging value types.
    ///
    /// \param value The value to be bridged.
    Expr *bridgeToObjectiveC(Expr *value, Type objcType) {
      auto result = new (ctx) BridgeToObjCExpr(value, objcType);
      return cs.cacheType(result);
    }

    /// Bridge the given object from Objective-C to its value type.
    ///
    /// This routine should only be used for bridging value types.
    ///
    /// \param object The object, whose type should already be of the type
    /// that the value type bridges through.
    ///
    /// \param valueType The value type to which we are bridging.
    ///
    /// \param conditional Whether the bridging should be conditional. If false,
    /// uses forced bridging.
    ///
    /// \returns a value of type \c valueType (optional if \c conditional) that
    /// stores the bridged result or (when \c conditional) an empty optional if
    /// conditional bridging fails.
    Expr *bridgeFromObjectiveC(Expr *object, Type valueType, bool conditional) {
      if (!conditional) {
        auto result = new (ctx) BridgeFromObjCExpr(object, valueType);
        return cs.cacheType(result);
      }

      // Find the _BridgedToObjectiveC protocol.
      auto bridgedProto =
          ctx.getProtocol(KnownProtocolKind::ObjectiveCBridgeable);

      // Try to find the conformance of the value type to _BridgedToObjectiveC.
      auto bridgedToObjectiveCConformance
        = checkConformance(valueType, bridgedProto);

      FuncDecl *fn = nullptr;

      if (bridgedToObjectiveCConformance) {
        assert(bridgedToObjectiveCConformance.getConditionalRequirements()
                   .empty() &&
               "cannot conditionally conform to _BridgedToObjectiveC");
        // The conformance to _BridgedToObjectiveC is statically known.
        // Retrieve the bridging operation to be used if a static conformance
        // to _BridgedToObjectiveC can be proven.
        fn = conditional ? ctx.getConditionallyBridgeFromObjectiveCBridgeable()
                         : ctx.getForceBridgeFromObjectiveCBridgeable();
      } else {
        // Retrieve the bridging operation to be used if a static conformance
        // to _BridgedToObjectiveC cannot be proven.
        fn = conditional ? ctx.getConditionallyBridgeFromObjectiveC()
                         : ctx.getForceBridgeFromObjectiveC();
      }

      if (!fn) {
        ctx.Diags.diagnose(object->getLoc(), diag::missing_bridging_function,
                           conditional);
        return nullptr;
      }

      // Form a reference to the function. The bridging operations are generic,
      // so we need to form substitutions and compute the resulting type.
      auto genericSig = fn->getGenericSignature();
      auto subMap = SubstitutionMap::get(
          genericSig, valueType, bridgedToObjectiveCConformance);

      ConcreteDeclRef fnSpecRef(fn, subMap);

      auto resultType = OptionalType::get(valueType);

      auto result = new (ctx)
          ConditionalBridgeFromObjCExpr(object, resultType, fnSpecRef);
      return cs.cacheType(result);
    }

    /// Bridge the given object from Objective-C to its value type.
    ///
    /// This routine should only be used for bridging value types.
    ///
    /// \param object The object, whose type should already be of the type
    /// that the value type bridges through.
    ///
    /// \param valueType The value type to which we are bridging.
    ///
    /// \returns a value of type \c valueType that stores the bridged result.
    Expr *forceBridgeFromObjectiveC(Expr *object, Type valueType) {
      return bridgeFromObjectiveC(object, valueType, false);
    }
    
  public:
    /// Simplify the expression type and return the expression.
    ///
    /// This routine is used for 'simple' expressions that only need their
    /// types simplified, with no further computation.
    Expr *simplifyExprType(Expr *expr) {
      auto toType = simplifyType(cs.getType(expr));
      cs.setType(expr, toType);
      return expr;
    }

    Expr *visitErrorExpr(ErrorExpr *expr) {
      // Do nothing with error expressions.
      return expr;
    }

    Expr *visitCodeCompletionExpr(CodeCompletionExpr *expr) {
      // Do nothing with code completion expressions.
      auto toType = simplifyType(cs.getType(expr));
      cs.setType(expr, toType);
      return expr;
    }

    Expr *handleIntegerLiteralExpr(LiteralExpr *expr) {
      // If the literal has been assigned a builtin integer type,
      // don't mess with it.
      if (cs.getType(expr)->is<AnyBuiltinIntegerType>())
        return expr;

      ProtocolDecl *protocol = TypeChecker::getProtocol(
          ctx, expr->getLoc(),
          KnownProtocolKind::ExpressibleByIntegerLiteral);
      ProtocolDecl *builtinProtocol = TypeChecker::getProtocol(
          ctx, expr->getLoc(),
          KnownProtocolKind::ExpressibleByBuiltinIntegerLiteral);

      // For type-sugar reasons, prefer the spelling of the default literal
      // type.
      auto type = simplifyType(cs.getType(expr));
      if (auto defaultType = TypeChecker::getDefaultType(protocol, dc)) {
        if (defaultType->isEqual(type))
          type = defaultType;
      }
      if (auto floatProtocol = TypeChecker::getProtocol(
              ctx, expr->getLoc(),
              KnownProtocolKind::ExpressibleByFloatLiteral)) {
        if (auto defaultFloatType =
                TypeChecker::getDefaultType(floatProtocol, dc)) {
          if (defaultFloatType->isEqual(type))
            type = defaultFloatType;
        }
      }

      DeclName initName(ctx, DeclBaseName::createConstructor(),
                        {ctx.Id_integerLiteral});
      DeclName builtinInitName(ctx, DeclBaseName::createConstructor(),
                               {ctx.Id_builtinIntegerLiteral});

      auto *result = convertLiteralInPlace(
          expr, type, protocol, ctx.Id_IntegerLiteralType, initName,
          builtinProtocol, builtinInitName, diag::integer_literal_broken_proto,
          diag::builtin_integer_literal_broken_proto);
      if (result) {
        // TODO: It seems that callers expect this to have types assigned...
        result->setType(cs.getType(result));
      }
      return result;
    }
    
    Expr *visitNilLiteralExpr(NilLiteralExpr *expr) {
      auto type = simplifyType(cs.getType(expr));

      // By far the most common 'nil' literal is for Optional<T>.none.
      // We don't have to look up the witness in this case since SILGen
      // knows how to lower it directly.
      if (auto objectType = type->getOptionalObjectType()) {
        cs.setType(expr, type);
        return expr;
      }

      auto *protocol = TypeChecker::getProtocol(
          ctx, expr->getLoc(), KnownProtocolKind::ExpressibleByNilLiteral);

      // For type-sugar reasons, prefer the spelling of the default literal
      // type.
      if (auto defaultType = TypeChecker::getDefaultType(protocol, dc)) {
        if (defaultType->isEqual(type))
          type = defaultType;
      }

      DeclName initName(ctx, DeclBaseName::createConstructor(),
                        {ctx.Id_nilLiteral});
      return convertLiteralInPlace(expr, type, protocol,
                                   Identifier(), initName,
                                   nullptr,
                                   Identifier(),
                                   diag::nil_literal_broken_proto,
                                   diag::nil_literal_broken_proto);
    }

    
    Expr *visitIntegerLiteralExpr(IntegerLiteralExpr *expr) {
      return handleIntegerLiteralExpr(expr);
    }

    Expr *visitFloatLiteralExpr(FloatLiteralExpr *expr) {
      // If the literal has been assigned a builtin float type,
      // don't mess with it.
      if (cs.getType(expr)->is<BuiltinFloatType>())
        return expr;

      ProtocolDecl *protocol = TypeChecker::getProtocol(
          ctx, expr->getLoc(),
          KnownProtocolKind::ExpressibleByFloatLiteral);
      ProtocolDecl *builtinProtocol = TypeChecker::getProtocol(
          ctx, expr->getLoc(),
          KnownProtocolKind::ExpressibleByBuiltinFloatLiteral);

      // For type-sugar reasons, prefer the spelling of the default literal
      // type.
      auto type = simplifyType(cs.getType(expr));
      if (auto defaultType = TypeChecker::getDefaultType(protocol, dc)) {
        if (defaultType->isEqual(type))
          type = defaultType;
      }

      // Get the _MaxBuiltinFloatType decl, or look for it if it's not cached.
      auto maxFloatTypeDecl = ctx.get_MaxBuiltinFloatTypeDecl();
      // Presence of this declaration has been validated in CSGen.
      assert(maxFloatTypeDecl);

      auto maxType = maxFloatTypeDecl->getUnderlyingType();

      DeclName initName(ctx, DeclBaseName::createConstructor(),
                        {ctx.Id_floatLiteral});
      DeclName builtinInitName(ctx, DeclBaseName::createConstructor(),
                               {ctx.Id_builtinFloatLiteral});

      expr->setBuiltinType(maxType);
      return convertLiteralInPlace(
          expr, type, protocol, ctx.Id_FloatLiteralType, initName,
          builtinProtocol, builtinInitName, diag::float_literal_broken_proto,
          diag::builtin_float_literal_broken_proto);
    }

    Expr *visitBooleanLiteralExpr(BooleanLiteralExpr *expr) {
      if (cs.getType(expr) && cs.getType(expr)->is<BuiltinIntegerType>())
        return expr;

      ProtocolDecl *protocol = TypeChecker::getProtocol(
          ctx, expr->getLoc(),
          KnownProtocolKind::ExpressibleByBooleanLiteral);
      ProtocolDecl *builtinProtocol = TypeChecker::getProtocol(
          ctx, expr->getLoc(),
          KnownProtocolKind::ExpressibleByBuiltinBooleanLiteral);
      if (!protocol || !builtinProtocol)
        return nullptr;

      auto type = simplifyType(cs.getType(expr));
      DeclName initName(ctx, DeclBaseName::createConstructor(),
                        {ctx.Id_booleanLiteral});
      DeclName builtinInitName(ctx, DeclBaseName::createConstructor(),
                               {ctx.Id_builtinBooleanLiteral});
      return convertLiteralInPlace(
          expr, type, protocol, ctx.Id_BooleanLiteralType, initName,
          builtinProtocol, builtinInitName, diag::boolean_literal_broken_proto,
          diag::builtin_boolean_literal_broken_proto);
    }

    Expr *handleStringLiteralExpr(LiteralExpr *expr) {
      auto stringLiteral = dyn_cast<StringLiteralExpr>(expr);
      auto magicLiteral = dyn_cast<MagicIdentifierLiteralExpr>(expr);
      assert(bool(stringLiteral) != bool(magicLiteral) &&
             "literal must be either a string literal or a magic literal");

      auto type = simplifyType(cs.getType(expr));

      bool isStringLiteral = true;
      bool isGraphemeClusterLiteral = false;
      ProtocolDecl *protocol = TypeChecker::getProtocol(
          ctx, expr->getLoc(), KnownProtocolKind::ExpressibleByStringLiteral);

      if (!checkConformance(type, protocol)) {
        // If the type does not conform to ExpressibleByStringLiteral, it should
        // be ExpressibleByExtendedGraphemeClusterLiteral.
        protocol = TypeChecker::getProtocol(
            ctx, expr->getLoc(),
            KnownProtocolKind::ExpressibleByExtendedGraphemeClusterLiteral);
        isStringLiteral = false;
        isGraphemeClusterLiteral = true;
      }
      if (!checkConformance(type, protocol)) {
        // ... or it should be ExpressibleByUnicodeScalarLiteral.
        protocol = TypeChecker::getProtocol(
            ctx, expr->getLoc(),
            KnownProtocolKind::ExpressibleByUnicodeScalarLiteral);
        isStringLiteral = false;
        isGraphemeClusterLiteral = false;
      }

      // For type-sugar reasons, prefer the spelling of the default literal
      // type.
      if (auto defaultType = TypeChecker::getDefaultType(protocol, dc)) {
        if (defaultType->isEqual(type))
          type = defaultType;
      }

      ProtocolDecl *builtinProtocol;
      Identifier literalType;
      DeclName literalFuncName;
      DeclName builtinLiteralFuncName;
      Diag<> brokenProtocolDiag;
      Diag<> brokenBuiltinProtocolDiag;

      if (isStringLiteral) {
        literalType = ctx.Id_StringLiteralType;

        literalFuncName = DeclName(ctx, DeclBaseName::createConstructor(),
                                   {ctx.Id_stringLiteral});

        builtinProtocol = TypeChecker::getProtocol(
            ctx, expr->getLoc(),
            KnownProtocolKind::ExpressibleByBuiltinStringLiteral);
        builtinLiteralFuncName =
            DeclName(ctx, DeclBaseName::createConstructor(),
                     {ctx.Id_builtinStringLiteral,
                      ctx.getIdentifier("utf8CodeUnitCount"),
                      ctx.getIdentifier("isASCII")});
        if (stringLiteral)
          stringLiteral->setEncoding(StringLiteralExpr::UTF8);
        else
          magicLiteral->setStringEncoding(StringLiteralExpr::UTF8);

        brokenProtocolDiag = diag::string_literal_broken_proto;
        brokenBuiltinProtocolDiag = diag::builtin_string_literal_broken_proto;
      } else if (isGraphemeClusterLiteral) {
        literalType = ctx.Id_ExtendedGraphemeClusterLiteralType;
        literalFuncName = DeclName(ctx, DeclBaseName::createConstructor(),
                                   {ctx.Id_extendedGraphemeClusterLiteral});
        builtinLiteralFuncName =
            DeclName(ctx, DeclBaseName::createConstructor(),
                     {ctx.Id_builtinExtendedGraphemeClusterLiteral,
                      ctx.getIdentifier("utf8CodeUnitCount"),
                      ctx.getIdentifier("isASCII")});

        builtinProtocol = TypeChecker::getProtocol(
            ctx, expr->getLoc(),
            KnownProtocolKind::
                ExpressibleByBuiltinExtendedGraphemeClusterLiteral);
        brokenProtocolDiag =
            diag::extended_grapheme_cluster_literal_broken_proto;
        brokenBuiltinProtocolDiag =
            diag::builtin_extended_grapheme_cluster_literal_broken_proto;
      } else {
        // Otherwise, we should have just one Unicode scalar.
        literalType = ctx.Id_UnicodeScalarLiteralType;

        literalFuncName = DeclName(ctx, DeclBaseName::createConstructor(),
                                   {ctx.Id_unicodeScalarLiteral});
        builtinLiteralFuncName =
            DeclName(ctx, DeclBaseName::createConstructor(),
                     {ctx.Id_builtinUnicodeScalarLiteral});

        builtinProtocol = TypeChecker::getProtocol(
            ctx, expr->getLoc(),
            KnownProtocolKind::ExpressibleByBuiltinUnicodeScalarLiteral);

        brokenProtocolDiag = diag::unicode_scalar_literal_broken_proto;
        brokenBuiltinProtocolDiag =
            diag::builtin_unicode_scalar_literal_broken_proto;

        stringLiteral->setEncoding(StringLiteralExpr::OneUnicodeScalar);
      }

      return convertLiteralInPlace(expr,
                                   type,
                                   protocol,
                                   literalType,
                                   literalFuncName,
                                   builtinProtocol,
                                   builtinLiteralFuncName,
                                   brokenProtocolDiag,
                                   brokenBuiltinProtocolDiag);
    }
    
    Expr *visitStringLiteralExpr(StringLiteralExpr *expr) {
      return handleStringLiteralExpr(expr);
    }

    Expr *
    visitInterpolatedStringLiteralExpr(InterpolatedStringLiteralExpr *expr) {
      // Figure out the string type we're converting to.
      auto openedType = cs.getType(expr);
      auto type = simplifyType(openedType);
      cs.setType(expr, type);

      auto loc = expr->getStartLoc();

      auto fetchProtocolInitWitness =
          [&](KnownProtocolKind protocolKind, Type type,
              ArrayRef<Identifier> argLabels) -> ConcreteDeclRef {
        auto proto = TypeChecker::getProtocol(ctx, loc, protocolKind);
        assert(proto && "Missing string interpolation protocol?");

        auto conformance = checkConformance(type, proto);
        assert(conformance && "string interpolation type conforms to protocol");

        DeclName constrName(ctx, DeclBaseName::createConstructor(), argLabels);

        ConcreteDeclRef witness =
            conformance.getWitnessByName(constrName);
        if (!witness || !isa<AbstractFunctionDecl>(witness.getDecl()))
          return nullptr;
        return witness;
      };

      auto *interpolationProto = TypeChecker::getProtocol(
          ctx, expr->getLoc(),
          KnownProtocolKind::ExpressibleByStringInterpolation);
      auto associatedTypeDecl =
          interpolationProto->getAssociatedType(ctx.Id_StringInterpolation);
      if (associatedTypeDecl == nullptr) {
        ctx.Diags.diagnose(expr->getStartLoc(),
                           diag::interpolation_broken_proto);
        return nullptr;
      }
      auto interpolationType =
        simplifyType(DependentMemberType::get(openedType, associatedTypeDecl));

      // Fetch needed witnesses.
      ConcreteDeclRef builderInit = fetchProtocolInitWitness(
          KnownProtocolKind::StringInterpolationProtocol, interpolationType,
          {ctx.Id_literalCapacity, ctx.Id_interpolationCount});
      if (!builderInit) return nullptr;
      expr->setBuilderInit(builderInit);

      ConcreteDeclRef resultInit = fetchProtocolInitWitness(
          KnownProtocolKind::ExpressibleByStringInterpolation, type,
          {ctx.Id_stringInterpolation});
      if (!resultInit) return nullptr;
      expr->setInitializer(resultInit);

      // Make the integer literals for the parameters.
      auto buildExprFromUnsigned = [&](unsigned value) {
        LiteralExpr *expr = IntegerLiteralExpr::createFromUnsigned(ctx, value, loc);
        cs.setType(expr, ctx.getIntType());
        return handleIntegerLiteralExpr(expr);
      };

      expr->setLiteralCapacityExpr(
          buildExprFromUnsigned(expr->getLiteralCapacity()));
      expr->setInterpolationCountExpr(
          buildExprFromUnsigned(expr->getInterpolationCount()));

      // This OpaqueValueExpr represents the result of builderInit above in
      // silgen.
      OpaqueValueExpr *interpolationExpr =
          new (ctx) OpaqueValueExpr(expr->getSourceRange(), interpolationType);
      cs.setType(interpolationExpr, interpolationType);
      expr->setInterpolationExpr(interpolationExpr);

      auto appendingExpr = expr->getAppendingExpr();
      appendingExpr->setSubExpr(interpolationExpr);

      return expr;
    }

    Expr *visitRegexLiteralExpr(RegexLiteralExpr *expr) {
      simplifyExprType(expr);
      expr->setInitializer(
          ctx.getRegexInitDecl(cs.getType(expr)));
      return expr;
    }

    Expr *visitMagicIdentifierLiteralExpr(MagicIdentifierLiteralExpr *expr) {
#if SWIFT_BUILD_SWIFT_SYNTAX
      if (ctx.LangOpts.hasFeature(Feature::BuiltinMacros)) {
        auto expandedType = solution.simplifyType(solution.getType(expr));
        cs.setType(expr, expandedType);

        auto locator = cs.getConstraintLocator(expr);
        auto overload = solution.getOverloadChoice(locator);

        auto macro = cast<MacroDecl>(overload.choice.getDecl());
        ConcreteDeclRef macroRef = resolveConcreteDeclRef(macro, locator);
        auto *expansion = MacroExpansionExpr::create(
            dc, expr->getStartLoc(),
            /*module name=*/DeclNameRef(), /*module name loc=*/DeclNameLoc(),
            DeclNameRef(macro->getName()),
            DeclNameLoc(expr->getLoc()), SourceLoc(), {}, SourceLoc(), nullptr,
            MacroRole::Expression, /*isImplicit=*/true, expandedType);
        expansion->setMacroRef(macroRef);
        (void)evaluateOrDefault(ctx.evaluator,
                                ExpandMacroExpansionExprRequest{expansion},
                                std::nullopt);
        if (expansion->getRewritten()) {
          cs.cacheExprTypes(expansion);
          return expansion;
        }

        // Fall through to use old implementation.
      }
#endif

      switch (expr->getKind()) {
#define MAGIC_STRING_IDENTIFIER(NAME, STRING)                                  \
      case MagicIdentifierLiteralExpr::NAME:                                   \
        return handleStringLiteralExpr(expr);
#define MAGIC_INT_IDENTIFIER(NAME, STRING)                                     \
      case MagicIdentifierLiteralExpr::NAME:                                   \
        return handleIntegerLiteralExpr(expr);
#define MAGIC_POINTER_IDENTIFIER(NAME, STRING)                                 \
      case MagicIdentifierLiteralExpr::NAME:                                   \
        return expr;
#include "swift/AST/MagicIdentifierKinds.def"
      }


      llvm_unreachable("Unhandled MagicIdentifierLiteralExpr in switch.");
    }

    Expr *visitObjectLiteralExpr(ObjectLiteralExpr *expr) {
      if (cs.getType(expr) && !cs.getType(expr)->hasTypeVariable())
        return expr;

      // Figure out the type we're converting to.
      auto openedType = cs.getType(expr);
      auto type = simplifyType(openedType);
      cs.setType(expr, type);

      Type conformingType = type;
      if (auto baseType = conformingType->getOptionalObjectType()) {
        // The type may be optional due to a failable initializer in the
        // protocol.
        conformingType = baseType;
      }

      // Find the appropriate object literal protocol.
      auto proto = TypeChecker::getLiteralProtocol(ctx, expr);
      assert(proto && "Missing object literal protocol?");
      auto conformance = checkConformance(conformingType, proto);
      assert(conformance && "object literal type conforms to protocol");

      auto constrName = TypeChecker::getObjectLiteralConstructorName(ctx, expr);

      ConcreteDeclRef witness = conformance.getWitnessByName(constrName);

      auto selectedOverload = solution.getOverloadChoice(
          cs.getConstraintLocator(expr, ConstraintLocator::ConstructorMember));

      auto fnType = simplifyType(selectedOverload.adjustedOpenedType)
                        ->castTo<FunctionType>();

      auto newArgs = coerceCallArguments(
          expr->getArgs(), fnType, witness, /*applyExpr=*/nullptr,
          cs.getConstraintLocator(expr, ConstraintLocator::ApplyArgument),
          /*appliedPropertyWrappers=*/{});

      expr->setInitializer(witness);
      expr->setArgs(newArgs);
      return expr;
    }

    /// Add an implicit force unwrap of an expression that references an
    /// implicitly unwrapped optional T!.
    Expr *forceUnwrapIUO(Expr *expr) {
      auto optTy = cs.getType(expr);
      auto objectTy = optTy->getWithoutSpecifierType()->getOptionalObjectType();
      assert(objectTy && "Trying to unwrap non-optional?");

      // Preserve l-valueness of the result.
      if (optTy->is<LValueType>())
        objectTy = LValueType::get(objectTy);

      expr = new (ctx) ForceValueExpr(expr, expr->getEndLoc(),
                                      /*forcedIUO*/ true);
      cs.setType(expr, objectTy);
      expr->setImplicit();
      return expr;
    }

    /// Retrieve the number of implicit force unwraps required for an implicitly
    /// unwrapped optional reference at a given locator.
    unsigned getIUOForceUnwrapCount(ConstraintLocatorBuilder locator,
                                    IUOReferenceKind refKind) {
      // Adjust the locator depending on the type of IUO reference.
      auto loc = locator;
      switch (refKind) {
      case IUOReferenceKind::ReturnValue:
        loc = locator.withPathElement(ConstraintLocator::FunctionResult);
        break;
      case IUOReferenceKind::Value:
        break;
      }
      auto *iuoLocator = cs.getConstraintLocator(
          loc, {ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice});
      auto *dynamicLocator = cs.getConstraintLocator(
          loc, {ConstraintLocator::DynamicLookupResult});

      // First check whether we recorded an implicit unwrap for an IUO.
      unsigned unwrapCount = 0;
      if (solution.DisjunctionChoices.lookup(iuoLocator))
        unwrapCount += 1;

      // Next check if we recorded an implicit unwrap for dynamic lookup.
      if (solution.DisjunctionChoices.lookup(dynamicLocator))
        unwrapCount += 1;

      return unwrapCount;
    }

    Expr *
    forceUnwrapIfExpected(Expr *expr, ConstraintLocatorBuilder locator,
                          IUOReferenceKind refKind = IUOReferenceKind::Value) {
      auto unwrapCount = getIUOForceUnwrapCount(locator, refKind);
      for (unsigned i = 0; i < unwrapCount; ++i)
        expr = forceUnwrapIUO(expr);
      return expr;
    }

    Expr *visitDeclRefExpr(DeclRefExpr *expr) {
      auto locator = cs.getConstraintLocator(expr);

      // Check whether this is a reference to `__buildSelf`, and if so,
      // replace it with a type expression with fully resolved type.
      if (auto *var = dyn_cast<VarDecl>(expr->getDecl())) {
        if (var->getName() == ctx.Id_builderSelf) {
          assert(expr->isImplicit() && var->isImplicit());
          auto builderTy =
            solution.getResolvedType(var)->getMetatypeInstanceType();

          return cs.cacheType(
            TypeExpr::createImplicitHack(expr->getLoc(), builderTy, ctx));
        }
      }

      // Find the overload choice used for this declaration reference.
      auto selected = solution.getOverloadChoice(locator);
      return buildDeclRef(selected, expr->getNameLoc(), locator,
                          expr->isImplicit());
    }

    Expr *visitSuperRefExpr(SuperRefExpr *expr) {
      return simplifyExprType(expr);
    }

    Expr *visitTypeExpr(TypeExpr *expr) {
      auto toType = simplifyType(cs.getType(expr));
      assert(toType->is<MetatypeType>());
      cs.setType(expr, toType);
      return expr;
    }

    Expr *visitTypeValueExpr(TypeValueExpr *expr) {
      auto toType = simplifyType(cs.getType(expr));
      ASSERT(toType->isEqual(expr->getParamDecl()->getValueType()));
      cs.setType(expr, toType);

      auto declRefRepr = cast<DeclRefTypeRepr>(expr->getRepr());
      auto resolvedTy =
          TypeResolution::resolveContextualType(declRefRepr, cs.DC,
                                             TypeResolverContext::InExpression,
                                                nullptr, nullptr, nullptr);

      if (!resolvedTy || resolvedTy->hasError())
        return nullptr;

      expr->setParamType(resolvedTy);

      return expr;
    }

    Expr *visitOtherConstructorDeclRefExpr(OtherConstructorDeclRefExpr *expr) {
      cs.setType(expr, expr->getDecl()->getInitializerInterfaceType());
      return expr;
    }

    Expr *visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *expr) {
      return simplifyExprType(expr);
    }

    Expr *visitOverloadedDeclRefExpr(OverloadedDeclRefExpr *expr) {
      // Determine the declaration selected for this overloaded reference.
      auto locator = cs.getConstraintLocator(expr);
      auto selected = solution.getOverloadChoice(locator);

      return buildDeclRef(selected, expr->getNameLoc(), locator,
                          expr->isImplicit());
    }

    Expr *visitUnresolvedDeclRefExpr(UnresolvedDeclRefExpr *expr) {
      // FIXME: We should have generated an overload set from this, in which
      // case we can emit a typo-correction error here but recover well.
      return nullptr;
    }

    Expr *visitUnresolvedSpecializeExpr(UnresolvedSpecializeExpr *expr) {
      // Our specializations should have resolved the subexpr to the right type.
      return expr->getSubExpr();
    }

    Expr *visitMemberRefExpr(MemberRefExpr *expr) {
      auto memberLocator = cs.getConstraintLocator(expr,
                                                   ConstraintLocator::Member);
      auto selected = solution.getOverloadChoice(memberLocator);
      return buildMemberRef(
          expr->getBase(), expr->getDotLoc(), selected, expr->getNameLoc(),
          cs.getConstraintLocator(expr), memberLocator, expr->isImplicit(),
          expr->getAccessSemantics());
    }

    Expr *visitDynamicMemberRefExpr(DynamicMemberRefExpr *expr) {
      llvm_unreachable("already type-checked?");
    }

    Expr *visitUnresolvedMemberExpr(UnresolvedMemberExpr *expr) {
      Type resultTy = simplifyType(cs.getType(expr));

      // Find the selected member and base type.
      auto memberLocator = cs.getConstraintLocator(
                             expr, ConstraintLocator::UnresolvedMember);
      auto selected = solution.getOverloadChoice(memberLocator);

      // Unresolved member lookup always happens in a metatype so dig out the
      // instance type.
      auto baseTy = selected.choice.getBaseType()->getMetatypeInstanceType();
      baseTy = simplifyType(baseTy);

      // The base expression is simply the metatype of the base type.
      // FIXME: This location info is bogus.
      auto base = TypeExpr::createImplicitHack(expr->getDotLoc(), baseTy, ctx);
      cs.cacheExprTypes(base);

      // Build the member reference.
      auto *exprLoc = cs.getConstraintLocator(expr);
      auto result = buildMemberRef(
          base, expr->getDotLoc(), selected, expr->getNameLoc(), exprLoc,
          memberLocator, expr->isImplicit(), AccessSemantics::Ordinary);
      if (!result)
        return nullptr;

      return coerceToType(result, resultTy, cs.getConstraintLocator(expr));
    }

  private:
    /// A list of "suspicious" optional injections.
    SmallVector<InjectIntoOptionalExpr *, 4> SuspiciousOptionalInjections;

    /// A list of implicit coercions of noncopyable types.
    SmallVector<Expr *, 4> ConsumingCoercions;

    /// Create a member reference to the given constructor.
    Expr *applyCtorRefExpr(Expr *expr, Expr *base, SourceLoc dotLoc,
                           DeclNameLoc nameLoc, bool implicit,
                           ConstraintLocator *ctorLocator,
                           SelectedOverload overload) {
      auto locator = cs.getConstraintLocator(expr);
      auto choice = overload.choice;
      assert(choice.getKind() != OverloadChoiceKind::DeclViaDynamic);
      auto *ctor = cast<ConstructorDecl>(choice.getDecl());

      // If the subexpression is a metatype, build a direct reference to the
      // constructor.
      if (cs.getType(base)->is<AnyMetatypeType>()) {
        return buildMemberRef(base, dotLoc, overload, nameLoc, locator,
                              ctorLocator, implicit, AccessSemantics::Ordinary);
      }

      // The subexpression must be either 'self' or 'super'.
      if (!base->isSuperExpr()) {
        // 'super' references have already been fully checked; handle the
        // 'self' case below.
        auto &de = ctx.Diags;
        bool diagnoseBadInitRef = true;
        auto arg = base->getSemanticsProvidingExpr();
        if (auto dre = dyn_cast<DeclRefExpr>(arg)) {
          if (dre->getDecl()->getName() == ctx.Id_self) {
            // We have a reference to 'self'.
            diagnoseBadInitRef = false;
            // Make sure the reference to 'self' occurs within an initializer.
            if (!dyn_cast_or_null<ConstructorDecl>(
                   dc->getInnermostMethodContext())) {
              if (!SuppressDiagnostics)
                de.diagnose(dotLoc, diag::init_delegation_outside_initializer);
              return nullptr;
            }
          }
        }

        // If we need to diagnose this as a bad reference to an initializer,
        // do so now.
        if (diagnoseBadInitRef) {
          // Determine whether 'super' would have made sense as a base.
          bool hasSuper = false;
          if (auto func = dc->getInnermostMethodContext()) {
            if (auto classDecl = func->getDeclContext()->getSelfClassDecl()) {
              hasSuper = classDecl->hasSuperclass();
            }
          }

          if (SuppressDiagnostics)
            return nullptr;

          de.diagnose(dotLoc, diag::bad_init_ref_base, hasSuper);
        }
      }

      // Build a partial application of the delegated initializer.
      auto callee = resolveConcreteDeclRef(ctor, ctorLocator);
      Expr *ctorRef = buildOtherConstructorRef(overload.adjustedOpenedFullType, callee,
                                               base, nameLoc, ctorLocator,
                                               implicit);
      auto *call =
          DotSyntaxCallExpr::create(ctx, ctorRef, dotLoc,
                                    Argument::unlabeled(base));

      return finishApply(call, cs.getType(expr), locator, ctorLocator);
    }

    /// Give the deprecation warning for referring to a global function
    /// when there's a method from a conditional conformance in a smaller/closer
    /// scope.
    void
    diagnoseDeprecatedConditionalConformanceOuterAccess(UnresolvedDotExpr *UDE,
                                                        ValueDecl *choice) {
      auto getValueDecl = [](DeclContext *context) -> ValueDecl * {
        if (auto generic = context->getSelfNominalTypeDecl()) {
          return generic;
        } else if (context->isModuleScopeContext())
          return context->getParentModule();
        else
          llvm_unreachable("Unsupported base");
      };

      auto result =
          TypeChecker::lookupUnqualified(dc, UDE->getName(), UDE->getLoc());
      assert(result && "names can't just disappear");
      // These should all come from the same place.
      auto exampleInner = result.front();
      auto innerChoice = exampleInner.getValueDecl();
      auto innerDC = exampleInner.getDeclContext()->getInnermostTypeContext();
      auto innerParentDecl = getValueDecl(innerDC);

      auto choiceDC = choice->getDeclContext();
      auto choiceParentDecl = getValueDecl(choiceDC);

      auto &DE = ctx.Diags;
      DE.diagnose(UDE->getLoc(),
                  diag::warn_deprecated_conditional_conformance_outer_access,
                  UDE->getName(), choice, choiceParentDecl, innerChoice,
                  innerParentDecl);

      auto name = choiceParentDecl->getName().getBaseIdentifier();
      SmallString<32> namePlusDot = name.str();
      namePlusDot.push_back('.');

      DE.diagnose(UDE->getLoc(),
                  diag::fix_deprecated_conditional_conformance_outer_access,
                  namePlusDot, choice)
          .fixItInsert(UDE->getStartLoc(), namePlusDot);
    }

    Expr *applyMemberRefExpr(Expr *expr, Expr *base, SourceLoc dotLoc,
                             DeclNameLoc nameLoc, bool implicit) {
      // If we have a constructor member, handle it as a constructor.
      auto ctorLocator = cs.getConstraintLocator(
                           expr,
                           ConstraintLocator::ConstructorMember);
      if (auto selected = solution.getOverloadChoiceIfAvailable(ctorLocator)) {
        return applyCtorRefExpr(
            expr, base, dotLoc, nameLoc, implicit, ctorLocator, *selected);
      }

      // Determine the declaration selected for this overloaded reference.
      auto memberLocator = cs.getConstraintLocator(expr,
                                                   ConstraintLocator::Member);
      auto selected = solution.getOverloadChoice(memberLocator);

      if (!selected.choice.getBaseType()) {
        // This is one of the "outer alternatives", meaning the innermost
        // methods didn't work out.
        //
        // The only way to get here is via an UnresolvedDotExpr with outer
        // alternatives.
        auto UDE = cast<UnresolvedDotExpr>(expr);
        diagnoseDeprecatedConditionalConformanceOuterAccess(
            UDE, selected.choice.getDecl());

        return buildDeclRef(selected, nameLoc, memberLocator, implicit);
      }

      switch (selected.choice.getKind()) {
      case OverloadChoiceKind::DeclViaBridge: {
        base = cs.coerceToRValue(base);

        // Look through an implicitly unwrapped optional.
        auto baseTy = cs.getType(base);
        auto baseMetaTy = baseTy->getAs<MetatypeType>();
        auto baseInstTy = (baseMetaTy ? baseMetaTy->getInstanceType() : baseTy);
        auto classTy = ctx.getBridgedToObjC(dc, baseInstTy);

        if (baseMetaTy) {
          // FIXME: We're dropping side effects in the base here!
          base = TypeExpr::createImplicitHack(base->getLoc(), classTy, ctx);
          cs.cacheExprTypes(base);
        } else {
          // Bridge the base to its corresponding Objective-C object.
          base = bridgeToObjectiveC(base, classTy);
        }

        // Fall through to build the member reference.
        LLVM_FALLTHROUGH;
      }

      case OverloadChoiceKind::Decl:
      case OverloadChoiceKind::DeclViaUnwrappedOptional:
      case OverloadChoiceKind::DeclViaDynamic:
        return buildMemberRef(base, dotLoc, selected, nameLoc,
                              cs.getConstraintLocator(expr), memberLocator,
                              implicit, AccessSemantics::Ordinary);

      case OverloadChoiceKind::TupleIndex: {
        Type toType = simplifyType(cs.getType(expr));

        auto baseTy = cs.getType(base);
        // If the base type is not a tuple l-value, access to
        // its elements supposed to be r-value as well.
        //
        // This is modeled in constraint system in a way
        // that when member type is resolved by `resolveOverload`
        // it would take r-value type of the element at
        // specified index, but if it's a type variable it
        // could still be bound to l-value later.
        if (!baseTy->is<LValueType>())
          toType = toType->getRValueType();

        // If the result type is an rvalue and the base contains lvalues,
        // need a full tuple coercion to properly load & set access kind
        // on all underlying elements before taking a single element.
        if (!toType->hasLValueType() && baseTy->hasLValueType())
          base = coerceToType(base, baseTy->getRValueType(),
                              cs.getConstraintLocator(base));

        return cs.cacheType(new (ctx)
                            TupleElementExpr(base, dotLoc,
                                             selected.choice.getTupleIndex(),
                                             nameLoc.getBaseNameLoc(), toType));
      }

      case OverloadChoiceKind::MaterializePack: {
        auto baseTy = solution.getResolvedType(base);

        // Load the base tuple if necessary, materialization
        // operates on r-value types only.
        if (baseTy->is<LValueType>())
          base = coerceToType(base, baseTy->getRValueType(),
                              cs.getConstraintLocator(base));

        auto packType = solution.getResolvedType(expr);
        return cs.cacheType(
            MaterializePackExpr::create(ctx,
                                        base, expr->getEndLoc(),
                                        packType));
      }

      case OverloadChoiceKind::ExtractFunctionIsolation: {
        auto isolationType = solution.getResolvedType(expr);
        auto *extractExpr = new (ctx)
          ExtractFunctionIsolationExpr(base,
                                       expr->getEndLoc(),
                                       isolationType);
        return cs.cacheType(extractExpr);
      }

      case OverloadChoiceKind::KeyPathApplication:
        llvm_unreachable("should only happen in a subscript");

      case OverloadChoiceKind::DynamicMemberLookup:
      case OverloadChoiceKind::KeyPathDynamicMemberLookup: {
        return buildDynamicMemberLookupRef(
            expr, base, dotLoc, nameLoc.getStartLoc(), selected, memberLocator);
      }
      }

      llvm_unreachable("Unhandled OverloadChoiceKind in switch.");
    }

    /// Builds an argument list for making a call to the given
    /// `@dynamicMemberLookup` subscript by inserting any necessary default
    /// arguments.
    ArgumentList *buildArgumentListForDynamicMemberLookupSubscript(
        const SelectedOverload &overload, SourceLoc componentLoc,
        ConstraintLocator *locator) {
      ASSERT(overload.choice.isAnyDynamicMemberLookup() &&
             "Overload must be for dynamic member lookup");
      ASSERT(isa<SubscriptDecl>(overload.choice.getDecl()));

      auto memberLoc = cs.getCalleeLocator(locator);

      // We specifically don't want the full type here to avoid losing parameter
      // pack references.
      auto fnType = overload.adjustedOpenedType->castTo<FunctionType>();

      auto params = fnType->getParams();
      ASSERT(params.size() >= 1);

      auto param = params[0];
      auto paramTy = simplifyType(param.getParameterType());

      Expr *argExpr;
      if (overload.choice.isKeyPathDynamicMemberLookup()) {
        argExpr = buildKeyPathDynamicMemberArgExpr(paramTy, componentLoc,
                                                   memberLoc);
      } else {
        auto fieldName =
            overload.choice.getName().getBaseIdentifier().str();
        argExpr = buildDynamicMemberLookupArgExpr(fieldName, componentLoc,
                                                  paramTy);
      }

      return ArgumentList::forImplicitSingle(ctx, ctx.Id_dynamicMember, argExpr);
    }

    /// Form a type checked expression for the argument of a
    /// @dynamicMemberLookup subscript index parameter.
    Expr *buildDynamicMemberLookupArgExpr(StringRef name, SourceLoc loc,
                                          Type literalTy) {
      // Build and type check the string literal index value to the specific
      // string type expected by the subscript.
      auto *nameExpr = new (ctx) StringLiteralExpr(name, loc, /*implicit*/true);
      cs.setType(nameExpr, literalTy);
      return handleStringLiteralExpr(nameExpr);
    }

    Expr *buildDynamicMemberLookupRef(Expr *expr, Expr *base, SourceLoc dotLoc,
                                      SourceLoc nameLoc,
                                      const SelectedOverload &overload,
                                      ConstraintLocator *memberLocator) {
      // Application of a DynamicMemberLookup result turns
      // a member access of `x.foo` into `x[dynamicMember: "foo", ...]`, or
      // `x[dynamicMember: KeyPath<T, U>, ...]`
      auto componentLoc =
          overload.choice.getKind() == OverloadChoiceKind::DynamicMemberLookup
              ? nameLoc
              : dotLoc;
      auto *argList = buildArgumentListForDynamicMemberLookupSubscript(
          overload, componentLoc, memberLocator);

      if (!argList) {
        return nullptr;
      }

      // Build and return a subscript that uses this string as the index.
      return buildSubscript(base, argList, cs.getConstraintLocator(expr),
                            memberLocator,
                            /*isImplicit*/ true,
                            /*isDynamicMemberLookup*/true,
                            AccessSemantics::Ordinary, overload);
    }

  public:
    Expr *visitUnresolvedDotExpr(UnresolvedDotExpr *expr) {
      return applyMemberRefExpr(expr, expr->getBase(), expr->getDotLoc(),
                                expr->getNameLoc(), expr->isImplicit());
    }

    Expr *visitSequenceExpr(SequenceExpr *expr) {
      llvm_unreachable("Expression wasn't parsed?");
    }

    Expr *visitArrowExpr(ArrowExpr *expr) {
      llvm_unreachable("Arrow expr wasn't converted to type?");
    }

    Expr *visitIdentityExpr(IdentityExpr *expr) {
      cs.setType(expr, cs.getType(expr->getSubExpr()));
      return expr;
    }

    /// Given an expression that has a single sub-expression,
    /// resolve and set the type for the expression and coerce
    /// its sub-expression to the resolved type. This is a common
    /// operation for expressions like Copy, Consume, and *Try.
    template <class E>
    Expr *transformExprWithSubExpr(E *expr) {
      simplifyExprType(expr);

      auto *subExpr = expr->getSubExpr();
      subExpr = coerceToType(subExpr, cs.getType(expr),
                             cs.getConstraintLocator(subExpr));
      if (!subExpr)
        return nullptr;

      expr->setSubExpr(subExpr);
      return expr;
    }

    Expr *visitCopyExpr(CopyExpr *expr) {
      return transformExprWithSubExpr(expr);
    }

    Expr *visitConsumeExpr(ConsumeExpr *expr) {
      return transformExprWithSubExpr(expr);
    }

    Expr *visitAnyTryExpr(AnyTryExpr *expr) {
      return transformExprWithSubExpr(expr);
    }

    Expr *visitOptionalTryExpr(OptionalTryExpr *expr) {
      // Prior to Swift 5, 'try?' simply wraps the type of its sub-expression
      // in an Optional, regardless of the sub-expression type.
      //
      // In Swift 5+, the type of a 'try?' expression of static type T is:
      //  - Equal to T if T is optional
      //  - Equal to T? if T is not optional
      //
      // The result is that in Swift 5, 'try?' avoids producing nested optionals.

      if (!ctx.isLanguageModeAtLeast(LanguageMode::v5)) {
        // Nothing to do for Swift 4 and earlier!
        return simplifyExprType(expr);
      }

      return transformExprWithSubExpr(expr);
    }

    Expr *visitParenExpr(ParenExpr *expr) {
      Expr *result = expr;
      auto type = simplifyType(cs.getType(expr));

      // A ParenExpr can end up with a tuple type if it contains
      // a pack expansion. Rewrite it to a TupleExpr.
      if (isa<PackExpansionExpr>(expr->getSubExpr())) {
        result = TupleExpr::create(ctx, expr->getLParenLoc(),
                                   {expr->getSubExpr()},
                                   /*elementNames=*/{},
                                   /*elementNameLocs=*/{},
                                   expr->getRParenLoc(),
                                   expr->isImplicit());
      }

      cs.setType(result, type);
      return result;
    }

    Expr *visitUnresolvedMemberChainResultExpr(
        UnresolvedMemberChainResultExpr *expr) {
      // Since this expression only exists to give the result type of an
      // unresolved member chain visibility in the AST, remove it from the AST
      // now that we have a solution and coerce the subexpr to the resulting
      // type.
      auto *subExpr = expr->getSubExpr();
      auto type = simplifyType(cs.getType(expr));
      subExpr = coerceToType(
          subExpr, type,
          cs.getConstraintLocator(
              expr, ConstraintLocator::UnresolvedMemberChainResult));
      cs.setType(subExpr, type);
      return subExpr;
    }

    Expr *visitTupleExpr(TupleExpr *expr) {
      return simplifyExprType(expr);
    }

    Expr *visitSubscriptExpr(SubscriptExpr *expr) {
      auto *memberLocator =
          cs.getConstraintLocator(expr, ConstraintLocator::SubscriptMember);
      auto overload = solution.getOverloadChoice(memberLocator);
      if (overload.choice.isKeyPathDynamicMemberLookup()) {
        return buildDynamicMemberLookupRef(
            expr, expr->getBase(), expr->getArgs()->getStartLoc(), SourceLoc(),
            overload, memberLocator);
      }

      return buildSubscript(expr->getBase(), expr->getArgs(),
                            cs.getConstraintLocator(expr), memberLocator,
                            expr->isImplicit(), /*isDynamicMemberLookup*/false,
                            expr->getAccessSemantics(),
                            overload);
    }

    /// "Finish" an array expression by filling in the semantic expression.
    ArrayExpr *finishArrayExpr(ArrayExpr *expr) {
      Type arrayTy = cs.getType(expr);
      Type elementType;

      if (arrayTy->isInlineArray() || arrayTy->is_InlineArray()) {
        // <let count: Int, Element>
        elementType = arrayTy->castTo<BoundGenericStructType>()->getGenericArgs()[1];
      } else {
        ProtocolDecl *arrayProto = TypeChecker::getProtocol(
            ctx, expr->getLoc(), KnownProtocolKind::ExpressibleByArrayLiteral);
        assert(arrayProto && "type-checked array literal w/o protocol?!");

        auto conformance = checkConformance(arrayTy, arrayProto);
        assert(conformance && "Type does not conform to protocol?");

        DeclName name(ctx, DeclBaseName::createConstructor(),
                      {ctx.Id_arrayLiteral});
        ConcreteDeclRef witness =
            conformance.getWitnessByName(name);
        if (!witness || !isa<AbstractFunctionDecl>(witness.getDecl()))
          return nullptr;
        expr->setInitializer(witness);

        elementType = expr->getElementType();
      }

      for (unsigned i = 0, n = expr->getNumElements(); i != n; ++i) {
        expr->setElement(
            i, coerceToType(expr->getElement(i), elementType,
                            cs.getConstraintLocator(
                                expr, {LocatorPathElt::TupleElement(i)})));
      }

      return expr;
    }

    Expr *visitArrayExpr(ArrayExpr *expr) {
      Type openedType = cs.getType(expr);
      Type arrayTy = simplifyType(openedType);
      cs.setType(expr, arrayTy);
      if (!finishArrayExpr(expr)) return nullptr;

      // If the array element type was defaulted, note that in the expression.
      if (solution.DefaultedConstraints.count(cs.getConstraintLocator(expr)))
        expr->setIsTypeDefaulted();

      return expr;
    }

    /// "Finish" a dictionary expression by filling in the semantic expression.
    DictionaryExpr *finishDictionaryExpr(DictionaryExpr *expr) {
      Type dictionaryTy = cs.getType(expr);

      ProtocolDecl *dictionaryProto = TypeChecker::getProtocol(
          ctx, expr->getLoc(),
          KnownProtocolKind::ExpressibleByDictionaryLiteral);

      auto conformance = checkConformance(dictionaryTy, dictionaryProto);
      if (conformance.isInvalid())
        return nullptr;

      DeclName name(ctx, DeclBaseName::createConstructor(),
                    {ctx.Id_dictionaryLiteral});
      ConcreteDeclRef witness =
          conformance.getWitnessByName(name);
      if (!witness || !isa<AbstractFunctionDecl>(witness.getDecl()))
        return nullptr;
      expr->setInitializer(witness);

      auto elementType = expr->getElementType();

      for (unsigned i = 0, n = expr->getNumElements(); i != n; ++i) {
        expr->setElement(
            i, coerceToType(expr->getElement(i), elementType,
                            cs.getConstraintLocator(
                                expr, {LocatorPathElt::TupleElement(i)})));
      }

      return expr;
    }

    Expr *visitDictionaryExpr(DictionaryExpr *expr) {
      Type openedType = cs.getType(expr);
      Type dictionaryTy = simplifyType(openedType);
      cs.setType(expr, dictionaryTy);
      if (!finishDictionaryExpr(expr)) return nullptr;

      // If the dictionary key or value type was defaulted, note that in the
      // expression.
      if (solution.DefaultedConstraints.count(cs.getConstraintLocator(expr)))
        expr->setIsTypeDefaulted();

      return expr;
    }

    Expr *visitDynamicSubscriptExpr(DynamicSubscriptExpr *expr) {
      auto *memberLocator =
          cs.getConstraintLocator(expr, ConstraintLocator::SubscriptMember);
      return buildSubscript(expr->getBase(), expr->getArgs(),
                            cs.getConstraintLocator(expr), memberLocator,
                            expr->isImplicit(), /*isDynamicMemberLookup=*/false,
                            AccessSemantics::Ordinary,
                            solution.getOverloadChoice(memberLocator));
    }

    Expr *visitTupleElementExpr(TupleElementExpr *expr) {
      return simplifyExprType(expr);
    }

    Expr *visitCaptureListExpr(CaptureListExpr *expr) {
      // The type of the capture list is the type of the closure contained
      // inside it.
      cs.setType(expr, cs.getType(expr->getClosureBody()));
      return expr;
    }

    Expr *visitClosureExpr(ClosureExpr *expr) {
      llvm_unreachable("Handled by the walker directly");
    }

    Expr *visitAutoClosureExpr(AutoClosureExpr *expr) {
      llvm_unreachable("Already type-checked");
    }

    Expr *visitInOutExpr(InOutExpr *expr) {
      auto objectTy = cs.getType(expr->getSubExpr())->getRValueType();

      // The type is simply inout of whatever the lvalue's object type was.
      cs.setType(expr, InOutType::get(objectTy));
      return expr;
    }

    Expr *visitVarargExpansionExpr(VarargExpansionExpr *expr) {
      return transformExprWithSubExpr(expr);
    }

    Expr *visitPackExpansionExpr(PackExpansionExpr *expr) {
      // Set the opened pack element environment for this pack expansion.
      // Assert that we have an opened element environment, otherwise we'll get
      // an ASTVerifier crash when pack archetypes or element archetypes appear
      // inside the pack expansion expression.
      auto *environment = solution.getPackExpansionEnvironment(expr);
      assert(environment);
      expr->setGenericEnvironment(environment);

      return simplifyExprType(expr);
    }

    Expr *visitPackElementExpr(PackElementExpr *expr) {
      if (auto *packRefExpr = expr->getPackRefExpr()) {
        packRefExpr = cs.coerceToRValue(packRefExpr);
        auto packRefType = cs.getType(packRefExpr);
        if (auto patternType =
                getPatternTypeOfSingleUnlabeledPackExpansionTuple(
                    packRefType)) {
          auto *materializedPackExpr = MaterializePackExpr::create(
              ctx, packRefExpr, packRefExpr->getLoc(),
              patternType, /*implicit*/ true);
          cs.cacheType(materializedPackExpr);
          expr->setPackRefExpr(materializedPackExpr);
        }
      }
      return simplifyExprType(expr);
    }

    Expr *visitMaterializePackExpr(MaterializePackExpr *expr) {
      llvm_unreachable("MaterializePackExpr already type-checked");
    }

    Expr *visitDynamicTypeExpr(DynamicTypeExpr *expr) {
      Expr *base = expr->getBase();
      base = cs.coerceToRValue(base);
      expr->setBase(base);

      return simplifyExprType(expr);
    }

    Expr *visitOpaqueValueExpr(OpaqueValueExpr *expr) {
      return expr;
    }

    Expr *visitPropertyWrapperValuePlaceholderExpr(
        PropertyWrapperValuePlaceholderExpr *expr) {
      // If there is no opaque value placeholder, the enclosing init(wrappedValue:)
      // expression cannot be reused, so we only need the original wrapped value
      // argument in the AST.
      if (!expr->getOpaqueValuePlaceholder())
        return expr->getOriginalWrappedValue();

      return expr;
    }

    Expr *visitAppliedPropertyWrapperExpr(AppliedPropertyWrapperExpr *expr) {
      llvm_unreachable("Already type-checked");
    }

    Expr *visitDefaultArgumentExpr(DefaultArgumentExpr *expr) {
      llvm_unreachable("Already type-checked");
    }

    Expr *visitApplyExpr(ApplyExpr *expr) {
      auto [callLoc, calleeLoc] = CallLocators[expr];
      ASSERT(callLoc && calleeLoc);
      return finishApply(expr, cs.getType(expr), callLoc, calleeLoc);
    }

    Expr *visitRebindSelfInConstructorExpr(RebindSelfInConstructorExpr *expr) {
      Expr *subExpr = expr->getSubExpr();
      auto *inCtor = cast<ConstructorDecl>(dc->getInnermostMethodContext());

      // A non-failable initializer cannot delegate nor chain to
      //  * a throwing initializer via 'try?'
      //  * a failable initializer, unless the failability kind is IUO or the
      //    result is explicitly forced
      if (!inCtor->isFailable() && cs.getType(subExpr)->isOptional()) {
        bool isChaining;
        auto *otherCtorRef = expr->getCalledConstructor(isChaining);
        auto *otherCtor = otherCtorRef->getDecl();
        assert(otherCtor);

        Expr *newSubExpr = subExpr;
        auto &de = ctx.Diags;

        // Look through 'try', 'try!', and identity expressions.
        subExpr = subExpr->getValueProvidingExpr();

        // Diagnose if we find a 'try?'.
        // FIXME: We could put up with occurrences of 'try?' if they do not apply
        // directly to the called ctor, e.g. 'try? try self.init()', or if the
        // called ctor isn't throwing.
        if (auto *OTE = dyn_cast<OptionalTryExpr>(subExpr)) {
          if (SuppressDiagnostics)
            return nullptr;

          // Give the user the option of using 'try!' or making the enclosing
          // initializer failable.
          de.diagnose(OTE->getTryLoc(),
                      diag::delegate_chain_nonoptional_to_optional_try,
                      isChaining);
          de.diagnose(OTE->getTryLoc(), diag::init_delegate_force_try)
              .fixItReplace({OTE->getTryLoc(), OTE->getQuestionLoc()}, "try!");
          de.diagnose(inCtor->getLoc(), diag::init_propagate_failure)
              .fixItInsertAfter(inCtor->getLoc(), "?");

          subExpr = OTE->getSubExpr();
        }

        while (true) {
          subExpr = subExpr->getSemanticsProvidingExpr();

          // Look through optional injections.
          if (auto *IIOE = dyn_cast<InjectIntoOptionalExpr>(subExpr)) {
            subExpr = IIOE->getSubExpr();
            continue;
          }

          // Look through all try expressions.
          if (auto *ATE = dyn_cast<AnyTryExpr>(subExpr)) {
            subExpr = ATE->getSubExpr();
            continue;
          }

          break;
        }

        // If we're still hitting an optional and the called ctor is failable,
        // diagnose only if the failability kind is not IUO. Note that the
        // expression type alone is not always indicative of the ctor's
        // failability, because it could be declared on 'Optional' itself.
        if (cs.getType(subExpr)->isOptional() && otherCtor->isFailable()) {
          if (!otherCtor->isImplicitlyUnwrappedOptional()) {
            if (SuppressDiagnostics)
              return nullptr;

            // Give the user the option of adding '!' or making the enclosing
            // initializer failable.
            de.diagnose(otherCtorRef->getLoc(),
                        diag::delegate_chain_nonoptional_to_optional,
                        isChaining, otherCtor);
            de.diagnose(otherCtorRef->getLoc(), diag::init_force_unwrap)
                .fixItInsertAfter(expr->getEndLoc(), "!");
            de.diagnose(inCtor->getLoc(), diag::init_propagate_failure)
                .fixItInsertAfter(inCtor->getLoc(), "?");
          }

          // Recover by injecting the force operation (the first option).
          newSubExpr = forceUnwrapIUO(newSubExpr);
        }

        expr->setSubExpr(newSubExpr);
      }

      return expr;
    }

    Expr *visitTernaryExpr(TernaryExpr *expr) {
      auto resultTy = simplifyType(cs.getType(expr));
      cs.setType(expr, resultTy);

      auto cond = cs.coerceToRValue(expr->getCondExpr());
      expr->setCondExpr(cond);

      // Coerce the then/else branches to the common type.
      expr->setThenExpr(coerceToType(
          expr->getThenExpr(), resultTy,
          cs.getConstraintLocator(expr, LocatorPathElt::TernaryBranch(true))));
      expr->setElseExpr(coerceToType(
          expr->getElseExpr(), resultTy,
          cs.getConstraintLocator(expr, LocatorPathElt::TernaryBranch(false))));

      return expr;
    }
    
    Expr *visitImplicitConversionExpr(ImplicitConversionExpr *expr) {
      llvm_unreachable("Already type-checked");
    }

    Expr *visitIsExpr(IsExpr *expr) {
      // Turn the subexpression into an rvalue.
      auto sub = cs.coerceToRValue(expr->getSubExpr());
      expr->setSubExpr(sub);

      // Simplify and update the type we checked against.
      auto *const castTypeRepr = expr->getCastTypeRepr();

      const auto fromType = cs.getType(sub);
      const auto toType = simplifyType(cs.getType(castTypeRepr));
      expr->setCastType(toType);
      cs.setType(castTypeRepr, toType);

      auto castKind = TypeChecker::typeCheckCheckedCast(
          fromType, toType, CheckedCastContextKind::IsExpr, dc);

      if (diagnoseUnsupportedFRTDowncast(ctx, expr->getLoc(),
                                         fromType, toType))
        return nullptr;

      switch (castKind) {
      case CheckedCastKind::Unresolved:
        expr->setCastKind(CheckedCastKind::ValueCast);
        break;
          
      case CheckedCastKind::Coercion:
      case CheckedCastKind::BridgingCoercion:
      case CheckedCastKind::ValueCast:
      case CheckedCastKind::ArrayDowncast:
      case CheckedCastKind::DictionaryDowncast:
      case CheckedCastKind::SetDowncast:
        // Valid checks.
        expr->setCastKind(castKind);
        break;
      }

      // SIL-generation magically turns this into a Bool; make sure it can.
      if (!ctx.getBoolBuiltinInitDecl()) {
        ctx.Diags.diagnose(expr->getLoc(), diag::broken_stdlib_type, "Bool");
        // Continue anyway.
      }

      // Dig through the optionals in the from/to types.
      SmallVector<Type, 2> fromOptionals;
      fromType->lookThroughAllOptionalTypes(fromOptionals);
      SmallVector<Type, 2> toOptionals;
      toType->lookThroughAllOptionalTypes(toOptionals);

      // If we have an imbalance of optionals or a collection
      // downcast, handle this as a checked cast followed by a
      // a 'hasValue' check.
      if (fromOptionals.size() != toOptionals.size() ||
          castKind == CheckedCastKind::ArrayDowncast ||
          castKind == CheckedCastKind::DictionaryDowncast ||
          castKind == CheckedCastKind::SetDowncast) {
        auto *const cast =
            ConditionalCheckedCastExpr::createImplicit(ctx, sub, toType);
        cast->setType(OptionalType::get(toType));
        cast->setCastType(toType);
        cs.setType(cast, cast->getType());

        // Type-check this conditional case.
        Expr *result = handleConditionalCheckedCastExpr(cast, castTypeRepr);
        if (!result)
          return nullptr;

        // Extract a Bool from the resulting expression.
        TypeChecker::requireOptionalIntrinsics(ctx, expr->getLoc());

        // Match the optional value against its `Some` case.
        auto *someDecl = ctx.getOptionalSomeDecl();
        auto isSomeExpr =
            new (ctx) EnumIsCaseExpr(result, castTypeRepr, someDecl);
        auto boolDecl = ctx.getBoolDecl();

        if (!boolDecl) {
          ctx.Diags.diagnose(SourceLoc(), diag::broken_stdlib_type, "Bool");
        }

        cs.setType(isSomeExpr, boolDecl ? ctx.getBoolType() : Type());
        return isSomeExpr;
      }

      return expr;
    }

    /// The kind of cast we're working with for handling optional bindings.
    enum class OptionalBindingsCastKind {
      /// An explicit bridging conversion, spelled "as".
      Bridged,
      /// A forced cast, spelled "as!".
      Forced,
      /// A conditional cast, spelled "as?".
      Conditional,
    };

    /// Handle optional operands and results in an explicit cast.
    Expr *handleOptionalBindingsForCast(ExplicitCastExpr *cast, 
                                        Type finalResultType,
                                        OptionalBindingsCastKind castKind) {
      return handleOptionalBindings(cast->getSubExpr(), finalResultType,
                                    castKind,
        [&](Expr *sub, Type resultType) -> Expr* {

        // Complain about conditional casts to CF class types; they can't
        // actually be conditionally checked.
        if (castKind == OptionalBindingsCastKind::Conditional) {
          Type destValueType = resultType->getOptionalObjectType();
          auto destObjectType = destValueType;
          if (auto metaTy = destObjectType->getAs<MetatypeType>())
            destObjectType = metaTy->getInstanceType();
          if (auto destClass = destObjectType->getClassOrBoundGenericClass()) {
            if (destClass->getForeignClassKind() ==
                  ClassDecl::ForeignKind::CFType) {
              if (SuppressDiagnostics)
                return nullptr;

              auto &de = ctx.Diags;
              de.diagnose(cast->getLoc(), diag::conditional_downcast_foreign,
                          destValueType);
              ConcreteDeclRef refDecl = sub->getReferencedDecl();
              if (refDecl) {
                de.diagnose(cast->getLoc(),
                            diag::note_explicitly_compare_cftypeid,
                            refDecl.getDecl()->getBaseName(), destValueType);
              }
            }
          }
        }

        // Set the expression as the sub-expression of the cast, then
        // use the cast as the inner operation.
        cast->setSubExpr(sub);
        cs.setType(cast, resultType);
        return cast;
      });
    }

    /// A helper function to build an operation.  The inner result type
    /// is the expected type of the operation; it will be a non-optional
    /// type unless the castKind is Conditional.
    using OperationBuilderRef =
      llvm::function_ref<Expr*(Expr *subExpr, Type innerResultType)>;

    /// Handle optional operands and results in an explicit cast.
    Expr *handleOptionalBindings(Expr *subExpr, Type finalResultType,
                                 OptionalBindingsCastKind castKind,
                                 OperationBuilderRef buildInnerOperation) {

      unsigned destExtraOptionals;
      bool forceExtraSourceOptionals;
      switch (castKind) {
      case OptionalBindingsCastKind::Bridged:
        destExtraOptionals = 0;
        forceExtraSourceOptionals = true;
        break;

      case OptionalBindingsCastKind::Forced:
        destExtraOptionals = 0;
        forceExtraSourceOptionals = true;
        break;

      case OptionalBindingsCastKind::Conditional:
        destExtraOptionals = 1;
        forceExtraSourceOptionals = false;
        break;
      }

      // FIXME: some of this work needs to be delayed until runtime to
      // properly account for archetypes dynamically being optional
      // types.  For example, if we're casting T to NSView?, that
      // should succeed if T=NSObject? and its value is actually nil.
      Type srcType = cs.getType(subExpr);

      SmallVector<Type, 4> srcOptionals;
      srcType = srcType->lookThroughAllOptionalTypes(srcOptionals);

      SmallVector<Type, 4> destOptionals;
      auto destValueType
        = finalResultType->lookThroughAllOptionalTypes(destOptionals);

      auto isBridgeToAnyObject =
        castKind == OptionalBindingsCastKind::Bridged &&
        destValueType->isAnyObject();

      // If the destination value type is 'AnyObject' when performing a
      // bridging operation, or if the destination value type could dynamically
      // be an optional type, leave any extra optionals on the source in place.
      // Only apply the latter condition in Swift 5 mode to best preserve
      // compatibility with Swift 4.1's casting behaviour.
      if (isBridgeToAnyObject || (ctx.isLanguageModeAtLeast(LanguageMode::v5) &&
                                  destValueType->canDynamicallyBeOptionalType(
                                      /*includeExistential*/ false))) {
        auto destOptionalsCount = destOptionals.size() - destExtraOptionals;
        if (srcOptionals.size() > destOptionalsCount) {
          srcType = srcOptionals[destOptionalsCount];
          srcOptionals.erase(srcOptionals.begin() + destOptionalsCount,
                             srcOptionals.end());
        }
      }

      // When performing a bridging operation, if the destination type
      // is more optional than the source, we'll add extra optional injections
      // at the end.
      SmallVector<Type, 4> destOptionalInjections;
      if (castKind == OptionalBindingsCastKind::Bridged &&
          destOptionals.size() > srcOptionals.size()) {
        // Remove the extra optionals from destOptionals, but keep them around
        // separately so we can perform the injections on the final result of
        // the cast.
        auto cutPoint = destOptionals.end() - srcOptionals.size();
        destOptionalInjections.append(destOptionals.begin(), cutPoint);
        destOptionals.erase(destOptionals.begin(), cutPoint);

        finalResultType = destOptionals.empty() ? destValueType
                                                : destOptionals.front();
      }

      // Local function to add the optional injections to final result.
      auto addFinalOptionalInjections = [&](Expr *result) {
        for (auto destType : llvm::reverse(destOptionalInjections)) {
          result =
              cs.cacheType(new (ctx) InjectIntoOptionalExpr(result, destType));
        }

        return result;
      };

      // There's nothing special to do if the operand isn't optional
      // (or is insufficiently optional) and we don't need any bridging.
      if (srcOptionals.empty()
          || (srcOptionals.size() < destOptionals.size() - destExtraOptionals)) {
        Expr *result = buildInnerOperation(subExpr, finalResultType);
        if (!result) return nullptr;
        return addFinalOptionalInjections(result);
      }

      // The outermost N levels of optionals on the operand must all
      // be present or the cast fails.  The innermost M levels of
      // optionals on the operand are reflected in the requested
      // destination type, so we should map these nils into the result.
      unsigned numRequiredOptionals =
        srcOptionals.size() - (destOptionals.size() - destExtraOptionals);

      // The number of OptionalEvaluationExprs between the point of the
      // inner cast and the enclosing OptionalEvaluationExpr (exclusive)
      // which represents failure for the entire operation.
      unsigned failureDepth = destOptionals.size() - destExtraOptionals;

      // Drill down on the operand until it's non-optional.
      SourceLoc fakeQuestionLoc = subExpr->getEndLoc();
      for (unsigned i : indices(srcOptionals)) {
        Type valueType =
          (i + 1 == srcOptionals.size() ? srcType : srcOptionals[i+1]);

        // As we move into the range of mapped optionals, start
        // lowering the depth.
        unsigned depth = failureDepth;
        if (i >= numRequiredOptionals) {
          depth -= (i - numRequiredOptionals) + 1;
        } else if (forceExtraSourceOptionals) {
          // For a forced cast, force the required optionals.
          subExpr = new (ctx) ForceValueExpr(subExpr, fakeQuestionLoc);
          cs.setType(subExpr, valueType);
          subExpr->setImplicit(true);
          continue;
        }

        subExpr = cs.cacheType(new (ctx) BindOptionalExpr(
            subExpr, fakeQuestionLoc, depth, valueType));
        subExpr->setImplicit(true);
      }

      // If this is a conditional cast, the result type will always
      // have at least one level of optional, which should become the
      // type of the checked-cast expression.
      Expr *result;
      if (castKind == OptionalBindingsCastKind::Conditional) {
        assert(!destOptionals.empty() &&
               "result of checked cast is not an optional type");
        result = buildInnerOperation(subExpr, destOptionals.back());
      } else {
        result = buildInnerOperation(subExpr, destValueType);
      }
      if (!result) return nullptr;

      // If we're casting to an optional type, we need to capture the
      // final M bindings.

      if (destOptionals.size() > destExtraOptionals) {
        if (castKind == OptionalBindingsCastKind::Conditional) {
          // If the innermost cast fails, the entire expression fails.  To
          // get this behavior, we have to bind and then re-inject the result.
          // (SILGen should know how to peephole this.)
          result = cs.cacheType(new (ctx) BindOptionalExpr(
              result, result->getEndLoc(), failureDepth, destValueType));
          result->setImplicit(true);
        }

        for (unsigned i = destOptionals.size(); i != 0; --i) {
          Type destType = destOptionals[i-1];
          result =
              cs.cacheType(new (ctx) InjectIntoOptionalExpr(result, destType));
          result =
              cs.cacheType(new (ctx) OptionalEvaluationExpr(result, destType));
        }

      // Otherwise, we just need to capture the failure-depth binding.
      } else if (!forceExtraSourceOptionals) {
        result = cs.cacheType(
            new (ctx) OptionalEvaluationExpr(result, finalResultType));
      }

      return addFinalOptionalInjections(result);
    }

    bool hasForcedOptionalResult(ExplicitCastExpr *expr) {
      const auto *const TR = expr->getCastTypeRepr();
      if (TR && TR->getKind() == TypeReprKind::ImplicitlyUnwrappedOptional) {
        auto *locator = cs.getConstraintLocator(
            expr, ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice);
        return solution.getDisjunctionChoice(locator);
      }
      return false;
    }

    Expr *visitCoerceExpr(CoerceExpr *expr) {
      auto *coerced = visitCoerceExprImpl(expr);
      if (!coerced)
        return nullptr;

      // If we need to insert a force-unwrap for coercions of the form
      // 'as T!', do so now.
      if (hasForcedOptionalResult(expr))
        coerced = forceUnwrapIUO(coerced);

      return coerced;
    }

    Expr *visitCoerceExprImpl(CoerceExpr *expr) {
      if (auto *castTypeRepr = expr->getCastTypeRepr()) {
        // Simplify and update the type we're coercing to.
        auto toType = simplifyType(cs.getType(castTypeRepr));
        expr->setCastType(toType);
        cs.setType(castTypeRepr, toType);
      } else {
        assert(expr->isImplicit());
        assert(expr->getCastType());
      }

      cs.setType(expr, expr->getCastType());

      // If this is a literal that got converted into constructor call
      // lets put proper source information in place.
      if (expr->isLiteralInit()) {
        auto toType = expr->getCastType();
        auto *literalInit = expr->getSubExpr();
        if (auto *call = dyn_cast<CallExpr>(literalInit)) {
          cs.forEachExpr(call->getFn(), [&](Expr *subExpr) -> Expr * {
            auto *TE = dyn_cast<TypeExpr>(subExpr);
            if (!TE)
              return subExpr;

            auto type = cs.getInstanceType(TE);

            assert(!type->hasError());

            if (!type->isEqual(toType))
              return subExpr;

            return cs.cacheType(TypeExpr::createImplicitHack(
                expr->getLoc(), toType, ctx));
          });
        }

        if (auto *literal = dyn_cast<NumberLiteralExpr>(literalInit)) {
          literal->setExplicitConversion();
        } else {
          literalInit->setImplicit(false);
        }

        // Keep the coercion around, because it contains the source range
        // for the original constructor call.
        return expr;
      }

      // Turn the subexpression into an rvalue.
      auto rvalueSub = cs.coerceToRValue(expr->getSubExpr());
      expr->setSubExpr(rvalueSub);

      // If we weren't explicitly told by the caller which disjunction choice,
      // get it from the solution to determine whether we've picked a coercion
      // or a bridging conversion.
      auto *locator =
          cs.getConstraintLocator(expr, ConstraintLocator::CoercionOperand);
      auto choice = solution.getDisjunctionChoice(locator);

      // Handle the coercion/bridging of the underlying subexpression, where
      // optionality has been removed.
      if (choice == 0) {
        // Convert the subexpression.
        Expr *sub = expr->getSubExpr();
        auto subLoc =
            cs.getConstraintLocator(sub, ConstraintLocator::CoercionOperand);
        sub = solution.coerceToType(sub, expr->getCastType(), subLoc);
        if (!sub)
          return nullptr;

        expr->setSubExpr(sub);
        return expr;
      }

      // Bridging conversion.
      assert(choice == 1 && "should be bridging");

      // Handle optional bindings.
      Expr *sub = handleOptionalBindings(
          expr->getSubExpr(), expr->getCastType(),
          OptionalBindingsCastKind::Bridged,
          [&](Expr *sub, Type toInstanceType) {
            return buildObjCBridgeExpr(sub, toInstanceType, locator);
          });

      if (!sub)
        return nullptr;

      expr->setSubExpr(sub);
      return expr;
    }

    // Rewrite ForcedCheckedCastExpr based on what the solver computed.
    Expr *visitForcedCheckedCastExpr(ForcedCheckedCastExpr *expr) {
      // The subexpression is always an rvalue.
      auto sub = cs.coerceToRValue(expr->getSubExpr());
      expr->setSubExpr(sub);

      const auto fromType = cs.getType(sub);

      Type toType;
      SourceRange castTypeRange;

      // Simplify and update the type we're casting to.
      if (auto *const castTypeRepr = expr->getCastTypeRepr()) {
        toType = simplifyType(cs.getType(castTypeRepr));
        castTypeRange = castTypeRepr->getSourceRange();

        cs.setType(castTypeRepr, toType);
        expr->setCastType(toType);
      } else {
        assert(expr->isImplicit());
        assert(expr->getCastType());

        toType = expr->getCastType();
      }

      if (hasForcedOptionalResult(expr))
        toType = toType->getOptionalObjectType();

      const auto castKind = TypeChecker::typeCheckCheckedCast(
          fromType, toType, CheckedCastContextKind::ForcedCast, dc);

      if (diagnoseUnsupportedFRTDowncast(ctx, expr->getLoc(),
                                         fromType, toType))
        return nullptr;

      switch (castKind) {
        /// Invalid cast.
      case CheckedCastKind::Unresolved:
        if (expr->isImplicit())
          return nullptr;

        expr->setCastKind(CheckedCastKind::ValueCast);
        break;
      case CheckedCastKind::Coercion:
      case CheckedCastKind::BridgingCoercion: {
        expr->setCastKind(castKind);
        cs.setType(expr, toType);
        return expr;
      }

      // Valid casts.
      case CheckedCastKind::ArrayDowncast:
      case CheckedCastKind::DictionaryDowncast:
      case CheckedCastKind::SetDowncast:
      case CheckedCastKind::ValueCast:
        expr->setCastKind(castKind);
        break;
      }

      return handleOptionalBindingsForCast(expr, simplifyType(cs.getType(expr)),
                                           OptionalBindingsCastKind::Forced);
    }

    Expr *visitConditionalCheckedCastExpr(ConditionalCheckedCastExpr *expr) {
      auto *const castTypeRepr = expr->getCastTypeRepr();

      // If there is no type repr, it means this is implicit cast which
      // should have a type set.
      if (!castTypeRepr) {
        assert(expr->isImplicit());
        assert(expr->getCastType());

        auto sub = cs.coerceToRValue(expr->getSubExpr());
        expr->setSubExpr(sub);

        return expr;
      }

      // Simplify and update the type we're casting to.
      const auto toType = simplifyType(cs.getType(castTypeRepr));
      expr->setCastType(toType);

      cs.setType(castTypeRepr, toType);

      // If we need to insert a force-unwrap for coercions of the form
      // 'as! T!', do so now.
      if (hasForcedOptionalResult(expr)) {
        auto *coerced = handleConditionalCheckedCastExpr(expr, castTypeRepr);
        if (!coerced)
          return nullptr;

        return forceUnwrapIUO(coerced);
      }

      return handleConditionalCheckedCastExpr(expr, castTypeRepr);
    }

    Expr *handleConditionalCheckedCastExpr(ConditionalCheckedCastExpr *expr,
                                           TypeRepr *castTypeRepr) {
      assert(castTypeRepr &&
             "cast requires TypeRepr; implicit casts are superfluous");

      // The subexpression is always an rvalue.
      auto sub = cs.coerceToRValue(expr->getSubExpr());
      expr->setSubExpr(sub);

      // Simplify and update the type we're casting to.
      const auto fromType = cs.getType(sub);
      const auto toType = expr->getCastType();

      auto castKind = TypeChecker::typeCheckCheckedCast(
          fromType, toType, CheckedCastContextKind::ConditionalCast, dc);

      if (diagnoseUnsupportedFRTDowncast(ctx, expr->getLoc(),
                                         fromType, toType))
        return nullptr;

      switch (castKind) {
      // Invalid cast.
      case CheckedCastKind::Unresolved:
        expr->setCastKind(CheckedCastKind::ValueCast);
        break;

      case CheckedCastKind::Coercion:
      case CheckedCastKind::BridgingCoercion: {
        expr->setCastKind(castKind);
        cs.setType(expr, OptionalType::get(toType));
        return expr;
      }

      // Valid casts.
      case CheckedCastKind::ArrayDowncast:
      case CheckedCastKind::DictionaryDowncast:
      case CheckedCastKind::SetDowncast:
      case CheckedCastKind::ValueCast:
        expr->setCastKind(castKind);
        break;
      }

      return handleOptionalBindingsForCast(expr, simplifyType(cs.getType(expr)),
                                         OptionalBindingsCastKind::Conditional);
    }

    Expr *visitAssignExpr(AssignExpr *expr) {
      // Convert the source to the simplified destination type.
      auto destTy = simplifyType(cs.getType(expr->getDest()));
      // Conversion is recorded as anchored on an assignment itself by
      // constraint generator and that has to be preserved here in case
      // anything depends on the locator (i.e. Double<->CGFloat implicit
      // conversion).
      Expr *src = coerceToType(expr->getSrc(), destTy->getRValueType(),
                               cs.getConstraintLocator(expr));
      if (!src)
        return nullptr;

      expr->setSrc(src);

      if (!SuppressDiagnostics) {
        // If we're performing an assignment to a weak or unowned variable from
        // a constructor call, emit a warning that the instance will be
        // immediately deallocated.
        diagnoseUnownedImmediateDeallocation(ctx, expr);
      }
      return expr;
    }
    
    Expr *visitDiscardAssignmentExpr(DiscardAssignmentExpr *expr) {
      return simplifyExprType(expr);
    }

    Expr *visitUnresolvedPatternExpr(UnresolvedPatternExpr *expr) {
      llvm_unreachable("Should have diagnosed");
    }

    Expr *visitBindOptionalExpr(BindOptionalExpr *expr) {
      return simplifyExprType(expr);
    }

    Expr *visitOptionalEvaluationExpr(OptionalEvaluationExpr *expr) {
      Type optType = simplifyType(cs.getType(expr));

      // If this is an optional chain that isn't chaining anything, and if the
      // subexpression is already optional (not IUO), then this is a noop:
      // reject it.  This avoids confusion of the model (where the programmer
      // thought it was doing something) and keeps pointless ?'s out of the
      // code.
      if (!SuppressDiagnostics) {
        auto &de = ctx.Diags;
        if (auto *Bind = dyn_cast<BindOptionalExpr>(
                        expr->getSubExpr()->getSemanticsProvidingExpr())) {
          if (cs.getType(Bind->getSubExpr())->isEqual(optType)) {
            de.diagnose(expr->getLoc(), diag::optional_chain_noop, optType)
                .fixItRemove(Bind->getQuestionLoc());
          } else {
            de.diagnose(expr->getLoc(), diag::optional_chain_isnt_chaining);
          }
        }
      }

      Expr *subExpr = coerceToType(expr->getSubExpr(), optType,
                                   cs.getConstraintLocator(expr));
      if (!subExpr) return nullptr;

      expr->setSubExpr(subExpr);
      cs.setType(expr, optType);
      return expr;
    }

    Expr *visitForceValueExpr(ForceValueExpr *expr) {
      return simplifyExprType(expr);
    }

    Expr *visitOpenExistentialExpr(OpenExistentialExpr *expr) {
      llvm_unreachable("Already type-checked");
    }
    
    Expr *visitMakeTemporarilyEscapableExpr(MakeTemporarilyEscapableExpr *expr){
      llvm_unreachable("Already type-checked");
    }

    Expr *visitKeyPathApplicationExpr(KeyPathApplicationExpr *expr){
      // This should already be type-checked, but we may have had to re-
      // check it for failure diagnosis.
      return simplifyExprType(expr);
    }
    
    Expr *visitEnumIsCaseExpr(EnumIsCaseExpr *expr) {
      // Should already be type-checked.
      return simplifyExprType(expr);
    }

    Expr *visitLazyInitializerExpr(LazyInitializerExpr *expr) {
      llvm_unreachable("Already type-checked");
    }
    
    Expr *visitEditorPlaceholderExpr(EditorPlaceholderExpr *E) {
      return simplifyExprType(E);
    }

    Expr *visitObjCSelectorExpr(ObjCSelectorExpr *E) {
      // Dig out the reference to a declaration.
      Expr *subExpr = E->getSubExpr();
      ValueDecl *foundDecl = nullptr;
      while (subExpr) {
        // Declaration reference.
        if (auto declRef = dyn_cast<DeclRefExpr>(subExpr)) {
          foundDecl = declRef->getDecl();
          break;
        }

        // Constructor reference.
        if (auto ctorRef = dyn_cast<OtherConstructorDeclRefExpr>(subExpr)) {
          foundDecl = ctorRef->getDecl();
          break;
        }

        // Member reference.
        if (auto memberRef = dyn_cast<MemberRefExpr>(subExpr)) {
          foundDecl = memberRef->getMember().getDecl();
          break;
        }

        // Dynamic member reference.
        if (auto dynMemberRef = dyn_cast<DynamicMemberRefExpr>(subExpr)) {
          foundDecl = dynMemberRef->getMember().getDecl();
          break;
        }

        // Look through parentheses.
        if (auto paren = dyn_cast<ParenExpr>(subExpr)) {
          subExpr = paren->getSubExpr();
          continue;
        }

        // Look through "a.b" to "b".
        if (auto dotSyntax = dyn_cast<DotSyntaxBaseIgnoredExpr>(subExpr)) {
          subExpr = dotSyntax->getRHS();
          continue;
        }

        // Look through self-rebind expression.
        if (auto rebindSelf = dyn_cast<RebindSelfInConstructorExpr>(subExpr)) {
          subExpr = rebindSelf->getSubExpr();
          continue;
        }

        // Look through optional binding within the monadic "?".
        if (auto bind = dyn_cast<BindOptionalExpr>(subExpr)) {
          subExpr = bind->getSubExpr();
          continue;
        }

        // Look through optional evaluation of the monadic "?".
        if (auto optEval = dyn_cast<OptionalEvaluationExpr>(subExpr)) {
          subExpr = optEval->getSubExpr();
          continue;
        }

        // Look through an implicit force-value.
        if (auto force = dyn_cast<ForceValueExpr>(subExpr)) {
          subExpr = force->getSubExpr();
          continue;
        }

        // Look through implicit open-existential operations.
        if (auto open = dyn_cast<OpenExistentialExpr>(subExpr)) {
          if (open->isImplicit()) {
            subExpr = open->getSubExpr();
            continue;
          }
          break;
        }

        // Look to the referenced member in a self-application.
        if (auto selfApply = dyn_cast<SelfApplyExpr>(subExpr)) {
          subExpr = selfApply->getFn();
          continue;
        }

        // Look through implicit conversions.
        if (auto conversion = dyn_cast<ImplicitConversionExpr>(subExpr)) {
          subExpr = conversion->getSubExpr();
          continue;
        }

        // Look through explicit coercions.
        if (auto coercion = dyn_cast<CoerceExpr>(subExpr)) {
          subExpr = coercion->getSubExpr();
          continue;
        }

        break;
      }

      if (!subExpr) return nullptr;

      // If we didn't find any declaration at all, we're stuck.
      auto &de = ctx.Diags;
      if (!foundDecl) {
        de.diagnose(E->getLoc(), diag::expr_selector_no_declaration)
            .highlight(subExpr->getSourceRange());
        return E;
      }

      // Check whether we found an entity that #selector could refer to.
      // If we found a method or initializer, check it.
      AbstractFunctionDecl *method = nullptr;
      if (auto func = dyn_cast<AbstractFunctionDecl>(foundDecl)) {
        // Methods and initializers.

        // If this isn't a method, complain.
        if (!func->getDeclContext()->isTypeContext()) {
          de.diagnose(E->getLoc(), diag::expr_selector_not_method,
                      func->getDeclContext()->isModuleScopeContext(),
                      func)
              .highlight(subExpr->getSourceRange());
          de.diagnose(func, diag::decl_declared_here, func);
          return E;
        }

        // Check that we requested a method.
        switch (E->getSelectorKind()) {
        case ObjCSelectorExpr::Method:
          break;

        case ObjCSelectorExpr::Getter:
        case ObjCSelectorExpr::Setter:
          // Complain that we cannot ask for the getter or setter of a
          // method.
          de.diagnose(E->getModifierLoc(),
                      diag::expr_selector_expected_property,
                      E->getSelectorKind() == ObjCSelectorExpr::Setter,
                      foundDecl)
              .fixItRemoveChars(E->getModifierLoc(),
                                E->getSubExpr()->getStartLoc());

          // Update the AST to reflect the fix.
          E->overrideObjCSelectorKind(ObjCSelectorExpr::Method, SourceLoc());
          break;
        }

        // Note the method we're referring to.
        method = func;
      } else if (auto var = dyn_cast<VarDecl>(foundDecl)) {
        // Properties.

        // If this isn't a property on a type, complain.
        if (!var->getDeclContext()->isTypeContext()) {
          de.diagnose(E->getLoc(), diag::expr_selector_not_property,
                      isa<ParamDecl>(var), var)
              .highlight(subExpr->getSourceRange());
          de.diagnose(var, diag::decl_declared_here, var);
          return E;
        }

        // Check that we requested a property getter or setter.
        switch (E->getSelectorKind()) {
        case ObjCSelectorExpr::Method: {
          bool isSettable = var->isSettable(dc) &&
            var->isSetterAccessibleFrom(dc);
          auto primaryDiag =
              de.diagnose(E->getLoc(), diag::expr_selector_expected_method,
                          isSettable, var);
          primaryDiag.highlight(subExpr->getSourceRange());

          // The point at which we will insert the modifier.
          SourceLoc modifierLoc = E->getSubExpr()->getStartLoc();

          // If the property is settable, we don't know whether the
          // user wanted the getter or setter. Provide notes for each.
          if (isSettable) {
            // Add notes for the getter and setter, respectively.
            de.diagnose(modifierLoc, diag::expr_selector_add_modifier, false,
                        var)
                .fixItInsert(modifierLoc, "getter: ");
            de.diagnose(modifierLoc, diag::expr_selector_add_modifier, true,
                        var)
                .fixItInsert(modifierLoc, "setter: ");

            // Bail out now. We don't know what the user wanted, so
            // don't fill in the details.
            return E;
          }

          // The property is non-settable, so add "getter:".
          primaryDiag.fixItInsert(modifierLoc, "getter: ");
          E->overrideObjCSelectorKind(ObjCSelectorExpr::Getter, modifierLoc);
          method = var->getOpaqueAccessor(AccessorKind::Get);
          break;
        }

        case ObjCSelectorExpr::Getter:
          method = var->getOpaqueAccessor(AccessorKind::Get);
          break;

        case ObjCSelectorExpr::Setter:
          // Make sure we actually have a setter.
          if (!var->isSettable(dc)) {
            de.diagnose(E->getLoc(), diag::expr_selector_property_not_settable,
                        var);
            de.diagnose(var, diag::decl_declared_here, var);
            return E;
          }

          // Make sure the setter is accessible.
          if (!var->isSetterAccessibleFrom(dc)) {
            de.diagnose(E->getLoc(),
                        diag::expr_selector_property_setter_inaccessible, var);
            de.diagnose(var, diag::decl_declared_here, var);
            return E;
          }

          method = var->getOpaqueAccessor(AccessorKind::Set);
          break;
        }
      } else {
        // Cannot reference with #selector.
        de.diagnose(E->getLoc(), diag::expr_selector_no_declaration)
            .highlight(subExpr->getSourceRange());
        de.diagnose(foundDecl, diag::decl_declared_here, foundDecl);
        return E;
      }
      assert(method && "Didn't find a method?");

      // The declaration we found must be exposed to Objective-C.
      if (!method->isObjC()) {
        // If the method declaration lies in a protocol and we're providing
        // a default implementation of the method through a protocol extension
        // and using it as a selector, then bail out as adding @objc to the
        // protocol might not be the right thing to do and could lead to
        // problems.
        if (auto protocolDecl = dyn_cast<ProtocolDecl>(foundDecl->getDeclContext())) {
          de.diagnose(E->getLoc(), diag::expr_selector_cannot_be_used,
                      foundDecl, protocolDecl);
          return E;
        }

        de.diagnose(E->getLoc(), diag::expr_selector_not_objc, foundDecl)
            .highlight(subExpr->getSourceRange());
        de.diagnose(foundDecl, diag::make_decl_objc, foundDecl)
            .fixItInsert(foundDecl->getAttributeInsertionLoc(false), "@objc ");
        return E;
      }

      // Note which method we're referencing.
      E->setMethod(method);
      return E;
    }

    Expr *visitKeyPathExpr(KeyPathExpr *E) {
      if (E->isObjC()) {
        cs.setType(E, cs.getType(E->getObjCStringLiteralExpr()));
        return E;
      }

      simplifyExprType(E);
      
      if (cs.getType(E)->hasError())
        return E;

      // If a component is already resolved, then all of them should be
      // resolved, and we can let the expression be. This might happen when
      // re-checking a failed system for diagnostics.
      if (!E->getComponents().empty()
          && E->getComponents().front().isResolved()) {
        assert([&]{
          for (auto &c : E->getComponents())
            if (!c.isResolved())
              return false;
          return true;
        }());
        return E;
      }

      SmallVector<KeyPathExpr::Component, 4> resolvedComponents;

      // Resolve each of the components.
      bool didOptionalChain = false;
      bool isFunctionType = false;
      auto baseTy = cs.simplifyType(solution.getKeyPathRootType(E));
      Type leafTy;
      Type exprType = cs.getType(E);
      if (auto fnTy = exprType->getAs<FunctionType>()) {
        leafTy = fnTy->getResult();
        isFunctionType = true;
      } else if (auto *existential = exprType->getAs<ExistentialType>()) {
        auto layout = existential->getExistentialLayout();
        auto keyPathTy = layout.explicitSuperclass->castTo<BoundGenericType>();
        leafTy = keyPathTy->getGenericArgs()[1];
      } else {
        auto keyPathTy = exprType->castTo<BoundGenericType>();
        leafTy = keyPathTy->getGenericArgs()[1];
      }

      // Track the type of the current component. Once we finish projecting
      // through each component of the key path, we should reach the leafTy.
      auto componentTy = baseTy;
      for (unsigned i : indices(E->getComponents())) {
        auto &origComponent = E->getMutableComponents()[i];

        auto kind = origComponent.getKind();
        auto componentLocator =
            cs.getConstraintLocator(E, LocatorPathElt::KeyPathComponent(i));

        // Get a locator such that it includes any additional elements to point
        // to the component's callee, e.g a SubscriptMember for a subscript
        // component.
        auto calleeLoc = cs.getCalleeLocator(componentLocator);

        bool isDynamicMember = false;
        // If this is an unresolved link, make sure we resolved it.
        if (kind == KeyPathExpr::Component::Kind::UnresolvedMember ||
            kind == KeyPathExpr::Component::Kind::UnresolvedSubscript) {
          auto foundDecl = solution.getOverloadChoice(calleeLoc);
          isDynamicMember = foundDecl.choice.isAnyDynamicMemberLookup();

          // If this was a @dynamicMemberLookup property, then we actually
          // form a subscript reference, so switch the kind.
          if (isDynamicMember) {
            kind = KeyPathExpr::Component::Kind::UnresolvedSubscript;
          }
        }

        switch (kind) {
        case KeyPathExpr::Component::Kind::UnresolvedMember: {
          buildKeyPathMemberComponent(solution.getOverloadChoice(calleeLoc),
                                      origComponent.getLoc(), calleeLoc,
                                      resolvedComponents);
          break;
        }
        case KeyPathExpr::Component::Kind::UnresolvedSubscript: {
          buildKeyPathSubscriptComponent(
              solution.getOverloadChoice(calleeLoc), origComponent.getLoc(),
              origComponent.getArgs(), componentLocator, resolvedComponents);
          break;
        }
        case KeyPathExpr::Component::Kind::UnresolvedApply: {
          // Get the calleeLoc of the member that requires application
          // resolution of its arguments. Keypath methods are a member component
          // followed by an unapplied component, so use the member component to
          // find the overload.
          auto memberComponentLoc = cs.getConstraintLocator(
              E, LocatorPathElt::KeyPathComponent(i - 1));

          buildKeyPathApplyComponent(
              solution.getOverloadChoice(memberComponentLoc),
              origComponent.getLoc(), origComponent.getArgs(), componentLocator,
              memberComponentLoc, resolvedComponents);
          break;
        }
        case KeyPathExpr::Component::Kind::OptionalChain: {
          didOptionalChain = true;
          // Chaining always forces the element to be an rvalue.
          auto objectTy =
              componentTy->getWithoutSpecifierType()->getOptionalObjectType();
          assert(objectTy);
          
          auto loc = origComponent.getLoc();
          resolvedComponents.push_back(
              KeyPathExpr::Component::forOptionalChain(objectTy, loc));
          break;
        }
        case KeyPathExpr::Component::Kind::OptionalForce: {
          // Handle force optional when it is the first component e.g.
          // \String?.!.count
          if (resolvedComponents.empty()) {
            auto loc = origComponent.getLoc();
            auto objectTy = componentTy->getOptionalObjectType();
            assert(objectTy);

            resolvedComponents.push_back(
                KeyPathExpr::Component::forOptionalForce(objectTy, loc));
          } else {
            buildKeyPathOptionalForceComponent(resolvedComponents);
          }
          break;
        }
        case KeyPathExpr::Component::Kind::Identity: {
          auto component = origComponent;
          component.setComponentType(componentTy);
          resolvedComponents.push_back(component);
          break;
        }
        case KeyPathExpr::Component::Kind::CodeCompletion:
          llvm_unreachable("solver-based completion shouldn't do CSApply");
          break;
        case KeyPathExpr::Component::Kind::Invalid:
          llvm_unreachable("should have been diagnosed");
          break;
        case KeyPathExpr::Component::Kind::Member:
        case KeyPathExpr::Component::Kind::Subscript:
        case KeyPathExpr::Component::Kind::Apply:
        case KeyPathExpr::Component::Kind::OptionalWrap:
        case KeyPathExpr::Component::Kind::TupleElement:
          llvm_unreachable("already resolved");
          break;
        case KeyPathExpr::Component::Kind::DictionaryKey:
          llvm_unreachable("DictionaryKey only valid in #keyPath");
          break;
        }
        componentTy = resolvedComponents.back().getComponentType();
      }

      // Wrap a non-optional result if there was chaining involved.
      if (didOptionalChain &&
          !componentTy->getWithoutSpecifierType()->isEqual(leafTy)) {
        auto component = KeyPathExpr::Component::forOptionalWrap(leafTy);
        resolvedComponents.push_back(component);
        // Optional chaining forces the component to be r-value.
        componentTy = OptionalType::get(componentTy->getWithoutSpecifierType());
      }

      // Set the resolved components, and cache their types.
      E->setComponents(ctx, resolvedComponents);
      cs.cacheExprTypes(E);

      // See whether there's an equivalent ObjC key path string we can produce
      // for interop purposes.
      checkAndSetObjCKeyPathString(E);

      if (!isFunctionType)
        return E;

      // If we've gotten here, the user has used key path literal syntax to form
      // a closure. The type checker has given E a function type to indicate
      // this.
      //
      // Since functions support more conversions than generic types, we may
      // have ended up with a type of (baseTy) -> leafTy, where the actual type
      // of the key path is some subclass of KeyPath<baseTy, componentTy>, and
      // with componentTy: leafTy.
      //
      // We're going to change E's type to KeyPath<baseTy, componentTy> and
      // then wrap it in a larger closure expression which we will convert to
      // appropriate type.

      auto kpResultTy = componentTy->getWithoutSpecifierType();

      // Compute KeyPath<baseTy, leafTy> and set E's type back to it.
      auto kpDecl = ctx.getKeyPathDecl();
      auto keyPathTy =
          BoundGenericType::get(kpDecl, nullptr, { baseTy, kpResultTy });
      E->setType(keyPathTy);
      cs.cacheType(E);

      // To ensure side effects of the key path expression (mainly indices in
      // subscripts) are only evaluated once, we use a capture list to evaluate
      // the key path immediately and capture it in the function value created.
      // The result looks like:
      //
      //     return "{ [$kp$ = \(E)] in $0[keyPath: $kp$] }"

      FunctionType::ExtInfo closureInfo;
      auto closureTy =
          FunctionType::get({FunctionType::Param(baseTy)}, kpResultTy,
                            closureInfo);
      auto closure = new (ctx)
          AutoClosureExpr(/*set body later*/nullptr, kpResultTy, dc);

      auto param = new (ctx) ParamDecl(
          SourceLoc(),
          /*argument label*/ SourceLoc(), Identifier(),
          /*parameter name*/ SourceLoc(), ctx.getIdentifier("$0"), closure);
      param->setInterfaceType(baseTy->mapTypeOutOfEnvironment());
      param->setSpecifier(ParamSpecifier::Default);
      param->setImplicit();
      
      auto params = ParameterList::create(ctx, SourceLoc(),
                                          param, SourceLoc());

      closure->setParameterList(params);

      // The capture list.
      VarDecl *outerParam = new (ctx) VarDecl(/*static*/ false,
                                          VarDecl::Introducer::Let,
                                          SourceLoc(),
                                          ctx.getIdentifier("$kp$"),
                                          dc);
      outerParam->setImplicit();
      outerParam->setInterfaceType(keyPathTy->mapTypeOutOfEnvironment());

      auto *outerParamPat =
          NamedPattern::createImplicit(ctx, outerParam, keyPathTy);

      solution.setExprTypes(E);
      auto *outerParamDecl = PatternBindingDecl::createImplicit(
          ctx, StaticSpellingKind::None, outerParamPat, E, dc);

      auto outerParamCapture = CaptureListEntry(outerParamDecl);
      auto captureExpr = CaptureListExpr::create(ctx, outerParamCapture,
                                                 closure);
      captureExpr->setImplicit();

      // let paramRef = "$0"
      auto *paramRef = new (ctx)
          DeclRefExpr(param, DeclNameLoc(E->getLoc()), /*Implicit=*/true);
      paramRef->setType(baseTy);
      cs.cacheType(paramRef);

      // let outerParamRef = "$kp$"
      auto outerParamRef = new (ctx)
          DeclRefExpr(outerParam, DeclNameLoc(E->getLoc()), /*Implicit=*/true);
      outerParamRef->setType(keyPathTy);
      cs.cacheType(outerParamRef);

      // let application = "\(paramRef)[keyPath: \(outerParamRef)]"
      auto *application = new (ctx)
          KeyPathApplicationExpr(paramRef,
                                 E->getStartLoc(), outerParamRef, E->getEndLoc(),
                                 kpResultTy, /*implicit=*/true);
      cs.cacheType(application);

      // Finish up the inner closure.
      closure->setParameterList(ParameterList::create(ctx, {param}));
      closure->setBody(application);
      closure->setType(closureTy);
      cs.cacheType(closure);
      
      captureExpr->setType(closureTy);
      cs.cacheType(captureExpr);
      
      return coerceToType(captureExpr, exprType, cs.getConstraintLocator(E));
    }

    void buildKeyPathOptionalForceComponent(
        SmallVectorImpl<KeyPathExpr::Component> &components) {
      assert(!components.empty());

      // Unwrap the last component type, preserving @lvalue-ness.
      auto optionalTy = components.back().getComponentType();
      Type objectTy;
      if (auto lvalue = optionalTy->getAs<LValueType>()) {
        objectTy = lvalue->getObjectType()->getOptionalObjectType();
        objectTy = LValueType::get(objectTy);
      } else {
        objectTy = optionalTy->getOptionalObjectType();
      }
      assert(objectTy);

      auto loc = components.back().getLoc();
      components.push_back(
          KeyPathExpr::Component::forOptionalForce(objectTy, loc));
    }

    void buildKeyPathMemberComponent(
        const SelectedOverload &overload, SourceLoc componentLoc,
        ConstraintLocator *locator,
        SmallVectorImpl<KeyPathExpr::Component> &components) {
      auto resolvedTy = simplifyType(overload.adjustedOpenedType);
      if (auto *member = overload.choice.getDeclOrNull()) {
        if (auto varDecl = dyn_cast<VarDecl>(member)) {
          // Key paths don't work with mutating-get properties.
          assert(!varDecl->isGetterMutating());
        }

        // Compute the concrete reference to the member.
        auto ref = resolveConcreteDeclRef(member, locator);
        components.push_back(
            KeyPathExpr::Component::forMember(ref, resolvedTy, componentLoc));
      } else {
        auto fieldIndex = overload.choice.getTupleIndex();
        components.push_back(KeyPathExpr::Component::forTupleElement(
            fieldIndex, resolvedTy, componentLoc));
      }

      auto unwrapCount =
          getIUOForceUnwrapCount(locator, IUOReferenceKind::Value);
      for (unsigned i = 0; i < unwrapCount; ++i)
        buildKeyPathOptionalForceComponent(components);
    }

    void buildKeyPathSubscriptComponent(
        const SelectedOverload &overload, SourceLoc componentLoc,
        ArgumentList *args, ConstraintLocator *locator,
        SmallVectorImpl<KeyPathExpr::Component> &components) {
      auto subscript = cast<SubscriptDecl>(overload.choice.getDecl());
      assert(!subscript->isGetterMutating());
      auto memberLoc = cs.getCalleeLocator(locator);

      // Compute substitutions to refer to the member.
      auto ref = resolveConcreteDeclRef(subscript, memberLoc);

      // If this is a `@dynamicMemberLookup` reference to resolve a property
      // through the `subscript(dynamicMember:...)` member, restore the
      // openedType and origComponent to its full reference as if the user
      // wrote out the subscript manually.
      if (overload.choice.isAnyDynamicMemberLookup()) {
        args = buildArgumentListForDynamicMemberLookupSubscript(
            overload, componentLoc, locator);
      }

      auto subscriptType =
          simplifyType(overload.adjustedOpenedType)->castTo<AnyFunctionType>();
      auto resolvedTy = subscriptType->getResult();

      // Coerce the indices to the type the subscript expects.
      args = coerceCallArguments(
          args, subscriptType, ref, /*applyExpr*/ nullptr,
          cs.getConstraintLocator(locator, ConstraintLocator::ApplyArgument),
          /*appliedPropertyWrappers*/ {});

      // We need to be able to hash the captured index values in order for
      // KeyPath itself to be hashable, so check that all of the subscript
      // index components are hashable and collect their conformances here.
      SmallVector<ProtocolConformanceRef, 4> conformances;

      auto hashable = ctx.getProtocol(KnownProtocolKind::Hashable);

      auto fnType = overload.adjustedOpenedType->castTo<FunctionType>();
      SmallVector<Identifier, 4> newLabels;
      for (auto &param : fnType->getParams()) {
        newLabels.push_back(param.getLabel());

        auto indexType = simplifyType(param.getParameterType());
        // Index type conformance to Hashable protocol has been
        // verified by the solver, we just need to get it again
        // with all of the generic parameters resolved.
        auto hashableConformance = checkConformance(indexType, hashable);
        assert(hashableConformance);

        conformances.push_back(hashableConformance);
      }

      auto comp = KeyPathExpr::Component::forSubscript(
          ctx, ref, args, resolvedTy, ctx.AllocateCopy(conformances));
      components.push_back(comp);

      auto unwrapCount =
          getIUOForceUnwrapCount(memberLoc, IUOReferenceKind::ReturnValue);
      for (unsigned i = 0; i < unwrapCount; ++i)
        buildKeyPathOptionalForceComponent(components);
    }

    void buildKeyPathApplyComponent(
        const SelectedOverload &overload, SourceLoc componentLoc,
        ArgumentList *args, ConstraintLocator *applyLocator,
        ConstraintLocator *memberLocator,
        SmallVectorImpl<KeyPathExpr::Component> &components) {
      auto &ctx = cs.getASTContext();
      auto fnType = overload.adjustedOpenedType->castTo<FunctionType>();

      // Compute substitutions to refer to the member.
      auto ref =
          resolveConcreteDeclRef(overload.choice.getDecl(), memberLocator);

      // Coerce the args to the type the method/initializer expects.
      args = coerceCallArguments(
          args, fnType, ref, /*applyExpr*/ nullptr,
          cs.getConstraintLocator(applyLocator,
                                  ConstraintLocator::ApplyArgument),
          /*appliedPropertyWrappers*/ {});

      // We need to be able to hash the captured args in order for KeyPath
      // itself to be hashable, so check that all of the arg components are
      // hashable and collect their conformances here.
      SmallVector<ProtocolConformanceRef, 4> conformances;

      auto hashable = ctx.getProtocol(KnownProtocolKind::Hashable);

      SmallVector<Identifier, 4> newLabels;
      for (auto &param : fnType->getParams()) {
        newLabels.push_back(param.getLabel());

        auto indexType = simplifyType(param.getParameterType());
        // Arg type conformance to Hashable protocol has been
        // verified by the solver, we just need to get it again
        // with all of the generic parameters resolved.
        auto hashableConformance = checkConformance(indexType, hashable);
        assert(hashableConformance);

        conformances.push_back(hashableConformance);
      }

      auto comp = KeyPathExpr::Component::forApply(
          args, fnType->getResult(), ctx.AllocateCopy(conformances));
      components.push_back(comp);

      auto unwrapCount =
          getIUOForceUnwrapCount(applyLocator, IUOReferenceKind::ReturnValue);
      for (unsigned i = 0; i < unwrapCount; ++i)
        buildKeyPathOptionalForceComponent(components);
    }

    Expr *visitCurrentContextIsolationExpr(CurrentContextIsolationExpr *E) {
      E->setType(simplifyType(cs.getType(E)));
      return E;
    }

    Expr *visitExtractFunctionIsolationExpr(ExtractFunctionIsolationExpr *E) {
      llvm_unreachable("found ExtractFunctionIsolationExpr in CSApply");
    }

    Expr *visitKeyPathDotExpr(KeyPathDotExpr *E) {
      llvm_unreachable("found KeyPathDotExpr in CSApply");
    }

    Expr *visitSingleValueStmtExpr(SingleValueStmtExpr *E) {
      llvm_unreachable("Handled by the walker directly");
    }

    Expr *visitTapExpr(TapExpr *E) {
      auto type = simplifyType(cs.getType(E));

      E->getVar()->setInterfaceType(type->mapTypeOutOfEnvironment());

      cs.setType(E, type);
      E->setType(type);

      return E;
    }

    Expr *visitTypeJoinExpr(TypeJoinExpr *E) {
      llvm_unreachable("already type-checked?");
    }

    Expr *visitMacroExpansionExpr(MacroExpansionExpr *E) {
      auto expandedType = solution.simplifyType(solution.getType(E));
      cs.setType(E, expandedType);

      auto locator = cs.getConstraintLocator(E);
      auto overload = solution.getOverloadChoice(locator);

      auto macro = cast<MacroDecl>(overload.choice.getDecl());
      ConcreteDeclRef macroRef = resolveConcreteDeclRef(macro, locator);
      E->setMacroRef(macroRef);
      E->setType(expandedType);

      auto fnType =
          simplifyType(overload.adjustedOpenedType)->castTo<FunctionType>();

      auto newArgs = coerceCallArguments(
          E->getArgs(), fnType, macroRef, /*applyExpr=*/nullptr,
          cs.getConstraintLocator(E, ConstraintLocator::ApplyArgument),
          solution.getAppliedPropertyWrappers(E));
      if (!newArgs)
        return nullptr;

      E->setArgs(newArgs);

      // FIXME: Expansion should be lazy.
      // i.e. 'ExpandMacroExpansionExprRequest' should be sinked into
      // 'getRewritten()', and performed on-demand. Unfortunately that requires
      // auditing every ASTWalker's `getMacroWalkingBehavior` since
      // `MacroWalking::Expansion` does not actually kick expansion.
      if (!cs.Options.contains(ConstraintSystemFlags::DisableMacroExpansions) &&
          // Do not expand macros inside macro arguments. For example for
          // '#stringify(#assert(foo))' when typechecking `#assert(foo)`,
          // we don't want to expand it.
          llvm::none_of(llvm::ArrayRef(ExprStack).drop_back(1),
                        [](Expr *E) { return isa<MacroExpansionExpr>(E); })) {
        // We need to delay the expansion until we're done applying the solution
        // since running MiscDiagnostics on the expansion may walk up and query
        // the type of a parent closure, e.g `diagnoseImplicitSelfUseInClosure`.
        addMacroToExpand(E);
      }

      cs.cacheExprTypes(E);
      return E;
    }

    ApplyExpr *preWalkApplyExpr(ApplyExpr *apply) {
      // If we've already seen this apply, continue. This is necessary since
      // we can nest the apply in an outer `callAsFunction` apply below.
      if (CallLocators.contains(apply))
        return apply;

      // Make a note of its call/callee locator prior to rewriting.
      auto *callLoc = cs.getConstraintLocator(apply);
      auto *calleeLoc = cs.getCalleeLocator(callLoc);
      CallLocators.insert({apply, {callLoc, calleeLoc}});

      // Fix-up the apply if needed for an implicit `callAsFunction` for cases
      // where we've split e.g `T(...) {}` into `T(...).callAsFunction({})`.
      auto callAsFnIter = solution.ImplicitCallAsFunctions.find(calleeLoc);
      if (callAsFnIter != solution.ImplicitCallAsFunctions.end()) {
        // Update the argument list to only include the non-trailing args.
        apply->setArgs(callAsFnIter->second.BaseArgs);

        // Pull out the new args and form the call to `callAsFunction`.
        auto *dotExpr = callAsFnIter->second.Member;
        auto *newArgs = solution.getArgumentList(
            cs.getConstraintLocator(dotExpr, ConstraintLocator::ApplyArgument));

        auto *implicitCall = CallExpr::createImplicit(ctx, apply, newArgs);
        cs.setType(implicitCall, solution.getResolvedType(dotExpr));

        auto *memberLoc = cs.getConstraintLocator(dotExpr);
        auto *memberCalleeLoc = cs.getConstraintLocator(
            memberLoc, {LocatorPathElt::ApplyFunction(),
                        LocatorPathElt::ImplicitCallAsFunction()});
        CallLocators.insert({implicitCall, {memberLoc, memberCalleeLoc}});
        return implicitCall;
      }
      return apply;
    }

    /// Interface for ExprWalker
    Expr *walkToExprPre(Expr *expr) {
      if (auto *apply = dyn_cast<ApplyExpr>(expr))
        expr = preWalkApplyExpr(apply);

      ExprStack.push_back(expr);
      return expr;
    }

    Expr *walkToExprPost(Expr *expr) {
      Expr *result = visit(expr);

      assert(expr == ExprStack.back());
      ExprStack.pop_back();

      return result;
    }

    void finalize() {
      assert(ExprStack.empty());
      assert(OpenedExistentials.empty());

      // Look at all of the suspicious optional injections
      for (auto injection : SuspiciousOptionalInjections) {
        if (auto *cast = findForcedDowncast(ctx, injection->getSubExpr())) {
          if (!isa<ParenExpr>(injection->getSubExpr())) {
            ctx.Diags.diagnose(
                injection->getLoc(), diag::inject_forced_downcast,
                cs.getType(injection->getSubExpr())->getRValueType());
            auto exclaimLoc = cast->getExclaimLoc();
            ctx.Diags
                .diagnose(exclaimLoc, diag::forced_to_conditional_downcast,
                          cs.getType(injection)->getOptionalObjectType())
                .fixItReplace(exclaimLoc, "?");
            ctx.Diags
                .diagnose(cast->getStartLoc(),
                          diag::silence_inject_forced_downcast)
                .fixItInsert(cast->getStartLoc(), "(")
                .fixItInsertAfter(cast->getEndLoc(), ")");
          }
        }
      }

      // Diagnose the implicit coercions of noncopyable values that happen in
      // a context where it isn't "obviously" consuming already.
      for (auto *coercion : ConsumingCoercions) {
        assert(coercion->isImplicit());
        ctx.Diags
            .diagnose(coercion->getLoc(),
                      diag::consume_expression_needed_for_cast,
                      cs.getType(coercion));
        ctx.Diags
            .diagnose(coercion->getLoc(),
                      diag::add_consume_to_silence)
            .fixItInsert(coercion->getStartLoc(), "consume ");
      }

      // Type-check any local decls encountered.
      for (auto *D : LocalDeclsToTypeCheck)
        TypeChecker::typeCheckDecl(D);

      // Expand any macros encountered.
      // FIXME: Expansion should be lazy.
      auto &eval = ctx.evaluator;
      for (auto *E : MacrosToExpand) {
        (void)evaluateOrDefault(eval, ExpandMacroExpansionExprRequest{E},
                                std::nullopt);
      }
    }

    /// Diagnose an optional injection that is probably not what the
    /// user wanted, because it comes from a forced downcast, or from an
    /// implicitly consumed noncopyable type.
    void diagnoseOptionalInjection(InjectIntoOptionalExpr *injection,
                                   ConstraintLocatorBuilder locator) {
      // Check whether we have a forced downcast.
      if (findForcedDowncast(ctx, injection->getSubExpr()))
        SuspiciousOptionalInjections.push_back(injection);

      /// Check if it needs an explicit consume, due to this being a cast.
      auto *module = dc->getParentModule();
      auto origType = solution.getResolvedType(injection->getSubExpr());
      if (willHaveConfusingConsumption(origType, locator, cs) &&
          canAddExplicitConsume(solution, module, injection->getSubExpr()))
        ConsumingCoercions.push_back(injection);
    }

    void diagnoseExistentialErasureOf(Expr *fromExpr, Expr *toExpr,
                                      ConstraintLocatorBuilder locator) {
      auto *module = dc->getParentModule();
      auto fromType = solution.getResolvedType(fromExpr);
      if (willHaveConfusingConsumption(fromType, locator, cs) &&
          canAddExplicitConsume(solution, module, fromExpr)) {
        ConsumingCoercions.push_back(toExpr);
      }
    }
  };
} // end anonymous namespace

ConcreteDeclRef
Solution::resolveLocatorToDecl(ConstraintLocator *locator) const {
  // Get the callee locator without looking through applies, ensuring we only
  // return a decl for a direct reference.
  auto *calleeLoc =
      constraintSystem->getCalleeLocator(locator, /*lookThroughApply*/ false);
  auto overload = getOverloadChoiceIfAvailable(calleeLoc);
  if (!overload)
    return ConcreteDeclRef();

  return resolveConcreteDeclRef(overload->choice.getDeclOrNull(), locator);
}

/// Returns the concrete callee which 'owns' the default argument at a given
/// index. This looks through inheritance for inherited default args.
static ConcreteDeclRef getDefaultArgOwner(ConcreteDeclRef owner,
                                          unsigned index) {
  auto *param = getParameterAt(owner, index);
  assert(param);
  if (param->getDefaultArgumentKind() == DefaultArgumentKind::Inherited) {
    return getDefaultArgOwner(owner.getOverriddenDecl(), index);
  }
  return owner;
}

static bool canPeepholeTupleConversion(Expr *expr,
                                       ArrayRef<unsigned> sources) {
  if (!isa<TupleExpr>(expr))
    return false;

  for (unsigned i = 0, e = sources.size(); i != e; ++i) {
    if (sources[i] != i)
      return false;
  }

  return true;
}

Expr *ExprRewriter::coerceTupleToTuple(Expr *expr,
                                       TupleType *fromTuple,
                                       TupleType *toTuple,
                                       ConstraintLocatorBuilder locator,
                                       ArrayRef<unsigned> sources) {
  // If the input expression is a tuple expression, we can convert it in-place.
  if (canPeepholeTupleConversion(expr, sources)) {
    auto *tupleExpr = cast<TupleExpr>(expr);

    for (unsigned i = 0, e = tupleExpr->getNumElements(); i != e; ++i) {
      auto *fromElt = tupleExpr->getElement(i);

      // Actually convert the source element.
      auto toEltType = toTuple->getElementType(i);

      auto *toElt
        = coerceToType(fromElt, toEltType,
                      locator.withPathElement(
                        LocatorPathElt::TupleElement(i)));
      if (!toElt)
        return nullptr;

      tupleExpr->setElement(i, toElt);
    }

    tupleExpr->setType(toTuple);
    cs.cacheType(tupleExpr);

    return tupleExpr;
  }

  // Build a list of OpaqueValueExprs that matches the structure
  // of expr's type.
  //
  // Each OpaqueValueExpr's type is equal to the type of the
  // corresponding element of fromTuple.
  SmallVector<OpaqueValueExpr *, 4> destructured;
  for (unsigned i = 0, e = sources.size(); i != e; ++i) {
    auto fromEltType = fromTuple->getElementType(i);
    auto *opaqueElt =
        new (ctx) OpaqueValueExpr(expr->getSourceRange(), fromEltType);
    cs.cacheType(opaqueElt);
    destructured.push_back(opaqueElt);
  }

  // Convert each OpaqueValueExpr to the correct type.
  SmallVector<Expr *, 4> converted;
  SmallVector<Identifier, 4> origLabels;
  SmallVector<Identifier, 4> labels;
  SmallVector<TupleTypeElt, 4> convertedElts;

  bool anythingShuffled = false;
  for (unsigned i = 0, e = sources.size(); i != e; ++i) {
    unsigned source = sources[i];
    auto *fromElt = destructured[source];
    auto fromLabel = fromTuple->getElement(i).getName();

    // Actually convert the source element.
    auto toEltType = toTuple->getElementType(i);
    auto toLabel = toTuple->getElement(i).getName();

    // If we're shuffling positions and labels, we have to warn about this
    // conversion.
    if (i != sources[i] && fromLabel != toLabel)
      anythingShuffled = true;

    auto *toElt
      = coerceToType(fromElt, toEltType,
                     locator.withPathElement(
                       LocatorPathElt::TupleElement(source)));
    if (!toElt)
      return nullptr;

    converted.push_back(toElt);
    labels.push_back(toLabel);
    origLabels.push_back(fromLabel);
    convertedElts.emplace_back(toEltType, toLabel);
  }

  // Shuffling tuple elements is an anti-pattern worthy of a diagnostic.  We
  // will form the shuffle for now, but a future compiler should decline to
  // do so and begin the process of removing them altogether.
  if (anythingShuffled) {
    auto concatLabels = [](SmallVectorImpl<Identifier> &labels,
                           SmallVectorImpl<char> &out) {
      llvm::raw_svector_ostream OS(out);
      for (auto label : labels) {
        DeclName(label).print(OS, /*skipEmpty*/ false, /*escapeIfNeeded*/ true);
        OS << ':';
      }
    };
    SmallString<16> fromLabelStr;
    concatLabels(origLabels, fromLabelStr);
    SmallString<16> toLabelStr;
    concatLabels(labels, toLabelStr);

    if (ctx.isLanguageModeAtLeast(LanguageMode::future)) {
      ctx.Diags.diagnose(expr->getLoc(), diag::reordering_tuple_shuffle,
                         fromLabelStr, toLabelStr);
    } else {
      ctx.Diags.diagnose(expr->getLoc(),
                         diag::warn_reordering_tuple_shuffle_deprecated,
                         fromLabelStr, toLabelStr);
    }
  }

  // Create the result tuple, written in terms of the destructured
  // OpaqueValueExprs.
  auto *result = TupleExpr::createImplicit(ctx, converted, labels);
  result->setType(TupleType::get(convertedElts, ctx));
  cs.cacheType(result);

  // Create the tuple conversion.
  return cs.cacheType(
      DestructureTupleExpr::create(ctx, destructured, expr, result, toTuple));
}

static Type getMetatypeSuperclass(Type t) {
  if (auto *metaTy = t->getAs<MetatypeType>())
    return MetatypeType::get(getMetatypeSuperclass(
                               metaTy->getInstanceType()));

  if (auto *metaTy = t->getAs<ExistentialMetatypeType>())
    return ExistentialMetatypeType::get(getMetatypeSuperclass(
                                          metaTy->getInstanceType()));

  return t->getSuperclass();
}

Expr *ExprRewriter::coerceSuperclass(Expr *expr, Type toType) {
  auto fromType = cs.getType(expr);

  auto fromInstanceType = fromType;
  auto toInstanceType = toType;

  while (fromInstanceType->is<AnyMetatypeType>() &&
         toInstanceType->is<MetatypeType>()) {
    fromInstanceType = fromInstanceType->getMetatypeInstanceType();
    toInstanceType = toInstanceType->getMetatypeInstanceType();
  }

  if (fromInstanceType->is<ArchetypeType>()) {
    // Coercion from archetype to its (concrete) superclass.
    auto superclass = getMetatypeSuperclass(fromType);

    expr =
      cs.cacheType(
        new (ctx) ArchetypeToSuperExpr(expr, superclass));

    if (!superclass->isEqual(toType))
      return coerceSuperclass(expr, toType);

    return expr;

  }

  if (fromInstanceType->isExistentialType()) {
    // Coercion from superclass-constrained existential to its
    // concrete superclass.
    auto fromArchetype =
        ExistentialArchetypeType::getAny(fromType->getCanonicalType());

    auto *archetypeVal = cs.cacheType(new (ctx) OpaqueValueExpr(
        expr->getSourceRange(), fromArchetype));

    auto *result = coerceSuperclass(archetypeVal, toType);

    return cs.cacheType(
      new (ctx) OpenExistentialExpr(expr, archetypeVal, result,
                                           toType));
  }

  // Coercion from subclass to superclass.
  if (toType->is<MetatypeType>()) {
    return cs.cacheType(
      new (ctx) MetatypeConversionExpr(expr, toType));
  }

  return cs.cacheType(
    new (ctx) DerivedToBaseExpr(expr, toType));
}

/// Given that the given expression is an implicit conversion added
/// to the target by coerceToType, find out how many OptionalEvaluationExprs
/// it includes and the target.
static unsigned getOptionalEvaluationDepth(Expr *expr, Expr *target) {
  unsigned depth = 0;
  while (true) {
    // Look through sugar expressions.
    expr = expr->getSemanticsProvidingExpr();

    // If we find the target expression, we're done.
    if (expr == target) return depth;

    // If we see an optional evaluation, the depth goes up.
    if (auto optEval = dyn_cast<OptionalEvaluationExpr>(expr)) {
      ++depth;
      expr = optEval->getSubExpr();

    // We have to handle any other expressions that can be introduced by
    // coerceToType.
    } else if (auto bind = dyn_cast<BindOptionalExpr>(expr)) {
      expr = bind->getSubExpr();
    } else if (auto force = dyn_cast<ForceValueExpr>(expr)) {
      expr = force->getSubExpr();
    } else if (auto open = dyn_cast<OpenExistentialExpr>(expr)) {
      depth += getOptionalEvaluationDepth(open->getSubExpr(),
                                          open->getOpaqueValue());
      expr = open->getExistentialValue();
    } else if (auto call = dyn_cast<CallExpr>(expr)) {
      // CGFloat <-> Double conversions lower to constructor calls.
      expr = call->getArgs()->getExpr(0);

    // Otherwise, look through implicit conversions.
    } else {
      expr = cast<ImplicitConversionExpr>(expr)->getSubExpr();
    }
  }
}

Expr *ExprRewriter::coerceOptionalToOptional(Expr *expr, Type toType,
                                             ConstraintLocatorBuilder locator) {
  Type fromType = cs.getType(expr);

  TypeChecker::requireOptionalIntrinsics(ctx, expr->getLoc());

  SmallVector<Type, 4> fromOptionals;
  (void)fromType->lookThroughAllOptionalTypes(fromOptionals);

  SmallVector<Type, 4> toOptionals;
  (void)toType->lookThroughAllOptionalTypes(toOptionals);

  assert(!toOptionals.empty());
  assert(!fromOptionals.empty());

  // If we are adding optionals but the types are equivalent up to the common
  // depth, peephole the optional-to-optional conversion into a series of nested
  // injections.
  auto toDepth = toOptionals.size();
  auto fromDepth = fromOptionals.size();
  if (toDepth > fromDepth &&
      toOptionals[toOptionals.size() - fromDepth]->isEqual(fromType)) {
    auto diff = toDepth - fromDepth;
    while (diff--) {
      Type type = toOptionals[diff];
      expr = cs.cacheType(new (ctx) InjectIntoOptionalExpr(expr, type));
      diagnoseOptionalInjection(cast<InjectIntoOptionalExpr>(expr), locator);
    }

    return expr;
  }

  Type fromValueType = fromType->getOptionalObjectType();
  Type toValueType = toType->getOptionalObjectType();

  // The depth we use here will get patched after we apply the coercion.
  auto bindOptional =
      new (ctx) BindOptionalExpr(expr, expr->getSourceRange().End,
                                 /*depth*/ 0, fromValueType);

  expr = cs.cacheType(bindOptional);
  expr->setImplicit(true);
  expr = coerceToType(expr, toValueType, locator);
  if (!expr) return nullptr;

  unsigned depth = getOptionalEvaluationDepth(expr, bindOptional);
  bindOptional->setDepth(depth);

  expr = cs.cacheType(new (ctx) InjectIntoOptionalExpr(expr, toType));

  expr = cs.cacheType(new (ctx) OptionalEvaluationExpr(expr, toType));
  expr->setImplicit(true);
  return expr;
}

/// Determine whether the given expression is a reference to an
/// unbound instance member of a type.
static bool isReferenceToMetatypeMember(ConstraintSystem &cs, Expr *expr) {
  expr = expr->getSemanticsProvidingExpr();
  if (auto dotIgnored = dyn_cast<DotSyntaxBaseIgnoredExpr>(expr))
    return cs.getType(dotIgnored->getLHS())->is<AnyMetatypeType>();
  if (auto dotSyntax = dyn_cast<DotSyntaxCallExpr>(expr))
    return cs.getType(dotSyntax->getBase())->is<AnyMetatypeType>();
  return false;
}

static bool hasCurriedSelf(ConstraintSystem &cs, ConcreteDeclRef callee,
                           ApplyExpr *apply) {
  // If we do not have a callee, return false.
  if (!callee) {
    return false;
  }

  // Only calls to members of types can have curried 'self'.
  auto calleeDecl = callee.getDecl();
  if (!calleeDecl->getDeclContext()->isTypeContext()) {
    return false;
  }

  // Would have `self`, if we're not applying it.
  if (auto *call = dyn_cast<CallExpr>(apply)) {
    if (!calleeDecl->isInstanceMember() ||
        !isReferenceToMetatypeMember(cs, call->getDirectCallee())) {
      return true;
    }
    return false;
  }

  // Operators have curried self.
  if (isa<PrefixUnaryExpr>(apply) || isa<PostfixUnaryExpr>(apply) ||
      isa<BinaryExpr>(apply)) {
    return true;
  }

  // Otherwise, we have a normal application.
  return false;
}

/// Apply the contextually Sendable flag to the given expression,
static void applyContextualClosureFlags(Expr *expr, unsigned paramIdx,
                                        const ParameterListInfo &paramInfo,
                                        bool requiresDynamicIsolationChecking,
                                        bool isMacroArg) {
  if (auto closure = dyn_cast<ClosureExpr>(expr)) {
    closure->setAllowsImplicitSelfCapture(
        paramInfo.isImplicitSelfCapture(paramIdx));

    auto [inheritActorContext, modifier] =
        paramInfo.inheritsActorContext(paramIdx);
    closure->setInheritsActorContext(inheritActorContext, modifier);

    closure->setIsPassedToSendingParameter(
        paramInfo.isSendingParameter(paramIdx));
    closure->setRequiresDynamicIsolationChecking(
        requiresDynamicIsolationChecking);
    closure->setIsMacroArgument(isMacroArg);
    return;
  }

  if (auto captureList = dyn_cast<CaptureListExpr>(expr)) {
    applyContextualClosureFlags(captureList->getClosureBody(), paramIdx,
                                paramInfo, requiresDynamicIsolationChecking,
                                isMacroArg);
  }

  if (auto identity = dyn_cast<IdentityExpr>(expr)) {
    applyContextualClosureFlags(identity->getSubExpr(), paramIdx, paramInfo,
                                requiresDynamicIsolationChecking, isMacroArg);
  }
}

// For variadic generic declarations we need to compute a substituted
// version of bindings because all of the packs are exploaded in the
// substituted function type.
//
// \code
// func fn<each T>(_: repeat each T) {}
//
// fn("", 42)
// \endcode
//
// The type of `fn` in the call is `(String, Int) -> Void` but bindings
// have only one parameter at index `0` with two argument positions: 0, 1.
static bool shouldSubstituteParameterBindings(ConcreteDeclRef callee) {
  auto subst = callee.getSubstitutions();
  if (subst.empty())
    return false;

  auto sig = subst.getGenericSignature();
  return llvm::any_of(
      sig.getGenericParams(),
      [&](const GenericTypeParamType *GP) { return GP->isParameterPack(); });
}

/// Compute parameter binding substitutions by exploding pack expansions
/// into multiple bindings (if they matched more than one argument) and
/// ignoring empty ones.
static void computeParameterBindingsSubstitutions(
    ConcreteDeclRef callee, ArrayRef<AnyFunctionType::Param> params,
    ArrayRef<ParamBinding> origBindings,
    SmallVectorImpl<ParamBinding> &substitutedBindings) {
  for (unsigned bindingIdx = 0, numBindings = origBindings.size();
       bindingIdx != numBindings; ++bindingIdx) {
    if (origBindings[bindingIdx].size() > 1) {
      const auto &param = params[substitutedBindings.size()];
      if (!param.isVariadic()) {
#ifndef NDEBUG
        auto *PD = getParameterAt(callee.getDecl(), bindingIdx);
        assert(PD && PD->getInterfaceType()->is<PackExpansionType>());
#endif
        // Explode binding set to match substituted function parameters.
        for (auto argIdx : origBindings[bindingIdx])
          substitutedBindings.push_back({argIdx});
        continue;
      }
    }

    const auto &bindings = origBindings[bindingIdx];
    if (bindings.size() == 0) {
      auto *PD = getParameterAt(callee.getDecl(), bindingIdx);
      // Skip pack expansions with no arguments because they are not
      // present in the substituted function type.
      if (PD->getInterfaceType()->is<PackExpansionType>())
        continue;
    }

    substitutedBindings.push_back(bindings);
  }
}

ArgumentList *ExprRewriter::coerceCallArguments(
    ArgumentList *args, AnyFunctionType *funcType, ConcreteDeclRef callee,
    ApplyExpr *apply, ConstraintLocatorBuilder locator,
    ArrayRef<AppliedPropertyWrapper> appliedPropertyWrappers) {
  assert(locator.endsWith<LocatorPathElt::ApplyArgument>());

  auto &ctx = getConstraintSystem().getASTContext();
  auto params = funcType->getParams();
  unsigned appliedWrapperIndex = 0;

  // Local function to produce a locator to refer to the given parameter.
  auto getArgLocator =
      [&](unsigned argIdx, unsigned paramIdx,
          ParameterTypeFlags flags) -> ConstraintLocatorBuilder {
    return locator.withPathElement(
        LocatorPathElt::ApplyArgToParam(argIdx, paramIdx, flags));
  };

  // Determine whether this application has curried self.
  bool skipCurriedSelf = apply ? hasCurriedSelf(cs, callee, apply) : true;
  // Determine the parameter bindings.
  ParameterListInfo paramInfo(params, skipCurriedSelf, callee);

  // If this application is an init(wrappedValue:) call that needs an injected
  // wrapped value placeholder, the first non-defaulted argument must be
  // wrapped in an OpaqueValueExpr.
  bool shouldInjectWrappedValuePlaceholder =
      target && target->shouldInjectWrappedValuePlaceholder(apply);

  auto injectWrappedValuePlaceholder =
      [&](Expr *arg, bool isAutoClosure = false) -> Expr * {
        auto *placeholder = PropertyWrapperValuePlaceholderExpr::create(
            ctx, arg->getSourceRange(), cs.getType(arg),
            target->propertyWrapperHasInitialWrappedValue() ? arg : nullptr,
            isAutoClosure);
        cs.cacheType(placeholder);
        cs.cacheType(placeholder->getOpaqueValuePlaceholder());
        shouldInjectWrappedValuePlaceholder = false;
        return placeholder;
      };

  bool closuresRequireDynamicIsolationChecking = [&]() {
    auto *decl = callee.getDecl();
    // If this is something like `{ @MainActor in ... }()`, let's consider
    // callee as concurrency checked.
    if (!decl)
      return false;

    if (auto declaredIn = decl->findImport(dc))
      return !declaredIn->module.importedModule->isConcurrencyChecked();

    // Both the caller and the allee are in the same module.
    if (dc->getParentModule() == decl->getModuleContext()) {
      return !dc->getASTContext().isLanguageModeAtLeast(LanguageMode::v6);
    }

    // If we cannot figure out where the callee came from, let's conservatively
    // assume that closure arguments require dynamic isolation checks.
    return true;
  }();

  auto applyFlagsToArgument = [&paramInfo,
                               &closuresRequireDynamicIsolationChecking,
                               &locator](unsigned paramIdx, Expr *argument) {
    if (!isClosureLiteralExpr(argument))
      return;

    bool isMacroArg = isExpr<MacroExpansionExpr>(locator.getAnchor());

    applyContextualClosureFlags(argument, paramIdx, paramInfo,
                                closuresRequireDynamicIsolationChecking,
                                isMacroArg);
  };

  // Quickly test if any further fix-ups for the argument types are necessary.
  auto matches = args->matches(params, [&](Expr *E) { return cs.getType(E); });
  if (matches && !shouldInjectWrappedValuePlaceholder) {
    for (unsigned paramIdx : indices(params)) {
      applyFlagsToArgument(paramIdx, args->getExpr(paramIdx));
    }
    return args;
  }

  // Determine the parameter bindings that were applied.
  auto *locatorPtr = cs.getConstraintLocator(locator);
  assert(solution.argumentMatchingChoices.count(locatorPtr) == 1);
  auto parameterBindings = solution.argumentMatchingChoices.find(locatorPtr)
                               ->second.parameterBindings;
  bool shouldSubstituteBindings = shouldSubstituteParameterBindings(callee);

  SmallVector<ParamBinding, 4> substitutedBindings;
  if (shouldSubstituteBindings) {
    computeParameterBindingsSubstitutions(callee, params, parameterBindings,
                                          substitutedBindings);
  } else {
    substitutedBindings = parameterBindings;
  }

  // First verify the correct number of arguments are being bound.
  {
    SmallSetVector<unsigned, 4> boundArgs;
    for (auto &boundArgIdxs : substitutedBindings) {
      for (auto idx : boundArgIdxs) {
        auto inserted = boundArgs.insert(idx);
        ASSERT(inserted && "Passing same arg twice?");
      }
    }
    ASSERT(boundArgs.size() == args->size() && "Args and params don't line up");
  }

  SmallVector<Argument, 4> newArgs;
  for (unsigned paramIdx = 0, numParams = substitutedBindings.size();
       paramIdx != numParams; ++paramIdx) {
    // Extract the parameter.
    const auto &param = params[paramIdx];
    auto paramLabel = param.getLabel();

    // Handle variadic parameters.
    if (param.isVariadic()) {
      assert(!param.isInOut());

      SmallVector<Expr *, 4> variadicArgs;

      // The first argument of this vararg parameter may have had a label;
      // save its location.
      auto &varargIndices = substitutedBindings[paramIdx];
      SourceLoc labelLoc;
      if (!varargIndices.empty())
        labelLoc = args->getLabelLoc(varargIndices[0]);

      // Convert the arguments.
      for (auto argIdx : varargIndices) {
        auto *arg = args->getExpr(argIdx);
        auto argType = cs.getType(arg);

        // If the argument type exactly matches, this just works.
        if (argType->isEqual(param.getPlainType())) {
          variadicArgs.push_back(arg);
          continue;
        }

        // Convert the argument.
        auto convertedArg = coerceToType(
            arg, param.getPlainType(),
            getArgLocator(argIdx, paramIdx, param.getParameterFlags()));
        if (!convertedArg)
          return nullptr;

        // Add the converted argument.
        variadicArgs.push_back(convertedArg);
      }

      SourceLoc start, end;
      if (!variadicArgs.empty()) {
        start = variadicArgs.front()->getStartLoc();
        end = variadicArgs.back()->getEndLoc();
      }

      // Collect them into an ArrayExpr.
      auto *arrayExpr = ArrayExpr::create(ctx, start, variadicArgs, {}, end,
                                          param.getParameterType());
      arrayExpr->setImplicit();
      cs.cacheType(arrayExpr);

      // Wrap the ArrayExpr in a VarargExpansionExpr.
      auto *varargExpansionExpr =
          VarargExpansionExpr::createArrayExpansion(ctx, arrayExpr);
      cs.cacheType(varargExpansionExpr);

      newArgs.push_back(Argument(labelLoc, paramLabel, varargExpansionExpr));
      continue;
    }

    // Handle default arguments.
    if (substitutedBindings[paramIdx].empty()) {
      auto paramIdxForDefault = paramIdx;
      // If bindings were substituted we need to find "original"
      // (or contextless) parameter index for the default argument.
      if (shouldSubstituteBindings) {
        auto *paramList = callee.getDecl()->getParameterList();
        ASSERT(paramList);
        paramIdxForDefault =
            paramList->getOrigParamIndex(callee.getSubstitutions(), paramIdx);
      }

      auto owner = getDefaultArgOwner(callee, paramIdx);
      auto paramTy = param.getParameterType();
      auto *defArg = new (ctx) DefaultArgumentExpr(
          owner, paramIdxForDefault, args->getStartLoc(), paramTy, dc);

      cs.cacheType(defArg);
      newArgs.emplace_back(SourceLoc(), param.getLabel(), defArg);
      continue;
    }

    // Otherwise, we have a plain old ordinary argument.

    // Extract the argument used to initialize this parameter.
    assert(substitutedBindings[paramIdx].size() == 1);
    unsigned argIdx = substitutedBindings[paramIdx].front();
    auto arg = args->get(argIdx);
    auto *argExpr = arg.getExpr();
    auto argType = cs.getType(argExpr);

    // Update the argument label to match the parameter. This may be necessary
    // for things like trailing closures and args to property wrapper params.
    arg.setLabel(param.getLabel());

    // Determine whether the argument should be marked as having
    // implicit self capture, inheriting actor context, is passed to a
    // `sending` parameter etc.
    applyFlagsToArgument(paramIdx, argExpr);

    auto canShortcutConversion = [&](Type argType, Type paramType) {
      if (shouldInjectWrappedValuePlaceholder ||
          paramInfo.hasExternalPropertyWrapper(paramIdx))
        return false;

      return argType->isEqual(paramType);
    };

    auto paramType = param.getOldType();

    if (canShortcutConversion(argType, paramType)) {
      newArgs.push_back(arg);
      continue;
    }

    Expr *convertedArg = nullptr;
    auto argRequiresAutoClosureExpr = [&](const AnyFunctionType::Param &param,
                                          Type argType) {
      if (!param.isAutoClosure())
        return false;

      // Since it was allowed to pass function types to @autoclosure
      // parameters in Swift versions < 5, it has to be handled as
      // a regular function conversion by `coerceToType`.
      if (isAutoClosureArgument(argExpr)) {
        // In Swift >= 5 mode we only allow `@autoclosure` arguments
        // to be used by value if parameter would return a function
        // type (it just needs to get wrapped into autoclosure expr),
        // otherwise argument must always form a call.
        return ctx.isLanguageModeAtLeast(LanguageMode::v5);
      }

      return true;
    };

    if (paramInfo.hasExternalPropertyWrapper(paramIdx)) {
      auto *paramDecl = getParameterAt(callee, paramIdx);
      assert(paramDecl);

      ASSERT(appliedWrapperIndex < appliedPropertyWrappers.size());
      auto appliedWrapper = appliedPropertyWrappers[appliedWrapperIndex++];
      auto wrapperType = solution.simplifyType(appliedWrapper.wrapperType);
      auto initKind = appliedWrapper.initKind;

      AppliedPropertyWrapperExpr::ValueKind valueKind;
      PropertyWrapperValuePlaceholderExpr *generatorArg;
      auto initInfo = paramDecl->getPropertyWrapperInitializerInfo();
      if (initKind == PropertyWrapperInitKind::ProjectedValue) {
        valueKind = AppliedPropertyWrapperExpr::ValueKind::ProjectedValue;
        generatorArg = initInfo.getProjectedValuePlaceholder();
      } else {
        valueKind = AppliedPropertyWrapperExpr::ValueKind::WrappedValue;
        generatorArg = initInfo.getWrappedValuePlaceholder();
      }

      // Coerce the property wrapper argument type to the input type of
      // the property wrapper generator function. The wrapper generator
      // has the same generic signature as the enclosing function, so we
      // can use substitutions from the callee.
      Type generatorInputType =
          generatorArg->getType().subst(callee.getSubstitutions());
      auto argLoc = getArgLocator(argIdx, paramIdx, param.getParameterFlags());

      if (generatorArg->isAutoClosure()) {
        auto *closureType = generatorInputType->castTo<FunctionType>();
        argExpr = coerceToType(
            argExpr, closureType->getResult(),
            argLoc.withPathElement(ConstraintLocator::AutoclosureResult));
        argExpr = cs.buildAutoClosureExpr(argExpr, closureType, dc);
      }

      argExpr = coerceToType(argExpr, generatorInputType, argLoc);

      // Wrap the argument in an applied property wrapper expr, which will
      // later turn into a call to the property wrapper generator function.
      argExpr = AppliedPropertyWrapperExpr::create(ctx, callee, paramDecl,
                                                   argExpr->getStartLoc(),
                                                   wrapperType, argExpr,
                                                   valueKind);
      cs.cacheExprTypes(argExpr);
    }

    auto argLoc = getArgLocator(argIdx, paramIdx, param.getParameterFlags());

    // If the argument is an existential type that has been opened, perform
    // the open operation.
    if (argType->getWithoutSpecifierType()->isAnyExistentialType() &&
        paramType->hasOpenedExistential()) {
      // FIXME: Look for an opened existential and use it. We need to
      // know how far out we need to go to close the existentials. Huh.
      auto knownOpened = solution.OpenedExistentialTypes.find(
          cs.getConstraintLocator(argLoc));
      if (knownOpened != solution.OpenedExistentialTypes.end()) {
        argExpr = openExistentialReference(
            argExpr, knownOpened->second, callee.getDecl(),
            apply ? apply->getLoc() : argExpr->getLoc());
        argType = cs.getType(argExpr);
      }
    }

    if (argRequiresAutoClosureExpr(param, argType)) {
      assert(!param.isInOut());

      // If parameter is an autoclosure, we need to make sure that:
      //   - argument type is coerced to parameter result type
      //   - implicit autoclosure is created to wrap argument expression
      //   - new types are propagated to constraint system
      auto *closureType = param.getPlainType()->castTo<FunctionType>();

      argExpr = coerceToType(
          argExpr, closureType->getResult(),
          argLoc.withPathElement(ConstraintLocator::AutoclosureResult));

      if (shouldInjectWrappedValuePlaceholder) {
        // If init(wrappedValue:) takes an autoclosure, then we want
        // the effect of autoclosure forwarding of the placeholder
        // autoclosure. The only way to do this is to call the placeholder
        // autoclosure when passing it to the init.
        bool isDefaultWrappedValue =
            target->propertyWrapperHasInitialWrappedValue();
        auto *placeholder = injectWrappedValuePlaceholder(
            cs.buildAutoClosureExpr(argExpr, closureType, dc,
                                    isDefaultWrappedValue),
            /*isAutoClosure=*/true);
        argExpr = CallExpr::createImplicitEmpty(ctx, placeholder);
        argExpr->setType(closureType->getResult());
        cs.cacheType(argExpr);
      }

      convertedArg = cs.buildAutoClosureExpr(argExpr, closureType, dc);
    } else {
      convertedArg = coerceToType(argExpr, paramType, argLoc);
    }

    // Perform the wrapped value placeholder injection
    if (shouldInjectWrappedValuePlaceholder)
      convertedArg = injectWrappedValuePlaceholder(convertedArg);

    if (!convertedArg)
      return nullptr;

    // Write back the rewritten argument to the original argument list. This
    // ensures it has the same semantic argument information as the rewritten
    // argument list, which may be required by IDE logic.
    args->setExpr(argIdx, convertedArg);

    arg.setExpr(convertedArg);
    newArgs.push_back(arg);
  }

  ASSERT(appliedWrapperIndex == appliedPropertyWrappers.size());
  return ArgumentList::createTypeChecked(ctx, args, newArgs);
}

/// Looks through any non-semantic expressions and a capture list
/// to find out whether the given expression is an explicit closure.
static ClosureExpr *isExplicitClosureExpr(Expr *expr) {
  if (auto IE = dyn_cast<IdentityExpr>(expr))
    return isExplicitClosureExpr(IE->getSubExpr());

  if (auto CLE = dyn_cast<CaptureListExpr>(expr))
    return isExplicitClosureExpr(CLE->getClosureBody());

  return dyn_cast<ClosureExpr>(expr);
}

/// Whether the given expression is a closure that should inherit
/// the actor context from where it was formed.
static bool closureInheritsActorContext(Expr *expr) {
  if (auto *CE = isExplicitClosureExpr(expr))
    return CE->inheritsActorContext();
  return false;
}

/// Determine whether the given expression is a closure that
/// is explicitly marked as `@concurrent`.
static bool isClosureMarkedAsConcurrent(Expr *expr) {
  if (auto *CE = isExplicitClosureExpr(expr))
    return CE->getAttrs().hasAttribute<ConcurrentAttr>();
  return false;
}

/// If the expression is an explicit closure expression (potentially wrapped in
/// IdentityExprs), change the type of the closure and identities to the
/// specified type and return true.  Otherwise, return false with no effect.
static bool applyTypeToClosureExpr(ConstraintSystem &cs,
                                   Expr *expr, Type toType) {
  // Look through identity expressions, like parens.
  if (auto IE = dyn_cast<IdentityExpr>(expr)) {
    if (!applyTypeToClosureExpr(cs, IE->getSubExpr(), toType))
      return false;

    cs.setType(IE, cs.getType(IE->getSubExpr()));
    return true;
  }

  // Look through capture lists.
  if (auto CLE = dyn_cast<CaptureListExpr>(expr)) {
    if (!applyTypeToClosureExpr(cs, CLE->getClosureBody(), toType)) return false;
    cs.setType(CLE, toType);
    return true;
  }

  // If we found an explicit ClosureExpr, update its type.
  if (auto CE = dyn_cast<ClosureExpr>(expr)) {
    cs.setType(CE, toType);
    return true;
  }

  // Otherwise fail.
  return false;
}

// Look through sugar and DotSyntaxBaseIgnoredExprs.
static Expr *
getSemanticExprForDeclOrMemberRef(Expr *expr) {
  auto semanticExpr = expr->getSemanticsProvidingExpr();
  while (auto ignoredBase = dyn_cast<DotSyntaxBaseIgnoredExpr>(semanticExpr)){
    semanticExpr = ignoredBase->getRHS()->getSemanticsProvidingExpr();
  }
  return semanticExpr;
}

static void
maybeDiagnoseUnsupportedDifferentiableConversion(ConstraintSystem &cs,
                                                 Expr *expr,
                                                 AnyFunctionType *toType) {
  ASTContext &ctx = cs.getASTContext();
  Type fromType = cs.getType(expr);
  auto fromFnType = fromType->getAs<AnyFunctionType>();
  // Conversion between two different differentiable function types is not
  // yet supported.
  if (fromFnType->isDifferentiable() && toType->isDifferentiable() &&
      fromFnType->getDifferentiabilityKind() !=
          toType->getDifferentiabilityKind()) {
    ctx.Diags.diagnose(expr->getLoc(),
                       diag::invalid_differentiable_function_conversion_expr);
    return;
  }
  // Conversion from a non-`@differentiable` function to a `@differentiable` is
  // only allowed from a closure expression or a declaration/member reference.
  if (!fromFnType->isDifferentiable() && toType->isDifferentiable()) {
    std::function<void(Expr *)> maybeDiagnoseFunctionRef;
    maybeDiagnoseFunctionRef = [&](Expr *semanticExpr) {
      if (auto *capture = dyn_cast<CaptureListExpr>(semanticExpr))
        semanticExpr = capture->getClosureBody();
      if (isa<ClosureExpr>(semanticExpr)) return;
      if (auto *declRef = dyn_cast<DeclRefExpr>(semanticExpr)) {
        if (isa<AbstractFunctionDecl>(declRef->getDecl())) return;
        // If the referenced decl is a function parameter, the user may want
        // to change the declaration to be a '@differentiable' closure. Emit a
        // note with a fix-it.
        if (auto *paramDecl = dyn_cast<ParamDecl>(declRef->getDecl())) {
          ctx.Diags.diagnose(
              expr->getLoc(),
              diag::invalid_differentiable_function_conversion_expr);
          if (paramDecl->getInterfaceType()->is<AnyFunctionType>()) {
            auto *typeRepr = paramDecl->getTypeRepr();
            while (auto *attributed = dyn_cast<AttributedTypeRepr>(typeRepr))
              typeRepr = attributed->getTypeRepr();
            std::string attributeString = "@differentiable";
            auto *funcTypeRepr = cast<FunctionTypeRepr>(typeRepr);
            auto paramListLoc = funcTypeRepr->getArgsTypeRepr()->getStartLoc();
            ctx.Diags.diagnose(paramDecl->getLoc(),
                    diag::invalid_differentiable_function_conversion_parameter,
                    attributeString)
                .highlight(paramDecl->getTypeRepr()->getSourceRange())
                .fixItInsert(paramListLoc, attributeString + " ");
          }
          return;
        }
      } else if (auto *memberRef = dyn_cast<MemberRefExpr>(semanticExpr)) {
        if (isa<FuncDecl>(memberRef->getMember().getDecl())) return;
      } else if (auto *dotSyntaxCall =
                     dyn_cast<DotSyntaxCallExpr>(semanticExpr)) {
        // Recurse on the function expression.
        auto *fnExpr = dotSyntaxCall->getFn()->getSemanticsProvidingExpr();
        maybeDiagnoseFunctionRef(fnExpr);
        return;
      } else if (auto *autoclosureExpr = dyn_cast<AutoClosureExpr>(semanticExpr)) {
        // Peer through curry thunks.
        if (auto *unwrappedFnExpr = autoclosureExpr->getUnwrappedCurryThunkExpr()) {
          maybeDiagnoseFunctionRef(unwrappedFnExpr);
          return;
        }
      } else if (auto conv = dyn_cast<FunctionConversionExpr>(semanticExpr)) {
        // Look through a function conversion that only adds or removes
        // `@Sendable`.
        auto ty1 = conv->getType()->castTo<AnyFunctionType>();
        auto ty2 = conv->getSubExpr()->getType()->castTo<AnyFunctionType>();

        if (ty1->withExtInfo(ty1->getExtInfo().withSendable(false))
            ->isEqual(ty2->withExtInfo(ty2->getExtInfo().withSendable(false)))) {
          maybeDiagnoseFunctionRef(conv->getSubExpr()->getSemanticsProvidingExpr());
          return;
        }
      }

      ctx.Diags.diagnose(expr->getLoc(),
                         diag::invalid_differentiable_function_conversion_expr);
    };
    maybeDiagnoseFunctionRef(getSemanticExprForDeclOrMemberRef(expr));
  }
}

static void
maybeDiagnoseUnsupportedFunctionConversion(ConstraintSystem &cs, Expr *expr,
                                           AnyFunctionType *toType) {
  auto &de = cs.getASTContext().Diags;
  Type fromType = cs.getType(expr);
  auto fromFnType = fromType->getAs<AnyFunctionType>();
  
  // Conversions to C function pointer type are limited. Since a C function
  // pointer captures no context, we can only do the necessary thunking or
  // codegen if the original function is a direct reference to a global function
  // or context-free closure or local function.
  if (toType->getRepresentation()
       == AnyFunctionType::Representation::CFunctionPointer) {
    // Can convert from an ABI-compatible C function pointer.
    if (fromFnType
        && fromFnType->getRepresentation()
            == AnyFunctionType::Representation::CFunctionPointer)
      return;
    
    // Can convert a decl ref to a global or local function that doesn't
    // capture context. Look through ignored bases too.
    // TODO: Look through static method applications to the type.
    auto semanticExpr = getSemanticExprForDeclOrMemberRef(expr);
    auto maybeDiagnoseFunctionRef = [&](FuncDecl *fn) {
      // TODO: We could allow static (or class final) functions too by
      // "capturing" the metatype in a thunk.
      if (fn->getDeclContext()->isTypeContext()) {
        de.diagnose(expr->getLoc(), diag::c_function_pointer_from_method);
      } else if (fn->getGenericParams()) {
        de.diagnose(expr->getLoc(),
                    diag::c_function_pointer_from_generic_function);
      }
    };
    
    // Look through a function conversion that only adds or removes
    // `@Sendable`.
    if (auto conv = dyn_cast<FunctionConversionExpr>(semanticExpr)) {
      auto ty1 = conv->getType()->castTo<AnyFunctionType>();
      auto ty2 = conv->getSubExpr()->getType()->castTo<AnyFunctionType>();
      if (ty1->withExtInfo(ty1->getExtInfo().withSendable(false))
             ->isEqual(ty2->withExtInfo(ty2->getExtInfo().withSendable(false)))){
        semanticExpr = conv->getSubExpr()->getSemanticsProvidingExpr();
      }
    }
    
    if (auto declRef = dyn_cast<DeclRefExpr>(semanticExpr)) {
      if (auto fn = dyn_cast<FuncDecl>(declRef->getDecl())) {
        return maybeDiagnoseFunctionRef(fn);
      }
    }
    
    if (auto memberRef = dyn_cast<MemberRefExpr>(semanticExpr)) {
      if (auto fn = dyn_cast<FuncDecl>(memberRef->getMember().getDecl())) {
        return maybeDiagnoseFunctionRef(fn);
      }
    }

    // Unwrap closures with explicit capture lists.
    if (auto capture = dyn_cast<CaptureListExpr>(semanticExpr))
      semanticExpr = capture->getClosureBody();
    
    // Can convert a literal closure that doesn't capture context.
    if (isa<ClosureExpr>(semanticExpr))
      return;

    // Diagnose cases like:
    //   func f() { print(w) }; func g(_ : @convention(c) () -> ()) {}
    //   let k = f; g(k) // error
    //   func m() { let x = 0; g({ print(x) }) } // error
    // (See also: [NOTE: diagnose-swift-to-c-convention-change])
    de.diagnose(expr->getLoc(),
                diag::invalid_c_function_pointer_conversion_expr);
  }
}

/// Build the conversion of an element in a collection upcast.
static Expr *buildElementConversion(ExprRewriter &rewriter,
                                    SourceRange srcRange, Type srcType,
                                    Type destType, bool bridged,
                                    ConstraintLocatorBuilder locator,
                                    Expr *element) {
  if (bridged && TypeChecker::typeCheckCheckedCast(
                     srcType, destType, CheckedCastContextKind::None,
                     rewriter.dc) != CheckedCastKind::Coercion) {
    if (auto conversion =
          rewriter.buildObjCBridgeExpr(element, destType, locator))
        return conversion;
  }

  return rewriter.coerceToType(element, destType, locator);
}

static CollectionUpcastConversionExpr::ConversionPair
buildOpaqueElementConversion(ExprRewriter &rewriter, SourceRange srcRange,
                             Type srcType, Type destType,
                             bool bridged, ConstraintLocatorBuilder locator,
                             unsigned typeArgIndex) {
  // Build the conversion.
  auto &cs = rewriter.getConstraintSystem();
  ASTContext &ctx = cs.getASTContext();
  auto opaque =
      rewriter.cs.cacheType(new (ctx) OpaqueValueExpr(srcRange, srcType));

  Expr *conversion = buildElementConversion(
      rewriter, srcRange, srcType, destType, bridged,
      locator.withPathElement(LocatorPathElt::GenericArgument(typeArgIndex)),
      opaque);

  return { opaque, conversion };
}

void ExprRewriter::peepholeArrayUpcast(ArrayExpr *expr, Type toType,
                                       bool bridged, Type elementType,
                                       ConstraintLocatorBuilder locator) {
  // Update the type of the array literal.
  cs.setType(expr, toType);
  // FIXME: finish{Array,Dictionary}Expr invoke cacheExprTypes after forming
  // the semantic expression for the dictionary literal, which will undo the
  // type we set here if this dictionary literal is nested unless we update
  // the expr type as well.
  expr->setType(toType);

  // Convert the elements.
  ConstraintLocatorBuilder innerLocator =
    locator.withPathElement(LocatorPathElt::GenericArgument(0));
  for (auto &element : expr->getElements()) {
    if (auto newElement = buildElementConversion(*this, expr->getLoc(),
                                                 cs.getType(element),
                                                 elementType,
                                                 bridged, innerLocator,
                                                 element)) {
      element = newElement;
    }
  }

  (void)finishArrayExpr(expr);
}

void ExprRewriter::peepholeDictionaryUpcast(DictionaryExpr *expr,
                                            Type toType, bool bridged,
                                            Type keyType, Type valueType,
                                            ConstraintLocatorBuilder locator) {
  // Update the type of the dictionary literal.
  cs.setType(expr, toType);
  // FIXME: finish{Array,Dictionary}Expr invoke cacheExprTypes after forming
  // the semantic expression for the dictionary literal, which will undo the
  // type we set here if this dictionary literal is nested unless we update
  // the expr type as well.
  expr->setType(toType);

  ConstraintLocatorBuilder valueLocator =
    locator.withPathElement(LocatorPathElt::GenericArgument(1));

  // Convert the elements.
  TupleTypeElt tupleTypeElts[2] = { keyType, valueType };
  auto tupleType = TupleType::get(tupleTypeElts, ctx);
  for (auto element : expr->getElements()) {
    if (auto tuple = dyn_cast<TupleExpr>(element)) {
      auto key = tuple->getElement(0);
      if (auto newKey = buildElementConversion(*this, expr->getLoc(),
                                               cs.getType(key), keyType,
                                               bridged, valueLocator, key))
        tuple->setElement(0, newKey);

      auto value = tuple->getElement(1);
      if (auto newValue = buildElementConversion(*this, expr->getLoc(),
                                                 cs.getType(value), valueType,
                                                 bridged, valueLocator,
                                                 value)) {
        tuple->setElement(1, newValue);
      }

      cs.setType(tuple, tupleType);
      // FIXME: finish{Array,Dictionary}Expr invoke cacheExprTypes after forming
      // the semantic expression for the dictionary literal, which will undo the
      // type we set here if this dictionary literal is nested unless we update
      // the expr type as well.
      tuple->setType(tupleType);
    }
  }

  (void)finishDictionaryExpr(expr);
}

bool ExprRewriter::peepholeCollectionUpcast(Expr *expr, Type toType,
                                            bool bridged,
                                            ConstraintLocatorBuilder locator) {
  // Recur into parenthesized expressions.
  if (auto paren = dyn_cast<ParenExpr>(expr)) {
    // If we can't peephole the subexpression, we're done.
    if (!peepholeCollectionUpcast(paren->getSubExpr(), toType, bridged,
                                  locator))
      return false;

    // Update the type of this expression.
    auto parenTy = cs.getType(paren->getSubExpr());
    cs.setType(paren, parenTy);
    // FIXME: finish{Array,Dictionary}Expr invoke cacheExprTypes after forming
    // the semantic expression for the dictionary literal, which will undo the
    // type we set here if this dictionary literal is nested unless we update
    // the expr type as well.
    paren->setType(parenTy);
    return true;
  }

  // Array literals.
  if (auto arrayLiteral = dyn_cast<ArrayExpr>(expr)) {
    if (auto elementType = toType->getArrayElementType()) {
      peepholeArrayUpcast(arrayLiteral, toType, bridged, elementType, locator);
      return true;
    }

    if (std::optional<Type> elementType = ConstraintSystem::isSetType(toType)) {
      peepholeArrayUpcast(arrayLiteral, toType, bridged, *elementType, locator);
      return true;
    }

    return false;
  }

  // Dictionary literals.
  if (auto dictLiteral = dyn_cast<DictionaryExpr>(expr)) {
    if (auto elementType = ConstraintSystem::isDictionaryType(toType)) {
      peepholeDictionaryUpcast(dictLiteral, toType, bridged,
                               elementType->first, elementType->second,
                               locator);
      return true;
    }

    return false;
  }

  return false;
}

Expr *ExprRewriter::buildCollectionUpcastExpr(
                                            Expr *expr, Type toType,
                                            bool bridged,
                                            ConstraintLocatorBuilder locator) {
  if (peepholeCollectionUpcast(expr, toType, bridged, locator))
    return expr;

  // Build the first value conversion.
  auto fromArgs = cs.getType(expr)->castTo<BoundGenericType>()->getGenericArgs();
  auto toArgs = toType->castTo<BoundGenericType>()->getGenericArgs();
  auto conv =
    buildOpaqueElementConversion(*this, expr->getLoc(),
                                 fromArgs[0], toArgs[0],
                                 bridged, locator, 0);

  // For single-parameter collections, form the upcast.
  if (toType->isArray() || ConstraintSystem::isSetType(toType)) {
    return cs.cacheType(
              new (ctx) CollectionUpcastConversionExpr(expr, toType, {}, conv));
  }

  assert(ConstraintSystem::isDictionaryType(toType) &&
         "Unhandled collection upcast");

  // Build the second value conversion.
  auto conv2 =
    buildOpaqueElementConversion(*this, expr->getLoc(),
                                 fromArgs[1], toArgs[1],
                                 bridged, locator, 1);

  return cs.cacheType(
           new (ctx) CollectionUpcastConversionExpr(expr, toType, conv, conv2));

}

Expr *ExprRewriter::buildObjCBridgeExpr(Expr *expr, Type toType,
                                        ConstraintLocatorBuilder locator) {
  Type fromType = cs.getType(expr);

  // Bridged collection casts always succeed, so we treat them as
  // collection "upcasts".
  if ((fromType->isArray() && toType->isArray())
      || (ConstraintSystem::isDictionaryType(fromType)
          && ConstraintSystem::isDictionaryType(toType))
      || (ConstraintSystem::isSetType(fromType)
          && ConstraintSystem::isSetType(toType))) {
    return buildCollectionUpcastExpr(expr, toType, /*bridged=*/true, locator);
  }

  // Bridging from a Swift type to an Objective-C class type.
  if (toType->isAnyObject() ||
      (fromType->getRValueType()->isPotentiallyBridgedValueType() &&
       (toType->isBridgeableObjectType() || toType->isExistentialType()))) {
    // Bridging to Objective-C.
    Expr *objcExpr = bridgeToObjectiveC(expr, toType);
    if (!objcExpr)
      return nullptr;

    // We might have a coercion of a Swift type to a CF type toll-free
    // bridged to Objective-C.
    //
    // FIXME: Ideally we would instead have already recorded a restriction
    // when solving the constraint, and we wouldn't need to duplicate this
    // part of coerceToType() here.
    if (auto foreignClass = toType->getClassOrBoundGenericClass()) {
      if (foreignClass->getForeignClassKind() ==
            ClassDecl::ForeignKind::CFType) {
        return cs.cacheType(new (ctx)
                                ForeignObjectConversionExpr(objcExpr, toType));
      }
    }

    return coerceToType(objcExpr, toType, locator);
  }

  // Bridging from an Objective-C class type to a Swift type.
  return forceBridgeFromObjectiveC(expr, toType);
}

Expr *ExprRewriter::coerceExistential(Expr *expr, Type toType,
                                      ConstraintLocatorBuilder locator) {
  Type fromType = cs.getType(expr);
  Type fromInstanceType = fromType;
  Type toInstanceType = toType;

  // For existential-to-existential coercions, open the source existential.
  Type openedFromType;
  if (fromType->isAnyExistentialType()) {
    openedFromType = ExistentialArchetypeType::getAny(fromType->getCanonicalType());
  }

  Type openedFromInstanceType = openedFromType;

  // Look through metatypes.
  while (fromInstanceType->is<AnyMetatypeType>() &&
         toInstanceType->is<ExistentialMetatypeType>()) {
    fromInstanceType = fromInstanceType->getMetatypeInstanceType();
    if (openedFromInstanceType)
      openedFromInstanceType = openedFromInstanceType->getMetatypeInstanceType();
    toInstanceType = toInstanceType->getMetatypeInstanceType();
  }

  /// Collect the conformances for all the protocols of an existential type.
  /// If the source type is also existential, we don't want to check conformance
  /// because most protocols do not conform to themselves -- however we still
  /// allow the conversion here, except the ErasureExpr ends up with trivial
  /// conformances.

  // Use the requirements of any parameterized protocols to build out fake
  // argument conversions that can be used to infer opaque types.
  SmallVector<CollectionUpcastConversionExpr::ConversionPair, 4> argConversions;

  auto fromConstraintType = fromInstanceType;
  if (auto existential = fromConstraintType->getAs<ExistentialType>())
    fromConstraintType = existential->getConstraintType();

  auto toConstraintType = toInstanceType;
  if (auto existential = toConstraintType->getAs<ExistentialType>())
    toConstraintType = existential->getConstraintType();

  auto fromPPT = fromConstraintType->getAs<ParameterizedProtocolType>();
  auto toPPT = toConstraintType->getAs<ParameterizedProtocolType>();

  if (fromPPT && toPPT) {
    assert(fromPPT->getArgs().size() >= toPPT->getArgs().size());
    for (unsigned i = 0; i < toPPT->getArgs().size(); ++i) {
      auto firstTy = fromPPT->getArgs()[i];
      auto secondTy = toPPT->getArgs()[i];
      auto conv =
        buildOpaqueElementConversion(*this, expr->getLoc(),
                                     firstTy,
                                     secondTy,
                                     /*bridged*/ false,
                                     locator, i);
      argConversions.push_back(conv);
    }
  } else if ((fromPPT || toPPT) &&
             !fromInstanceType->isExistentialType()) {
    auto parameterized = fromConstraintType;
    auto base = toConstraintType;
    if (toPPT)
      std::swap(parameterized, base);

    SmallVector<Requirement, 4> reqs;
    parameterized->castTo<ParameterizedProtocolType>()
                 ->getRequirements(base, reqs);

    for (unsigned i = 0; i < reqs.size(); ++i) {
      const auto &req = reqs[i];
      assert(req.getKind() == RequirementKind::SameType);
      auto conv =
        buildOpaqueElementConversion(*this, expr->getLoc(),
                                     req.getFirstType(),
                                     req.getSecondType(),
                                     /*bridged*/ false,
                                     locator, i);
      argConversions.push_back(conv);
    }
  }

  // For existential-to-existential coercions, open the source existential.
  if (openedFromType) {
    auto *archetypeVal = cs.cacheType(
        new (ctx) OpaqueValueExpr(expr->getSourceRange(), openedFromType));

    auto conformances =
        collectExistentialConformances(openedFromInstanceType->getCanonicalType(),
                                       toInstanceType->getCanonicalType(),
                                       /*allowMissing=*/true);

    auto *result = cs.cacheType(ErasureExpr::create(ctx, archetypeVal, toType,
                                                    conformances,
                                                    argConversions));
    return cs.cacheType(
        new (ctx) OpenExistentialExpr(expr, archetypeVal, result,
                                      cs.getType(result)));
  }

  // Load tuples with lvalue elements.
  if (auto tupleType = fromType->getAs<TupleType>()) {
    if (tupleType->hasLValueType()) {
      expr = cs.coerceToRValue(expr);
    }
  }

  auto conformances =
      collectExistentialConformances(fromInstanceType->getCanonicalType(),
                                     toInstanceType->getCanonicalType(),
                                     /*allowMissing=*/true);

  return cs.cacheType(ErasureExpr::create(ctx, expr, toType,
                                          conformances, argConversions));
}

Expr *ConstraintSystem::addImplicitLoadExpr(Expr *expr) {
  return TypeChecker::addImplicitLoadExpr(
      getASTContext(), expr, [this](Expr *expr) { return getType(expr); },
      [this](Expr *expr, Type type) { setType(expr, type); });
}

Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
                                 ConstraintLocatorBuilder locator) {
  ASSERT(toType && !toType->hasError() &&
         !toType->hasTypeVariableOrPlaceholder());

  // Diagnose conversions to invalid function types that couldn't be performed
  // beforehand because of placeholders.
  if (auto *fnTy = toType->getAs<FunctionType>()) {
    auto contextTy = cs.getContextualType(expr, /*forConstraint=*/false);
    if (cs.getConstraintLocator(locator)->isForContextualType() && contextTy &&
        contextTy->hasPlaceholder()) {
      bool hadError = TypeChecker::diagnoseInvalidFunctionType(
          fnTy, expr->getLoc(), std::nullopt, dc, std::nullopt);
      if (hadError)
        return nullptr;
    }
  }

  // The type we're converting from.
  Type fromType = cs.getType(expr);

  // If the types are already equivalent, we don't have to do anything.
  if (fromType->isEqual(toType))
    return expr;

  // If the solver recorded what we should do here, just do it immediately.
  auto knownRestriction = solution.ConstraintRestrictions.find(
                            { fromType->getCanonicalType(),
                              toType->getCanonicalType() });
  if (knownRestriction != solution.ConstraintRestrictions.end()) {
    switch (knownRestriction->second) {
    case ConversionRestrictionKind::DeepEquality: {
      // HACK: Fix problem related to Swift 4 mode (with assertions),
      // since Swift 4 mode allows passing arguments with extra parens
      // to parameters which don't expect them, it should be supported
      // by "deep equality" type - Optional<T> e.g.
      // ```swift
      // func foo(_: (() -> Void)?) {}
      // func bar() -> ((()) -> Void)? { return nil }
      // foo(bar) // This expression should compile in Swift 3 mode
      // ```
      //
      // See also: https://github.com/apple/swift/issues/49345
      if (ctx.isLanguageModeAtLeast(LanguageMode::v4) &&
          !ctx.isLanguageModeAtLeast(LanguageMode::v5)) {
        auto obj1 = fromType->getOptionalObjectType();
        auto obj2 = toType->getOptionalObjectType();

        if (obj1 && obj2) {
          auto *fn1 = obj1->getAs<AnyFunctionType>();
          auto *fn2 = obj2->getAs<AnyFunctionType>();

          if (fn1 && fn2) {
            auto params1 = fn1->getParams();
            auto params2 = fn2->getParams();

            // This handles situations like argument: (()), parameter: ().
            if (params1.size() == 1 && params2.empty()) {
              auto tupleTy = params1.front().getOldType()->getAs<TupleType>();
              if (tupleTy && tupleTy->getNumElements() == 0)
                break;
            }
          }
        }
      }

      // `any Sendable` -> `Any` conversion is allowed in generic
      // argument positions.
      {
        auto erasedFromType = fromType->stripConcurrency(
            /*recursive=*/true, /*dropGlobalActor=*/false);
        auto erasedToType = toType->stripConcurrency(
            /*recursive=*/true, /*dropGlobalActor=*/false);

        if (erasedFromType->isEqual(erasedToType))
          return cs.cacheType(new (ctx) UnsafeCastExpr(expr, toType));
      }

      auto &err = llvm::errs();
      err << "fromType->getCanonicalType() = ";
      fromType->getCanonicalType()->dump(err);
      err << "toType->getCanonicalType() = ";
      toType->getCanonicalType()->dump(err);
      llvm_unreachable("Should be handled above");
    }

    case ConversionRestrictionKind::Superclass:
    case ConversionRestrictionKind::ExistentialMetatypeToMetatype:
      return coerceSuperclass(expr, toType);

    case ConversionRestrictionKind::Existential:
    case ConversionRestrictionKind::MetatypeToExistentialMetatype: {
      auto coerced = coerceExistential(expr, toType, locator);
      diagnoseExistentialErasureOf(expr, coerced, locator);
      return coerced;
    }

    case ConversionRestrictionKind::ClassMetatypeToAnyObject: {
      assert(ctx.LangOpts.EnableObjCInterop &&
             "metatypes can only be cast to objects w/ objc runtime!");
      return cs.cacheType(new (ctx) ClassMetatypeToObjectExpr(expr, toType));
    }
    case ConversionRestrictionKind::ExistentialMetatypeToAnyObject: {
      assert(ctx.LangOpts.EnableObjCInterop &&
             "metatypes can only be cast to objects w/ objc runtime!");
      return cs.cacheType(new (ctx)
                              ExistentialMetatypeToObjectExpr(expr, toType));
    }
    case ConversionRestrictionKind::ProtocolMetatypeToProtocolClass: {
      return cs.cacheType(new (ctx) ProtocolMetatypeToObjectExpr(expr, toType));
    }
        
    case ConversionRestrictionKind::ValueToOptional: {
      auto toGenericType = toType->castTo<BoundGenericType>();
      assert(toGenericType->getDecl()->isOptionalDecl());
      TypeChecker::requireOptionalIntrinsics(ctx, expr->getLoc());

      Type valueType = toGenericType->getGenericArgs()[0];
      expr = coerceToType(expr, valueType, locator);
      if (!expr) return nullptr;

      auto *result =
          cs.cacheType(new (ctx) InjectIntoOptionalExpr(expr, toType));
      diagnoseOptionalInjection(result, locator);
      return result;
    }

    case ConversionRestrictionKind::OptionalToOptional:
      return coerceOptionalToOptional(expr, toType, locator);

    case ConversionRestrictionKind::ArrayUpcast: {
      // Build the value conversion.
      return buildCollectionUpcastExpr(expr, toType, /*bridged=*/false,
                                       locator);
    }

    case ConversionRestrictionKind::HashableToAnyHashable: {
      // We want to check conformance on the rvalue, as that's what has
      // the Hashable conformance
      expr = cs.coerceToRValue(expr);

      // Find the conformance of the source type to Hashable.
      auto hashable = ctx.getProtocol(KnownProtocolKind::Hashable);
      auto conformance = checkConformance(cs.getType(expr), hashable);
      assert(conformance && "must conform to Hashable");

      return cs.cacheType(
          new (ctx) AnyHashableErasureExpr(expr, toType, conformance));
    }

    case ConversionRestrictionKind::DictionaryUpcast: {
      // Build the value conversion.
      return buildCollectionUpcastExpr(expr, toType, /*bridged=*/false,
                                       locator);
    }

    case ConversionRestrictionKind::SetUpcast: {
      // Build the value conversion.
      return buildCollectionUpcastExpr(expr, toType, /*bridged=*/false, locator);
    }

    case ConversionRestrictionKind::InoutToPointer:
    case ConversionRestrictionKind::InoutToCPointer: {
      bool isOptional = false;
      Type unwrappedTy = toType;
      if (Type unwrapped = toType->getOptionalObjectType()) {
        isOptional = true;
        unwrappedTy = unwrapped;
      }
      PointerTypeKind pointerKind;
      auto toEltType = unwrappedTy->getAnyPointerElementType(pointerKind);
      assert(toEltType && "not a pointer type?"); (void) toEltType;

      TypeChecker::requirePointerArgumentIntrinsics(ctx, expr->getLoc());
      Expr *result =
          cs.cacheType(new (ctx) InOutToPointerExpr(expr, unwrappedTy));
      if (isOptional)
        result = cs.cacheType(new (ctx) InjectIntoOptionalExpr(result, toType));
      return result;
    }

    case ConversionRestrictionKind::ArrayToPointer:
    case ConversionRestrictionKind::ArrayToCPointer: {
      bool isOptional = false;
      Type unwrappedTy = toType;
      if (Type unwrapped = toType->getOptionalObjectType()) {
        isOptional = true;
        unwrappedTy = unwrapped;
      }

      TypeChecker::requirePointerArgumentIntrinsics(ctx, expr->getLoc());
      Expr *result =
          cs.cacheType(new (ctx) ArrayToPointerExpr(expr, unwrappedTy));
      if (isOptional)
        result = cs.cacheType(new (ctx) InjectIntoOptionalExpr(result, toType));
      return result;
    }
    
    case ConversionRestrictionKind::StringToPointer: {
      bool isOptional = false;
      Type unwrappedTy = toType;
      if (Type unwrapped = toType->getOptionalObjectType()) {
        isOptional = true;
        unwrappedTy = unwrapped;
      }

      TypeChecker::requirePointerArgumentIntrinsics(ctx, expr->getLoc());
      Expr *result =
          cs.cacheType(new (ctx) StringToPointerExpr(expr, unwrappedTy));
      if (isOptional)
        result = cs.cacheType(new (ctx) InjectIntoOptionalExpr(result, toType));
      return result;
    }
    
    case ConversionRestrictionKind::PointerToPointer:
    case ConversionRestrictionKind::PointerToCPointer: {
      TypeChecker::requirePointerArgumentIntrinsics(ctx, expr->getLoc());
      Type unwrappedToTy = toType->getOptionalObjectType();

      // Optional to optional.
      if (Type unwrappedFromTy = cs.getType(expr)->getOptionalObjectType()) {
        assert(unwrappedToTy && "converting optional to non-optional");
        Expr *boundOptional = cs.cacheType(
            new (ctx) BindOptionalExpr(expr, SourceLoc(),
                                       /*depth*/ 0, unwrappedFromTy));
        Expr *converted = cs.cacheType(
            new (ctx) PointerToPointerExpr(boundOptional, unwrappedToTy));
        Expr *rewrapped =
            cs.cacheType(new (ctx) InjectIntoOptionalExpr(converted, toType));
        return cs.cacheType(new (ctx)
                                OptionalEvaluationExpr(rewrapped, toType));
      }

      // Non-optional to optional.
      if (unwrappedToTy) {
        Expr *converted =
            cs.cacheType(new (ctx) PointerToPointerExpr(expr, unwrappedToTy));
        return cs.cacheType(new (ctx)
                                InjectIntoOptionalExpr(converted, toType));
      }

      // Non-optional to non-optional.
      return cs.cacheType(new (ctx) PointerToPointerExpr(expr, toType));
    }

    case ConversionRestrictionKind::CFTollFreeBridgeToObjC: {
      auto foreignClass = fromType->getClassOrBoundGenericClass();
      auto objcType = foreignClass->getAttrs().getAttribute<ObjCBridgedAttr>()
                        ->getObjCClass()->getDeclaredInterfaceType();
      auto asObjCClass =
          cs.cacheType(new (ctx) ForeignObjectConversionExpr(expr, objcType));
      return coerceToType(asObjCClass, toType, locator);
    }

    case ConversionRestrictionKind::ObjCTollFreeBridgeToCF: {
      auto foreignClass = toType->getClassOrBoundGenericClass();
      auto objcType = foreignClass->getAttrs().getAttribute<ObjCBridgedAttr>()
                        ->getObjCClass()->getDeclaredInterfaceType();
      Expr *result = coerceToType(expr, objcType, locator);
      if (!result)
        return nullptr;

      return cs.cacheType(new (ctx)
                              ForeignObjectConversionExpr(result, toType));
    }

    case ConversionRestrictionKind::CGFloatToDouble:
    case ConversionRestrictionKind::DoubleToCGFloat: {
      auto decl = (knownRestriction->second == ConversionRestrictionKind::CGFloatToDouble
                    ? ctx.getDoubleInitDecl() : ctx.getCGFloatInitDecl());

      if (!decl) {
        ctx.Diags.diagnose(expr->getLoc(), diag::broken_stdlib_type,
                           toType->getAnyNominal()->getName().str());
        auto *errorExpr = new (ctx) ErrorExpr(SourceRange(), toType);
        cs.setType(errorExpr, toType);

        return errorExpr;
      }

      auto *initType = decl.getDecl()->getInterfaceType()->castTo<FunctionType>();

      auto *ctorRefExpr = new (ctx)
          DeclRefExpr(decl, DeclNameLoc(expr->getLoc()), /*Implicit=*/true);
      ctorRefExpr->setType(initType);
      auto *typeExpr = TypeExpr::createImplicit(toType, ctx);
      auto *innerCall = ConstructorRefCallExpr::create(ctx, ctorRefExpr, typeExpr,
                                                       initType->getResult());
      cs.cacheExprTypes(innerCall);

      auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {cs.coerceToRValue(expr)});
      auto *outerCall = CallExpr::createImplicit(ctx, innerCall, argList);
      outerCall->setType(toType);
      cs.setType(outerCall, toType);

      return outerCall;
    }
    }
  }

  // Use an opaque type to abstract a value of the underlying concrete type.
  // The full check here would be that `toType` and `fromType` are structurally
  // equal except in any position where `toType` has an opaque archetype. The
  // below is just an approximate check since the above would be expensive to
  // verify and still relies on the type checker ensuing `fromType` is
  // compatible with any opaque archetypes.
  if (toType->getCanonicalType()->hasOpaqueArchetype() &&
      cs.getConstraintLocator(locator)->isForContextualType()) {
    // Find the opaque type declaration. We need its generic signature.
    OpaqueTypeDecl *opaqueDecl = nullptr;
    bool found = toType->getCanonicalType().findIf([&](Type type) {
      if (auto opaqueType = type->getAs<OpaqueTypeArchetypeType>()) {
        opaqueDecl = opaqueType->getDecl();
        return true;
      }

      return false;
    });
    (void)found;
    assert(found && "No opaque type archetype?");

    // Compute the substitutions for the opaque type declaration.
    auto opaqueLocator = solution.getConstraintSystem().getOpenOpaqueLocator(
        locator, opaqueDecl);
    SubstitutionMap substitutions = solution.computeSubstitutions(
        opaqueDecl, opaqueDecl->getOpaqueInterfaceGenericSignature(),
        opaqueLocator);

    // If we don't have substitutions, this is an opaque archetype from
    // another declaration being manipulated, and not an erasure of a
    // concrete type to an opaque type inside its defining declaration.
    if (!substitutions.empty()) {
      // Compute the underlying type by replacing all opaque archetypes with
      // the fixed type of their opened type.
      auto underlyingType = toType.transformRec(
        [&](TypeBase *type) -> std::optional<Type> {
          if (auto *opaqueType = dyn_cast<OpaqueTypeArchetypeType>(type)) {
            if (opaqueType->getDecl() == opaqueDecl) {
              return opaqueType->getInterfaceType().subst(substitutions);
            }
          }
          return std::nullopt;
        });

      // Coerce the result expression to the underlying type.
      // FIXME: Wrong locator?
      auto *subExpr = coerceToType(expr, underlyingType, locator);

      return cs.cacheType(
          new (ctx) UnderlyingToOpaqueExpr(subExpr, toType, substitutions));
    }
  }

  // Handle "from specific" coercions before "catch all" coercions.
  auto desugaredFromType = fromType->getDesugaredType();
  switch (desugaredFromType->getKind()) {
  // Coercions from an lvalue: load or perform implicit address-of. We perform
  // these coercions first because they are often the first step in a multi-step
  // coercion.
  case TypeKind::LValue: {
    auto fromLValue = cast<LValueType>(desugaredFromType);

    auto injectUnsafeLValueCast = [&](Type fromObjType,
                                      Type toObjType) -> Expr * {
      auto restriction = solution.getConversionRestriction(
          fromObjType->getCanonicalType(), toObjType->getCanonicalType());
      ASSERT(restriction == ConversionRestrictionKind::DeepEquality);
      return cs.cacheType(
          new (ctx) ABISafeConversionExpr(expr, LValueType::get(toObjType)));
    };

    // @lvalue <A> -> @lvalue <B> is only allowed if there is a
    // deep equality conversion restriction between the types.
    // This supports `any Sendable` -> `Any` conversion in generic
    // argument positions.
    if (auto *toLValue = toType->getAs<LValueType>()) {
      return injectUnsafeLValueCast(fromLValue->getObjectType(),
                                    toLValue->getObjectType());
    }

    auto toIO = toType->getAs<InOutType>();
    if (!toIO)
      return coerceToType(cs.addImplicitLoadExpr(expr), toType, locator);

    // @lvalue <A> -> inout <B> has to use an unsafe cast <A> -> <B>:
    // @lvalue <A> <cast to> @lvalue B -> inout B.
    //
    // This can happen due to any Sendable -> Any conversion in generic
    // argument positions. We need to inject a cast to get @l-value to
    // match `inout` type exactly.
    if (!toIO->getObjectType()->isEqual(fromLValue->getObjectType())) {
      expr = injectUnsafeLValueCast(fromLValue->getObjectType(),
                                    toIO->getObjectType());
    }

    // In an 'inout' operator like "i += 1", the operand is converted from
    // an implicit lvalue to an inout argument.
    return cs.cacheType(new (ctx) InOutExpr(expr->getStartLoc(), expr,
                                            toIO->getObjectType(),
                                            /*isImplicit*/ true));
  }

  case TypeKind::InOut: {
    auto *inOutExpr = getAsExpr<InOutExpr>(expr);
    if (!inOutExpr)
      break;

    // If there is an `any Sendable` -> `Any` mismatch here,
    // the conversion should be performed on l-value and the
    // address taken from that. This is something that is already
    // done as part of implicit `inout` injection for operators
    // and could be reused here.
    return coerceToType(inOutExpr->getSubExpr(), toType, locator);
  }

  case TypeKind::Pack:
  case TypeKind::PackElement: {
    llvm_unreachable("Unimplemented!");
  }

  case TypeKind::PackExpansion: {
    auto toExpansionType = toType->getAs<PackExpansionType>();
    auto *expansion = dyn_cast<PackExpansionExpr>(expr);

    auto *elementEnv = expansion->getGenericEnvironment();
    auto toElementType = elementEnv->mapContextualPackTypeIntoElementContext(
        toExpansionType->getPatternType());

    auto *pattern = coerceToType(expansion->getPatternExpr(),
                                 toElementType, locator);
    auto *packEnv = cs.DC->getGenericEnvironmentOfContext();
    auto patternType = packEnv->mapElementTypeIntoPackContext(toElementType);
    auto shapeType = toExpansionType->getCountType();
    auto expansionTy = PackExpansionType::get(patternType, shapeType);

    expansion->setPatternExpr(pattern);
    expansion->setType(expansionTy);
    return cs.cacheType(expansion);
  }

  case TypeKind::BuiltinTuple:
    llvm_unreachable("BuiltinTupleType should not show up here");

  // Coerce from a tuple to a tuple.
  case TypeKind::Tuple: {
    auto fromTuple = cast<TupleType>(desugaredFromType);
    auto toTuple = toType->getAs<TupleType>();
    if (!toTuple)
      break;

    if (fromTuple->hasLValueType() && !toTuple->hasLValueType())
      return coerceToType(cs.coerceToRValue(expr), toType, locator);

    SmallVector<unsigned, 4> sources;
    if (!computeTupleShuffle(fromTuple, toTuple, sources)) {
      return coerceTupleToTuple(expr, fromTuple, toTuple,
                                locator, sources);
    }
    break;
  }

  case TypeKind::PrimaryArchetype:
  case TypeKind::ExistentialArchetype:
  case TypeKind::OpaqueTypeArchetype:
  case TypeKind::PackArchetype:
  case TypeKind::ElementArchetype:
    if (!cast<ArchetypeType>(desugaredFromType)->requiresClass())
      break;
    LLVM_FALLTHROUGH;

  // Coercion from a subclass to a superclass.
  //
  // FIXME: Can we rig things up so that we always have a Superclass
  // conversion restriction in this case?
  case TypeKind::DynamicSelf:
  case TypeKind::BoundGenericClass:
  case TypeKind::Class: {
    if (!toType->getClassOrBoundGenericClass())
      break;
    for (auto fromSuperClass = fromType->getSuperclass();
         fromSuperClass;
         fromSuperClass = fromSuperClass->getSuperclass()) {
      if (fromSuperClass->isEqual(toType)) {
        return coerceSuperclass(expr, toType);
      }
    }
    break;
  }

  // Coercion from one function type to another, this produces a
  // FunctionConversionExpr in its full generality.
  case TypeKind::Function: {
    auto fromFunc = cast<FunctionType>(desugaredFromType);
    auto toFunc = toType->getAs<FunctionType>();
    if (!toFunc)
      break;

    // Default argument generator must return escaping functions. Therefore, we
    // leave an explicit escape to noescape cast here such that SILGen can skip
    // the cast and emit a code for the escaping function.
    bool isInDefaultArgumentContext = false;
    if (auto initializerCtx = dyn_cast<Initializer>(dc))
      isInDefaultArgumentContext = (initializerCtx->getInitializerKind() ==
                                    InitializerKind::DefaultArgument);
    auto toEI = toFunc->getExtInfo();
    assert(toType->is<FunctionType>());

    // Handle implicit conversions between non-@differentiable and
    // @differentiable functions.
    {
      auto fromEI = fromFunc->getExtInfo();
      auto isFromDifferentiable = fromEI.isDifferentiable();
      auto isToDifferentiable = toEI.isDifferentiable();
      // Handle implicit conversion from @differentiable.
      if (isFromDifferentiable && !isToDifferentiable) {
        fromFunc = fromFunc->getWithoutDifferentiability()
            ->castTo<FunctionType>();
        switch (fromEI.getDifferentiabilityKind()) {
        // TODO: Ban `Normal` and `Forward` cases.
        case DifferentiabilityKind::Normal:
        case DifferentiabilityKind::Forward:
        case DifferentiabilityKind::Reverse:
          expr = cs.cacheType(new (ctx)
              DifferentiableFunctionExtractOriginalExpr(expr, fromFunc));
          break;
        case DifferentiabilityKind::Linear:
          expr = cs.cacheType(new (ctx)
              LinearFunctionExtractOriginalExpr(expr, fromFunc));
          break;
        case DifferentiabilityKind::NonDifferentiable:
          llvm_unreachable("Cannot be NonDifferentiable");
        }
      }
      // Handle implicit conversion from non-@differentiable to @differentiable.
      maybeDiagnoseUnsupportedDifferentiableConversion(cs, expr, toFunc);
      if (!isFromDifferentiable && isToDifferentiable) {
        auto newEI =
            fromEI.intoBuilder()
                .withDifferentiabilityKind(toEI.getDifferentiabilityKind())
                .build();
        SmallVector<AnyFunctionType::Param, 4> params(fromFunc->getParams());
        assert(params.size() == toFunc->getParams().size() &&
               "unexpected @differentiable conversion");
        // Propagate @noDerivate from target function type
        for (auto paramAndIndex : llvm::enumerate(toFunc->getParams())) {
          if (!paramAndIndex.value().isNoDerivative())
            continue;

          auto &param = params[paramAndIndex.index()];
          param =
            param.withFlags(param.getParameterFlags().withNoDerivative(true));
        }

        fromFunc = FunctionType::get(params, fromFunc->getResult(), newEI);
        switch (toEI.getDifferentiabilityKind()) {
        // TODO: Ban `Normal` and `Forward` cases.
        case DifferentiabilityKind::Normal:
        case DifferentiabilityKind::Forward:
        case DifferentiabilityKind::Reverse:
          expr = cs.cacheType(new (ctx)
                              DifferentiableFunctionExpr(expr, fromFunc));
          break;
        case DifferentiabilityKind::Linear:
          expr = cs.cacheType(new (ctx) LinearFunctionExpr(expr, fromFunc));
          break;
        case DifferentiabilityKind::NonDifferentiable:
          llvm_unreachable("Cannot be NonDifferentiable");
        }
      }
    }

    // If we have a ClosureExpr, then we can safely propagate @Sendable
    // to the closure without invalidating prior analysis.
    auto fromEI = fromFunc->getExtInfo();
    if (toEI.isSendable() && !fromEI.isSendable()) {
      auto newFromFuncType = fromFunc->withExtInfo(fromEI.withSendable());
      if (applyTypeToClosureExpr(cs, expr, newFromFuncType)) {
        fromFunc = newFromFuncType->castTo<FunctionType>();

        // Propagating the 'concurrent' bit might have satisfied the entire
        // conversion. If so, we're done, otherwise keep converting.
        if (fromFunc->isEqual(toType))
          return expr;
      }
    }

    // If we have a ClosureExpr, then we can safely propagate a global actor
    // to the closure if it's not explicitly marked as `@concurrent` without
    // invalidating prior analysis.
    fromEI = fromFunc->getExtInfo();
    if (toEI.getGlobalActor() && !fromEI.getGlobalActor() &&
        !isClosureMarkedAsConcurrent(expr)) {
      auto newFromFuncType =
          fromFunc->withExtInfo(fromEI.withGlobalActor(toEI.getGlobalActor()));
      if (applyTypeToClosureExpr(cs, expr, newFromFuncType)) {
        fromFunc = newFromFuncType->castTo<FunctionType>();

        // Propagating the global actor bit might have satisfied the entire
        // conversion. If so, we're done, otherwise keep converting.
        if (fromFunc->isEqual(toType))
          return expr;
      }
    }

    /// Whether the given effect is polymorphic at this location.
    auto isEffectPolymorphic = [&](EffectKind kind) -> bool {
      if (!locator.endsWith<LocatorPathElt::ApplyArgToParam>())
        return false;

      if (auto *call = getAsExpr<ApplyExpr>(locator.getAnchor())) {
        if (auto *declRef = dyn_cast<DeclRefExpr>(call->getFn())) {
          if (auto *fn = dyn_cast<AbstractFunctionDecl>(declRef->getDecl()))
            return fn->hasPolymorphicEffect(kind);
        }
      }

      return false;
    };

    // If we have a ClosureExpr, and we can safely propagate 'async' to the
    // closure, do that here.
    fromEI = fromFunc->getExtInfo();
    bool shouldPropagateAsync =
        !isEffectPolymorphic(EffectKind::Async) || closureInheritsActorContext(expr);
    if (toEI.isAsync() && !fromEI.isAsync() && shouldPropagateAsync) {
      auto newFromFuncType = fromFunc->withExtInfo(fromEI.withAsync());
      if (applyTypeToClosureExpr(cs, expr, newFromFuncType)) {
        fromFunc = newFromFuncType->castTo<FunctionType>();

        // Propagating 'async' might have satisfied the entire conversion.
        // If so, we're done, otherwise keep converting.
        if (fromFunc->isEqual(toType))
          return expr;
      }
    }

    // If we have a ClosureExpr, then we can safely propagate the lifetime
    // dependence info to the closure without invalidating prior analysis.
    fromEI = fromFunc->getExtInfo();
    if (!toEI.getLifetimeDependencies().empty() &&
        fromEI.getLifetimeDependencies().empty()) {
      auto newFromFuncType = fromFunc->withExtInfo(
          fromEI.withLifetimeDependencies(toEI.getLifetimeDependencies()));
      if (applyTypeToClosureExpr(cs, expr, newFromFuncType)) {
        fromFunc = newFromFuncType->castTo<FunctionType>();

        // Propagating 'lifetime dependence info' might have satisfied the
        // entire conversion. If so, we're done, otherwise keep converting.
        if (fromFunc->isEqual(toType))
          return expr;
      }
    }

    // If we have a ClosureExpr, then we can safely propagate the 'no escape'
    // bit to the closure without invalidating prior analysis.
    fromEI = fromFunc->getExtInfo();
    if (toEI.isNoEscape() && !fromEI.isNoEscape()) {
      auto newFromFuncType = fromFunc->withExtInfo(fromEI.withNoEscape());
      if (!isInDefaultArgumentContext &&
          applyTypeToClosureExpr(cs, expr, newFromFuncType)) {
        fromFunc = newFromFuncType->castTo<FunctionType>();
        // Propagating the 'no escape' bit might have satisfied the entire
        // conversion.  If so, we're done, otherwise keep converting.
        if (fromFunc->isEqual(toType))
          return expr;
      } else if (isInDefaultArgumentContext) {
        // First apply the conversion *without* noescape attribute.
        if (!newFromFuncType->isEqual(toType)) {
          auto escapingToFuncTy =
              toFunc->withExtInfo(toEI.withNoEscape(false));
          maybeDiagnoseUnsupportedFunctionConversion(cs, expr, toFunc);
          expr = cs.cacheType(
              new (ctx) FunctionConversionExpr(expr, escapingToFuncTy));
        }
        // Apply an explicit function conversion *only* for the escape to
        // noescape conversion. This conversion will be stripped by the
        // default argument generator. (We can't return a @noescape function)
        auto newExpr =
            cs.cacheType(new (ctx) FunctionConversionExpr(expr, toFunc));
        return newExpr;
      }
    }

    // If we have a ClosureExpr, then we can safely propagate the
    // '@called(once)' bit to the closure without invalidating prior analysis.
    fromEI = fromFunc->getExtInfo();
    if (toEI.isCalledOnce() && !fromEI.isCalledOnce()) {
      auto newFromFuncType = fromFunc->withExtInfo(fromEI.withCalledOnce());
      if (applyTypeToClosureExpr(cs, expr, newFromFuncType)) {
        fromFunc = newFromFuncType->castTo<FunctionType>();

        // Propagating '@called(once)' might have satisfied the entire
        // conversion. If so, we're done, otherwise keep converting.
        if (fromFunc->isEqual(toType))
          return expr;
      }
    }

    if (ctx.LangOpts.isDynamicActorIsolationCheckingEnabled()) {
      // Passing a synchronous global actor-isolated function value and
      // parameter that expects a synchronous nonisolated function type could
      // require a runtime check to ensure that function is always called in
      // expected context.
      if (toEI.getIsolation().isNonIsolated() && fromEI.getGlobalActor() &&
          !toEI.isAsync()) {
        // Runtime check is required when isolation function value
        // is passed to an API that comes from a module that doesn't
        // have full static concurrency checking enabled.
        auto requiresRuntimeCheck = [&]() {
          if (!locator.endsWith<LocatorPathElt::ApplyArgToParam>())
            return false;

          ConstraintLocator *calleeLoc = nullptr;
          if (auto *call = getAsExpr<ApplyExpr>(locator.getAnchor())) {
            calleeLoc = CallLocators[call].calleeLoc;
          } else {
            calleeLoc =
                solution.getCalleeLocator(cs.getConstraintLocator(locator));
          }

          auto overload = solution.getOverloadChoiceIfAvailable(calleeLoc);
          if (!(overload && overload->choice.isDecl()))
            return false;

          auto *decl = overload->choice.getDecl();
          // Function values passed to C/ObjC APIs are already thunked
          // and that's where the check is going to go.
          if (decl->hasClangNode())
            return false;

          auto declaredIn = decl->findImport(dc);
          if (!declaredIn)
            return false;

          return !declaredIn->module.importedModule->isConcurrencyChecked();
        };

        if (requiresRuntimeCheck()) {
          auto isolatedToType =
              FunctionType::get(toFunc->getParams(), toFunc->getResult(),
                                toEI.withGlobalActor(fromEI.getGlobalActor()));

          // Global actor might not be the only difference, let's introduce
          // a function conversion first but with matching isolation.
          expr = cs.cacheType(new (ctx)
                                  FunctionConversionExpr(expr, isolatedToType));

          return cs.cacheType(new (ctx)
                                  ActorIsolationErasureExpr(expr, toType));
        }
      }
    }

    maybeDiagnoseUnsupportedFunctionConversion(cs, expr, toFunc);

    return cs.cacheType(new (ctx) FunctionConversionExpr(expr, toType));
  }

  // Coercions from one metatype to another.
  case TypeKind::Metatype: {
    if (auto toMeta = toType->getAs<MetatypeType>())
      return cs.cacheType(new (ctx) MetatypeConversionExpr(expr, toMeta));
    LLVM_FALLTHROUGH;
  }
  // Coercions from metatype to objects.
  case TypeKind::ExistentialMetatype: {
    auto fromMeta = cast<AnyMetatypeType>(desugaredFromType);
    if (toType->isAnyObject()) {
      assert(ctx.LangOpts.EnableObjCInterop
             && "metatype-to-object conversion requires objc interop");
      if (fromMeta->is<MetatypeType>()) {
        assert(fromMeta->getInstanceType()->mayHaveSuperclass()
               && "metatype-to-object input should be a class metatype");
        return cs.cacheType(new (ctx) ClassMetatypeToObjectExpr(expr, toType));
      }
      
      if (fromMeta->is<ExistentialMetatypeType>()) {
        assert(fromMeta->getInstanceType()->getCanonicalType()
                       ->getExistentialLayout().requiresClass()
               && "metatype-to-object input should be a class metatype");
        return cs.cacheType(new (ctx)
                                ExistentialMetatypeToObjectExpr(expr, toType));
      }
      
      llvm_unreachable("unhandled metatype kind");
    }
    
    if (auto toClass = toType->getClassOrBoundGenericClass()) {
      if (toClass->getName() == ctx.Id_Protocol
          && toClass->getModuleContext()->getName()
              == ctx.Id_ObjectiveC) {
        assert(ctx.LangOpts.EnableObjCInterop
               && "metatype-to-object conversion requires objc interop");
        assert(fromMeta->is<MetatypeType>()
               && fromMeta->getInstanceType()->is<ProtocolType>()
               && "protocol-metatype-to-Protocol only works for single "
                  "protocols");
        return cs.cacheType(new (ctx)
                                ProtocolMetatypeToObjectExpr(expr, toType));
      }
    }

    break;
  }

#define SUGARED_TYPE(Name, Parent) case TypeKind::Name:
#define BUILTIN_TYPE(Name, Parent) case TypeKind::Name:
#define UNCHECKED_TYPE(Name, Parent) case TypeKind::Name:
#define ARTIFICIAL_TYPE(Name, Parent) case TypeKind::Name:
#define TYPE(Name, Parent)
#include "swift/AST/TypeNodes.def"
  case TypeKind::Error:
  case TypeKind::Module:
  case TypeKind::Enum:
  case TypeKind::Struct:
  case TypeKind::Protocol:
  case TypeKind::ProtocolComposition:
  case TypeKind::ParameterizedProtocol:
  case TypeKind::Existential:
  case TypeKind::BoundGenericEnum:
  case TypeKind::BoundGenericStruct:
  case TypeKind::GenericFunction:
  case TypeKind::GenericTypeParam:
  case TypeKind::DependentMember:
  case TypeKind::Integer:
  case TypeKind::Hidden:
    break;
  }

  // Uninhabited types can be coerced to any other type via an UnreachableExpr.
  if (fromType->isUninhabited())
    return cs.cacheType(UnreachableExpr::create(ctx, expr, toType));

  // "Catch all" coercions.
  auto desugaredToType = toType->getDesugaredType();
  switch (desugaredToType->getKind()) {
  // Coercions from a type to an existential type.
  case TypeKind::Existential:
  case TypeKind::ExistentialMetatype:
  case TypeKind::ProtocolComposition:
  case TypeKind::ParameterizedProtocol:
  case TypeKind::Protocol:
    return coerceExistential(expr, toType, locator);

  // Coercion to Optional<T>.
  case TypeKind::BoundGenericEnum: {
    auto toGenericType = cast<BoundGenericEnumType>(desugaredToType);
    if (!toGenericType->getDecl()->isOptionalDecl())
      break;
    TypeChecker::requireOptionalIntrinsics(ctx, expr->getLoc());

    if (cs.getType(expr)->getOptionalObjectType())
      return coerceOptionalToOptional(expr, toType, locator);

    Type valueType = toGenericType->getGenericArgs()[0];
    expr = coerceToType(expr, valueType, locator);
    if (!expr) return nullptr;

    auto *result = cs.cacheType(new (ctx) InjectIntoOptionalExpr(expr, toType));
    diagnoseOptionalInjection(result, locator);
    return result;
  }

  case TypeKind::BoundGenericStruct: {
    auto toStruct = cast<BoundGenericStructType>(desugaredToType);
    if (!toStruct->isArray() && !toStruct->isDictionary())
      break;

    if (toStruct->getDecl() == cs.getType(expr)->getAnyNominal())
      return buildCollectionUpcastExpr(expr, toType, /*bridged=*/false,
                                       locator);

    break;
  }

#define SUGARED_TYPE(Name, Parent) case TypeKind::Name:
#define BUILTIN_TYPE(Name, Parent) case TypeKind::Name:
#define UNCHECKED_TYPE(Name, Parent) case TypeKind::Name:
#define ARTIFICIAL_TYPE(Name, Parent) case TypeKind::Name:
#define TYPE(Name, Parent)
#include "swift/AST/TypeNodes.def"
  case TypeKind::Error:
  case TypeKind::Module:
  case TypeKind::Tuple:
  case TypeKind::Enum:
  case TypeKind::Struct:
  case TypeKind::Class:
  case TypeKind::BoundGenericClass:
  case TypeKind::Metatype:
  case TypeKind::DynamicSelf:
  case TypeKind::PrimaryArchetype:
  case TypeKind::ExistentialArchetype:
  case TypeKind::OpaqueTypeArchetype:
  case TypeKind::PackArchetype:
  case TypeKind::ElementArchetype:
  case TypeKind::GenericTypeParam:
  case TypeKind::DependentMember:
  case TypeKind::Function:
  case TypeKind::GenericFunction:
  case TypeKind::LValue:
  case TypeKind::InOut:
  case TypeKind::Pack:
  case TypeKind::PackExpansion:
  case TypeKind::PackElement:
  case TypeKind::Integer:
  case TypeKind::Hidden:
    break;

  case TypeKind::BuiltinTuple:
    llvm_unreachable("BuiltinTupleType should not show up here");
  }

  // Allow existential-to-supertype conversion if all protocol
  // bounds are marker protocols. Normally this requires a
  // conversion restriction but there are situations related
  // to `@preconcurrency` where the `& Sendable` would be stripped
  // transparently to the solver.
  if (auto *existential = fromType->getAs<ExistentialType>()) {
    if (auto *PCT = existential->getConstraintType()
                        ->getAs<ProtocolCompositionType>()) {
      if (PCT->withoutMarkerProtocols()->isEqual(toType)) {
        return coerceSuperclass(expr, toType);
      }
    }
  }

  ABORT([&](auto &out) {
    out << "Unhandled coercion:\n";
    fromType->dump(out);
    toType->dump(out);
  });
}

static bool isSelfRefInInitializer(Expr *baseExpr,
                                   DeclContext *useDC) {
  auto *CD = dyn_cast<ConstructorDecl>(useDC);
  return CD && baseExpr->isSelfExprOf(CD);
}

/// Detect whether an assignment to \c baseExpr.member in the given
/// decl context can potentially be initialization of a property wrapper.
static bool isPotentialPropertyWrapperInit(Expr *baseExpr,
                                           ValueDecl *member,
                                           DeclContext *UseDC) {
  // Member is not a wrapped property
  auto *VD = dyn_cast<VarDecl>(member);
  if (!(VD && VD->hasAttachedPropertyWrapper()))
    return false;

  // Assignment to a wrapped property can only be re-written to
  // initialization in an init.
  return isSelfRefInInitializer(baseExpr, UseDC);
}

/// Detect whether an assignment to \c baseExpr.member in the given
/// decl context can potentially be initialization via an init accessor.
static bool isPotentialInitViaInitAccessor(Expr *baseExpr,
                                           ValueDecl *member,
                                           DeclContext *useDC) {
  auto *VD = dyn_cast<VarDecl>(member);
  if (!(VD && VD->hasInitAccessor()))
    return false;

  return isSelfRefInInitializer(baseExpr, useDC);
}

/// Adjust the given type to become the self type when referring to
/// the given member.
static Type adjustSelfTypeForMember(Expr *baseExpr,
                                    Type baseTy, ValueDecl *member,
                                    DeclContext *UseDC) {
  assert(!baseTy->is<LValueType>());

  auto inOutTy = baseTy->getAs<InOutType>();
  if (!inOutTy)
    return baseTy;

  auto baseObjectTy = inOutTy->getObjectType();

  if (isa<ConstructorDecl>(member))
    return baseObjectTy;

  if (auto func = dyn_cast<FuncDecl>(member)) {
    // If 'self' is an inout type, turn the base type into an lvalue
    // type with the same qualifiers.
    if (func->isMutating())
      return baseTy;

    // Otherwise, return the rvalue type.
    return baseObjectTy;
  }

  // If the base of the access is mutable, then we may be invoking a getter or
  // setter that requires the base to be mutable.
  auto *SD = cast<AbstractStorageDecl>(member);
  bool isSettableFromHere =
      SD->isSettable(UseDC) && SD->isSetterAccessibleFrom(UseDC);

  // If neither the property's getter nor its setter are mutating,
  // the base can be an rvalue unless the assignment is potentially
  // initializing a property wrapper or using init accessor. If the
  // assignment can be re-written to property wrapper or init accessor
  // initialization, the base type should be an lvalue.
  if (!SD->isGetterMutating() &&
      (!isSettableFromHere || !SD->isSetterMutating()) &&
      !isPotentialPropertyWrapperInit(baseExpr, member, UseDC) &&
      !isPotentialInitViaInitAccessor(baseExpr, member, UseDC))
    return baseObjectTy;

  if (isa<SubscriptDecl>(member))
    return baseTy;

  return LValueType::get(baseObjectTy);
}

Expr *
ExprRewriter::coerceSelfArgumentToType(Expr *expr,
                                       Type baseTy, ValueDecl *member,
                                       ConstraintLocatorBuilder locator) {
  Type toType = adjustSelfTypeForMember(expr, baseTy, member, dc);
  return coerceToType(expr, toType, locator);
}

Expr *ExprRewriter::convertLiteralInPlace(
    LiteralExpr *literal, Type type, ProtocolDecl *protocol,
    Identifier literalType, DeclName literalFuncName,
    ProtocolDecl *builtinProtocol, DeclName builtinLiteralFuncName,
    Diag<> brokenProtocolDiag, Diag<> brokenBuiltinProtocolDiag) {
  // Check whether this literal type conforms to the builtin protocol. If so,
  // initialize via the builtin protocol.
  if (builtinProtocol) {
    auto builtinConformance = checkConformance(type, builtinProtocol);
    if (builtinConformance) {
      // Find the witness that we'll use to initialize the type via a builtin
      // literal.
      auto witness = builtinConformance.getWitnessByName(
          builtinLiteralFuncName);
      if (!witness || !isa<AbstractFunctionDecl>(witness.getDecl()))
        return nullptr;

      // Form a reference to the builtin conversion function.

      // Set the builtin initializer.
      dyn_cast<BuiltinLiteralExpr>(literal)->setBuiltinInitializer(witness);

      // The literal expression has this type.
      cs.setType(literal, type);

      return literal;
    }
  }

  // This literal type must conform to the (non-builtin) protocol.
  assert(protocol && "requirements should have stopped recursion");
  auto conformance = checkConformance(type, protocol);
  assert(conformance && "must conform to literal protocol");

  // Dig out the literal type and perform a builtin literal conversion to it.
  if (!literalType.empty()) {
    // Extract the literal type.
    Type builtinLiteralType =
        conformance.getTypeWitnessByName(literalType);
    if (builtinLiteralType->hasError())
      return nullptr;

    // Perform the builtin conversion.
    if (!convertLiteralInPlace(literal, builtinLiteralType, nullptr,
                               Identifier(), DeclName(), builtinProtocol,
                               builtinLiteralFuncName, brokenProtocolDiag,
                               brokenBuiltinProtocolDiag))
      return nullptr;
  }

  // Find the witness that we'll use to initialize the literal value.
  auto witness =
      conformance.getWitnessByName(literalFuncName);
  if (!witness || !isa<AbstractFunctionDecl>(witness.getDecl()))
    return nullptr;

  // Set the initializer.
  literal->setInitializer(witness);

  // The literal expression has this type.
  cs.setType(literal, type);

  return literal;
}

// Returns true if the given method and method type are a valid
// `@dynamicCallable` required `func dynamicallyCall` method.
static bool isValidDynamicCallableMethod(FuncDecl *method,
                                         AnyFunctionType *methodType) {
  auto &ctx = method->getASTContext();
  if (method->getBaseIdentifier() != ctx.Id_dynamicallyCall)
    return false;
  if (methodType->getParams().size() != 1)
    return false;
  auto argumentLabel = methodType->getParams()[0].getLabel();
  if (argumentLabel != ctx.Id_withArguments &&
      argumentLabel != ctx.Id_withKeywordArguments)
    return false;
  return true;
}

// Build a reference to a `callAsFunction` method.
static Expr *buildCallAsFunctionMethodRef(
    ExprRewriter &rewriter, ApplyExpr *apply, SelectedOverload selected,
    ConstraintLocator *calleeLoc) {
  assert(calleeLoc->isLastElement<LocatorPathElt::ImplicitCallAsFunction>());
  assert(cast<FuncDecl>(selected.choice.getDecl())->isCallAsFunctionMethod());

  // Create direct reference to `callAsFunction` method.
  auto *fn = apply->getFn();
  auto *args = apply->getArgs();

  // HACK: Temporarily push the fn expr onto the expr stack to make sure we
  // don't try to prematurely close an existential when applying the curried
  // member ref. This can be removed once existential opening is refactored not
  // to rely on the shape of the AST prior to rewriting.
  rewriter.ExprStack.push_back(fn);
  SWIFT_DEFER {
    rewriter.ExprStack.pop_back();
  };

  // Anchor the implicit `.callAsFunction` name. Usually `args->getStartLoc()`
  // is fine — for `foo(x)` that's the `(`, a sensible anchor. But for a bare
  // trailing closure (`foo { ... }`) it's the `{`, which bleeds into the
  // member ref's end loc and breaks downstream fix-its
  // (see fixItEncloseTrailingClosure). Fall back to the end of the base in
  // that case.
  auto nameLoc = args->getLParenLoc().isValid() ? args->getStartLoc()
                                                : fn->getEndLoc();
  auto *declRef = rewriter.buildMemberRef(
      fn, /*dotLoc*/ SourceLoc(), selected, DeclNameLoc(nameLoc),
      calleeLoc, calleeLoc, /*implicit*/ true, AccessSemantics::Ordinary);
  if (!declRef)
    return nullptr;
  declRef->setImplicit(apply->isImplicit());
  return declRef;
}

// Resolve `@dynamicCallable` applications.
std::pair<Expr *, ArgumentList *> ExprRewriter::buildDynamicCallable(
    ApplyExpr *apply, SelectedOverload selected, FuncDecl *method,
    AnyFunctionType *methodType, ConstraintLocatorBuilder loc) {
  auto *fn = apply->getFn();

  auto *args = apply->getArgs();

  // Get resolved `dynamicallyCall` method and verify it.
  assert(isValidDynamicCallableMethod(method, methodType));
  auto params = methodType->getParams();
  auto argumentType = params[0].getParameterType();

  // Determine which method was resolved: a `withArguments` method or a
  // `withKeywordArguments` method.
  auto argumentLabel = methodType->getParams()[0].getLabel();
  bool useKwargsMethod = argumentLabel == ctx.Id_withKeywordArguments;

  // HACK: Temporarily push the fn expr onto the expr stack to make sure we
  // don't try to prematurely close an existential when applying the curried
  // member ref. This can be removed once existential opening is refactored not
  // to rely on the shape of the AST prior to rewriting.
  ExprStack.push_back(fn);
  SWIFT_DEFER {
    ExprStack.pop_back();
  };

  // Construct expression referencing the `dynamicallyCall` method.
  auto member = buildMemberRef(fn, SourceLoc(), selected,
                               DeclNameLoc(), loc, loc,
                               /*implicit=*/true, AccessSemantics::Ordinary);

  // Construct argument to the method (either an array or dictionary
  // expression).
  Expr *argExpr = nullptr;
  if (!useKwargsMethod) {
    argExpr = ArrayExpr::create(ctx, SourceLoc(), args->getArgExprs(), {},
                                SourceLoc());
    cs.setType(argExpr, argumentType);
    finishArrayExpr(cast<ArrayExpr>(argExpr));
  } else {
    auto dictLitProto =
        ctx.getProtocol(KnownProtocolKind::ExpressibleByDictionaryLiteral);
    auto conformance = checkConformance(argumentType, dictLitProto);
    auto keyType = conformance.getTypeWitnessByName(ctx.Id_Key);
    auto valueType = conformance.getTypeWitnessByName(ctx.Id_Value);
    SmallVector<Identifier, 4> names;
    SmallVector<Expr *, 4> dictElements;
    for (auto arg : *args) {
      Expr *labelExpr =
        new (ctx) StringLiteralExpr(arg.getLabel().get(), arg.getLabelLoc(),
                                    /*Implicit*/ true);
      cs.setType(labelExpr, keyType);
      handleStringLiteralExpr(cast<LiteralExpr>(labelExpr));

      Expr *valueExpr = coerceToType(arg.getExpr(), valueType, loc);
      assert(valueExpr && "Failed to coerce?");
      Expr *pair = TupleExpr::createImplicit(ctx, {labelExpr, valueExpr}, {});
      auto eltTypes = { TupleTypeElt(keyType), TupleTypeElt(valueType) };
      cs.setType(pair, TupleType::get(eltTypes, ctx));
      dictElements.push_back(pair);
    }
    argExpr = DictionaryExpr::create(ctx, SourceLoc(), dictElements, {},
                                     SourceLoc());
    cs.setType(argExpr, argumentType);
    finishDictionaryExpr(cast<DictionaryExpr>(argExpr));
  }
  argExpr->setImplicit();

  auto *argList = ArgumentList::forImplicitSingle(ctx, argumentLabel, argExpr);
  return std::make_pair(member, argList);
}

Expr *ExprRewriter::finishApply(ApplyExpr *apply, Type openedType,
                                ConstraintLocatorBuilder locator,
                                ConstraintLocatorBuilder calleeLocator) {
  auto args = apply->getArgs();
  auto *fn = apply->getFn();

  auto finishApplyOfDeclWithSpecialTypeCheckingSemantics
    = [&](ApplyExpr *apply,
          ConcreteDeclRef declRef,
          Type openedType) -> Expr* {
      switch (TypeChecker::getDeclTypeCheckingSemantics(declRef.getDecl())) {
      case DeclTypeCheckingSemantics::TypeOf: {
        // Resolve into a DynamicTypeExpr.
        auto args = apply->getArgs();

        auto appliedWrappers = solution.getAppliedPropertyWrappers(
            calleeLocator.getAnchor());
        auto fnType = cs.getType(fn)->getAs<FunctionType>();
        args = coerceCallArguments(
            args, fnType, declRef, apply,
            locator.withPathElement(ConstraintLocator::ApplyArgument),
            appliedWrappers);
        if (!args)
          return nullptr;

        auto replacement = new (ctx)
            DynamicTypeExpr(apply->getFn()->getLoc(), args->getStartLoc(),
                            args->getExpr(0), args->getEndLoc(), Type());
        cs.setType(replacement, simplifyType(openedType));
        return replacement;
      }
      
      case DeclTypeCheckingSemantics::WithoutActuallyEscaping: {
        // Resolve into a MakeTemporarilyEscapableExpr.
        auto *args = apply->getArgs();
        assert(args->size() == 2 && "should have two arguments");
        auto *nonescaping = args->getExpr(0);
        auto *body = args->getExpr(1);
        auto bodyTy = cs.getType(body)->getWithoutSpecifierType();
        auto bodyFnTy = bodyTy->castTo<FunctionType>();
        auto resultType = bodyFnTy->getResult();
        
        // The body is immediately called, so is obviously noescape.
        // Coerce the argument function to be escaping even if it happens to
        // be nonescaping, since we need the dynamic state of the escaping
        // closure to do the dynamic noescape check.
        auto bodyArgFnTy = bodyFnTy->getParams()[0].getPlainType()
          ->castTo<FunctionType>();
          
        bodyArgFnTy = cast<FunctionType>(
          bodyArgFnTy->withExtInfo(bodyArgFnTy->getExtInfo().withNoEscape(false)));
        bodyFnTy = cast<FunctionType>(
          FunctionType::get(bodyFnTy->getParams()[0].withType(bodyArgFnTy),
                            bodyFnTy->getResult())
            ->withExtInfo(bodyFnTy->getExtInfo().withNoEscape()));
        body = coerceToType(body, bodyFnTy, locator);
        assert(body && "can't make nonescaping?!");

        auto escapable = new (ctx)
            OpaqueValueExpr(apply->getFn()->getSourceRange(), Type());
        cs.setType(escapable, bodyArgFnTy);

        auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {escapable});
        auto callSubExpr = CallExpr::createImplicit(ctx, body, argList);
        cs.cacheSubExprTypes(callSubExpr);
        cs.setType(callSubExpr, resultType);
        
        auto replacement = new (ctx)
          MakeTemporarilyEscapableExpr(apply->getFn()->getLoc(),
                                       apply->getArgs()->getStartLoc(),
                                       nonescaping,
                                       callSubExpr,
                                       apply->getArgs()->getEndLoc(),
                                       escapable,
                                       apply);
        cs.setType(replacement, resultType);
        return replacement;
      }
      
      case DeclTypeCheckingSemantics::OpenExistential: {
        // Resolve into an OpenExistentialExpr.
        auto *args = apply->getArgs();
        assert(args->size() == 2 && "should have two arguments");

        auto *existential = cs.coerceToRValue(args->getExpr(0));
        auto *body = cs.coerceToRValue(args->getExpr(1));

        auto bodyFnTy = cs.getType(body)->castTo<FunctionType>();
        auto openedTy = getBaseType(bodyFnTy, /*wantsRValue*/ false);
        auto resultTy = bodyFnTy->getResult();

        // The body is immediately called, so is obviously noescape.
        bodyFnTy = cast<FunctionType>(
          bodyFnTy->withExtInfo(bodyFnTy->getExtInfo().withNoEscape()));
        body = coerceToType(body, bodyFnTy, locator);
        assert(body && "can't make nonescaping?!");

        auto openedInstanceTy = openedTy;
        auto existentialInstanceTy = cs.getType(existential);
        if (auto metaTy = openedTy->getAs<MetatypeType>()) {
          openedInstanceTy = metaTy->getInstanceType();
          existentialInstanceTy = existentialInstanceTy
            ->castTo<ExistentialMetatypeType>()
            ->getExistentialInstanceType();
        }
        assert(openedInstanceTy->castTo<ExistentialArchetypeType>()
                   ->getExistentialType()
                   ->isEqual(existentialInstanceTy));

        auto opaqueValue =
            new (ctx) OpaqueValueExpr(apply->getSourceRange(), openedTy);
        cs.setType(opaqueValue, openedTy);

        auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {opaqueValue});
        auto callSubExpr = CallExpr::createImplicit(ctx, body, argList);
        cs.cacheSubExprTypes(callSubExpr);
        cs.setType(callSubExpr, resultTy);
        
        auto replacement = new (ctx)
          OpenExistentialExpr(existential, opaqueValue, callSubExpr,
                              resultTy);
        cs.setType(replacement, resultTy);

        // Embedded Swift prohibits opened existentials.
        if (auto behavior = shouldDiagnoseEmbeddedLimitations(
                dc, apply->getLoc())) {
          ctx.Diags.diagnose(apply->getLoc(),
                             diag::open_existential_in_embedded_swift,
                             existentialInstanceTy)
            .limitBehavior(*behavior);
        }

        return replacement;
      }
      
      case DeclTypeCheckingSemantics::Normal:
        return nullptr;
      }

      llvm_unreachable("Unhandled DeclTypeCheckingSemantics in switch.");
    };

  // Resolve the callee for the application if we have one.
  ConcreteDeclRef callee;
  auto *calleeLoc = cs.getConstraintLocator(calleeLocator);
  auto overload = solution.getOverloadChoiceIfAvailable(calleeLoc);
  // If this is a call through an implicit `dynamicMember:` subscript
  // of a `@dynamicMemberLookup` type there is no callee because the
  // call happens on a value returned by the subscript invocation and
  // not necessary the member looked up.
  if (overload && !overload->choice.isAnyDynamicMemberLookup()) {
    auto *decl = overload->choice.getDeclOrNull();
    callee = resolveConcreteDeclRef(decl, calleeLoc);
  }

  // Make sure we have a function type that is callable. This helps ensure
  // Type::mayBeCallable stays up-to-date.
  auto fnRValueTy = cs.getType(fn)->getRValueType();
  assert(fnRValueTy->mayBeCallable(dc));

  // If this is an implicit call to a `callAsFunction` method, build the
  // appropriate member reference.
  if (fnRValueTy->isCallAsFunctionType(dc)) {
    fn = buildCallAsFunctionMethodRef(*this, apply, *overload, calleeLoc);
    if (!fn)
      return nullptr;
  }

  // Resolve a `@dynamicCallable` application.
  auto applyFunctionLoc =
      locator.withPathElement(ConstraintLocator::ApplyFunction);
  if (auto selected = solution.getOverloadChoiceIfAvailable(
          cs.getConstraintLocator(applyFunctionLoc))) {
    auto *method = dyn_cast<FuncDecl>(selected->choice.getDecl());
    auto methodType =
        simplifyType(selected->adjustedOpenedType)->getAs<AnyFunctionType>();
    if (method && methodType) {
      // Handle a call to a @dynamicCallable method.
      if (isValidDynamicCallableMethod(method, methodType))
        std::tie(fn, args) = buildDynamicCallable(apply, *selected, method,
                                                  methodType, applyFunctionLoc);
    }
  }

  // The function is always an rvalue.
  fn = cs.coerceToRValue(fn);

  // Resolve applications of decls with special semantics.
  if (auto declRef =
      dyn_cast<DeclRefExpr>(getSemanticExprForDeclOrMemberRef(fn))) {
    if (auto special =
        finishApplyOfDeclWithSpecialTypeCheckingSemantics(apply,
                                                          declRef->getDeclRef(),
                                                          openedType)) {
      return special;
    }
  }

  // If we're applying a function that resulted from a covariant
  // function conversion, strip off that conversion.
  // FIXME: It would be nicer if we could build the ASTs properly in the
  // first shot.
  Type covariantResultType;
  if (auto covariant = dyn_cast<CovariantFunctionConversionExpr>(fn)) {
    // Strip off one layer of application from the covariant result.
    covariantResultType
      = cs.getType(covariant)->castTo<AnyFunctionType>()->getResult();
   
    // Use the subexpression as the function.
    fn = covariant->getSubExpr();
  }

  // An immediate application of a closure literal is always noescape.
  if (isClosureLiteralExpr(fn)) {
    if (auto fnTy = cs.getType(fn)->getAs<FunctionType>()) {
      fnTy = cast<FunctionType>(
        fnTy->withExtInfo(fnTy->getExtInfo().withNoEscape()));
      fn = coerceToType(fn, fnTy, locator);
    }
  }

  apply->setFn(fn);

  // For function application, convert the argument to the input type of
  // the function.
  if (auto fnType = cs.getType(fn)->getAs<FunctionType>()) {
    auto appliedWrappers = solution.getAppliedPropertyWrappers(
        calleeLocator.getAnchor());

    args = coerceCallArguments(
        args, fnType, callee, apply,
        locator.withPathElement(ConstraintLocator::ApplyArgument),
        appliedWrappers);
    if (!args)
      return nullptr;

    apply->setArgs(args);
    cs.setType(apply, fnType->getResult());

    // If this is a call to a distributed method thunk,
    // let's mark the call as implicitly throwing.
    if (isDistributedThunk(callee, apply->getFn())) {
      apply->setImplicitlyThrows(true);
    }

    solution.setExprTypes(apply);
    Expr *result = TypeChecker::substituteInputSugarTypeForResult(apply);
    cs.cacheExprTypes(result);

    // If we have a covariant result type, perform the conversion now.
    if (covariantResultType) {
      if (covariantResultType->is<FunctionType>())
        result = cs.cacheType(new (ctx) CovariantFunctionConversionExpr(
            result, covariantResultType));
      else
        result = cs.cacheType(new (ctx) CovariantReturnConversionExpr(
            result, covariantResultType));
    }

    // Try closing existentials, if there are any.
    closeExistentials(result, locator);

    // We may also need to force the result for an IUO. We don't apply this on
    // SelfApplyExprs, as the force unwraps should be inserted at the result of
    // main application, not on the curried member reference.
    if (!isa<SelfApplyExpr>(apply)) {
      result = forceUnwrapIfExpected(result, calleeLocator,
                                     IUOReferenceKind::ReturnValue);
    }
    return result;
  }

  // FIXME: Handle unwrapping everywhere else.

  // We have a type constructor.
  auto metaTy = cs.getType(fn)->castTo<AnyMetatypeType>();
  auto ty = metaTy->getInstanceType();

  // If we're "constructing" a tuple type, it's simply a conversion.
  if (auto tupleTy = ty->getAs<TupleType>()) {
    auto *packed = apply->getArgs()->packIntoImplicitTupleOrParen(
        ctx, [&](Expr *E) { return cs.getType(E); });
    cs.cacheType(packed);
    auto *result = coerceToType(packed, tupleTy, cs.getConstraintLocator(packed));
    // Resetting the types of tuple is necessary because
    // `packIntoImplicitTupleOrParen` sets types in AST
    // where `coerceToType` only updates constraint system
    // cache. This creates a mismatch that would not be
    // corrected by `setExprTypes` if this tuple is used
    // as an argument that is wrapped in an autoclosure.
    solution.setExprTypes(result);
    return result;
  }

  // We're constructing a value of nominal type.
  assert(ty->getNominalOrBoundGenericNominal() || ty->is<DynamicSelfType>() ||
         ty->isExistentialType() || ty->is<ArchetypeType>());

  // Consider the constructor decl reference expr 'implicit', but the
  // constructor call expr itself has the apply's 'implicitness'.
  Expr *declRef = buildMemberRef(
      fn, /*dotLoc=*/SourceLoc(), overload.value(), DeclNameLoc(fn->getEndLoc()),
      locator, calleeLoc, /*implicit=*/true, AccessSemantics::Ordinary);
  if (!declRef)
    return nullptr;

  if (!isa<AutoClosureExpr>(declRef))
    declRef->setImplicit(apply->isImplicit());

  apply->setFn(declRef);

  // Tail-recur to actually call the constructor.
  return finishApply(apply, openedType, locator, calleeLoc);
}

bool ExprRewriter::isDistributedThunk(ConcreteDeclRef ref, Expr *context) {
  auto *FD = dyn_cast_or_null<AbstractFunctionDecl>(ref.getDecl());

  if (!(FD && FD->isInstanceMember() && FD->isDistributed()))
    return false;

  if (!isa<SelfApplyExpr>(context))
    return false;

  auto *actor = getReferencedParamOrCapture(
      cast<SelfApplyExpr>(context)->getBase(),
      [&](OpaqueValueExpr *opaqueValue) -> Expr * {
        for (const auto &existential : OpenedExistentials) {
          if (existential.OpaqueValue == opaqueValue)
            return existential.ExistentialValue;
        }
        return nullptr;
      },
      []() -> VarDecl * {
        // FIXME: Need to communicate this.
        return nullptr;
      });

  if (!actor)
    return false;

  // If this is a method reference on an potentially isolated
  // actor then it cannot be a remote thunk.
  bool isPotentiallyIsolated = isPotentiallyIsolatedActor(
      actor, [&](ParamDecl *P) {
        return P->isIsolated() || solution.isolatedParams.count(P);
      });

  // Adjust the declaration context to the innermost context that is neither
  // a local function nor a closure, so that the actor reference is checked
  auto referenceDC = dc;
  while (true) {
    switch (referenceDC->getContextKind()) {
    case DeclContextKind::AbstractClosureExpr:
    case DeclContextKind::SerializedAbstractClosure:
    case DeclContextKind::Initializer:
      referenceDC = referenceDC->getParent();
      continue;

      case DeclContextKind::AbstractFunctionDecl:
      case DeclContextKind::GenericTypeDecl:
      case DeclContextKind::SubscriptDecl:
        if (auto value = dyn_cast<ValueDecl>(referenceDC->getAsDecl())) {
          if (value->isLocalCapture()) {
            referenceDC = referenceDC->getParent();
            continue;
          }
        }
        break;

      case DeclContextKind::EnumElementDecl:
      case DeclContextKind::ExtensionDecl:
      case DeclContextKind::FileUnit:
      case DeclContextKind::Package:
      case DeclContextKind::Module:
      case DeclContextKind::TopLevelCodeDecl:
      case DeclContextKind::SerializedTopLevelCodeDecl:
      case DeclContextKind::MacroDecl:
        break;
    }

    break;
  }

  // Create a simple actor reference, assuming that we might be in a
  // non-isolated context but knowing whether it's potentially isolated.
  // We only care about the "distributed" flag.
  ReferencedActor actorRef = ReferencedActor(
      actor, isPotentiallyIsolated, ReferencedActor::NonIsolatedContext);
  auto refResult = ActorReferenceResult::forReference(
      ref, context->getLoc(), referenceDC, std::nullopt, actorRef);
  switch (refResult) {
  case ActorReferenceResult::ExitsActorToNonisolated:
  case ActorReferenceResult::SameConcurrencyDomain:
    return false;

  case ActorReferenceResult::EntersActor:
    return refResult.options.contains(ActorReferenceResult::Flags::Distributed);
  }
}

// Return the precedence-yielding parent of 'expr', along with the index of
// 'expr' as the child of that parent. The precedence-yielding parent is the
// nearest ancestor of 'expr' which imposes a minimum precedence on 'expr'.
static std::pair<Expr *, unsigned> getPrecedenceParentAndIndex(
    Expr *expr, llvm::function_ref<Expr *(const Expr *)> getParent) {
  auto *parent = getParent(expr);
  if (!parent)
    return { nullptr, 0 };

  // Look through an unresolved chain wrappers, try, and await exprs, as they
  // have no effect on precedence; they will associate the same with any parent
  // operator as their sub-expression would.
  while (isa<UnresolvedMemberChainResultExpr>(parent) ||
         isa<AnyTryExpr>(parent) || isa<AwaitExpr>(parent) ||
         isa<UnsafeExpr>(parent)) {
    expr = parent;
    parent = getParent(parent);
    if (!parent)
      return { nullptr, 0 };
  }

  // Handle all cases where the answer isn't just going to be { parent, 0 }.
  if (auto tuple = dyn_cast<TupleExpr>(parent)) {
    // Get index of expression in tuple.
    auto tupleElems = tuple->getElements();
    auto elemIt = std::find(tupleElems.begin(), tupleElems.end(), expr);
    assert(elemIt != tupleElems.end() && "expr not found in parent TupleExpr");
    unsigned index = elemIt - tupleElems.begin();
    return { tuple, index };
  } else if (auto *BE = dyn_cast<BinaryExpr>(parent)) {
    if (BE->getLHS() == expr)
      return {parent, 0};
    if (BE->getRHS() == expr)
      return {parent, 1};
  } else if (auto *ternary = dyn_cast<TernaryExpr>(parent)) {
    unsigned index;
    if (expr == ternary->getCondExpr()) {
      index = 0;
    } else if (expr == ternary->getThenExpr()) {
      index = 1;
    } else if (expr == ternary->getElseExpr()) {
      index = 2;
    } else {
      llvm_unreachable("expr not found in parent TernaryExpr");
    }
    return { ternary, index };
  } else if (auto assignExpr = dyn_cast<AssignExpr>(parent)) {
    unsigned index;
    if (expr == assignExpr->getSrc()) {
      index = 0;
    } else if (expr == assignExpr->getDest()) {
      index = 1;
    } else {
      llvm_unreachable("expr not found in parent AssignExpr");
    }
    return { assignExpr, index };
  }

  return { parent, 0 };
}

/// Return true if, when replacing "<expr>" with "<expr> op <something>",
/// parentheses must be added around "<expr>" to allow the new operator
/// to bind correctly.
bool swift::exprNeedsParensInsideFollowingOperator(
    DeclContext *DC, Expr *expr,
    PrecedenceGroupDecl *followingPG) {
  if (expr->isInfixOperator()) {
    auto exprPG = TypeChecker::lookupPrecedenceGroupForInfixOperator(
        DC, expr, /*diagnose=*/false);
    if (!exprPG) return true;

    return DC->getASTContext().associateInfixOperators(exprPG, followingPG)
             != Associativity::Left;
  }

  // We want to parenthesize a 'try?' on the LHS, but we don't care about
  // capturing the new operator inside a 'try' or 'try!'.
  if (isa<OptionalTryExpr>(expr))
    return true;

  return false;
}

/// Return true if, when replacing "<expr>" with "<expr> op <something>"
/// within the given root expression, parentheses must be added around
/// the new operator to prevent it from binding incorrectly in the
/// surrounding context.
bool swift::exprNeedsParensOutsideFollowingOperator(
    DeclContext *DC, Expr *expr, PrecedenceGroupDecl *followingPG,
    llvm::function_ref<Expr *(const Expr *)> getParent) {
  Expr *parent;
  unsigned index;
  std::tie(parent, index) = getPrecedenceParentAndIndex(expr, getParent);
  if (!parent)
    return false;

  // If this is a call argument, no parens are needed.
  if (auto *args = parent->getArgs()) {
    if (!args->isImplicit() && args->findArgumentExpr(expr))
      return false;
  }

  // If this is a key-path, no parens needed if it's an arg of one of the
  // components.
  if (auto *KP = dyn_cast<KeyPathExpr>(parent)) {
    if (KP->findComponentWithSubscriptArg(expr))
      return false;
  }

  if (isa<ParenExpr>(parent) || isa<TupleExpr>(parent)) {
    if (!parent->isImplicit())
      return false;
  }

  if (isa<ClosureExpr>(parent) || isa<CollectionExpr>(parent))
    return false;

  if (parent->isInfixOperator()) {
    auto parentPG = TypeChecker::lookupPrecedenceGroupForInfixOperator(
        DC, parent, /*diagnose=*/false);
    if (!parentPG) return true;

    // If the index is 0, this is on the LHS of the parent.
    auto &Context = DC->getASTContext();
    if (index == 0) {
      return Context.associateInfixOperators(followingPG, parentPG)
               != Associativity::Left;
    } else {
      return Context.associateInfixOperators(parentPG, followingPG)
               != Associativity::Right;
    }
  }

  return true;
}

bool swift::exprNeedsParensBeforeAddingNilCoalescing(DeclContext *DC,
                                                     Expr *expr) {
  auto &ctx = DC->getASTContext();
  auto asPG = TypeChecker::lookupPrecedenceGroup(
                  DC, ctx.Id_NilCoalescingPrecedence, SourceLoc())
                  .getSingle();
  if (!asPG)
    return true;
  return exprNeedsParensInsideFollowingOperator(DC, expr, asPG);
}

bool swift::exprNeedsParensAfterAddingNilCoalescing(
    DeclContext *DC, Expr *expr,
    llvm::function_ref<Expr *(const Expr *)> getParent) {
  auto &ctx = DC->getASTContext();
  auto asPG = TypeChecker::lookupPrecedenceGroup(
                  DC, ctx.Id_NilCoalescingPrecedence, SourceLoc())
                  .getSingle();
  if (!asPG)
    return true;
  return exprNeedsParensOutsideFollowingOperator(DC, expr, asPG, getParent);
}

namespace {
  class SetExprTypes : public ASTWalker {
    const Solution &solution;

  public:
    explicit SetExprTypes(const Solution &solution)
        : solution(solution) {}

    MacroWalking getMacroWalkingBehavior() const override {
      return MacroWalking::ArgumentsAndExpansion;
    }

    PostWalkResult<Expr *> walkToExprPost(Expr *expr) override {
      auto &cs = solution.getConstraintSystem();
      auto exprType = cs.getType(expr);
      exprType = solution.simplifyType(exprType);
      // assert((!expr->getType() || expr->getType()->isEqual(exprType)) &&
      //       "Mismatched types!");
      assert(!exprType->getRecursiveProperties().isSolverAllocated() &&
             "Should not write solver allocated type into expression!");
      assert(!exprType->hasPlaceholder() &&
             "Should not write type placeholders into expression!");
      expr->setType(exprType);

      if (auto kp = dyn_cast<KeyPathExpr>(expr)) {
        for (auto i : indices(kp->getComponents())) {
          Type componentType;
          if (cs.hasType(kp, i)) {
            componentType = solution.simplifyType(cs.getType(kp, i));
            assert(
                !componentType->getRecursiveProperties().isSolverAllocated() &&
                "Should not write solver allocated type into key-path "
                "component!");
            assert(!componentType->hasPlaceholder() &&
                   "Should not write type placeholder into key-path component");
            kp->getMutableComponents()[i].setComponentType(componentType);
          }
        }
      }

      return Action::Continue(expr);
    }

    /// Ignore statements.
    PreWalkResult<Stmt *> walkToStmtPre(Stmt *stmt) override {
      return Action::SkipNode(stmt);
    }

    /// Ignore declarations.
    PreWalkAction walkToDeclPre(Decl *decl) override {
      return Action::SkipNode();
    }
  };

  class ExprWalker : public ASTWalker, public SyntacticElementTargetRewriter {
    ExprRewriter &Rewriter;

  public:
    ExprWalker(ExprRewriter &Rewriter) : Rewriter(Rewriter) { }

    Solution &getSolution() const override { return Rewriter.solution; }
    DeclContext *&getCurrentDC() const override { return Rewriter.dc; }

    void addLocalDeclToTypeCheck(Decl *D) override {
      Rewriter.addLocalDeclToTypeCheck(D);
    }

    bool shouldWalkIntoPropertyWrapperPlaceholderValue() override {
      // Property wrapper placeholder underlying values are filled in
      // with already-type-checked expressions. Don't walk into them.
      return false;
    }

    MacroWalking getMacroWalkingBehavior() const override {
      return MacroWalking::Arguments;
    }

    PreWalkResult<Expr *> walkToExprPre(Expr *expr) override {
      // For closures, update the parameter types and check the body.
      if (auto closure = dyn_cast<ClosureExpr>(expr)) {
        if (rewriteFunction(closure))
          return Action::Stop();

        if (AnyFunctionRef(closure).hasExternalPropertyWrapperParameters()) {
          auto *thunkTy = Rewriter.cs.getType(closure)->castTo<FunctionType>();
          return Action::SkipNode(Rewriter.buildSingleCurryThunk(
              closure, closure, thunkTy, thunkTy,
              Rewriter.cs.getConstraintLocator(closure)));
        }

        return Action::SkipNode(closure);
      }

      if (auto *SVE = dyn_cast<SingleValueStmtExpr>(expr)) {
        if (rewriteSingleValueStmtExpr(SVE))
          return Action::Stop();
        return Action::SkipNode(SVE);
      }

      if (auto tap = dyn_cast_or_null<TapExpr>(expr)) {
        if (rewriteTapExpr(tap))
          return Action::Stop();
        return Action::SkipNode(tap);
      }

      if (auto captureList = dyn_cast<CaptureListExpr>(expr)) {
        // Rewrite captures.
        for (const auto &capture : captureList->getCaptureList()) {
          if (!rewriteTarget(SyntacticElementTarget(capture.PBD)))
            return Action::Stop();
        }
      }

      expr = Rewriter.walkToExprPre(expr);
      return Action::Continue(expr);
    }

    PostWalkResult<Expr *> walkToExprPost(Expr *expr) override {
      auto *result = Rewriter.walkToExprPost(expr);
      if (!result)
        return Action::Stop();

      return Action::Continue(result);
    }

    /// Ignore statements.
    PreWalkResult<Stmt *> walkToStmtPre(Stmt *stmt) override {
      return Action::SkipNode(stmt);
    }

    /// Ignore declarations.
    PreWalkAction walkToDeclPre(Decl *decl) override {
      return Action::SkipNode();
    }

    [[nodiscard]]
    NullablePtr<Pattern> rewritePattern(Pattern *pattern, DeclContext *DC);

    /// Rewrite the target, producing a new target.
    [[nodiscard]]
    std::optional<SyntacticElementTarget>
    rewriteTarget(SyntacticElementTarget target) override;

    /// Rewrite the function for the given solution.
    ///
    /// \returns true if an error occurred.
    [[nodiscard]]
    bool rewriteFunction(AnyFunctionRef fn) {
      return Rewriter.cs.applySolution(fn, *this);
    }

    [[nodiscard]]
    bool rewriteSingleValueStmtExpr(SingleValueStmtExpr *SVE) {
      return Rewriter.cs.applySolutionToSingleValueStmt(SVE, *this);
    }

    [[nodiscard]]
    bool rewriteTapExpr(TapExpr *tap) {
      // First, let's visit the tap expression itself
      // and set all of the inferred types.
      if (!Rewriter.visitTapExpr(tap))
        return true;

      // Now, let's apply solution to the body
      return Rewriter.cs.applySolutionToBody(tap, *this);
    }
  };
} // end anonymous namespace

Expr *ConstraintSystem::coerceToRValue(Expr *expr) {
  return TypeChecker::coerceToRValue(
      getASTContext(), expr, [&](Expr *expr) { return getType(expr); },
      [&](Expr *expr, Type type) { setType(expr, type); });
}

namespace {
  /// Function object to compare source locations, putting invalid
  /// locations at the end.
  class CompareExprSourceLocs {
    SourceManager &sourceMgr;

  public:
    explicit CompareExprSourceLocs(SourceManager &sourceMgr)
      : sourceMgr(sourceMgr) { }

    bool operator()(ASTNode lhs, ASTNode rhs) const {
      if (static_cast<bool>(lhs) != static_cast<bool>(rhs)) {
        return static_cast<bool>(lhs);
      }

      auto lhsLoc = getLoc(lhs);
      auto rhsLoc = getLoc(rhs);
      if (lhsLoc.isValid() != rhsLoc.isValid())
        return lhsLoc.isValid();

      return sourceMgr.isBeforeInBuffer(lhsLoc, rhsLoc);
    }
  };

}

/// Emit the fixes computed as part of the solution, returning true if we were
/// able to emit an error message, or false if none of the fixits worked out.
bool ConstraintSystem::applySolutionFixes(const Solution &solution) {
  /// Collect the fixes on a per-expression basis.
  llvm::SmallDenseMap<ASTNode, SmallVector<ConstraintFix *, 4>> fixesPerAnchor;
  for (auto *fix : solution.Fixes) {
    fixesPerAnchor[fix->getAnchor()].push_back(fix);
  }

  // Collect all of the expressions that have fixes, and sort them by
  // source ordering.
  SmallVector<ASTNode, 4> orderedAnchors;
  for (const auto &fix : fixesPerAnchor) {
    orderedAnchors.push_back(fix.getFirst());
  }
  std::sort(orderedAnchors.begin(), orderedAnchors.end(),
            CompareExprSourceLocs(Context.SourceMgr));

  // Walk over each of the expressions, diagnosing fixes.
  bool diagnosedAnyErrors = false;

  for (auto anchor : orderedAnchors) {
    // Coalesce fixes with the same locator to avoid duplicating notes.
    auto fixes = fixesPerAnchor[anchor];

    using ConstraintFixVector = llvm::SmallVector<ConstraintFix *, 4>;
    llvm::SmallMapVector<ConstraintLocator *,
        llvm::SmallMapVector<FixKind, ConstraintFixVector, 4>, 4> aggregatedFixes;
    for (auto *fix : fixes)
      aggregatedFixes[fix->getLocator()][fix->getKind()].push_back(fix);

    for (auto fixesPerLocator : aggregatedFixes) {
      for (auto fixesPerKind : fixesPerLocator.second) {
        auto fixes = fixesPerKind.second;
        auto *primaryFix = fixes[0];
        ArrayRef<ConstraintFix *> secondaryFixes{fixes.begin() + 1, fixes.end()};

        auto diagnosed =
            primaryFix->coalesceAndDiagnose(solution, secondaryFixes);
        if (!primaryFix->isFatal()) {
          assert(diagnosed && "warnings should always be diagnosed");
          (void)diagnosed;
        } else {
          diagnosedAnyErrors |= diagnosed;
        }
      }
    }
  }

  return diagnosedAnyErrors;
}

/// Pattern match if an initializer has as its value a callee that returns
/// a sent value.
static bool isSendingInitializer(Expr *initializer) {
  auto *await = dyn_cast<AwaitExpr>(initializer);
  if (!await)
    return false;

  auto *call = dyn_cast<CallExpr>(await->getSubExpr());
  if (!call)
    return false;

  auto *fType = call->getFn()->getType()->getAs<AnyFunctionType>();
  if (!fType)
    return false;

  return fType->hasSendingResult();
}

/// For the initializer of an `async let`, wrap it in an autoclosure and then
/// a call to that autoclosure, so that the code for the child task is captured
/// entirely within the autoclosure. This captures the semantics of the
/// operation but not the timing, e.g., the call itself will actually occur
/// when one of the variables in the async let is referenced.
static Expr *wrapAsyncLetInitializer(
      ConstraintSystem &cs, Expr *initializer, DeclContext *dc) {
  auto &ctx = dc->getASTContext();

  // Form the autoclosure type. It is always 'async', and will be 'throws'.
  Type initializerType = initializer->getType();
  bool throws = TypeChecker::canThrow(ctx, initializer)
                  .has_value();
  bool hasSendingeResult = isSendingInitializer(initializer);
  bool isSendable =
      !ctx.LangOpts.hasFeature(Feature::RegionBasedIsolation);
  assert((isSendable || ctx.LangOpts.hasFeature(
                            Feature::SendingArgsAndResults)) &&
         "Region Isolation should imply SendingArgsAndResults");
  auto extInfo = ASTExtInfoBuilder()
                     .withAsync()
                     .withThrows(throws, /*FIXME:*/ Type())
                     .withSendable(isSendable)
                     .withSendingResult(hasSendingeResult)
                     .build();

  // Form the autoclosure expression. The actual closure here encapsulates the
  // child task.
  auto closureType = FunctionType::get({ }, initializerType, extInfo);
  Expr *autoclosureExpr = cs.buildAutoClosureExpr(
      initializer, closureType, dc, /*isDefaultWrappedValue=*/false,
      /*isAsyncLetWrapper=*/true);

  // Call the autoclosure so that the AST types line up. SILGen will ignore the
  // actual calls and translate them into a different mechanism.
  auto autoclosureCall = CallExpr::createImplicitEmpty(ctx, autoclosureExpr);
  autoclosureCall->setType(initializerType);
  // FIXME: Use thrown type from above.
  if (throws) {
    autoclosureCall->setThrows(
        ThrownErrorDestination::forMatchingContextType(
          ctx.getErrorExistentialType()));
  } else {
    autoclosureCall->setThrows(nullptr);
  }

  // For a throwing expression, wrap the call in a 'try'.
  Expr *resultInit = autoclosureCall;
  if (throws) {
    resultInit = new (ctx) TryExpr(
        SourceLoc(), resultInit, initializerType, /*implicit=*/true);
  }

  // Wrap the call in an 'await'.
  resultInit = new (ctx) AwaitExpr(
      SourceLoc(), resultInit, initializerType, /*implicit=*/true);

  cs.cacheExprTypes(resultInit);
  return resultInit;
}

static Pattern *rewriteExprPattern(const SyntacticElementTarget &matchTarget,
                                   Type patternTy,
                                   SyntacticElementTargetRewriter &rewriter) {
  auto *EP = matchTarget.getExprPattern();

  // See if we can simplify to another kind of pattern.
  if (auto simplified = TypeChecker::trySimplifyExprPattern(EP, patternTy))
    return simplified.get();

  auto resultTarget = rewriter.rewriteTarget(matchTarget);
  if (!resultTarget)
    return nullptr;

  EP->setMatchExpr(resultTarget->getAsExpr());
  EP->getMatchVar()->setInterfaceType(patternTy->mapTypeOutOfEnvironment());
  EP->setType(patternTy);
  return EP;
}

/// Attempt to rewrite either an ExprPattern, or a pattern that was solved as
/// an ExprPattern, e.g an EnumElementPattern that could not refer to an enum
/// case.
static std::optional<Pattern *>
tryRewriteExprPattern(Pattern *P, Type patternTy,
                      SyntacticElementTargetRewriter &rewriter) {
  // See if we have a match expression target.
  auto matchTarget = rewriter.getSolution().getTargetFor(P);
  if (!matchTarget)
    return std::nullopt;

  return rewriteExprPattern(*matchTarget, patternTy, rewriter);
}

NullablePtr<Pattern> ExprWalker::rewritePattern(Pattern *pattern,
                                                DeclContext *DC) {
  auto &solution = Rewriter.solution;

  // Figure out the pattern type.
  auto patternTy = solution.getResolvedType(pattern);
  patternTy = patternTy->reconstituteSugar(/*recursive=*/false);

  // Coerce the pattern to its appropriate type.
  TypeResolutionOptions patternOptions(TypeResolverContext::InExpression);
  patternOptions |= TypeResolutionFlags::OverrideType;

  auto tryRewritePattern = [&](Pattern *EP, Type ty) {
    return ::tryRewriteExprPattern(EP, ty, *this);
  };

  auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC);
  return TypeChecker::coercePatternToType(contextualPattern, patternTy,
                                          patternOptions, tryRewritePattern);
}

/// Apply the given solution to the initialization target.
///
/// \returns the resulting initialization expression.
static std::optional<SyntacticElementTarget>
applySolutionToInitialization(SyntacticElementTarget target, Expr *initializer,
                              SyntacticElementTargetRewriter &rewriter) {
  auto &solution = rewriter.getSolution();
  auto wrappedVar = target.getInitializationWrappedVar();
  Type initType;
  if (wrappedVar) {
    initType = solution.getType(initializer);
  } else {
    initType = solution.getType(target.getInitializationPattern());
  }

  {
    // Figure out what type the constraints decided on.
    auto ty = solution.simplifyType(initType);
    initType = ty->getRValueType()->reconstituteSugar(/*recursive =*/false);
  }

  // Convert the initializer to the type of the pattern.
  auto &cs = solution.getConstraintSystem();
  auto &ctx = cs.getASTContext();

  auto locator = cs.getConstraintLocator(
      target.getAsExpr(), LocatorPathElt::ContextualType(CTP_Initialization));
  initializer = solution.coerceToType(initializer, initType, locator);
  if (!initializer)
    return std::nullopt;

  SyntacticElementTarget resultTarget = target;
  resultTarget.setExpr(initializer);

  // Record the property wrapper type and note that the initializer has
  // been subsumed by the backing property.
  if (wrappedVar) {
    ctx.setSideCachedPropertyWrapperBackingPropertyType(
        wrappedVar, initType->mapTypeOutOfEnvironment());

    // Record the semantic initializer on the outermost property wrapper.
    wrappedVar->getOutermostAttachedPropertyWrapper()->setSemanticInit(
        initializer);

    // If this is a wrapped parameter, we're done.
    if (isa<ParamDecl>(wrappedVar))
      return resultTarget;

    wrappedVar->getParentPatternBinding()->setInitializerSubsumed(0);
  }

  // Coerce the pattern to the type of the initializer.
  TypeResolutionOptions options =
      isa<EditorPlaceholderExpr>(initializer->getSemanticsProvidingExpr())
      ? TypeResolverContext::EditorPlaceholderExpr
      : target.getInitializationPatternBindingDecl()
        ? TypeResolverContext::PatternBindingDecl
        : TypeResolverContext::InExpression;
  options |= TypeResolutionFlags::OverrideType;

  // Determine the type of the pattern.
  Type finalPatternType = initializer->getType();
  if (wrappedVar) {
    if (!finalPatternType->hasError() && !finalPatternType->is<TypeVariableType>())
      finalPatternType = computeWrappedValueType(wrappedVar, finalPatternType);
  } else if (auto *typedPattern =
                 dyn_cast<TypedPattern>(target.getInitializationPattern())) {
    // When the pattern carries an explicit type annotation, prefer the type
    // the solver assigned to the pattern over the type of the coerced
    // initializer expression. The two share a canonical type, but if their
    // sugar differs the initializer's sugar wins through coercion, which can
    // have surprising results like printing an internal typealias into a
    // .swiftinterface for a public stored property.

    // FIXME: https://github.com/swiftlang/swift/issues/89690
    // The final type could be set to the init type unconditionally without any
    // regressions if constructor types were resugared consistently.
    if (typedPattern->getTypeRepr())
      finalPatternType = initType;
  }

  finalPatternType = finalPatternType->reconstituteSugar(/*recursive =*/false);

  auto tryRewritePattern = [&](Pattern *EP, Type ty) {
    return ::tryRewriteExprPattern(EP, ty, rewriter);
  };

  // Apply the solution to the pattern as well.
  auto contextualPattern = target.getContextualPattern();
  if (auto coercedPattern = TypeChecker::coercePatternToType(
          contextualPattern, finalPatternType, options, tryRewritePattern)) {
    resultTarget.setPattern(coercedPattern);
  } else {
    return std::nullopt;
  }

  // For an async let, wrap the initializer appropriately to make it a child
  // task.
  if (target.isAsyncLetInitializer()) {
    resultTarget.setExpr(wrapAsyncLetInitializer(
        cs, resultTarget.getAsExpr(), resultTarget.getDeclContext()));
  }

  // If this property has an opaque result type, set the underlying type
  // substitutions based on the initializer.
  if (auto var = resultTarget.getInitializationPattern()->getSingleVar()) {
    if (!var->getParsedAccessor(AccessorKind::Get)) {
      if (auto opaque = var->getOpaqueResultTypeDecl()) {
        SubstitutionMap substitutions;

        resultTarget.getAsExpr()->forEachChildExpr([&](Expr *expr) -> Expr * {
          if (auto coercionExpr = dyn_cast<UnderlyingToOpaqueExpr>(expr)) {
            auto newSubstitutions =
                coercionExpr->substitutions.mapReplacementTypesOutOfEnvironment();
            if (substitutions.empty()) {
              substitutions = newSubstitutions;
            } else {
              assert(substitutions.getCanonical() ==
                         newSubstitutions.getCanonical());
            }
          }
          return expr;
        });

        opaque->setUniqueUnderlyingTypeSubstitutions(substitutions);
      }
    }
  }


  return resultTarget;
}

/// Apply the given solution to the for-each statement target.
///
/// \returns the resulting initialization expression.
static std::optional<SyntacticElementTarget>
applySolutionToForEachStmtPreamble(SyntacticElementTarget target,
                                   SyntacticElementTargetRewriter &rewriter) {
  auto resultTarget = target;
  auto &solution = rewriter.getSolution();
  auto *stmt = target.getAsForEachStmt();

  {
    auto *sequenceExpr = stmt->getSequence();
    auto sequenceTarget = *solution.getTargetFor(sequenceExpr);
    auto rewrittenTarget = rewriter.rewriteTarget(sequenceTarget);
    if (!rewrittenTarget)
      return std::nullopt;

    stmt->setSequence(rewrittenTarget->getAsExpr());
  }

  // Coerce the pattern to the element type.
  {
    TypeResolutionOptions options(TypeResolverContext::ForEachStmt);
    options |= TypeResolutionFlags::OverrideType;

    auto tryRewritePattern = [&](Pattern *EP, Type ty) {
      return ::tryRewriteExprPattern(EP, ty, rewriter);
    };

    // Apply the solution to the pattern as well.
    auto contextualPattern = target.getContextualPattern();
    auto rewrittenPatternType =
        solution.getResolvedType(contextualPattern.getPattern());
    auto coercedPattern = TypeChecker::coercePatternToType(
        contextualPattern, rewrittenPatternType, options, tryRewritePattern);
    if (!coercedPattern)
      return std::nullopt;

    stmt->setPattern(coercedPattern);
    resultTarget.setPattern(coercedPattern);
  }

  return resultTarget;
}

std::optional<SyntacticElementTarget>
ExprWalker::rewriteTarget(SyntacticElementTarget target) {
  // Rewriting the target might abort in case one of visit methods returns
  // nullptr. In this case, no more walkToExprPost calls are issues and thus
  // nodes which were pushed on the Rewriter's ExprStack in walkToExprPre are
  // not popped of the stack again in walkTokExprPost. Usually, that's not an
  // issue if rewriting completely terminates because the ExprStack is never
  // used again. Here, however, we recover from a rewriting failure and continue
  // using the Rewriter. To make sure we don't continue with an ExprStack that
  // is still in the state when rewriting was aborted, save it here and restore
  // it once rewriting this target has finished.
  llvm::SaveAndRestore<SmallVector<Expr *, 8>> RestoreExprStack(
      Rewriter.ExprStack);
  auto &solution = Rewriter.solution;

  // Apply the solution to the target.
  SyntacticElementTarget result = target;
  if (auto expr = target.getAsExpr()) {
    Expr *rewrittenExpr = expr->walk(*this);
    if (!rewrittenExpr)
      return std::nullopt;

    /// Handle special cases for expressions.
    switch (target.getExprContextualTypePurpose()) {
    case CTP_Initialization: {
      auto initResultTarget =
          applySolutionToInitialization(target, rewrittenExpr, *this);
      if (!initResultTarget)
        return std::nullopt;

      result = *initResultTarget;
      break;
    }

    case CTP_Unused:
    case CTP_CaseStmt:
    case CTP_ReturnStmt:
    case CTP_ExprPattern:
    case CTP_ForEachSequence:
    case CTP_YieldByValue:
    case CTP_YieldByReference:
    case CTP_ThrowStmt:
    case CTP_DiscardStmt:
    case CTP_EnumCaseRawValue:
    case CTP_IntGenericParam:
    case CTP_DefaultParameter:
    case CTP_AutoclosureDefaultParameter:
    case CTP_CallArgument:
    case CTP_ClosureResult:
    case CTP_ArrayElement:
    case CTP_DictionaryKey:
    case CTP_DictionaryValue:
    case CTP_CoerceOperand:
    case CTP_AssignSource:
    case CTP_SubscriptAssignSource:
    case CTP_Condition:
    case CTP_WrappedProperty:
    case CTP_SingleValueStmtBranch:
      result.setExpr(rewrittenExpr);
      break;
    }
  } else if (auto stmtCondition = target.getAsStmtCondition()) {
    auto &cs = solution.getConstraintSystem();

    for (auto &condElement : *stmtCondition) {
      switch (condElement.getKind()) {
      case StmtConditionElement::CK_Availability:
        continue;

      case StmtConditionElement::CK_HasSymbol: {
        auto info = condElement.getHasSymbolInfo();
        auto target = *cs.getTargetFor(&condElement);
        auto resolvedTarget = rewriteTarget(target);
        if (!resolvedTarget) {
          info->setInvalid();
          return std::nullopt;
        }

        auto rewrittenExpr = resolvedTarget->getAsExpr();
        info->setSymbolExpr(rewrittenExpr);
        info->setReferencedDecl(
            TypeChecker::getReferencedDeclForHasSymbolCondition(rewrittenExpr));
        continue;
      }

      case StmtConditionElement::CK_Boolean: {
        auto target = *cs.getTargetFor(&condElement);
        auto resolvedTarget = rewriteTarget(target);
        if (!resolvedTarget)
          return std::nullopt;

        condElement.setBoolean(resolvedTarget->getAsExpr());
        continue;
      }

      case StmtConditionElement::CK_PatternBinding: {
        auto target = *cs.getTargetFor(&condElement);
        auto resolvedTarget = rewriteTarget(target);
        if (!resolvedTarget)
          return std::nullopt;

        condElement.setInitializer(resolvedTarget->getAsExpr());
        condElement.setPattern(resolvedTarget->getInitializationPattern());
        continue;
      }
      }
    }

    return target;
  } else if (auto caseLabelItem = target.getAsCaseLabelItem()) {
    ConstraintSystem &cs = solution.getConstraintSystem();
    auto info = *cs.getCaseLabelItemInfo(*caseLabelItem);

    auto pattern = rewritePattern(info.pattern, target.getDeclContext());
    if (!pattern)
      return std::nullopt;

    (*caseLabelItem)->setPattern(pattern.get(), /*resolved=*/true);

    // If there is a guard expression, coerce that.
    if (auto *guardExpr = info.guardExpr) {
      auto target = *cs.getTargetFor(guardExpr);
      auto resultTarget = rewriteTarget(target);
      if (!resultTarget)
        return std::nullopt;

      (*caseLabelItem)->setGuardExpr(resultTarget->getAsExpr());
    }

    return target;
  } else if (auto patternBinding = target.getAsPatternBinding()) {
    ConstraintSystem &cs = solution.getConstraintSystem();
    for (unsigned index : range(patternBinding->getNumPatternEntries())) {
      if (patternBinding->isInitializerChecked(index))
        continue;

      // Find the target for this.
      auto knownTarget = *cs.getTargetFor({patternBinding, index});

      // Rewrite the target.
      auto resultTarget = rewriteTarget(knownTarget);
      if (!resultTarget)
        return std::nullopt;

      auto *pattern = resultTarget->getInitializationPattern();
      // Record that the pattern has been fully validated,
      // this is important for subsequent call to typeCheckDecl
      // because otherwise it would try to re-typecheck pattern.
      patternBinding->setPattern(index, pattern,
                                 /*isFullyValidated=*/true);

      if (patternBinding->isExplicitlyInitialized(index) ||
          (patternBinding->isDefaultInitializable(index) &&
           pattern->hasStorage())) {
        patternBinding->setInit(index, resultTarget->getAsExpr());
        patternBinding->setInitializerChecked(index);
      }
    }

    return target;
  } else if (auto *wrappedVar = target.getAsUninitializedWrappedVar()) {
    // Get the outermost wrapper type from the solution
    auto outermostWrapper = wrappedVar->getOutermostAttachedPropertyWrapper();
    auto backingType = solution.simplifyType(
        solution.getType(outermostWrapper->getTypeExpr()));

    auto &ctx = solution.getConstraintSystem().getASTContext();
    ctx.setSideCachedPropertyWrapperBackingPropertyType(
        wrappedVar, backingType->mapTypeOutOfEnvironment());

    return target;
  } else if (target.getAsUninitializedVar()) {
    TypeResolutionOptions options(TypeResolverContext::PatternBindingDecl);

    auto contextualPattern = target.getContextualPattern();
    auto patternType = target.getTypeOfUninitializedVar();
    auto *PB = target.getPatternBindingOfUninitializedVar();

    // If this is a placeholder variable, let's override its type with
    // inferred one.
    if (isPlaceholderVar(PB)) {
      patternType = solution.getResolvedType(PB->getSingleVar());
      options |= TypeResolutionFlags::OverrideType;
    }

    auto tryRewritePattern = [&](Pattern *EP, Type ty) {
      return ::tryRewriteExprPattern(EP, ty, *this);
    };

    if (auto coercedPattern = TypeChecker::coercePatternToType(
            contextualPattern, patternType, options, tryRewritePattern)) {
      auto resultTarget = target;
      resultTarget.setPattern(coercedPattern);
      return resultTarget;
    }

    return std::nullopt;
  } else if (target.getAsForEachStmt()) {
    auto forEachPreambleResultTarget =
        applySolutionToForEachStmtPreamble(target, *this);
    if (!forEachPreambleResultTarget)
      return std::nullopt;

    result = *forEachPreambleResultTarget;
  } else {
    auto fn = *target.getAsFunction();
    if (rewriteFunction(fn))
      return std::nullopt;

    result.setFunctionBody(fn.getBody());
  }

  // Follow-up tasks.
  auto &cs = solution.getConstraintSystem();
  if (auto resultExpr = result.getAsExpr()) {
    Expr *expr = target.getAsExpr();
    assert(expr && "Can't have expression result without expression target");

    // We are supposed to use contextual type only if it is present and
    // this expression doesn't represent the implicit return of the single
    // expression function which got deduced to be `Never`.
    Type convertType = target.getExprConversionType();
    auto shouldCoerceToContextualType = [&]() {
      if (!convertType)
        return false;

      if (convertType->hasPlaceholder())
        return false;

      if (target.isOptionalSomePatternInit())
        return false;

      if (solution.simplifyType(convertType)->isVoid()) {
        auto contextPurpose = cs.getContextualTypePurpose(target.getAsExpr());
        if (contextPurpose == CTP_ClosureResult ||
            contextPurpose == CTP_SingleValueStmtBranch) {
          return false;
        }
      }
      return true;
    };

    // If we're supposed to convert the expression to some particular type,
    // do so now.
    if (shouldCoerceToContextualType()) {
      auto contextualTypePurpose = target.getExprContextualTypePurpose();

      auto *locator = target.getExprConvertTypeLocator();
      if (!locator) {
        locator = cs.getConstraintLocator(
            expr, LocatorPathElt::ContextualType(contextualTypePurpose));
      }
      assert(locator);

      resultExpr = Rewriter.coerceToType(
          resultExpr, solution.simplifyType(convertType), locator);
    } else if (cs.getType(resultExpr)->hasLValueType() &&
               !target.isDiscardedExpr()) {
      // We referenced an lvalue. Load it.
      resultExpr = Rewriter.coerceToType(
          resultExpr, cs.getType(resultExpr)->getRValueType(),
          cs.getConstraintLocator(expr,
                                  LocatorPathElt::ContextualType(
                                      target.getExprContextualTypePurpose())));
    }

    if (!resultExpr)
      return std::nullopt;

    // For an @autoclosure default parameter type, add the autoclosure
    // conversion.
    if (FunctionType *autoclosureParamType =
            target.getAsAutoclosureParamType()) {
      resultExpr = cs.buildAutoClosureExpr(resultExpr, autoclosureParamType,
                                           target.getDeclContext());
    }

    solution.setExprTypes(resultExpr);
    result.setExpr(resultExpr);

    if (cs.isDebugMode()) {
      auto &log = llvm::errs();
      log << "\n---Type-checked expression---\n";
      resultExpr->dump(log);
      log << "\n";
    }
  }

  return result;
}

/// Apply a given solution to the expression, producing a fully
/// type-checked expression.
std::optional<SyntacticElementTarget>
ConstraintSystem::applySolution(Solution &solution,
                                SyntacticElementTarget target) {
  // If any fixes needed to be applied to arrive at this solution, resolve
  // them to specific expressions.
  unsigned numResolvableFixes = 0;
  if (!solution.Fixes.empty()) {
    if (shouldSuppressDiagnostics())
      return std::nullopt;

    bool diagnosedErrorsViaFixes = applySolutionFixes(solution);
    bool canApplySolution = true;
    for (const auto fix : solution.Fixes) {
      if (fix->isFatal())
        canApplySolution = false;
      if (fix->impact() == SK_Fix && !fix->isFatal())
        ++numResolvableFixes;
    }

    // If all of the available fixes would result in a warning,
    // we can go ahead and apply this solution to AST.
    if (!canApplySolution) {
      // If we already diagnosed any errors via fixes, that's it.
      if (diagnosedErrorsViaFixes)
        return std::nullopt;

      // If we didn't manage to diagnose anything well, so fall back to
      // diagnosing mining the system to construct a reasonable error message.
      diagnoseFailureFor(target);
      return std::nullopt;
    }
  }

  // If there are no fixes recorded but score indicates that there
  // should have been at least one, let's fail application and
  // produce a fallback diagnostic to highlight the problem.
  {
    const auto &score = solution.getFixedScore();
    if (score.Data[SK_Fix] > numResolvableFixes || score.Data[SK_Hole] > 0) {
      maybeProduceFallbackDiagnostic(target.getLoc());
      return std::nullopt;
    }
  }

  // If the score indicates the solution is valid, ensure we don't have any
  // unresolved types.
  {
    auto isValidType = [&](Type ty) {
      return !ty->hasError() && !ty->hasTypeVariableOrPlaceholder();
    };
    for (auto pair : solution.typeBindings) {
      if (!isValidType(pair.second)) {
        ABORT([&](llvm::raw_ostream &out) {
          out << "Type binding has invalid type:\n";
          pair.first->dump(out);
          pair.second->dump(out);
        });
      }
    }
    for (auto &[_, type] : solution.nodeTypes) {
      ASSERT(isValidType(solution.simplifyType(type)) &&
             "node type has invalid type");
    }
    for (auto &[_, type] : solution.keyPathComponentTypes) {
      ASSERT(isValidType(solution.simplifyType(type)) &&
             "key path type has invalid type");
    }
  }

  ExprRewriter rewriter(*this, solution, target, shouldSuppressDiagnostics());
  ExprWalker walker(rewriter);
  auto resultTarget = walker.rewriteTarget(target);
  if (!resultTarget)
    return std::nullopt;

  rewriter.finalize();

  return resultTarget;
}

Expr *
Solution::coerceToType(Expr *expr, Type toType, ConstraintLocator *locator) {
  auto &cs = getConstraintSystem();
  ExprRewriter rewriter(cs, *this, std::nullopt, /*suppressDiagnostics=*/false);
  Expr *result = rewriter.coerceToType(expr, toType, locator);
  if (!result)
    return nullptr;

  setExprTypes(result);
  rewriter.finalize();
  return result;
}

bool Solution::hasType(ASTNode node) const {
  auto result = nodeTypes.find(node);
  if (result != nodeTypes.end())
    return true;

  auto &cs = getConstraintSystem();
  return cs.hasType(node);
}

bool Solution::hasType(const KeyPathExpr *KP, unsigned ComponentIndex) const {
  assert(KP && "Expected non-null key path parameter!");
  return keyPathComponentTypes.find(std::make_pair(KP, ComponentIndex))
            != keyPathComponentTypes.end();
}

Type Solution::getType(ASTNode node) const {
  auto result = nodeTypes.find(node);
  if (result != nodeTypes.end())
    return result->second;

  auto &cs = getConstraintSystem();
  return cs.getType(node);
}

Type Solution::getType(const KeyPathExpr *KP, unsigned I) const {
  assert(hasType(KP, I) && "Expected type to have been set!");
  return keyPathComponentTypes.find(std::make_pair(KP, I))->second;
}

TypeVariableType *
Solution::getKeyPathRootType(const KeyPathExpr *keyPath) const {
  auto result = getKeyPathRootTypeIfAvailable(keyPath);
  assert(result);
  return result;
}

TypeVariableType *
Solution::getKeyPathRootTypeIfAvailable(const KeyPathExpr *keyPath) const {
  auto result = KeyPaths.find(keyPath);
  if (result != KeyPaths.end())
    return std::get<0>(result->second);
  return nullptr;
}

Type Solution::getResolvedType(ASTNode node) const {
  return simplifyType(getType(node));
}

void Solution::setExprTypes(Expr *expr) const {
  if (!expr)
    return;

  SetExprTypes SET(*this);
  expr->walk(SET);
}

/// MARK: SolutionResult implementation.

SolutionResult SolutionResult::forSolved(Solution &&solution) {
  SolutionResult result(Kind::Success);
  void *memory = malloc(sizeof(Solution));
  result.solutions = new (memory) Solution(std::move(solution));
  result.numSolutions = 1;
  return result;
}

SolutionResult SolutionResult::forAmbiguous(
    MutableArrayRef<Solution> solutions) {
  assert(solutions.size() > 1 && "Not actually ambiguous");
  SolutionResult result(Kind::Ambiguous);
  result.solutions =
      (Solution *)malloc(sizeof(Solution) * solutions.size());
  result.numSolutions = solutions.size();
  std::uninitialized_copy(std::make_move_iterator(solutions.begin()),
                          std::make_move_iterator(solutions.end()),
                          result.solutions);
  return result;
}

SolutionResult
SolutionResult::forTooComplex(std::optional<SourceRange> affected) {
  SolutionResult result(Kind::TooComplex);
  result.TooComplexAt = affected;
  return result;
}

SolutionResult::~SolutionResult() {
  assert((!requiresDiagnostic() || emittedDiagnostic) &&
         "SolutionResult was destroyed without emitting a diagnostic");

  for (unsigned i : range(numSolutions)) {
    solutions[i].~Solution();
  }
  free(solutions);
}

const Solution &SolutionResult::getSolution() const {
  assert(numSolutions == 1 && "Wrong number of solutions");
  return solutions[0];
}

Solution &&SolutionResult::takeSolution() && {
  assert(numSolutions == 1 && "Wrong number of solutions");
  return std::move(solutions[0]);
}

ArrayRef<Solution> SolutionResult::getAmbiguousSolutions() const {
  assert(getKind() == Ambiguous);
  return llvm::ArrayRef(solutions, numSolutions);
}

MutableArrayRef<Solution> SolutionResult::takeAmbiguousSolutions() && {
  assert(getKind() == Ambiguous);
  markAsDiagnosed();
  return MutableArrayRef<Solution>(solutions, numSolutions);
}
