//===--- CSSimplify.cpp - Constraint Simplification -----------------------===//
//
// 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 simplifications of constraints within the constraint
// system.
//
//===----------------------------------------------------------------------===//

#include "CSDiagnostics.h"
#include "OpenedExistentials.h"
#include "TypeCheckConcurrency.h"
#include "TypeCheckEffects.h"
#include "TypeChecker.h"
#include "swift/AST/ASTPrinter.h"
#include "swift/AST/ConformanceLookup.h"
#include "swift/AST/Decl.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/GenericSignature.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/LookupKinds.h"
#include "swift/AST/NameLookupRequests.h"
#include "swift/AST/PackExpansionMatcher.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/PropertyWrappers.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/Requirement.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/StringExtras.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/Sema/CSFix.h"
#include "swift/Sema/Constraint.h"
#include "swift/Sema/ConstraintLocator.h"
#include "swift/Sema/ConstraintSystem.h"
#include "swift/Sema/IDETypeChecking.h"
#include "swift/Sema/PreparedOverload.h"
#include "swift/Sema/Score.h"
#include "swift/Sema/Subtyping.h"
#include "swift/Sema/TypeVariableType.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Support/Compiler.h"

using namespace swift;
using namespace constraints;

MatchCallArgumentListener::~MatchCallArgumentListener() { }

bool MatchCallArgumentListener::extraArgument(unsigned argIdx) { return true; }

std::optional<unsigned>
MatchCallArgumentListener::missingArgument(unsigned paramIdx,
                                           unsigned argInsertIdx) {
  return std::nullopt;
}

bool MatchCallArgumentListener::missingLabel(unsigned paramIdx) { return true; }
bool MatchCallArgumentListener::extraneousLabel(unsigned paramIdx) {
  return true;
}
bool MatchCallArgumentListener::incorrectLabel(unsigned paramIdx) {
  return true;
}

bool MatchCallArgumentListener::outOfOrderArgument(
    unsigned argIdx, unsigned prevArgIdx, ArrayRef<ParamBinding> bindings) {
  return true;
}

bool MatchCallArgumentListener::relabelArguments(ArrayRef<Identifier> newNames){
  return true;
}

bool MatchCallArgumentListener::shouldClaimArgDuringRecovery(unsigned argIdx) {
  return true;
}

bool MatchCallArgumentListener::canClaimArgIgnoringNameMismatch(
    const AnyFunctionType::Param &arg) {
  return false;
}

/// Produce a score (smaller is better) comparing a parameter name and
/// potentially-typo'd argument name.
///
/// \param paramName The name of the parameter.
/// \param argName The name of the argument.
/// \param maxScore The maximum score permitted by this comparison, or
/// 0 if there is no limit.
///
/// \returns the score, if it is good enough to even consider this a match.
/// Otherwise, an empty optional.
///
static std::optional<unsigned> scoreParamAndArgNameTypo(StringRef paramName,
                                                        StringRef argName,
                                                        unsigned maxScore) {
  using namespace camel_case;

  // Compute the edit distance.
  unsigned dist = argName.edit_distance(paramName, /*AllowReplacements=*/true,
                                        /*MaxEditDistance=*/maxScore);

  // If the edit distance would be too long, we're done.
  if (maxScore != 0 && dist > maxScore)
    return std::nullopt;

  // The distance can be zero due to the "with" transformation above.
  if (dist == 0)
    return 1;

  // If this is just a single character label on both sides,
  // simply return distance.
  if (paramName.size() == 1 && argName.size() == 1)
    return dist;

  // Only allow about one typo for every two properly-typed characters, which
  // prevents completely-wacky suggestions in many cases.
  if (dist > (argName.size() + 1) / 3)
    return std::nullopt;

  return dist;
}

bool constraints::isPackExpansionType(Type type) {
  if (type->is<PackExpansionType>())
    return true;

  if (auto *typeVar = type->getAs<TypeVariableType>())
    return typeVar->getImpl().isPackExpansion();

  return false;
}

bool constraints::isSingleUnlabeledPackExpansionTuple(Type type) {
  auto *tuple = type->getRValueType()->getAs<TupleType>();
  return tuple && (tuple->getNumElements() == 1) &&
         isPackExpansionType(tuple->getElementType(0)) &&
         !tuple->getElement(0).hasName();
}

Type constraints::getPatternTypeOfSingleUnlabeledPackExpansionTuple(Type type) {
  if (isSingleUnlabeledPackExpansionTuple(type)) {
    auto tuple = type->getRValueType()->castTo<TupleType>();
    const auto &tupleElement = tuple->getElementType(0);
    if (auto *expansion = tupleElement->getAs<PackExpansionType>()) {
      return expansion->getPatternType();
    }
    if (auto *typeVar = tupleElement->getAs<TypeVariableType>()) {
      auto *locator = typeVar->getImpl().getLocator();
      if (auto expansionElement =
              locator->getLastElementAs<LocatorPathElt::PackExpansionType>()) {
        return expansionElement->getOpenedType()->getPatternType();
      }
    }
  }
  return {};
}

bool constraints::containsPackExpansionType(ArrayRef<AnyFunctionType::Param> params) {
  return llvm::any_of(params, [&](const auto &param) {
    return isPackExpansionType(param.getPlainType());
  });
}

bool constraints::containsPackExpansionType(TupleType *tuple) {
  return llvm::any_of(tuple->getElements(), [&](const auto &elt) {
    return isPackExpansionType(elt.getType());
  });
}

bool constraints::doesMemberRefApplyCurriedSelf(Type baseTy,
                                                const ValueDecl *decl) {
  assert(decl->getDeclContext()->isTypeContext() &&
         "Expected a member reference");

  // For a reference to an instance method on a metatype, we want to keep the
  // curried self.  Metatype extension instance members are an exception: the
  // metatype value IS self, so the curried self is applied.
  if (decl->isInstanceMember()) {
    assert(baseTy);
    if (isa<AbstractFunctionDecl>(decl) &&
        baseTy->getRValueType()->is<AnyMetatypeType>()) {
      if (decl->getDeclContext()->isMetatypeExtension())
        return true;
      return false;
    }
  }

  // Otherwise the reference applies self.
  return true;
}

Expr *constraints::getArgumentLabelTargetExpr(Expr *fn) {
  // Dig out the function, looking through, parentheses, ?, and !.
  do {
    fn = fn->getSemanticsProvidingExpr();

    if (auto force = dyn_cast<ForceValueExpr>(fn)) {
      fn = force->getSubExpr();
      continue;
    }

    if (auto bind = dyn_cast<BindOptionalExpr>(fn)) {
      fn = bind->getSubExpr();
      continue;
    }

    return fn;
  } while (true);
}

/// Determine the default type-matching options to use when decomposing a
/// constraint into smaller constraints.
static ConstraintSystem::TypeMatchOptions getDefaultDecompositionOptions(
         ConstraintSystem::TypeMatchOptions flags) {
  return flags | ConstraintSystem::TMF_GenerateConstraints;
}

/// Whether the given parameter requires an argument.
static bool parameterRequiresArgument(ArrayRef<AnyFunctionType::Param> params,
                                      const ParameterListInfo &paramInfo,
                                      unsigned paramIdx) {
  return !paramInfo.hasDefaultArgument(paramIdx)
      && !params[paramIdx].isVariadic();
}

/// Determine whether the given parameter can accept a trailing closure for the
/// "backward" logic.
static bool backwardScanAcceptsTrailingClosure(
    const AnyFunctionType::Param &param) {
  Type paramTy = param.getPlainType();
  if (!paramTy)
    return true;

  paramTy = paramTy->lookThroughAllOptionalTypes();
  return paramTy->isTypeParameter() ||
      paramTy->is<ArchetypeType>() ||
      paramTy->is<AnyFunctionType>() ||
      paramTy->isTypeVariableOrMember() ||
      paramTy->isAny();
}

/// Determine whether any parameter from the given index up until the end
/// requires an argument to be provided.
///
/// \param params The parameters themselves.
/// \param paramInfo Declaration-provided information about the parameters.
/// \param firstParamIdx The first parameter to examine to determine whether any
/// parameter in the range \c [paramIdx, params.size()) requires an argument.
/// \param beforeLabel If non-empty, stop examining parameters when we reach
/// a parameter with this label.
static bool anyParameterRequiresArgument(
    ArrayRef<AnyFunctionType::Param> params, const ParameterListInfo &paramInfo,
    unsigned firstParamIdx, std::optional<Identifier> beforeLabel) {
  for (unsigned paramIdx : range(firstParamIdx, params.size())) {
    // If have been asked to stop when we reach a parameter with a particular
    // label, and we see a parameter with that label, we're done: no parameter
    // requires an argument.
    if (beforeLabel && *beforeLabel == params[paramIdx].getLabel())
      break;

    // If this parameter requires an argument, tell the caller.
    if (parameterRequiresArgument(params, paramInfo, paramIdx))
      return true;
  }

  // No parameters required arguments.
  return false;
}

static bool isCodeCompletionTypeVar(Type type) {
  if (auto *TVT = type->getAs<TypeVariableType>()) {
    if (TVT->getImpl().isCodeCompletionToken()) {
      return true;
    }
  }
  return false;
}

static bool matchCallArgumentsImpl(
    SmallVectorImpl<AnyFunctionType::Param> &args,
    ArrayRef<AnyFunctionType::Param> params, const ParameterListInfo &paramInfo,
    std::optional<unsigned> unlabeledTrailingClosureArgIndex, bool allowFixes,
    TrailingClosureMatching trailingClosureMatching,
    MatchCallArgumentListener &listener,
    SmallVectorImpl<ParamBinding> &parameterBindings) {
  assert(params.size() == paramInfo.size() && "Default map does not match");
  assert(!unlabeledTrailingClosureArgIndex ||
         *unlabeledTrailingClosureArgIndex < args.size());

  // Keep track of the parameter we're matching and what argument indices
  // got bound to each parameter.
  unsigned numParams = params.size();
  parameterBindings.clear();
  parameterBindings.resize(numParams);

  // Keep track of which arguments we have claimed from the argument tuple.
  unsigned numArgs = args.size();
  SmallVector<bool, 4> claimedArgs(numArgs, false);
  SmallVector<Identifier, 4> actualArgNames;
  unsigned numClaimedArgs = 0;

  // Indicates whether any of the arguments are potentially out-of-order,
  // requiring further checking at the end.
  bool potentiallyOutOfOrder = false;

  // Local function that claims the argument at \c argIdx, returning the
  // index of the claimed argument. This is primarily a helper for
  // \c claimNextNamed.
  auto claim = [&](Identifier expectedName, unsigned argIdx,
                   bool ignoreNameClash = false)  -> unsigned {
    // Make sure we can claim this argument.
    ASSERT(argIdx != numArgs && "Must have a valid index to claim");
    ASSERT(!claimedArgs[argIdx] && "Argument already claimed");

    // Prevent recording of an argument label mismatche for an unlabeled
    // trailing closure. An unlabeled trailing closure is necessarily the first
    // one and vice versa, per language syntax.
    if (unlabeledTrailingClosureArgIndex == argIdx) {
      expectedName = Identifier();
    }

    if (!actualArgNames.empty()) {
      // We're recording argument names; record this one.
      actualArgNames[argIdx] = expectedName;
    } else if (!ignoreNameClash && !args[argIdx].matchParameterLabel(expectedName)) {
      // We have an argument name mismatch. Start recording argument names.
      actualArgNames.resize(numArgs);

      // Figure out previous argument names from the parameter bindings.
      for (auto i : indices(params)) {
        const auto &param = params[i];
        bool firstArg = true;

        for (auto argIdx : parameterBindings[i]) {
          actualArgNames[argIdx] = firstArg ? param.getLabel() : Identifier();
          firstArg = false;
        }
      }

      // Record this argument name.
      actualArgNames[argIdx] = expectedName;
    }

    claimedArgs[argIdx] = true;
    ++numClaimedArgs;
    return argIdx;
  };

  // Local function that skips over any claimed arguments.
  auto skipClaimedArgs = [&](unsigned &nextArgIdx) {
    while (nextArgIdx != numArgs && claimedArgs[nextArgIdx])
      ++nextArgIdx;
    return nextArgIdx;
  };

  // Local function that retrieves the next unclaimed argument with the given
  // name (which may be empty). This routine claims the argument.
  auto claimNextNamed =
      [&](unsigned &nextArgIdx, Identifier paramLabel, bool ignoreNameMismatch,
          bool forVariadic = false) -> std::optional<unsigned> {
    // Skip over any claimed arguments.
    skipClaimedArgs(nextArgIdx);

    // If we've claimed all of the arguments, there's nothing more to do.
    if (numClaimedArgs == numArgs)
      return std::nullopt;

    // Go hunting for an unclaimed argument whose name does match.
    std::optional<unsigned> claimedWithSameName;
    unsigned firstArgIdx = nextArgIdx;
    for (unsigned i = nextArgIdx; i != numArgs; ++i) {
      auto argLabel = args[i].getLabel();
      bool claimIgnoringNameMismatch = false;

      if (!args[i].matchParameterLabel(paramLabel)) {
        // If this is an attempt to claim additional unlabeled arguments
        // for variadic parameter, we have to stop at first labeled argument.
        if (forVariadic)
          return std::nullopt;

        if ((i == firstArgIdx || ignoreNameMismatch) &&
            listener.canClaimArgIgnoringNameMismatch(args[i])) {
          // Avoid triggering relabelling fixes about the completion arg.
          claimIgnoringNameMismatch = true;
        } else {
          // Otherwise we can continue trying to find argument which
          // matches parameter with or without label.
          continue;
        }
      }

      // Skip claimed arguments.
      if (claimedArgs[i]) {
        assert(!forVariadic && "Cannot be for a variadic claim");
        // Note that we have already claimed an argument with the same name.
        if (!claimedWithSameName)
          claimedWithSameName = i;
        continue;
      }

      // We found a match.  If the match wasn't the next one, we have
      // potentially out of order arguments.
      if (i != nextArgIdx) {
        assert(!forVariadic && "Cannot be for a variadic claim");
        // Avoid claiming un-labeled defaulted parameters
        // by out-of-order un-labeled arguments or parts
        // of variadic argument sequence, because that might
        // be incorrect:
        // ```swift
        // func foo(_ a: Int, _ b: Int = 0, c: Int = 0, _ d: Int) {}
        // foo(1, c: 2, 3) // -> `3` will be claimed as '_ b:'.
        // ```
        if (argLabel.empty() && !claimIgnoringNameMismatch)
          continue;

        potentiallyOutOfOrder = true;
      }

      // Claim it.
      return claim(paramLabel, i, claimIgnoringNameMismatch);
    }

    // If we're not supposed to attempt any fixes, we're done.
    if (!allowFixes)
      return std::nullopt;

    // Several things could have gone wrong here, and we'll check for each
    // of them at some point:
    //   - The keyword argument might be redundant, in which case we can point
    //     out the issue.
    //   - The argument might be unnamed, in which case we try to fix the
    //     problem by adding the name.
    //   - The argument might have extraneous label, in which case we try to
    //     fix the problem by removing such label.
    //   - The keyword argument might be a typo for an actual argument name, in
    //     which case we should find the closest match to correct to.

    // Missing or extraneous label.
    if (nextArgIdx != numArgs && ignoreNameMismatch) {
      auto argLabel = args[nextArgIdx].getLabel();
      // Claim this argument if we are asked to ignore labeling failure,
      // only if argument doesn't have a label when parameter expected
      // it to, or vice versa.
      if (paramLabel.empty() || argLabel.empty())
        return claim(paramLabel, nextArgIdx);
    }

    // Redundant keyword arguments.
    if (claimedWithSameName) {
      // FIXME: We can provide better diagnostics here.
      return std::nullopt;
    }

    // Typo correction is handled in a later pass.
    return std::nullopt;
  };

  // Local function that attempts to bind the given parameter to arguments in
  // the list.
  bool haveUnfulfilledParams = false;
  auto bindNextParameter = [&](unsigned paramIdx, unsigned &nextArgIdx,
                               bool ignoreNameMismatch) {
    const auto &param = params[paramIdx];
    Identifier paramLabel = param.getLabel();

    // If we have the trailing closure argument and are performing a forward
    // match, look for the matching parameter.
    if (trailingClosureMatching == TrailingClosureMatching::Forward &&
        unlabeledTrailingClosureArgIndex &&
        skipClaimedArgs(nextArgIdx) == *unlabeledTrailingClosureArgIndex) {
      // If the parameter we are looking at does not support the (unlabeled)
      // trailing closure argument, this parameter is unfulfilled.
      if (!paramInfo.acceptsUnlabeledTrailingClosureArgument(paramIdx) &&
          !ignoreNameMismatch) {
        haveUnfulfilledParams = true;
        return;
      }

      // Let's consider current closure to be extraneous if:
      //
      // - current parameter has a default value and doesn't accept a trailing
      //   closure; and
      // - no other free parameter after this one accepts a trailing closure via
      //   forward or backward scan. This check makes sure that it's safe to
      //   reject and push it to the next parameter without affecting backward
      //   scan logic.
      //
      // In other words - let's push the closure argument through defaulted
      // parameters until it can be considered extraneous if no parameters
      // could possibly match it.
      if (!paramInfo.acceptsUnlabeledTrailingClosureArgument(paramIdx) &&
          !parameterRequiresArgument(params, paramInfo, paramIdx)) {
        if (llvm::none_of(
                range(paramIdx + 1, params.size()), [&](unsigned idx) {
                  return parameterBindings[idx].empty() &&
                         (paramInfo.acceptsUnlabeledTrailingClosureArgument(
                              idx) ||
                          backwardScanAcceptsTrailingClosure(params[idx]));
                })) {
          haveUnfulfilledParams = true;
          return;
        }

        // If one or more parameters can match the closure, let's check
        // whether backward scan is applicable here.
      }

      // If this parameter does not require an argument, consider applying a
      // backward-match rule that skips this parameter if doing so is the only
      // way to successfully match arguments to parameters.
      if (!parameterRequiresArgument(params, paramInfo, paramIdx) &&
          anyParameterRequiresArgument(
              params, paramInfo, paramIdx + 1,
              nextArgIdx + 1 < numArgs
                  ? std::optional<Identifier>(args[nextArgIdx + 1].getLabel())
                  : std::optional<Identifier>(std::nullopt))) {
        haveUnfulfilledParams = true;
        return;
      }

      // The argument is unlabeled, so mark the parameter as unlabeled as
      // well.
      paramLabel = Identifier();
    }

    // Handle variadic parameters.
    if (param.isVariadic() || isPackExpansionType(param.getPlainType())) {
      // Claim the next argument with the name of this parameter.
      auto claimed =
          claimNextNamed(nextArgIdx, paramLabel, ignoreNameMismatch);

      // If there was no such argument, leave the parameter unfulfilled.
      if (!claimed) {
        haveUnfulfilledParams = true;
        return;
      }

      // Record the first argument for the variadic.
      parameterBindings[paramIdx].push_back(*claimed);

      auto currentNextArgIdx = nextArgIdx;
      {
        nextArgIdx = *claimed;

        // Claim any additional unnamed arguments.
        while (true) {
          // If the next argument is the unlabeled trailing closure and the
          // variadic parameter does not accept the unlabeled trailing closure
          // argument, we're done.
          if (trailingClosureMatching == TrailingClosureMatching::Forward &&
              unlabeledTrailingClosureArgIndex &&
              skipClaimedArgs(nextArgIdx)
                  == *unlabeledTrailingClosureArgIndex &&
              !paramInfo.acceptsUnlabeledTrailingClosureArgument(paramIdx))
            break;

          if ((claimed = claimNextNamed(nextArgIdx, Identifier(), false, true)))
            parameterBindings[paramIdx].push_back(*claimed);
          else
            break;
        }
      }

      nextArgIdx = currentNextArgIdx;
      return;
    }

    // Try to claim an argument for this parameter.
    if (auto claimed =
            claimNextNamed(nextArgIdx, paramLabel, ignoreNameMismatch)) {
      parameterBindings[paramIdx].push_back(*claimed);
      return;
    }

    // There was no argument to claim. Leave the argument unfulfilled.
    haveUnfulfilledParams = true;
  };

  // If we have an unlabeled trailing closure and are matching backward, match
  // the trailing closure argument near the end.
  if (unlabeledTrailingClosureArgIndex &&
      trailingClosureMatching == TrailingClosureMatching::Backward) {
    assert(!claimedArgs[*unlabeledTrailingClosureArgIndex]);

    // One past the next parameter index to look at.
    unsigned prevParamIdx = numParams;

    // Scan backwards from the end to match the unlabeled trailing closure.
    std::optional<unsigned> unlabeledParamIdx;
    if (prevParamIdx > 0) {
      unsigned paramIdx = prevParamIdx - 1;

      bool lastAcceptsTrailingClosure =
          backwardScanAcceptsTrailingClosure(params[paramIdx]);

      // If the last parameter is defaulted, this might be
      // an attempt to use a trailing closure with previous
      // parameter that accepts a function type e.g.
      //
      // func foo(_: () -> Int, _ x: Int = 0) {}
      // foo { 42 }
      if (!lastAcceptsTrailingClosure && paramIdx > 0 &&
          paramInfo.hasDefaultArgument(paramIdx)) {
        auto paramType = params[paramIdx - 1].getPlainType();
        // If the parameter before defaulted last accepts.
        if (paramType->is<AnyFunctionType>()) {
          lastAcceptsTrailingClosure = true;
          paramIdx -= 1;
        }
      }

      if (lastAcceptsTrailingClosure)
        unlabeledParamIdx = paramIdx;
    }

    // Trailing closure argument couldn't be matched to anything. Fail fast.
    if (!unlabeledParamIdx) {
      return true;
    }

    // Claim the parameter/argument pair.
    claim(
        params[*unlabeledParamIdx].getLabel(),
        *unlabeledTrailingClosureArgIndex,
        /*ignoreNameClash=*/true);
    parameterBindings[*unlabeledParamIdx].push_back(
        *unlabeledTrailingClosureArgIndex);
  }

  {
    unsigned nextArgIdx = 0;
    // Mark through the parameters, binding them to their arguments.
    for (auto paramIdx : indices(params)) {
      if (parameterBindings[paramIdx].empty())
        bindNextParameter(paramIdx, nextArgIdx, false);
    }
  }

  // If we have any unclaimed arguments, complain about those.
  if (numClaimedArgs != numArgs) {
    // Find all of the named, unclaimed arguments.
    llvm::SmallVector<unsigned, 4> unclaimedNamedArgs;
    for (auto argIdx : indices(args)) {
      if (claimedArgs[argIdx]) continue;

      if (!listener.shouldClaimArgDuringRecovery(argIdx))
        continue;

      if (!args[argIdx].getLabel().empty())
        unclaimedNamedArgs.push_back(argIdx);
    }

    if (!unclaimedNamedArgs.empty()) {
      // Find all of the named, unfulfilled parameters.
      llvm::SmallVector<unsigned, 4> unfulfilledNamedParams;
      bool hasUnfulfilledUnnamedParams = false;
      for (auto paramIdx : indices(params)) {
        if (parameterBindings[paramIdx].empty()) {
          if (params[paramIdx].getLabel().empty())
            hasUnfulfilledUnnamedParams = true;
          else
            unfulfilledNamedParams.push_back(paramIdx);
        }
      }

      if (!unfulfilledNamedParams.empty()) {
        // Use typo correction to find the best matches.
        // FIXME: There is undoubtedly a good dynamic-programming algorithm
        // to find the best assignment here.
        for (auto argIdx : unclaimedNamedArgs) {
          auto argName = args[argIdx].getLabel();

          // Find the closest matching unfulfilled named parameter.
          unsigned bestScore = 0;
          unsigned best = 0;
          for (auto i : indices(unfulfilledNamedParams)) {
            unsigned param = unfulfilledNamedParams[i];
            auto paramName = params[param].getLabel();

            if (auto score = scoreParamAndArgNameTypo(paramName.str(),
                                                      argName.str(),
                                                      bestScore)) {
              if (*score < bestScore || bestScore == 0) {
                bestScore = *score;
                best = i;
              }
            }
          }

          // If we found a parameter to fulfill, do it.
          if (bestScore > 0) {
            // Bind this parameter to the argument.
            auto paramIdx = unfulfilledNamedParams[best];
            auto paramLabel = params[paramIdx].getLabel();

            parameterBindings[paramIdx].push_back(claim(paramLabel, argIdx));

            // Erase this parameter from the list of unfulfilled named
            // parameters, so we don't try to fulfill it again.
            unfulfilledNamedParams.erase(unfulfilledNamedParams.begin() + best);
            if (unfulfilledNamedParams.empty())
              break;
          }
        }

        // Update haveUnfulfilledParams, because we may have fulfilled some
        // parameters above.
        haveUnfulfilledParams = hasUnfulfilledUnnamedParams ||
                                !unfulfilledNamedParams.empty();
      }
    }

    // Find all of the unfulfilled parameters, and match them up
    // semi-positionally.
    if (numClaimedArgs != numArgs) {
      // Restart at the first argument/parameter.
      unsigned nextArgIdx = 0;
      haveUnfulfilledParams = false;
      for (auto paramIdx : indices(params)) {
        // Skip fulfilled parameters.
        if (!parameterBindings[paramIdx].empty())
          continue;

        bindNextParameter(paramIdx, nextArgIdx, true);

        if (!listener.shouldClaimArgDuringRecovery(nextArgIdx))
          continue;
      }
    }

    // If there are as many arguments as parameters but we still
    // haven't claimed all of the arguments, it could mean that
    // labels don't line up, if so let's try to claim arguments
    // with incorrect labels, and let OoO/re-labeling logic diagnose that.
    if (numArgs == numParams && numClaimedArgs != numArgs) {
      for (auto i : indices(args)) {
        if (claimedArgs[i] || !parameterBindings[i].empty())
          continue;

        // If parameter has a default value, we don't really
        // know if label doesn't match because it's incorrect
        // or argument belongs to some other parameter, so
        // we just leave this parameter unfulfilled.
        if (paramInfo.hasDefaultArgument(i))
          continue;

        if (!listener.shouldClaimArgDuringRecovery(i))
          continue;

        // Looks like there was no parameter claimed at the same
        // position, it could only mean that label is completely
        // different, because typo correction has been attempted already.
        parameterBindings[i].push_back(claim(params[i].getLabel(), i));
      }
    }

    // If we still haven't claimed all of the arguments,
    // fail if there is no recovery.
    if (numClaimedArgs != numArgs) {
      for (auto index : indices(claimedArgs)) {
        if (claimedArgs[index])
          continue;

        if (listener.extraArgument(index))
          return true;
      }
    }

    // FIXME: If we had the actual parameters and knew the body names, those
    // matches would be best.
    potentiallyOutOfOrder = true;
  }

  // If we have any unfulfilled parameters, check them now.
  std::optional<unsigned> prevArgIdx;
  if (haveUnfulfilledParams) {
    for (auto paramIdx : indices(params)) {
      // If we have a binding for this parameter, we're done.
      if (!parameterBindings[paramIdx].empty()) {
        prevArgIdx = parameterBindings[paramIdx].back();
        continue;
      }

      const auto &param = params[paramIdx];

      // Variadic parameters can be unfulfilled.
      if (param.isVariadic() || isPackExpansionType(param.getPlainType()))
        continue;

      // Parameters with defaults can be unfulfilled.
      if (paramInfo.hasDefaultArgument(paramIdx))
        continue;

      unsigned argInsertIdx = prevArgIdx ? *prevArgIdx + 1 : 0;
      if (auto newArgIdx = listener.missingArgument(paramIdx, argInsertIdx)) {
        parameterBindings[paramIdx].push_back(*newArgIdx);
        continue;
      }

      return true;
    }
  }

  // If any arguments were provided out-of-order, check whether we have
  // violated any of the reordering rules.
  if (potentiallyOutOfOrder) {
    // If we've seen label failures and now there is an out-of-order
    // parameter (or even worse - OoO parameter with label re-naming),
    // we most likely have no idea what would be the best
    // diagnostic for this situation, so let's just try to re-label.
    auto isOutOfOrderArgument = [&](unsigned toParamIdx, unsigned fromArgIdx,
                                    unsigned toArgIdx) {
      if (fromArgIdx <= toArgIdx) {
        return false;
      }

      auto newLabel = args[fromArgIdx].getLabel();
      auto oldLabel = args[toArgIdx].getLabel();

      if (newLabel != params[toParamIdx].getLabel()) {
        return false;
      }

      auto paramIdx = toParamIdx + 1;
      for (; paramIdx < params.size(); ++paramIdx) {
        // Looks like new position (excluding defaulted parameters),
        // has a valid label.
        if (oldLabel == params[paramIdx].getLabel())
          break;

        // If we are moving the position with a different label
        // and there is no default value for it, can't diagnose the
        // problem as a simple re-ordering.
        if (!paramInfo.hasDefaultArgument(paramIdx))
          return false;
      }

      // label was not found
      if (paramIdx == params.size()) {
        return false;
      }

      return true;
    };

    SmallVector<unsigned, 4> paramToArgMap;
    paramToArgMap.reserve(params.size());
    {
      unsigned argIdx = 0;
      for (const auto &binding : parameterBindings) {
        paramToArgMap.push_back(argIdx);
        // Ignore argument bindings that were synthesized due to missing args.
        argIdx += llvm::count_if(
            binding, [numArgs](unsigned argIdx) { return argIdx < numArgs; });
      }
    }

    // Enumerate the parameters and their bindings to see if any arguments are
    // our of order
    bool hadLabelMismatch = false;
    for (const auto paramIdx : indices(params)) {
      const auto toArgIdx = paramToArgMap[paramIdx];
      const auto &binding = parameterBindings[paramIdx];
      for (const auto paramBindIdx : indices(binding)) {
        // We've found the parameter that has an out of order
        // argument, and know the indices of the argument that
        // needs to move (fromArgIdx) and the argument location
        // it should move to (toArgIdx).
        const auto fromArgIdx = binding[paramBindIdx];

        // Ignore argument bindings that were synthesized due to missing args.
        if (fromArgIdx >= numArgs)
          continue;

        // Does nothing for variadic tail.
        if ((params[paramIdx].isVariadic() ||
             isPackExpansionType(params[paramIdx].getPlainType())) &&
            paramBindIdx > 0) {
          assert(args[fromArgIdx].getLabel().empty());
          continue;
        }

        // First let's double check if out-of-order argument is nothing
        // more than a simple label mismatch, because in situation where
        // one argument requires label and another one doesn't, but caller
        // doesn't provide either, problem is going to be identified as
        // out-of-order argument instead of label mismatch.
        const auto expectedLabel =
          fromArgIdx == unlabeledTrailingClosureArgIndex
            ? Identifier()
            : params[paramIdx].getLabel();
        const auto argumentLabel = args[fromArgIdx].getLabel();

        if (argumentLabel != expectedLabel) {
          // - The parameter is unnamed, in which case we try to fix the
          //   problem by removing the name.
          if (expectedLabel.empty()) {
            hadLabelMismatch = true;
            if (listener.extraneousLabel(paramIdx))
              return true;
          // - The argument is unnamed, in which case we try to fix the
          //   problem by adding the name.
          } else if (argumentLabel.empty()) {
            hadLabelMismatch = true;
            if (listener.missingLabel(paramIdx))
              return true;
          // - The argument label has a typo at the same position.
          } else if (fromArgIdx == toArgIdx) {
            hadLabelMismatch = true;
            if (listener.incorrectLabel(paramIdx))
              return true;
          }
        }

        if (fromArgIdx == toArgIdx) {
          // If the argument is in the right location, just continue
          continue;
        }

        // This situation looks like out-of-order argument but it's hard
        // to say exactly without considering other factors, because it
        // could be invalid labeling too.
        if (!hadLabelMismatch &&
            isOutOfOrderArgument(paramIdx, fromArgIdx, toArgIdx)) {
          return listener.outOfOrderArgument(
              fromArgIdx, toArgIdx, parameterBindings);
        }

        SmallVector<Identifier, 8> expectedLabels;
        llvm::transform(params, std::back_inserter(expectedLabels),
                        [](const AnyFunctionType::Param &param) {
                          return param.getLabel();
                        });
        return listener.relabelArguments(expectedLabels);
      }
    }
  }

  // If no arguments were renamed, the call arguments match up with the
  // parameters.
  if (actualArgNames.empty())
    return false;

  // The arguments were relabeled; notify the listener.
  return listener.relabelArguments(actualArgNames);
}

/// Determine whether call-argument matching requires us to try both the
/// forward and backward scanning directions to succeed.
static bool requiresBothTrailingClosureDirections(
    ArrayRef<AnyFunctionType::Param> args,
    ArrayRef<AnyFunctionType::Param> params, const ParameterListInfo &paramInfo,
    std::optional<unsigned> unlabeledTrailingClosureArgIndex) {
  // If there's no unlabeled trailing closure, direction doesn't matter.
  if (!unlabeledTrailingClosureArgIndex)
    return false;

  // If there are labeled trailing closure arguments, only scan forward.
  if (*unlabeledTrailingClosureArgIndex < args.size() - 1)
    return false;

  // If there are no parameters, it doesn't matter; only scan forward.
  if (params.empty())
    return false;

  // If backward matching is disabled, only scan forward.
  ASTContext &ctx = params.front().getPlainType()->getASTContext();
  if (ctx.LangOpts.hasFeature(Feature::ForwardTrailingClosures))
    return false;

  // If there are at least two parameters that meet the backward scan's
  // definition of "accepts trailing closure", or there is one such parameter
  // with a defaulted parameter after it, we'll need to do the scan
  // in both directions.
  bool sawAnyTrailingClosureParam = false;
  for (unsigned paramIdx : indices(params)) {
    const auto &param = params[paramIdx];
    if (backwardScanAcceptsTrailingClosure(param)) {
      if (sawAnyTrailingClosureParam)
        return true;

      sawAnyTrailingClosureParam = true;
      continue;
    }

    if (sawAnyTrailingClosureParam && paramInfo.hasDefaultArgument(paramIdx))
      return true;
  }

  // Only one parameter can match the trailing closure anyway, so don't bother
  // scanning twice.
  return false;
}

void MatchCallArgumentResult::dump(llvm::raw_ostream &out) {
  switch (trailingClosureMatching) {
  case TrailingClosureMatching::Forward:
    out << "[forward]";
    break;
  case TrailingClosureMatching::Backward:
    out << "[backward]";
    break;
  }

  auto printBindings = [&](const SmallVector<ParamBinding, 4> &parameterBindings) {
    for (unsigned i : indices(parameterBindings)) {
      const auto &bindings = parameterBindings[i];
      out << " [" << i << ": ";
      bool first = true;
      for (auto arg : bindings) {
        if (!first) {
          out << ", ";
        }
        out << arg;
        first = false;
      }
      if (first) {
        out << "<not present>";
      }
      out << "]";
    }
  };

  printBindings(parameterBindings);

  if (backwardParameterBindings.has_value()) {
    out << " backward: ";
    printBindings(*backwardParameterBindings);
  }

  out << "\n";
}

std::optional<MatchCallArgumentResult> constraints::matchCallArguments(
    SmallVectorImpl<AnyFunctionType::Param> &args,
    ArrayRef<AnyFunctionType::Param> params, const ParameterListInfo &paramInfo,
    std::optional<unsigned> unlabeledTrailingClosureArgIndex, bool allowFixes,
    MatchCallArgumentListener &listener,
    std::optional<TrailingClosureMatching> trailingClosureMatching) {

  /// Perform a single call to the implementation of matchCallArguments,
  /// invoking the listener and using the results from that match.
  auto singleMatchCall = [&](TrailingClosureMatching scanDirection)
      -> std::optional<MatchCallArgumentResult> {
    SmallVector<ParamBinding, 4> paramBindings;
    if (matchCallArgumentsImpl(
            args, params, paramInfo, unlabeledTrailingClosureArgIndex,
            allowFixes, scanDirection, listener, paramBindings))
      return std::nullopt;

    return MatchCallArgumentResult{scanDirection, std::move(paramBindings),
                                   std::nullopt};
  };

  // If we know that we won't have to perform both forward and backward
  // scanning for trailing closures, fast-path by performing just the
  // appropriate scan.
  if (trailingClosureMatching ||
      !requiresBothTrailingClosureDirections(
          args, params, paramInfo, unlabeledTrailingClosureArgIndex)) {
    return singleMatchCall(
        trailingClosureMatching.value_or(TrailingClosureMatching::Forward));
  }

  MatchCallArgumentListener noOpListener;

  // Try the forward direction first.
  SmallVector<ParamBinding, 4> forwardParamBindings;
  bool forwardFailed = matchCallArgumentsImpl(
      args, params, paramInfo, unlabeledTrailingClosureArgIndex, allowFixes,
      TrailingClosureMatching::Forward, noOpListener, forwardParamBindings);

  // Try the backward direction.
  SmallVector<ParamBinding, 4> backwardParamBindings;
  bool backwardFailed = matchCallArgumentsImpl(
      args, params, paramInfo, unlabeledTrailingClosureArgIndex, allowFixes,
      TrailingClosureMatching::Backward, noOpListener, backwardParamBindings);

  // If at least one of them failed, or they produced the same results, run
  // call argument matching again with the real visitor.
  if (forwardFailed || backwardFailed ||
      forwardParamBindings == backwardParamBindings) {
    // Run the forward scan unless the backward scan is the only one that
    // succeeded.
    auto scanDirection = backwardFailed || !forwardFailed
        ? TrailingClosureMatching::Forward
        : TrailingClosureMatching::Backward;
    return singleMatchCall(scanDirection);
  }

  // Both forward and backward succeeded, and produced different results.
  // Bundle them up and return both---without invoking the listener---so the
  // solver can choose.
  return MatchCallArgumentResult{
      TrailingClosureMatching::Forward,
      std::move(forwardParamBindings),
      std::move(backwardParamBindings)
  };
}

bool CompletionArgInfo::allowsMissingArgAt(unsigned argInsertIdx,
                                           AnyFunctionType::Param param) {
  // If the argument is before or at the index of the argument containing the
  // completion, the user would likely have already written it if they
  // intended this overload.
  if (completionIdx >= argInsertIdx) {
    return false;
  }

  // If the argument is after the first trailing closure, the user can only
  // continue on to write more trailing arguments, so only allow this overload
  // if the missing argument is of function type.
  if (firstTrailingIdx && argInsertIdx > *firstTrailingIdx) {
    if (param.isInOut()) {
      return false;
    }

    Type expectedTy = param.getPlainType()->lookThroughAllOptionalTypes();
    return expectedTy->is<FunctionType>() || expectedTy->isAny() ||
           expectedTy->isTypeVariableOrMember();
  }
  return true;
}

std::optional<CompletionArgInfo>
constraints::getCompletionArgInfo(ASTNode anchor, ConstraintSystem &CS) {
  auto *exprAnchor = getAsExpr(anchor);
  if (!exprAnchor)
    return std::nullopt;

  auto *args = exprAnchor->getArgs();
  if (!args)
    return std::nullopt;

  for (unsigned i : indices(*args)) {
    if (CS.containsIDEInspectionTarget(args->getExpr(i)))
      return CompletionArgInfo{i, args->getFirstTrailingClosureIndex(),
                               args->size()};
  }
  return std::nullopt;
}

class ArgumentFailureTracker : public MatchCallArgumentListener {
protected:
  ConstraintSystem &CS;
  NullablePtr<ValueDecl> Callee;
  SmallVectorImpl<AnyFunctionType::Param> &Arguments;
  ArrayRef<AnyFunctionType::Param> Parameters;
  std::optional<unsigned> UnlabeledTrailingClosureArgIndex;
  ConstraintLocatorBuilder Locator;

private:
  SmallVector<SynthesizedArg, 4> MissingArguments;
  SmallVector<std::pair<unsigned, AnyFunctionType::Param>, 4> ExtraArguments;

protected:
  /// Synthesizes an argument that is intended to match against a missing
  /// argument for the parameter at \p paramIdx.
  /// \returns The index of the new argument in \c Arguments.
  unsigned synthesizeArgument(unsigned paramIdx,
                              bool isAfterCodeCompletionLoc) {
    const auto &param = Parameters[paramIdx];

    unsigned newArgIdx = Arguments.size();
    auto *argLoc = CS.getConstraintLocator(
        Locator, {LocatorPathElt::ApplyArgToParam(newArgIdx, paramIdx,
                                                  param.getParameterFlags()),
                  LocatorPathElt::SynthesizedArgument(
                      newArgIdx, isAfterCodeCompletionLoc)});

    auto *argType = CS.createTypeVariable(
        argLoc, TVO_CanBindToInOut | TVO_CanBindToLValue |
                    TVO_CanBindToNoEscape | TVO_CanBindToHole);

    auto synthesizedArg = param.withType(argType);
    Arguments.push_back(synthesizedArg);
    return newArgIdx;
  }

public:
  ArgumentFailureTracker(
      ConstraintSystem &cs, ValueDecl *callee,
      SmallVectorImpl<AnyFunctionType::Param> &args,
      ArrayRef<AnyFunctionType::Param> params,
      std::optional<unsigned> unlabeledTrailingClosureArgIndex,
      ConstraintLocatorBuilder locator)
      : CS(cs), Callee(callee), Arguments(args), Parameters(params),
        UnlabeledTrailingClosureArgIndex(unlabeledTrailingClosureArgIndex),
        Locator(locator) {}

  ~ArgumentFailureTracker() override {
    if (!MissingArguments.empty()) {
      auto *fix = AddMissingArguments::create(CS, MissingArguments,
                                              CS.getConstraintLocator(Locator));

      // Not having an argument is the same impact as having a type mismatch.
      (void)CS.recordFix(fix,
                         FixImpact::TypeMismatch * MissingArguments.size());
    }
  }

  std::optional<unsigned> missingArgument(unsigned paramIdx,
                                          unsigned argInsertIdx) override {
    if (!CS.shouldAttemptFixes())
      return std::nullopt;

    unsigned newArgIdx =
        synthesizeArgument(paramIdx, /*isAfterCodeCompletionLoc=*/false);
    auto synthesizedArg = Arguments[newArgIdx];

    MissingArguments.push_back(SynthesizedArg{paramIdx, synthesizedArg});

    return newArgIdx;
  }

  bool extraArgument(unsigned argIdx) override {
    if (!CS.shouldAttemptFixes())
      return true;

    // If this is a trailing closure, let's check if the call is
    // to an init of a callable type. If so, let's not record it
    // as extraneous since it would be matched against implicitly
    // injected `.callAsFunction` call.
    if (UnlabeledTrailingClosureArgIndex &&
        argIdx == *UnlabeledTrailingClosureArgIndex && Callee) {
      if (auto *ctor = dyn_cast<ConstructorDecl>(Callee.get())) {
        auto resultTy = ctor->getResultInterfaceType();
        if (resultTy->isCallAsFunctionType(CS.DC))
          return true;
      }
    }

    ExtraArguments.push_back(std::make_pair(argIdx, Arguments[argIdx]));
    return false;
  }

  bool missingLabel(unsigned paramIndex) override {
    return !CS.shouldAttemptFixes();
  }

  bool extraneousLabel(unsigned paramIndex) override {
    return !CS.shouldAttemptFixes();
  }

  bool incorrectLabel(unsigned paramIndex) override {
    return !CS.shouldAttemptFixes();
  }

  bool outOfOrderArgument(
      unsigned argIdx, unsigned prevArgIdx,
      ArrayRef<ParamBinding> bindings) override {
    if (CS.shouldAttemptFixes()) {
      // If some of the arguments are missing/extraneous, no reason to
      // record a fix for this, increase the score so there is a way
      // to identify that there is something going on besides just missing
      // arguments.
      if (!MissingArguments.empty() || !ExtraArguments.empty()) {
        CS.increaseScore(SK_Fix, Locator);
        return false;
      }

      auto *fix = MoveOutOfOrderArgument::create(
          CS, argIdx, prevArgIdx, bindings, CS.getConstraintLocator(Locator));
      return CS.recordFix(fix);
    }

    return true;
  }

  bool relabelArguments(ArrayRef<Identifier> newLabels) override {
    if (!CS.shouldAttemptFixes())
      return true;

    // TODO(diagnostics): If re-labeling is mixed with extra arguments,
    // let's produce a fix only for extraneous arguments for now,
    // because they'd share a locator path which (currently) means
    // one fix would overwrite another.
    if (!ExtraArguments.empty()) {
      CS.increaseScore(SK_Fix, Locator);
      return false;
    }

    auto anchor = Locator.getBaseLocator()->getAnchor();
    if (!anchor)
      return true;

    unsigned numExtraneous = 0;
    unsigned numRenames = 0;
    unsigned numOutOfOrder = 0;

    for (unsigned i : indices(newLabels)) {
      // It's already known how many arguments are missing,
      // it would be accounted for in the impact.
      if (i >= Arguments.size())
        continue;

      auto argLabel = Arguments[i].getLabel();
      auto paramLabel = newLabels[i];

      if (argLabel == paramLabel)
        continue;

      if (!argLabel.empty()) {
        // Instead of this being a label mismatch which requires
        // re-labeling, this could be an out-of-order argument
        // instead which has a completely different impact.
        if (llvm::count(newLabels, argLabel) == 1) {
          ++numOutOfOrder;
        } else if (paramLabel.empty()) {
          ++numExtraneous;
        } else {
          ++numRenames;
        }
      }
    }

    auto *locator = CS.getConstraintLocator(Locator);
    auto *fix = RelabelArguments::create(CS, newLabels, locator);
    // Re-labeling fixes with extraneous/incorrect labels should be
    // lower priority vs. other fixes on same/different overload(s)
    // where labels did line up correctly.
    //
    // If there are not only labeling problems but also some of the
    // arguments are missing, let's account of that in the impact.
    auto impact = FixImpact::Mismatch + numOutOfOrder +
                  FixImpact::TypeMismatch * numExtraneous +
                  (FixImpact::TypeMismatch + 1) * numRenames +
                  FixImpact::TypeMismatch * MissingArguments.size();
    return CS.recordFix(fix, impact);
  }

  ArrayRef<std::pair<unsigned, AnyFunctionType::Param>>
  getExtraneousArguments() const {
    return ExtraArguments;
  }
};

/// Ignores any failures after the code completion token.
class CompletionArgumentTracker : public ArgumentFailureTracker {
  struct CompletionArgInfo ArgInfo;

public:
  CompletionArgumentTracker(
      ConstraintSystem &cs, ValueDecl *callee,
      SmallVectorImpl<AnyFunctionType::Param> &args,
      ArrayRef<AnyFunctionType::Param> params,
      std::optional<unsigned> unlabeledTrailingClosureArgIndex,
      ConstraintLocatorBuilder locator, struct CompletionArgInfo ArgInfo)
      : ArgumentFailureTracker(cs, callee, args, params,
                               unlabeledTrailingClosureArgIndex, locator),
        ArgInfo(ArgInfo) {}

  std::optional<unsigned> missingArgument(unsigned paramIdx,
                                          unsigned argInsertIdx) override {
    // When solving for code completion, if any argument contains the
    // completion location, later arguments shouldn't be considered missing
    // (causing the solution to have a worse score) as the user just hasn't
    // written them yet. Early exit to avoid recording them in this case.
    if (ArgInfo.allowsMissingArgAt(argInsertIdx, Parameters[paramIdx])) {
      return synthesizeArgument(paramIdx, /*isAfterCodeCompletionLoc=*/true);
    }

    return ArgumentFailureTracker::missingArgument(paramIdx, argInsertIdx);
  }

  bool extraArgument(unsigned argIdx) override {
    if (ArgInfo.isBefore(argIdx)) {
      return false;
    }
    if (argIdx == 0 && ArgInfo.completionIdx == 0) {
      return false;
    }
    return ArgumentFailureTracker::extraArgument(argIdx);
  }

  bool outOfOrderArgument(unsigned argIdx, unsigned prevArgIdx,
                          ArrayRef<ParamBinding> bindings) override {
    if (ArgInfo.isBefore(argIdx)) {
      return false;
    }

    return ArgumentFailureTracker::outOfOrderArgument(argIdx, prevArgIdx,
                                                      bindings);
  }

  bool shouldClaimArgDuringRecovery(unsigned argIdx) override {
    return !ArgInfo.isBefore(argIdx);
  }

  bool
  canClaimArgIgnoringNameMismatch(const AnyFunctionType::Param &arg) override {
    if (!isCodeCompletionTypeVar(arg.getPlainType())) {
      return false;
    }
    if (!arg.getLabel().empty()) {
      return false;
    }
    return true;
  }
};

static std::optional<std::pair<TypeVariableType *, Type>>
shouldOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx,
                                  Type paramTy, Type argTy, Expr *argExpr,
                                  ConstraintSystem &cs) {
  auto result = canOpenExistentialCallArgument(callee, paramIdx, paramTy, argTy);
  if (!result)
    return std::nullopt;

  // An argument expression that explicitly coerces to an existential
  // disables the implicit opening of the existential unless it's
  // wrapped in parens.
  if (argExpr) {
    if (auto argCast = dyn_cast<ExplicitCastExpr>(
            argExpr->getSemanticsProvidingExpr())) {
      if (auto typeRepr = argCast->getCastTypeRepr()) {
        if (auto toType = cs.getType(typeRepr)) {
          if (!isa<ParenExpr>(argExpr) && toType->isAnyExistentialType())
            return std::nullopt;
        }
      }
    }
  }

  return result;
}

// Match the argument of a call to the parameter.
static ConstraintSystem::SolutionKind matchCallArguments(
    ConstraintSystem &cs, FunctionType *contextualType, ArgumentList *argList,
    ArrayRef<AnyFunctionType::Param> args,
    ArrayRef<AnyFunctionType::Param> params, ConstraintKind subKind,
    ConstraintLocatorBuilder locator,
    std::optional<TrailingClosureMatching> trailingClosureMatching,
    SmallVectorImpl<std::pair<TypeVariableType *, ExistentialArchetypeType *>>
        &openedExistentials) {
  assert(subKind == ConstraintKind::OperatorArgumentConversion ||
         subKind == ConstraintKind::ArgumentConversion);
  auto *loc = cs.getConstraintLocator(locator);
  assert(loc->isLastElement<LocatorPathElt::ApplyArgument>());

  ValueDecl *callee = nullptr;
  bool appliedSelf = false;

  // Resolve the callee for the application.
  auto *calleeLocator = cs.getCalleeLocator(loc);
  if (auto overload = cs.findSelectedOverloadFor(calleeLocator)) {
    callee = overload->choice.getDeclOrNull();
    appliedSelf = hasAppliedSelf(cs, overload->choice);
  }

  ParameterListInfo paramInfo(params, callee, appliedSelf);

  // Make sure that argument list is available.
  assert(argList);

  // Apply labels to arguments.
  SmallVector<AnyFunctionType::Param, 8> argsWithLabels;
  argsWithLabels.append(args.begin(), args.end());
  AnyFunctionType::relabelParams(argsWithLabels, argList);

  // Special case when a single tuple argument if used
  // instead of N distinct arguments e.g.:
  //
  // func foo(_ x: Int, _ y: Int) {}
  // foo((1, 2)) // expected 2 arguments, got a single tuple with 2 elements.
  if (cs.shouldAttemptFixes() && argsWithLabels.size() == 1 &&
      llvm::count_if(indices(params), [&](unsigned paramIdx) {
        return !paramInfo.hasDefaultArgument(paramIdx);
      }) > 1) {
    const auto &arg = argsWithLabels.front();
    auto argTuple = arg.getPlainType()->getRValueType()->getAs<TupleType>();
    // Don't explode a tuple in cases where first parameter is a tuple as
    // well. That is a regular "missing argument case" even if their arity
    // is different e.g.
    //
    // func foo(_: (Int, Int), _: Int) {}
    // foo((1, 2)) // call is missing an argument for parameter #1
    if (argTuple && argTuple->getNumElements() == params.size() &&
        !params.front().getPlainType()->is<TupleType>()) {
      argsWithLabels.pop_back();
      // Let's make sure that labels associated with tuple elements
      // line up with what is expected by argument list.
      SmallVector<SynthesizedArg, 4> synthesizedArgs;
      for (unsigned i = 0, n = argTuple->getNumElements(); i != n; ++i) {
        const auto &elt = argTuple->getElement(i);

        // If tuple doesn't have a label for its first element
        // and parameter does, let's assume parameter's label
        // to aid argument matching. For example:
        //
        // \code
        // func test(val: Int, _: String) {}
        //
        // test(val: (42, "")) // expands into `(val: 42, "")`
        // \endcode
        Identifier label = elt.getName();
        if (i == 0 && !elt.hasName() && params[0].hasLabel()) {
          label = params[0].getLabel();
        }

        AnyFunctionType::Param argument(elt.getType(), label);
        synthesizedArgs.push_back(SynthesizedArg{i, argument});
        argsWithLabels.push_back(argument);
      }

      (void)cs.recordFix(
          AddMissingArguments::create(cs, synthesizedArgs,
                                      cs.getConstraintLocator(locator)),
          FixImpact::TypeMismatch * synthesizedArgs.size());
    }
  }

  // Match up the call arguments to the parameters.
  SmallVector<ParamBinding, 4> parameterBindings;
  TrailingClosureMatching selectedTrailingMatching =
      TrailingClosureMatching::Forward;

  {
    std::unique_ptr<ArgumentFailureTracker> listener;
    if (cs.isForCodeCompletion()) {
      if (auto completionInfo = getCompletionArgInfo(locator.getAnchor(), cs)) {
        listener = std::make_unique<CompletionArgumentTracker>(
            cs, callee, argsWithLabels, params,
            argList->getFirstTrailingClosureIndex(), locator, *completionInfo);
      }
    }
    if (!listener) {
      // We didn't create an argument tracker for code completion. Create a
      // normal one.
      listener = std::make_unique<ArgumentFailureTracker>(
          cs, callee, argsWithLabels, params,
          argList->getFirstTrailingClosureIndex(), locator);
    }
    auto callArgumentMatch = constraints::matchCallArguments(
        argsWithLabels, params, paramInfo,
        argList->getFirstTrailingClosureIndex(), cs.shouldAttemptFixes(),
        *listener, trailingClosureMatching);
    if (!callArgumentMatch)
      return ConstraintSystem::SolutionKind::Error;

    // If there are different results for both the forward and backward
    // scans, return an ambiguity: the caller will need to build a
    // disjunction.
    if (callArgumentMatch->backwardParameterBindings) {
      return ConstraintSystem::SolutionKind::Unsolved;
    }

    selectedTrailingMatching = callArgumentMatch->trailingClosureMatching;
    // Record the matching direction and parameter bindings used for this call.
    cs.recordMatchCallArgumentResult(cs.getConstraintLocator(locator),
                                     *callArgumentMatch);

    // If there was a disjunction because both forward and backward were
    // possible, increase the score for forward matches to bias toward the
    // (source-compatible) backward matches. The compiler will produce a
    // warning for such code.
    if (trailingClosureMatching &&
        *trailingClosureMatching == TrailingClosureMatching::Forward)
      cs.increaseScore(SK_ForwardTrailingClosure, locator);

    // Take the parameter bindings we selected.
    parameterBindings = std::move(callArgumentMatch->parameterBindings);

    auto extraArguments = listener->getExtraneousArguments();
    if (!extraArguments.empty()) {
      if (RemoveExtraneousArguments::isMinMaxNameShadowing(cs, locator))
        return ConstraintSystem::SolutionKind::Error;

      // First let's see whether this is a situation where a single
      // parameter is a tuple, but N distinct arguments were passed in.
      if (AllowTupleSplatForSingleParameter::attempt(
              cs, argsWithLabels, params, parameterBindings, locator)) {
        // Let's produce a generic "extraneous arguments"
        // diagnostic otherwise.
        auto *fix = RemoveExtraneousArguments::create(
            cs, contextualType, extraArguments,
            cs.getConstraintLocator(locator));

        for (const auto &extraArg : extraArguments) {
          auto argument = argList->get(extraArg.first);
          auto argType = extraArg.second.getPlainType();

          // Prevent closure resolution by binding it to a placeholder
          // because the main issue here is invalid overload and
          // errors produced from the closure body are going to be
          // superfluous.
          if (isExpr<ClosureExpr>(argument.getExpr())) {
            cs.recordTypeVariablesAsHoles(argType);
          } else {
            cs.recordAnyTypeVarAsPotentialHole(argType);
          }
        }

        if (cs.recordFix(fix,
                         FixImpact::InvalidReference * extraArguments.size()))
          return ConstraintSystem::SolutionKind::Error;
      }
    }
  }

  auto isSynthesizedArgument = [](const AnyFunctionType::Param &arg) -> bool {
    if (auto *typeVar = arg.getPlainType()->getAs<TypeVariableType>()) {
      auto *locator = typeVar->getImpl().getLocator();
      return locator->isLastElement<LocatorPathElt::SynthesizedArgument>();
    }

    return false;
  };

  for (unsigned paramIdx = 0, numParams = parameterBindings.size();
       paramIdx != numParams; ++paramIdx){
    // Determine the parameter type.
    const auto &param = params[paramIdx];
    auto paramTy = param.getOldType();

    // Type parameter packs ingest the entire set of argument bindings
    // as a pack type.
    //
    // We pull these out special because variadic parameters ban lots of
    // the more interesting typing constructs called out below like
    // inout and @autoclosure.
    if (paramInfo.isVariadicGenericParameter(paramIdx)) {
      // If generic parameter comes from a variadic type declaration it's
      // possible that it got specialized early and is no longer represented
      // by a pack expansion type. For example, consider expression -
      // `Test<Int>(42)` where `Test<each T>` and the initializer
      // is declared as `init(_: repeat each T)`. Although declaration
      // based information reports parameter at index 0 as variadic generic
      // the call site specializes it to `Int`.
      if (isPackExpansionType(paramTy)) {
        SmallVector<Type, 2> argTypes;
        for (auto argIdx : parameterBindings[paramIdx]) {
          auto argType = argsWithLabels[argIdx].getPlainType();
          argTypes.push_back(argType);
        }

        auto *argPack = PackType::get(cs.getASTContext(), argTypes);
        auto argPackExpansion = [&]() {
          if (argPack->getNumElements() == 1 &&
              argPack->getElementType(0)->is<PackExpansionType>()) {
            return argPack->getElementType(0)->castTo<PackExpansionType>();
          }

          return PackExpansionType::get(argPack, argPack);
        }();

        auto firstArgIdx =
            argTypes.empty() ? paramIdx : parameterBindings[paramIdx].front();

        cs.addConstraint(
            subKind, argPackExpansion, paramTy,
            locator.withPathElement(LocatorPathElt::ApplyArgToParam(
                firstArgIdx, paramIdx, param.getParameterFlags())));
        continue;
      }
    }

    // If type inference from default arguments is enabled, let's
    // add a constraint from the parameter if necessary, otherwise
    // there is nothing to do but move to the next parameter.
    if (parameterBindings[paramIdx].empty() && callee) {
      // Type inference from default value expressions.
      {
        auto *paramList = callee->getParameterList();
        if (!paramList)
          continue;

        // There is nothing to infer if parameter doesn't have any
        // generic parameters in its type.
        auto *PD = paramList->get(paramIdx);
        if (!PD->getInterfaceType()->hasTypeParameter())
          continue;

        // The type of the default value is going to be determined
        // based on a type deduced for the parameter at this call site.
        if (PD->hasCallerSideDefaultExpr())
          continue;

        auto defaultExprType = PD->getTypeOfDefaultExpr();

        // A caller side default.
        if (!defaultExprType || defaultExprType->hasError())
          continue;

        // If this is just a regular default type that should
        // work for all substitutions of generic parameter,
        // let's continue.
        if (defaultExprType->hasArchetype())
          continue;

        cs.addConstraint(
            ConstraintKind::ArgumentConversion, paramTy, defaultExprType,
            locator.withPathElement(LocatorPathElt::ApplyArgToParam(
                paramIdx, paramIdx, param.getParameterFlags())));
      }

      continue;
    }

    // See if we have a parameter label specified in the function's DeclNameLoc.
    Identifier compoundParamLabel;
    if (auto *E = getAsExpr(calleeLocator->getAnchor())) {
      auto nameLoc = E->getNameLoc();
      if (auto labelLoc = nameLoc.getArgumentLabelLoc(paramIdx)) {
        auto &ctx = cs.getASTContext();
        auto labelTok = Lexer::getTokenAtLocation(ctx.SourceMgr, labelLoc);
        compoundParamLabel = ctx.getIdentifier(labelTok.getText());
      }
    }

    // Compare each of the bound arguments for this parameter.
    for (auto argIdx : parameterBindings[paramIdx]) {
      auto loc = locator.withPathElement(LocatorPathElt::ApplyArgToParam(
          argIdx, paramIdx, param.getParameterFlags()));
      const auto &argument = argsWithLabels[argIdx];
      auto argTy = argument.getOldType();

      bool matchingAutoClosureResult = param.isAutoClosure();
      auto *argExpr = getArgumentExpr(locator.getAnchor(), argIdx);
      if (param.isAutoClosure() && !isSynthesizedArgument(argument)) {
        auto &ctx = cs.getASTContext();

        // If this is a call to a function with a closure argument and the
        // parameter is an autoclosure, let's just increment the score here
        // so situations like below are not ambiguous.
        //    func f<T>(_: () -> T) {}
        //    func f<T>(_: @autoclosure () -> T) {}
        //
        //    f { } // OK
        //    f { [v] in } // OK
        if (isExpr<ClosureExpr>(argExpr) || isExpr<CaptureListExpr>(argExpr)) {
          cs.increaseScore(SK_FunctionToAutoClosureConversion, loc);
        }

        // If the argument is not marked as @autoclosure or
        // this is Swift version >= 5 where forwarding is not allowed,
        // argument would always be wrapped into an implicit closure
        // at the end, so we can safely match against result type.
        if (ctx.isLanguageModeAtLeast(LanguageMode::v5) ||
            !isAutoClosureArgument(argExpr)) {
          // In Swift >= 5 mode there is no @autoclosure forwarding,
          // so let's match result types.
          if (auto *fnType = paramTy->getAs<FunctionType>()) {
            paramTy = fnType->getResult();
          }
        } else {
          // Matching @autoclosure argument to @autoclosure parameter
          // directly would mean introducing a function conversion
          // in Swift <= 4 mode.
          cs.increaseScore(SK_FunctionConversion, loc);
          matchingAutoClosureResult = false;
        }
      }

      // In case solver matched trailing based on the backward scan,
      // let's produce a warning which would suggest to add a label
      // to disambiguate in the future.
      if (selectedTrailingMatching == TrailingClosureMatching::Backward &&
          argIdx == *argList->getFirstTrailingClosureIndex()) {
        cs.recordFix(SpecifyLabelToAssociateTrailingClosure::create(
            cs, cs.getConstraintLocator(loc)));
      }

      // Type-erase any opened existentials from subsequent parameter types
      // unless the argument itself is a generic function, which could handle
      // the opened existentials.
      if (!openedExistentials.empty() && paramTy->hasTypeVariable() &&
          !cs.isArgumentGenericFunction(argTy, argExpr)) {
        for (const auto &opened : openedExistentials) {
          paramTy = typeEraseOpenedExistentialReference(
              paramTy, opened.second->getExistentialType(), opened.first,
              TypePosition::Contravariant);
        }
      }

      // If the argument is an existential type and the parameter is generic,
      // consider opening the existential type.
      if (auto typeVarAndBindingTy = shouldOpenExistentialCallArgument(
              callee, paramIdx, paramTy, argTy, argExpr, cs)) {
        // My kingdom for a decent "if let" in C++.
        TypeVariableType *typeVar;
        Type bindingTy;
        std::tie(typeVar, bindingTy) = *typeVarAndBindingTy;

        ExistentialArchetypeType *openedArchetype;

        // Open the argument type.
        argTy = argTy.transformRec([&](TypeBase *t) -> std::optional<Type> {
          if (t->isAnyExistentialType()) {
            Type openedTy;
            std::tie(openedTy, openedArchetype) =
                cs.openAnyExistentialType(t, cs.getConstraintLocator(loc));

            return openedTy;
          }

          return std::nullopt;
        });

        openedExistentials.push_back({typeVar, openedArchetype});
      }

      // If we have a compound function reference (e.g `fn($x:)`), respect
      // the parameter label given. Otherwise look at the argument label.
      auto wrapperArgLabel = compoundParamLabel.empty() ? argument.getLabel()
                                                        : compoundParamLabel;
      if (paramInfo.hasExternalPropertyWrapper(paramIdx) ||
          wrapperArgLabel.hasDollarPrefix()) {
        auto *param = getParameterAt(callee, paramIdx);
        assert(param);
        if (cs.applyPropertyWrapperToParameter(paramTy, argTy,
                                               const_cast<ParamDecl *>(param),
                                               wrapperArgLabel, subKind,
                                               cs.getConstraintLocator(loc),
                                               calleeLocator)
                == ConstraintSystem::SolutionKind::Error) {
          return ConstraintSystem::SolutionKind::Error;
        }
        continue;
      }

      // If argument comes for declaration it should loose
      // `@autoclosure` flag, because in context it's used
      // as a function type represented by autoclosure.
      //
      // Special case here are synthesized arguments because
      // they mirror parameter flags to ease diagnosis.
      assert(!argsWithLabels[argIdx].isAutoClosure() ||
             isSynthesizedArgument(argument));

      if (cs.getASTContext().TypeCheckerOpts.SolverEnableTransitiveConformance) {
        // If parameter is a generic parameter, let's copy its
        // conformance requirements (if any), to the argument
        // be able to filter mismatching choices earlier.
        if (auto *typeVar = paramTy->getAs<TypeVariableType>()) {
          auto *locator = typeVar->getImpl().getLocator();
          if (locator->isForGenericParameter()) {
            auto &CG = cs.getConstraintGraph();

            auto isTransferableConformance = [&typeVar](Constraint *constraint) {
              if (constraint->getKind() != ConstraintKind::ConformsTo &&
                  constraint->getKind() != ConstraintKind::NonisolatedConformsTo)
                return false;

              auto requirementTy = constraint->getFirstType();
              if (!requirementTy->isEqual(typeVar))
                return false;

              return constraint->getSecondType()->is<ProtocolType>();
            };

            for (auto *constraint : CG[typeVar].getConstraints()) {
              if (isTransferableConformance(constraint))
                cs.addConstraint(ConstraintKind::TransitivelyConformsTo, argTy,
                                 constraint->getSecondType(),
                                 constraint->getLocator());
            }
          }
        }
      }

      // Detect that there is sync -> async mismatch early on for
      // closure argument to avoid re-checking calls if there was
      // an overload choice with synchronous parameter of the same
      // shape e.g.
      //
      // func test(_: () -> Void) -> MyStruct {}
      // func test(_: () async -> Void) -> MyStruct {}
      //
      // test({ ... }).<member>...
      //
      // Synchronous overload is always better in this case so there
      // is no need to re-check follow-up `<member>`s and better
      // to short-circuit this path early.
      if (auto *fnType = paramTy->getAs<FunctionType>()) {
        if (fnType->isAsync()) {
          auto *typeVar = argTy->getAs<TypeVariableType>();
          if (typeVar && typeVar->getImpl().isClosureType()) {
            auto *locator = typeVar->getImpl().getLocator();
            auto *closure = castToExpr<ClosureExpr>(locator->getAnchor());
            if (!cs.getClosureType(closure)->isAsync())
              cs.increaseScore(SK_SyncInAsync, locator);
          }
        }
      }

      if (!argument.isCompileTimeLiteral() && param.isCompileTimeLiteral()) {
        auto *locator = cs.getConstraintLocator(loc);
        SourceRange range;
        // simplify locator so the anchor is the exact argument.
        cs.recordFix(NotCompileTimeLiteral::create(cs, paramTy,
          simplifyLocator(cs, locator, range)));
      }

      cs.addConstraint(
          subKind, argTy, paramTy,
          matchingAutoClosureResult
              ? loc.withPathElement(ConstraintLocator::AutoclosureResult)
              : loc,
          /*isFavored=*/false);
    }
  }

  return ConstraintSystem::SolutionKind::Solved;
}

ConstraintSystem::SolutionKind
ConstraintSystem::matchFunctionResultTypes(Type expectedResult, Type fnResult,
                                           TypeMatchOptions flags,
                                           ConstraintLocatorBuilder locator) {
  // If we have a callee with an IUO return, add a disjunction that can either
  // bind to the result or an unwrapped result.
  auto *calleeLoc = getCalleeLocator(getConstraintLocator(locator));
  auto *calleeResultLoc = getConstraintLocator(
      calleeLoc, ConstraintLocator::FunctionResult);
  auto selected = findSelectedOverloadFor(calleeLoc);

  // If we don't have a direct callee, this might be the second application
  // of a curried function reference, in which case we need to dig into the
  // inner call to find the callee.
  // FIXME: This is a bit of a hack. We should consider rewriting curried
  // applies as regular applies in PreCheckExpr to eliminate the need to special
  // case double applies in the solver.
  bool isSecondApply = false;
  if (!selected) {
    auto anchor = locator.getAnchor();
    if (auto *callExpr = getAsExpr<CallExpr>(anchor)) {
      if (auto *innerCall = getAsExpr<CallExpr>(callExpr->getSemanticFn())) {
        auto *innerCalleeLoc =
            getCalleeLocator(getConstraintLocator(innerCall));
        if (auto innerOverload = findSelectedOverloadFor(innerCalleeLoc)) {
          auto choice = innerOverload->choice;
          if (choice.getFunctionRefInfo().isDoubleApply()) {
            isSecondApply = true;
            selected.emplace(*innerOverload);
          }
        }
      }
    }
  }
  if (selected) {
    auto choice = selected->choice;

    // Subscripts found through dynamic lookup need special treatment. Unlike
    // other decls found through dynamic lookup, they cannot have an optional
    // applied to their reference, instead it's applied to their result. As
    // such, we may need to unwrap another level of optionality.
    if (choice.getKind() == OverloadChoiceKind::DeclViaDynamic &&
        isa<SubscriptDecl>(choice.getDecl())) {
      // Introduce a type variable to record whether we needed to unwrap the
      // outer optional.
      auto outerTy = createTypeVariable(calleeResultLoc, TVO_CanBindToLValue);
      buildDisjunctionForDynamicLookupResult(outerTy, fnResult,
                                             calleeResultLoc);
      fnResult = outerTy;
    }

    auto iuoKind = choice.getIUOReferenceKind(*this, isSecondApply);
    if (iuoKind == IUOReferenceKind::ReturnValue) {
      buildDisjunctionForImplicitlyUnwrappedOptional(expectedResult, fnResult,
                                                     calleeResultLoc);
      return SolutionKind::Solved;
    }
  }
  return matchTypes(expectedResult, fnResult, ConstraintKind::Bind, flags,
                    locator);
}

static bool isInPatternMatchingContext(ConstraintLocatorBuilder locator) {
  SmallVector<LocatorPathElt, 4> path;
  (void)locator.getLocatorParts(path);

  auto pathElement = llvm::find_if(path, [](LocatorPathElt &elt) {
    return elt.is<LocatorPathElt::PatternMatch>();
  });
  return pathElement != path.end();
}

namespace {

class TupleMatcher {
  TupleType *tuple1;
  TupleType *tuple2;

public:
  enum class MatchKind : uint8_t {
    Equality,
    Subtype,
    Conversion,
  };

  SmallVector<MatchedPair, 4> pairs;
  bool hasLabelMismatch = false;

  TupleMatcher(TupleType *tuple1, TupleType *tuple2)
      : tuple1(tuple1), tuple2(tuple2) {}

  bool match(MatchKind kind, ConstraintLocatorBuilder locator) {
    // FIXME: TuplePackMatcher should completely replace the non-variadic
    // case too eventually.
    if (containsPackExpansionType(tuple1) ||
        containsPackExpansionType(tuple2)) {
      TuplePackMatcher matcher(tuple1, tuple2, isPackExpansionType);
      if (matcher.match())
        return true;

      pairs = matcher.pairs;
      return false;
    }

    if (tuple1->getNumElements() != tuple2->getNumElements())
      return true;

    switch (kind) {
    case MatchKind::Equality:
      return matchEquality(isInPatternMatchingContext(locator));

    case MatchKind::Subtype:
      return matchSubtype();

    case MatchKind::Conversion:
      return matchConversion();
    }
  }

private:
  bool matchEquality(bool inPatternMatchingContext) {
    for (unsigned i = 0, n = tuple1->getNumElements(); i != n; ++i) {
      const auto &elt1 = tuple1->getElement(i);
      const auto &elt2 = tuple2->getElement(i);

      if (inPatternMatchingContext) {
        // FIXME: The fact that this isn't symmetric is wrong since this logic
        // is called for bind and equal constraints...
        if (elt2.hasName() && elt1.getName() != elt2.getName())
          return true;
      } else {
        // If the names don't match, we have a conflict.
        if (elt1.getName() != elt2.getName())
          return true;
      }

      pairs.emplace_back(elt1.getType(), elt2.getType(), i, i);
    }

    return false;
  }

  bool matchSubtype() {
    for (unsigned i = 0, n = tuple1->getNumElements(); i != n; ++i) {
      const auto &elt1 = tuple1->getElement(i);
      const auto &elt2 = tuple2->getElement(i);

      // If the names don't match, we may have a conflict.
      if (elt1.getName() != elt2.getName()) {
        // Make sure that this name isn't used at some other position.
        if (elt2.hasName() && tuple1->getNamedElementId(elt2.getName()) != -1)
          return true;

        // If both elements have names and they mismatch, make a note of it
        // so we can emit a warning.
        if (elt1.hasName() && elt2.hasName())
          hasLabelMismatch = true;
      }

      pairs.emplace_back(elt1.getType(), elt2.getType(), i, i);
    }

    return false;
  }

  bool matchConversion() {
    SmallVector<unsigned, 4> sources;
    if (computeTupleShuffle(tuple1, tuple2, sources))
      return true;

    for (unsigned idx2 = 0, n = sources.size(); idx2 != n; ++idx2) {
      unsigned idx1 = sources[idx2];

      auto lhs = tuple1->getElementType(idx1);
      auto rhs = tuple2->getElementType(idx2);

      pairs.emplace_back(lhs, rhs, idx1, idx2);
    }

    return false;
  }
};

}

ConstraintSystem::SolutionKind
ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2,
                                  ConstraintKind kind, TypeMatchOptions flags,
                                  ConstraintLocatorBuilder locator) {
  using TupleMatchKind = TupleMatcher::MatchKind;

  ConstraintKind subkind;
  TupleMatchKind matchKind;

  switch (kind) {
  case ConstraintKind::Bind:
  case ConstraintKind::Equal: {
    subkind = kind;
    matchKind = TupleMatchKind::Equality;
    break;
  }

  // NOTE: It was probably a mistake that BindToPointerType is handled like
  // Subtype; this was implicit in the old structure of the code due to bogus
  // use of operator<= on enum cases.
  case ConstraintKind::Subtype:
  case ConstraintKind::BindToPointerType: {
    subkind = kind;
    matchKind = TupleMatchKind::Subtype;
    break;
  }

  case ConstraintKind::Conversion:
  case ConstraintKind::ArgumentConversion:
  case ConstraintKind::OperatorArgumentConversion: {
    subkind = ConstraintKind::Conversion;
    matchKind = TupleMatchKind::Conversion;
    break;
  }

  case ConstraintKind::BindParam:
  case ConstraintKind::ApplicableFunction:
  case ConstraintKind::DynamicCallableApplicableFunction:
  case ConstraintKind::BindOverload:
  case ConstraintKind::CheckedCast:
  case ConstraintKind::SubclassOf:
  case ConstraintKind::NonisolatedConformsTo:
  case ConstraintKind::ConformsTo:
  case ConstraintKind::TransitivelyConformsTo:
  case ConstraintKind::Defaultable:
  case ConstraintKind::Disjunction:
  case ConstraintKind::Conjunction:
  case ConstraintKind::DynamicTypeOf:
  case ConstraintKind::EscapableFunctionOf:
  case ConstraintKind::OpenedExistentialOf:
  case ConstraintKind::KeyPath:
  case ConstraintKind::KeyPathApplication:
  case ConstraintKind::LiteralConformsTo:
  case ConstraintKind::ForEachElement:
  case ConstraintKind::OptionalObject:
  case ConstraintKind::UnresolvedValueMember:
  case ConstraintKind::ValueMember:
  case ConstraintKind::BridgingConversion:
  case ConstraintKind::OneWayEqual:
  case ConstraintKind::FallbackType:
  case ConstraintKind::UnresolvedMemberChainBase:
  case ConstraintKind::PropertyWrapper:
  case ConstraintKind::SyntacticElement:
  case ConstraintKind::BindTupleOfFunctionParams:
  case ConstraintKind::PackElementOf:
  case ConstraintKind::ShapeOf:
  case ConstraintKind::ExplicitGenericArguments:
  case ConstraintKind::SameShape:
  case ConstraintKind::MaterializePackExpansion:
  case ConstraintKind::LValueObject:
    llvm_unreachable("Bad constraint kind in matchTupleTypes()");
  }

  TupleMatcher matcher(tuple1, tuple2);

  if (matcher.match(matchKind, locator))
    return SolutionKind::Error;

  if (matcher.hasLabelMismatch) {
    // If we had a label mismatch, emit a warning. This is something we
    // shouldn't permit, as it's more permissive than what a conversion would
    // allow. Ideally we'd turn this into an error in Swift 6 mode.
    recordFix(AllowTupleLabelMismatch::create(*this, tuple1, tuple2,
                                              getConstraintLocator(locator)));
  }

  TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);

  for (auto pair : matcher.pairs) {
    auto result = matchTypes(pair.lhs, pair.rhs, subkind, subflags,
                             locator.withPathElement(
                                    LocatorPathElt::TupleElement(pair.lhsIdx)));
    if (result == SolutionKind::Error)
      return result;
  }

  return SolutionKind::Solved;
}

ConstraintSystem::SolutionKind
ConstraintSystem::matchPackTypes(PackType *pack1, PackType *pack2,
                                 ConstraintKind kind, TypeMatchOptions flags,
                                 ConstraintLocatorBuilder locator) {
  TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);

  PackMatcher matcher(pack1->getElementTypes(), pack2->getElementTypes(),
                      getASTContext(), isPackExpansionType);

  if (matcher.match())
    return SolutionKind::Error;

  for (auto pair : matcher.pairs) {
    auto result = matchTypes(pair.lhs, pair.rhs, kind, subflags,
                             locator.withPathElement(
                                 LocatorPathElt::PackElement(pair.lhsIdx)));
    if (result == SolutionKind::Error)
      return result;
  }

  return SolutionKind::Solved;
}

/// Utility function used when matching a pack expansion type against a
/// pack type.
///
/// Takes a pattern type and an original pack type, and returns an instantiated
/// pack type. The original pack type is then matched against the instantiated
/// pack type.
///
/// As a side effect, it binds each pack type variable occurring in the pattern
/// type to a new pack with the same shape as the original pack, but where the
/// elements are fresh type variables.
///
/// The instantiated pack has the same shape as the original pack, where the
/// ith element is the pattern type with each pack type variable replaced by the
/// ith element of its binding.
///
/// For example, given the pattern Foo<$T0> and the original pack
/// {Foo<Int>, Foo<String>...}, we're going to bind
///
/// $T0 := {$T1, $T2}
///
/// And return the new pack {Foo<$T1>, Foo<$T2>...}.
///
/// The caller will then match the original pack type against the instantiated
/// pack type, which will recover the bindings:
///
/// $T1 := Int
/// $T2 := String
///
static PackType *replaceTypeVariablesWithFreshPacks(ConstraintSystem &cs,
                                                    Type pattern,
                                                    PackType *pack,
                                                    ConstraintLocatorBuilder locator) {
  llvm::SmallSetVector<TypeVariableType *, 2> typeVarSet;
  llvm::MapVector<TypeVariableType *, SmallVector<Type, 2>> typeVars;

  pattern->walkPackReferences([&](Type t) {
    if (auto *typeVar = t->getAs<TypeVariableType>()) {
      if (typeVar->getImpl().canBindToPack())
        typeVarSet.insert(typeVar);
    }

    return false;
  });

  if (typeVarSet.empty())
    return nullptr;

  auto *loc = cs.getConstraintLocator(locator);

  // For each pack type variable occurring in the pattern type, compute a
  // binding pack type comprised of fresh type variables.
  for (auto *typeVar : typeVarSet) {
    auto &freshTypeVars = typeVars[typeVar];
    for (unsigned i = 0, e = pack->getNumElements(); i < e; ++i) {
      auto *packExpansionElt = pack->getElementType(i)->getAs<PackExpansionType>();

      // Preserve the pack expansion structure of the original pack. If the ith
      // element was a pack expansion type, create a new pack expansion type
      // wrapping a pack type variable. Otherwise, create a new scalar
      // type variable.
      //
      // FIXME: Other TVO_* flags for type variables?
      auto elementLoc = cs.getConstraintLocator(loc,
          LocatorPathElt::PackElement(freshTypeVars.size()));
      if (packExpansionElt != nullptr) {
        auto *freshTypeVar = cs.createTypeVariable(
            elementLoc,
            TVO_CanBindToPack |
                (typeVar->getImpl().canBindToHole() ? TVO_CanBindToHole : 0));
        freshTypeVars.push_back(PackExpansionType::get(
            freshTypeVar, packExpansionElt->getCountType()));
      } else {
        freshTypeVars.push_back(cs.createTypeVariable(
            elementLoc,
            typeVar->getImpl().canBindToHole() ? TVO_CanBindToHole : 0));
      }
    }
  }

  SmallVector<Type, 2> elts;

  // For each element of the original pack type, instantiate the pattern type by
  // replacing each pack type variable with the corresponding element of the
  // pack type variable's binding pack.
  for (unsigned i = 0, e = pack->getNumElements(); i < e; ++i) {
    auto *packExpansionElt = pack->getElementType(i)->getAs<PackExpansionType>();

    auto instantiatedPattern = pattern.transformRec([&](Type t)
                                                        -> std::optional<Type> {
      if (isPackExpansionType(t))
        return t;

      if (auto *typeVar = t->getAs<TypeVariableType>()) {
        if (typeVar->getImpl().canBindToPack()) {
          auto found = typeVars.find(typeVar);
          assert(found != typeVars.end());

          // The ith element of the binding pack is either a scalar type variable
          // or a pack expansion type wrapping a pack type variable.
          auto projectedType = (found->second)[i];
          if (packExpansionElt != nullptr) {
            projectedType = projectedType->castTo<PackExpansionType>()
                                         ->getPatternType();
            assert(projectedType->castTo<TypeVariableType>()
                                ->getImpl().canBindToPack());
          } else {
            assert(!projectedType->castTo<TypeVariableType>()
                                 ->getImpl().canBindToPack());
          }

          return projectedType;
        }
      }

      return std::nullopt;
    });

    if (packExpansionElt != nullptr) {
      elts.push_back(PackExpansionType::get(instantiatedPattern,
                                            packExpansionElt->getCountType()));
    } else {
      elts.push_back(instantiatedPattern);
    }
  }

  auto &ctx = cs.getASTContext();

  // Bind each pack type variable occurring in the pattern type to its
  // binding pack that was constructed above.
  for (const auto &pair : typeVars) {
    cs.addConstraint(ConstraintKind::Bind,
                     pair.first, PackType::get(ctx, pair.second), locator);
  }

  // Construct the instantiated pack type.
  return PackType::get(cs.getASTContext(), elts);
}

ConstraintSystem::SolutionKind
ConstraintSystem::matchPackExpansionTypes(PackExpansionType *expansion1,
                                          PackExpansionType *expansion2,
                                          ConstraintKind kind, TypeMatchOptions flags,
                                          ConstraintLocatorBuilder locator) {
  auto shapeLocator = locator.withPathElement(ConstraintLocator::PackShape);
  // The count types of two pack expansion types must have the same shape.
  addConstraint(ConstraintKind::SameShape, expansion1->getCountType(),
                expansion2->getCountType(),
                shapeLocator);

  auto pattern1 = expansion1->getPatternType();
  auto pattern2 = expansion2->getPatternType();

  if (shouldAttemptFixes()) {
    // If pack expansion types have different shapes, let's not attempt
    // to match their pattern types to avoid producing any extra errors
    // caused by shape differences.
    if (hasFixFor(getConstraintLocator(shapeLocator))) {
      recordAnyTypeVarAsPotentialHole(pattern1);
      recordAnyTypeVarAsPotentialHole(pattern2);
      return SolutionKind::Solved;
    }
  }

  auto *const pack1 = pattern1->getAs<PackType>();
  auto *const pack2 = pattern2->getAs<PackType>();

  // If both sides are expanded or neither side is, proceed to matching them
  // directly.
  // Otherwise, we have something like `Foo<$T0>` vs.
  // `Pack{Foo<Int>, Foo<String>}` or vice versa.
  // We're going to bind `$T0` to `Pack{Int, String}` and unfold `Foo<$T0>` into
  // `Pack{Foo<$T3>, Foo<$T4>} first.
  if ((bool)pack1 != (bool)pack2) {
    if (pack1) {
      pattern2 =
          replaceTypeVariablesWithFreshPacks(*this, pattern2, pack1, locator);
    } else {
      pattern1 =
          replaceTypeVariablesWithFreshPacks(*this, pattern1, pack2, locator);
    }

    if (!(pattern1 && pattern2)) {
      return SolutionKind::Error;
    }
  }

  // Continue matching.
  return matchTypes(pattern1, pattern2, kind, flags, locator);
}

/// Check where a representation is a subtype of another.
///
/// The subtype relationship is defined as:
/// 1. any representation R is a sub-type of itself.
/// 2. a thin representation is a subtype of any other representation.
/// 3. a thick representation is a subtype of any other thick representation.
///
/// For example, since `@convention(c)` is a thin representation, and
/// `@convention(swift)` is a thick representation,
/// `@convention(c) (A) -> B` is a sub-type of `(A) -> B`.
///
/// NOTE: Unlike typical subtyping relationships, this is not anti-symmetric.
/// For example, @convention(c) and @convention(thin) are subtypes of each other
/// but not equal.
static bool
isSubtypeOf(FunctionTypeRepresentation potentialSubRepr,
            FunctionTypeRepresentation potentialSuperRepr) {
  return (potentialSubRepr == potentialSuperRepr)
       || isThinRepresentation(potentialSubRepr)
       || isThickRepresentation(potentialSuperRepr);
}

/// Returns true if `constraint extInfo1 extInfo2` is satisfied.
static bool matchFunctionRepresentations(FunctionType::ExtInfo einfo1,
                                         FunctionType::ExtInfo einfo2,
                                         ConstraintKind kind,
                                         ConstraintSystemOptions options) {
  auto rep1 = einfo1.getRepresentation();
  auto rep2 = einfo2.getRepresentation();
  bool clangTypeMismatch =
      (options.contains(ConstraintSystemFlags::UseClangFunctionTypes) &&
       (einfo1.getClangTypeInfo() != einfo2.getClangTypeInfo()));
  switch (kind) {
  case ConstraintKind::Bind:
  case ConstraintKind::BindParam:
  case ConstraintKind::BindToPointerType:
  case ConstraintKind::Equal:
    return (rep1 == rep2) && !clangTypeMismatch;

  case ConstraintKind::Subtype: {
    // Breakdown of cases:
    // 1. isSubtypeOf(rep1, rep2) == false (hence rep1 != rep2):
    //    In this case, this function will return false, indicating that we
    //    can't convert. E.g. you can't convert from @convention(swift) to
    //    @convention(c).
    // 2. isSubtypeOf(rep1, rep2) == true and rep1 != rep2:
    //    In this case, this function will return true, indicating that we
    //    can convert, because the Clang type doesn't matter when converting
    //    between different representations. E.g. it is okay to convert from
    //    @convention(c) (regardless of cType) to @convention(swift).
    // 3. isSubtypeOf(rep1, rep2) == true and rep1 == rep2:
    //    In this case, the function returns !clangTypeMismatch, as we forbid
    //    conversions between @convention(c) functions with different cTypes.
    return isSubtypeOf(rep1, rep2) && ((rep1 != rep2) || !clangTypeMismatch);
  }

  // [NOTE: diagnose-swift-to-c-convention-change]: @convention(swift) ->
  // @convention(c) conversions are permitted only in certain cases.
  //
  //   var w = 3; func f() { print(w) }; func g(_ : @convention(c) () -> ()) {}
  //   g(f); // OK
  //   let h = f as @convention(c) () -> (); g(h) // OK
  //   let k = f; g(k) // error
  //   func m() { let x = 0; g({ print(x) }) } // error
  //   func n() { let y = 0; func p() { }; g(p); } // OK
  //   func q() { let z = 0; func r() { print(z) }; g(r); } // error
  //
  // Since checking for disallowed cases requires access to captures,
  // it is simpler to defer diagnosing (to CSApply/SILGen) and return true here.
  case ConstraintKind::Conversion:
  case ConstraintKind::ArgumentConversion:
  case ConstraintKind::OperatorArgumentConversion:
    // For now, forbid conversion if representations match but cTypes differ.
    //
    // let f : @convention(c, cType: "id (*)(void) __attribute__((ns_returns_retained))")
    //           () -> AnyObject = ...
    // let _ : @convention(c, cType: "id (*)(void)")
    //           () -> AnyObject = f // error
    // let g : @convention(c, cType: "void (*)(void *)")
    //           (OpaquePointer?) -> () = ...
    // let _ : @convention(c, cType: "void (*)(MyCtx *)")
    //           (OpaquePointer?) -> () = g // error
    if ((rep1 == rep2) && clangTypeMismatch) {
      return false;
    }
    return true;

  case ConstraintKind::BridgingConversion:
  case ConstraintKind::ApplicableFunction:
  case ConstraintKind::DynamicCallableApplicableFunction:
  case ConstraintKind::BindOverload:
  case ConstraintKind::CheckedCast:
  case ConstraintKind::SubclassOf:
  case ConstraintKind::NonisolatedConformsTo:
  case ConstraintKind::ConformsTo:
  case ConstraintKind::TransitivelyConformsTo:
  case ConstraintKind::Defaultable:
  case ConstraintKind::Disjunction:
  case ConstraintKind::Conjunction:
  case ConstraintKind::DynamicTypeOf:
  case ConstraintKind::EscapableFunctionOf:
  case ConstraintKind::OpenedExistentialOf:
  case ConstraintKind::KeyPath:
  case ConstraintKind::KeyPathApplication:
  case ConstraintKind::LiteralConformsTo:
  case ConstraintKind::ForEachElement:
  case ConstraintKind::OptionalObject:
  case ConstraintKind::UnresolvedValueMember:
  case ConstraintKind::ValueMember:
  case ConstraintKind::OneWayEqual:
  case ConstraintKind::FallbackType:
  case ConstraintKind::UnresolvedMemberChainBase:
  case ConstraintKind::PropertyWrapper:
  case ConstraintKind::SyntacticElement:
  case ConstraintKind::BindTupleOfFunctionParams:
  case ConstraintKind::PackElementOf:
  case ConstraintKind::ShapeOf:
  case ConstraintKind::ExplicitGenericArguments:
  case ConstraintKind::SameShape:
  case ConstraintKind::MaterializePackExpansion:
  case ConstraintKind::LValueObject:
    return true;
  }

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

/// Check whether given parameter list represents a single tuple
/// or type variable which could be later resolved to tuple.
/// This is useful for SE-0110 related fixes in `matchFunctionTypes`.
static bool isSingleTupleParam(ASTContext &ctx,
                               ArrayRef<AnyFunctionType::Param> params) {
  if (params.size() != 1)
    return false;

  const auto &param = params.front();
  if ((param.isVariadic() || isPackExpansionType(param.getPlainType())) ||
      param.isInOut() || param.hasLabel() || param.isIsolated())
    return false;

  auto paramType = param.getPlainType();

  // Support following case which was allowed until 5:
  //
  // func bar(_: (Int, Int) -> Void) {}
  // let foo: ((Int, Int)?) -> Void = { _ in }
  //
  // bar(foo) // Ok
  if (!ctx.isLanguageModeAtLeast(LanguageMode::v5))
    paramType = paramType->lookThroughAllOptionalTypes();

  // Parameter type should either a tuple or something that can become a
  // tuple later on.
  return (paramType->is<TupleType>() || paramType->isTypeVariableOrMember());
}

static ConstraintFix *fixRequirementFailure(ConstraintSystem &cs, Type type1,
                                            Type type2, ASTNode anchor,
                                            ArrayRef<LocatorPathElt> path);

static ConstraintFix *fixRequirementFailure(ConstraintSystem &cs, Type type1,
                                            Type type2,
                                            ConstraintLocatorBuilder locator) {
  SmallVector<LocatorPathElt, 4> path;

  auto anchor = locator.getLocatorParts(path);
  return fixRequirementFailure(cs, type1, type2, anchor, path);
}

static FixImpact
assessRequirementFailureImpact(ConstraintSystem &cs, Type requirementType,
                               ConstraintLocatorBuilder locator) {
  assert(requirementType);

  auto impact = FixImpact::Mismatch;
  auto anchor = locator.getAnchor();
  if (!anchor)
    return impact;

  // If this requirement is associated with a member reference and it
  // was possible to check it before overload choice is bound, that means
  // types came from the context (most likely Self, or associated type(s))
  // and failing this constraint makes member unrelated/inaccessible, so
  // the impact has to be adjusted accordingly in order for this fix not to
  // interfere with other overload choices.
  //
  // struct S<T> {}
  // extension S where T == AnyObject { func foo() {} }
  //
  // func bar(_ s: S<Int>) { s.foo() }
  //
  // In this case `foo` is only accessible if T == `AnyObject`, which makes
  // fix for same-type requirement higher impact vs. requirement associated
  // with method itself e.g. `func foo<U>() -> U where U : P {}` because
  // `foo` is accessible from any `S` regardless of what `T` is.
  //
  // Don't add this impact with the others, as we want to keep it consistent
  // across requirement failures to present the user with a choice.
  if (isExpr<UnresolvedDotExpr>(anchor) ||
      isExpr<UnresolvedMemberExpr>(anchor)) {
    auto *calleeLoc = cs.getCalleeLocator(cs.getConstraintLocator(locator));
    if (!cs.findSelectedOverloadFor(calleeLoc))
      return FixImpact::InvalidAST;
  }

  if (auto *UDE = getAsExpr<UnresolvedDotExpr>(anchor)) {
    if (isResultBuilderMethodReference(cs.getASTContext(), UDE))
      return FixImpact::InvalidAST + 2;
  }

  auto resolvedTy = cs.simplifyType(requirementType);

  // Increase the impact of a conformance fix for generic parameters on
  // operators where such conformance failures are not as important as argument
  // mismatches or contextual failures.
  if (auto *ODRE = getAsExpr<OverloadedDeclRefExpr>(anchor)) {
    if (locator.isForRequirement(RequirementKind::Conformance) &&
        resolvedTy->is<ArchetypeType>() && ODRE->isForOperator()) {
      impact += 1;
    }
  }

  if (locator.isForRequirement(RequirementKind::Conformance)) {
    // Increase the impact of a conformance fix for a standard library
    // or foundation type, as it's unlikely to be a good suggestion.
    {
      if (resolvedTy->isStdlibType()) {
        impact += FixImpact::TypeMismatch;
      }

      if (auto *NTD = resolvedTy->getAnyNominal()) {
        if (getKnownFoundationEntity(NTD->getNameStr()))
          impact += FixImpact::TypeMismatch;
      }
    }

    // Also do the same for the builtin compiler types Any and AnyObject, but
    // bump the impact even higher as they cannot conform to protocols at all.
    if (resolvedTy->isAny() || resolvedTy->isAnyObject())
      impact += FixImpact::TypeMismatch * 2;
  }

  // If this requirement is associated with an overload choice let's
  // tie impact to how many times this requirement type is mentioned.
  if (auto *ODRE = getAsExpr<OverloadedDeclRefExpr>(anchor)) {
    if (auto *typeVar = requirementType->getAs<TypeVariableType>()) {
      unsigned choiceImpact = 0;
      if (auto choice = cs.findSelectedOverloadFor(ODRE)) {
        choice->adjustedOpenedType.visit([&](Type type) {
          if (type->isEqual(typeVar))
            ++choiceImpact;
        });
      }
      // If the type is used multiple times in the signature, increase the
      // impact for every additional use.
      if (choiceImpact > 1)
        impact += choiceImpact - 1;
    }
  }

  // If this requirement is associated with a call that is itself
  // incorrect, let's increase impact to indicate that this failure
  // has a compounding effect on viability of the overload choice it
  // comes from.
  if (locator.endsWith<LocatorPathElt::AnyRequirement>()) {
    if (auto *expr = getAsExpr(anchor)) {
      if (auto *call = getAsExpr<ApplyExpr>(cs.getParentExpr(expr))) {
        if (call->getFn() == expr &&
            llvm::any_of(cs.getFixes(), [&](const auto &fix) {
              return getAsExpr(fix->getAnchor()) == call;
            }))
          impact += 2;
      }
    }
  }

  return impact;
}

/// Attempt to fix missing arguments by introducing type variables
/// and inferring their types from parameters.
static bool fixMissingArguments(ConstraintSystem &cs, ASTNode anchor,
                                SmallVectorImpl<AnyFunctionType::Param> &args,
                                ArrayRef<AnyFunctionType::Param> params,
                                unsigned numMissing,
                                ConstraintLocatorBuilder locator) {
  assert(args.size() < params.size());

  auto &ctx = cs.getASTContext();
  // If there are N parameters but a single closure argument
  // (which might be anonymous), it's most likely used as a
  // tuple e.g. `$0.0`.
  std::optional<TypeBase *> argumentTuple;
  if (isSingleTupleParam(ctx, args)) {
    auto argType = args.back().getPlainType();
    // Let's unpack argument tuple into N arguments, this corresponds
    // to something like `foo { (bar: (Int, Int)) in }` where `foo`
    // has a single parameter of type `(Int, Int) -> Void`.
    if (auto *tuple = argType->getAs<TupleType>()) {
      args.pop_back();
      for (const auto &elt : tuple->getElements())
        args.emplace_back(elt.getType(), elt.getName());

      // If unpacking a tuple results in more arguments than parameters
      // it would be diagnosed as a general mismatch because it's unclear
      // whether it's a problem with missing or extraneous parameters.
      if (args.size() > params.size())
        return true;
    } else if (auto *typeVar = argType->getAs<TypeVariableType>()) {
      auto isParam = [](const Expr *expr) {
        if (auto *DRE = dyn_cast<DeclRefExpr>(expr)) {
          if (auto *decl = DRE->getDecl())
            return isa<ParamDecl>(decl);
        }
        return false;
      };

      // Something like `foo { x in }` or `foo { $0 }`
      if (auto *closure = getAsExpr<ClosureExpr>(anchor)) {
        cs.forEachExpr(closure, [&](Expr *expr) -> Expr * {
          if (auto *UDE = dyn_cast<UnresolvedDotExpr>(expr)) {
            if (!isParam(UDE->getBase()))
              return expr;

            auto name = UDE->getName().getBaseIdentifier();
            unsigned index = 0;
            if (!name.str().getAsInteger(10, index) ||
                llvm::any_of(params, [&](const AnyFunctionType::Param &param) {
                  return param.getLabel() == name;
                })) {
              argumentTuple.emplace(typeVar);
              args.pop_back();
              return nullptr;
            }
          }
          return expr;
        });
      }
    }
  }

  for (unsigned i = args.size(), n = params.size(); i != n; ++i) {
    auto *argLoc = cs.getConstraintLocator(
        anchor, LocatorPathElt::SynthesizedArgument(i));
    args.push_back(params[i].withType(
        cs.createTypeVariable(argLoc, TVO_CanBindToNoEscape)));
  }

  SmallVector<SynthesizedArg, 4> synthesizedArgs;
  synthesizedArgs.reserve(numMissing);
  for (unsigned i = args.size() - numMissing, n = args.size(); i != n; ++i) {
    synthesizedArgs.push_back(SynthesizedArg{i, args[i]});
  }

  // Treat missing anonymous arguments as valid in closures containing the
  // code completion location, since they may have just not been written yet.
  if (cs.isForCodeCompletion()) {
    if (auto *closure = getAsExpr<ClosureExpr>(anchor)) {
      if (cs.containsIDEInspectionTarget(closure) &&
          (closure->hasAnonymousClosureVars() ||
           (args.empty() && closure->getInLoc().isInvalid())))
          return false;
    }
  }

  auto *fix = AddMissingArguments::create(cs, synthesizedArgs,
                                          cs.getConstraintLocator(locator));
  if (cs.recordFix(fix))
    return true;

  // If the argument was a single "tuple", let's bind newly
  // synthesized arguments to it.
  if (argumentTuple) {
    // We can ignore parameter flags here as we're imploding a tuple for a
    // simulated ((X, Y, Z)) -> R to (X, Y, Z) -> R conversion. As such, this is
    // similar to e.g { x, y, z in fn((x, y, z)) }.
    cs.addConstraint(ConstraintKind::Bind, *argumentTuple,
                     FunctionType::composeTuple(
                         ctx, args, ParameterFlagHandling::IgnoreNonEmpty),
                     cs.getConstraintLocator(anchor));
  }

  return false;
}

static bool fixExtraneousArguments(ConstraintSystem &cs,
                                   FunctionType *contextualType,
                                   ArrayRef<AnyFunctionType::Param> args,
                                   int numExtraneous,
                                   ConstraintLocatorBuilder locator) {
  SmallVector<std::pair<unsigned, AnyFunctionType::Param>, 4> extraneous;

  for (unsigned i = args.size() - numExtraneous, n = args.size(); i != n; ++i) {
    extraneous.push_back({i, args[i]});
    if (auto *typeVar = args[i].getPlainType()->getAs<TypeVariableType>()) {
      cs.recordPotentialHole(typeVar);
    }
  }

  return cs.recordFix(
      RemoveExtraneousArguments::create(cs, contextualType, extraneous,
                                        cs.getConstraintLocator(locator)),
      /*impact=*/FixImpact::TypeMismatch * numExtraneous);
}

bool ConstraintSystem::hasPreconcurrencyCallee(
    ConstraintLocatorBuilder locator) {
  auto calleeLocator = getCalleeLocator(getConstraintLocator(locator));
  auto calleeOverload = findSelectedOverloadFor(calleeLocator);
  if (!calleeOverload || !calleeOverload->choice.isDecl())
    return false;

  return calleeOverload->choice.getDecl()->preconcurrency();
}

/// Match the throwing specifier of the two function types.
static ConstraintSystem::SolutionKind
matchFunctionThrowing(ConstraintSystem &cs,
                      FunctionType *func1, FunctionType *func2,
                      ConstraintKind kind,
                      ConstraintSystem::TypeMatchOptions flags,
                      ConstraintLocatorBuilder locator) {
  // A function type that throws the error type E1 is a subtype of a function
  // that throws error type E2 when E1 is a subtype of E2. For the purpose
  // of this comparison, a non-throwing function has thrown error type 'Never',
  // and an untyped throwing function has thrown error type 'any Error'.
  Type thrownError1 = func1->getEffectiveThrownErrorTypeOrNever();
  Type thrownError2 = func2->getEffectiveThrownErrorTypeOrNever();
  if (!thrownError1 || !thrownError2)
    return ConstraintSystem::SolutionKind::Solved;

  // An attempt to erase typed throws into `throws` requires a function
  // conversion and a heap allocation for the error type, so if there is an
  // overload that takes typed throws it should be preferred.
  if (func1->getThrownError() && !func2->getThrownError() &&
      thrownError2->isErrorExistentialType()) {
    cs.increaseScore(
        SK_FunctionConversion,
        locator.withPathElement(LocatorPathElt::ThrownErrorType()));
  }

  switch (compareThrownErrorsForSubtyping(thrownError1, thrownError2, cs.DC)) {
  case ThrownErrorSubtyping::DropsThrows: {
    // We need to drop 'throws' to make this work.
    if (!cs.shouldAttemptFixes())
      return ConstraintSystem::SolutionKind::Error;

    auto *fix = DropThrowsAttribute::create(cs, func1, func2,
                                            cs.getConstraintLocator(locator));
    if (cs.recordFix(fix))
      return ConstraintSystem::SolutionKind::Error;

    return ConstraintSystem::SolutionKind::Solved;
  }

  case ThrownErrorSubtyping::ExactMatch:
    return ConstraintSystem::SolutionKind::Solved;

  case ThrownErrorSubtyping::Subtype:
    // We know this is going to work, but we might still need to generate a
    // constraint if one of the error types involves type variables.
    if (thrownError1->hasTypeVariable() || thrownError2->hasTypeVariable()) {
      // Fall through to the dependent case.
    } else if (kind < ConstraintKind::Subtype) {
      // We aren't allowed to have a subtype, so fail here.
      return ConstraintSystem::SolutionKind::Error;
    } else {
      // We have a subtype. All set!
      return ConstraintSystem::SolutionKind::Solved;
    }
    LLVM_FALLTHROUGH;

  case ThrownErrorSubtyping::Dependent: {
    // The presence of type variables in the thrown error types require that
    // we generate a constraint to unify the thrown error types, so do so now.
    ConstraintKind subKind = (kind < ConstraintKind::Subtype)
        ? ConstraintKind::Equal
        : ConstraintKind::Subtype;
    const auto subflags = getDefaultDecompositionOptions(flags);
    auto result = cs.matchTypes(
        thrownError1, thrownError2,
        subKind, subflags,
        locator.withPathElement(LocatorPathElt::ThrownErrorType()));
    if (result == ConstraintSystem::SolutionKind::Error)
      return ConstraintSystem::SolutionKind::Error;

    return ConstraintSystem::SolutionKind::Solved;
  }

  case ThrownErrorSubtyping::Mismatch: {
    auto thrownErrorLocator = cs.getConstraintLocator(
        locator.withPathElement(LocatorPathElt::ThrownErrorType()));
    if (!cs.shouldAttemptFixes())
      return ConstraintSystem::SolutionKind::Error;

    auto *fix = IgnoreThrownErrorMismatch::create(
        cs, thrownError1, thrownError2, thrownErrorLocator);
    if (cs.recordFix(fix))
      return ConstraintSystem::SolutionKind::Error;

    return ConstraintSystem::SolutionKind::Solved;
  }
  }
}

ConstraintSystem::SolutionKind ConstraintSystem::matchFunctionSendability(
    FunctionType *func1, FunctionType *func2, ConstraintKind kind,
    ConstraintSystem::TypeMatchOptions flags,
    ConstraintLocatorBuilder locator) {
  auto formUnsolved = [&]() {
    // If we're supposed to generate constraints, do so.
    if (flags.contains(TMF_GenerateConstraints)) {
      auto *constraint = Constraint::create(*this, kind, func1, func2,
                                            getConstraintLocator(locator));
      addUnsolvedConstraint(constraint);
      return SolutionKind::Solved;
    }
    return SolutionKind::Unsolved;
  };

  // First check to see if we have any sendable dependent function types, if
  // any of them still have unresolved type variables we need to wait until
  // they're fully resolved.
  auto dep1 = func1->getSendableDependentType();
  if (dep1) {
    dep1 = simplifyType(dep1);
    if (dep1->hasTypeVariable())
      return formUnsolved();
  }
  auto dep2 = func2->getSendableDependentType();
  if (dep2) {
    dep2 = simplifyType(dep2);
    if (dep2->hasTypeVariable())
      return formUnsolved();
  }

  // Sendability is given by either the sendability of the dependent type if
  // present, otherwise it's given by the function itself.
  auto func1Sendable = dep1 ? isSendableCapture(dep1) : func1->isSendable();
  auto func2Sendable = dep2 ? isSendableCapture(dep2) : func2->isSendable();

  if (func1Sendable != func2Sendable) {
    // A @Sendable function can be a subtype of a non-@Sendable function.
    if (kind < ConstraintKind::Subtype || func2Sendable) {
      if (AddSendableAttribute::attempt(*this, kind, func1, func2, locator))
        return SolutionKind::Error;
    }
  }
  return SolutionKind::Solved;
}

static bool isWitnessMatching(ConstraintLocatorBuilder locator) {
  SmallVector<LocatorPathElt, 4> path;
  (void) locator.getLocatorParts(path);
  return (path.size() == 1 &&
          path[0].is<LocatorPathElt::Witness>());
}

bool
ConstraintSystem::matchFunctionIsolations(FunctionType *func1,
                                          FunctionType *func2,
                                          ConstraintKind kind,
                                          TypeMatchOptions flags,
                                          ConstraintLocatorBuilder locator) {
  auto isolation1 = func1->getIsolation(), isolation2 = func2->getIsolation();

  // If we have a difference in isolation kind, we need a conversion.
  // Make sure that we're looking for a conversion, and increase the
  // function-conversion score to make sure this solution is worse than
  // an exact match.
  // FIXME: there may be a better way. see https://github.com/apple/swift/pull/62514
  auto matchIfConversion = [&](bool isErasure = false,
                               bool shouldIncreaseScore = true) -> bool {
    // We generally require a conversion here, but allow some lassitude
    // if we're doing witness-matching.
    if (kind < ConstraintKind::Subtype &&
        !(isErasure && isWitnessMatching(locator)))
      return false;

    if (shouldIncreaseScore)
      increaseScore(SK_FunctionConversion, locator);
    return true;
  };

  switch (isolation2.getKind()) {

  // Converting to a non-isolated type.
  case FunctionTypeIsolation::Kind::NonIsolated:
    switch (isolation1.getKind()) {
    // Exact match.
    case FunctionTypeIsolation::Kind::NonIsolated:
      return true;

    // A thunk is going to pass `nil` to the isolated parameter.
    case FunctionTypeIsolation::Kind::NonIsolatedNonsending:
      return matchIfConversion();

    // Erasing global-actor isolation to non-isolation can admit data
    // races; such violations are diagnosed by the actor isolation checker.
    // We deliberately do not allow actor isolation violations to influence
    // overload resolution to preserve the property that an expression can
    // be re-checked against a different isolation context for isolation
    // violations.
    //
    // This also applies to @isolated(any) because we want to be able to
    // decide that we contextually isolated to the function's dynamic
    // isolation.
    case FunctionTypeIsolation::Kind::GlobalActor:
    case FunctionTypeIsolation::Kind::Erased:
      return matchIfConversion();

    // Parameter isolation is value-dependent and cannot be erased.
    case FunctionTypeIsolation::Kind::Parameter:
      return false;
    }
    llvm_unreachable("bad kind");

  // Converting to a caller isolated async function type.
  case FunctionTypeIsolation::Kind::NonIsolatedNonsending:
    switch (isolation1.getKind()) {
    // Exact match.
    case FunctionTypeIsolation::Kind::NonIsolatedNonsending:
      return true;

    // Global actor: Thunk will hop to the global actor
    // and would ignore passed in isolation.
    // Erased: Just like global actor but would hop to
    // the isolation stored in the @isolated(any) function.
    case FunctionTypeIsolation::Kind::GlobalActor:
    case FunctionTypeIsolation::Kind::Erased:
      return matchIfConversion();

    // In this case the isolation is dependent on a
    // specific actor passed in as the isolation parameter
    // and the thunk won't have it.
    case FunctionTypeIsolation::Kind::Parameter:
      return false;

    // For asynchronous: Thunk would hop the appropriate actor.
    // For synchronous: Thunk would call the function without
    // a hop.
    case FunctionTypeIsolation::Kind::NonIsolated:
      // When a nonisolated/@concurrent closure is passed to a
      // nonisolated(nonsending) parameter the solver has to be conservative
      // and produce a conversion that could be dissolved by actor
      // isolation checker when it determines that nonisolated(nonsending)
      // could be assumed onto the closure direct (i.e. when there are no
      // isolated captures involved), so let's not increase a score unless
      // closure states it's isolation explicitly to avoid picking a subpar
      // solution.
      bool shouldIncreaseScore = true;
      if (func1->isAsync()) {
        auto *expr = locator.trySimplifyToExpr();

        if (expr)
          expr = expr->getSemanticsProvidingExpr();

        if (auto *cast = getAsExpr<ExplicitCastExpr>(expr))
          expr = cast->getSubExpr();

        // Look through captures, we only care about the closure itself.
        if (auto *captureList = getAsExpr<CaptureListExpr>(expr))
          expr = captureList->getClosureBody();

        if (auto *closure = getAsExpr<ClosureExpr>(expr)) {
          shouldIncreaseScore =
              closure->getAttrs().hasAttribute<ConcurrentAttr>();
        }
      } else {
        // Converting a sync function to nonisolated(nonsending) async one
        // doesn't require a thunk.
        shouldIncreaseScore = false;
      }

      return matchIfConversion(
          /*isErasure=*/false, shouldIncreaseScore);
    }
    llvm_unreachable("bad kind");

  // Converting to a global-actor-isolated type.
  case FunctionTypeIsolation::Kind::GlobalActor:
    switch (isolation1.getKind()) {
    // Both types are global-actor-isolated.  We *could* allow this as a
    // conversion even for different global actors if the destination type
    // is async, but we've decided we don't want to as a policy.
    case FunctionTypeIsolation::Kind::GlobalActor: {
      const auto subflags = getDefaultDecompositionOptions(flags);
      auto result = matchTypes(
          isolation1.getGlobalActorType(), isolation2.getGlobalActorType(),
          ConstraintKind::Equal, subflags,
          locator.withPathElement(LocatorPathElt::GlobalActorType()));
      return result != SolutionKind::Error;
    }

    // Adding global actor isolation to a non-isolated function is fine,
    // whether synchronous or asynchronous.
    case FunctionTypeIsolation::Kind::NonIsolated:
      return matchIfConversion();

    // A thunk is going to pass in an instance of a global actor
    // to the isolated parameter.
    case FunctionTypeIsolation::Kind::NonIsolatedNonsending:
      return matchIfConversion();

    // Parameter isolation cannot be altered in the same way.
    case FunctionTypeIsolation::Kind::Parameter:
      return false;

    // Don't allow dynamically-isolated function types to convert to
    // any specific isolation for the same policy reasons that we don't
    // want to allow global-actors to change.
    case FunctionTypeIsolation::Kind::Erased:
      return false;
    }
    llvm_unreachable("bad kind");

  // Converting to a parameter-isolated type.
  case FunctionTypeIsolation::Kind::Parameter:
    switch (isolation1.getKind()) {
    // Exact match.  We'll check that the isolated parameters match up later,
    // when we're looking at the parameters.
    case FunctionTypeIsolation::Kind::Parameter:
      return true;

    // Adding global actor isolation to a non-isolated function is fine,
    // whether synchronous or asynchronous.
    case FunctionTypeIsolation::Kind::NonIsolated:
    case FunctionTypeIsolation::Kind::GlobalActor:
      return matchIfConversion();

    // A thunk is going to forward the isolation.
    case FunctionTypeIsolation::Kind::NonIsolatedNonsending:
      return matchIfConversion();

    // Don't allow dynamically-isolated function types to convert to
    // any specific isolation for the same policy reasons that we don't
    // want to allow global-actors to change.
    case FunctionTypeIsolation::Kind::Erased:
      return false;
    }
    llvm_unreachable("bad kind");

  case FunctionTypeIsolation::Kind::Erased:
    switch (isolation1.getKind()) {
    // Exact match.
    case FunctionTypeIsolation::Kind::Erased:
      return true;

    // We can statically erase any kind of static isolation to dynamic
    // isolation as a conversion.
    case FunctionTypeIsolation::Kind::NonIsolated:
    case FunctionTypeIsolation::Kind::GlobalActor:
      return matchIfConversion(/*erasure*/ true);

    // It's not possible to form a thunk for this case because
    // we don't know what to pass to the isolated parameter.
    case FunctionTypeIsolation::Kind::NonIsolatedNonsending:
      return false;

    // Parameter isolation is value-dependent and can't be erased in the
    // abstract, though.  We need to be able to recover the isolation from
    // a value.
    case FunctionTypeIsolation::Kind::Parameter:
      return false;
    }
    llvm_unreachable("bad kind");
  }
  llvm_unreachable("bad kind");
}

bool ConstraintSystem::matchFunctionLifetimes(
    const LifetimeDependentInterface &from,
    const LifetimeDependentInterface &to, ConstraintLocatorBuilder locator) {

  // A function with a lifetime dependency in a generic context is equivalent to
  // one without that lifetime dependency when the substituted type is
  // Escapable.
  //
  // There is also a subtype relationship from less-constrained to
  // more-constrained lifetime dependencies (see
  // LifetimeDependentInterface::canConvertTo).
  if (!from.canConvertTo(to)) {
    auto escapable = getASTContext()
                         .getProtocol(KnownProtocolKind::Escapable)
                         ->getDeclaredType();

    for (auto &fromDep : from.getLifetimeDependencies()) {
      // If a dependency is present for the same target in both types, then
      // the dependency must match.
      if (to.hasTarget(fromDep.getTargetIndex())) {
        if (!from.canConvertTargetTo(fromDep, to))
          return false;
        continue;
      }

      // If the dependency is absent from the destination type, constrain the
      // corresponding parameter or result in the source type to be Escapable.
      addConstraint(ConstraintKind::ConformsTo,
                    from.getSourceOrTargetType(fromDep.getTargetIndex()),
                    escapable, locator);
    }
  }
  return true;
}

ConstraintSystem::SolutionKind
ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
                                     ConstraintKind kind, TypeMatchOptions flags,
                                     ConstraintLocatorBuilder locator) {
  // If the locator is for a @Sendable match, that's all we want to do.
  if (auto last = locator.last()) {
    if (last->is<LocatorPathElt::FunctionSendability>())
      return matchFunctionSendability(func1, func2, kind, flags, locator);
  }

  // Match the 'throws' effect.
  SolutionKind throwsResult =
      matchFunctionThrowing(*this, func1, func2, kind, flags, locator);
  if (throwsResult == SolutionKind::Error)
    return throwsResult;

  // A synchronous function can be a subtype of an 'async' function.
  if (func1->isAsync() != func2->isAsync()) {
    // Cannot drop 'async'.
    if (func1->isAsync() || kind < ConstraintKind::Subtype) {
      if (!shouldAttemptFixes())
        return SolutionKind::Error;

      auto *fix = DropAsyncAttribute::create(*this, func1, func2,
                                             getConstraintLocator(locator));
      if (recordFix(fix))
        return SolutionKind::Error;
    }

    bool forClosureInArgumentPosition =
        locator.endsWith<LocatorPathElt::ApplyArgToParam>() &&
        isa<ClosureExpr>(locator.trySimplifyToExpr());

    // Since it's possible to infer `async` from the body of a
    // closure, score for sync -> async mismatch is increased
    // while solver is matching arguments to parameters to
    // indicate than solution with such a mismatch is always
    // worse than one with synchronous functions on both sides.
    if (!forClosureInArgumentPosition)
      increaseScore(SK_SyncInAsync, locator);
  }

  if (func1->isCalledOnce() != func2->isCalledOnce()) {
    if (func1->isCalledOnce() ||
        (kind < ConstraintKind::Subtype && !isWitnessMatching(locator))) {
      if (!shouldAttemptFixes())
        return SolutionKind::Error;

      auto *fix = ExecutionSemanticsMismatch::create(
          *this, func1, func2, getConstraintLocator(locator));
      if (recordFix(fix, FixImpact::FunctionTypeMismatch))
        return SolutionKind::Error;
    }

    increaseScore(SK_FunctionConversion, locator);
  }

  // Match @Sendable.
  auto sendableResult = matchFunctionSendability(
      func1, func2, kind, TMF_GenerateConstraints,
      locator.withPathElement(ConstraintLocator::FunctionSendability));
  if (sendableResult == SolutionKind::Error)
    return sendableResult;

  // A non-@noescape function type can be a subtype of a @noescape function
  // type.
  if (func1->isNoEscape() != func2->isNoEscape() &&
      (func1->isNoEscape() || kind < ConstraintKind::Subtype)) {
    if (!shouldAttemptFixes())
      return SolutionKind::Error;

    auto *fix = MarkExplicitlyEscaping::create(*this, func1, func2,
                                               getConstraintLocator(locator));

    if (recordFix(fix))
      return SolutionKind::Error;
  }

  // () -> sending T can be a subtype of () -> T... but not vis-a-versa.
  if (func1->hasSendingResult() != func2->hasSendingResult() &&
      (!func1->hasSendingResult() || kind < ConstraintKind::Subtype)) {
    auto *fix = AllowSendingMismatch::create(*this, func1, func2,
                                             getConstraintLocator(locator));
    if (recordFix(fix))
      return SolutionKind::Error;
  }

  if (!matchFunctionIsolations(func1, func2, kind, flags, locator))
    return SolutionKind::Error;

  if (!matchFunctionLifetimes(func1, func2, locator))
    return SolutionKind::Error;

  // To contextual type increase the score to avoid ambiguity when solver can
  // find more than one viable binding different only in representation e.g.
  //   let _: (@convention(block) () -> Void)? = Bool.random() ? nil : {}
  // so same representation should be always favored.
  auto loc = getConstraintLocator(locator);
  if (loc->findLast<LocatorPathElt::ContextualType>() &&
      func1->getRepresentation() != func2->getRepresentation()) {
    increaseScore(SK_FunctionConversion, locator);
  }

  if (!matchFunctionRepresentations(func1->getExtInfo(), func2->getExtInfo(),
                                    kind, Options)) {
    return SolutionKind::Error;
  }

  // Determine how we match up the input/result types.
  ConstraintKind subKind;
  switch (kind) {
  case ConstraintKind::Bind:
  case ConstraintKind::BindParam:
  case ConstraintKind::BindToPointerType:
  case ConstraintKind::Equal:
    subKind = kind;
    break;

  case ConstraintKind::Subtype:
  case ConstraintKind::Conversion:
  case ConstraintKind::ArgumentConversion:
  case ConstraintKind::OperatorArgumentConversion:
    subKind = ConstraintKind::Subtype;
    break;

  case ConstraintKind::ApplicableFunction:
  case ConstraintKind::DynamicCallableApplicableFunction:
  case ConstraintKind::BindOverload:
  case ConstraintKind::CheckedCast:
  case ConstraintKind::SubclassOf:
  case ConstraintKind::NonisolatedConformsTo:
  case ConstraintKind::ConformsTo:
  case ConstraintKind::TransitivelyConformsTo:
  case ConstraintKind::Defaultable:
  case ConstraintKind::Disjunction:
  case ConstraintKind::Conjunction:
  case ConstraintKind::DynamicTypeOf:
  case ConstraintKind::EscapableFunctionOf:
  case ConstraintKind::OpenedExistentialOf:
  case ConstraintKind::KeyPath:
  case ConstraintKind::KeyPathApplication:
  case ConstraintKind::LiteralConformsTo:
  case ConstraintKind::ForEachElement:
  case ConstraintKind::OptionalObject:
  case ConstraintKind::UnresolvedValueMember:
  case ConstraintKind::ValueMember:
  case ConstraintKind::BridgingConversion:
  case ConstraintKind::OneWayEqual:
  case ConstraintKind::FallbackType:
  case ConstraintKind::UnresolvedMemberChainBase:
  case ConstraintKind::PropertyWrapper:
  case ConstraintKind::SyntacticElement:
  case ConstraintKind::BindTupleOfFunctionParams:
  case ConstraintKind::PackElementOf:
  case ConstraintKind::ShapeOf:
  case ConstraintKind::ExplicitGenericArguments:
  case ConstraintKind::SameShape:
  case ConstraintKind::MaterializePackExpansion:
  case ConstraintKind::LValueObject:
    llvm_unreachable("Not a relational constraint");
  }

  // Input types can be contravariant (or equal).
  auto argumentLocator =
      locator.withPathElement(ConstraintLocator::FunctionArgument);

  TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);

  SmallVector<AnyFunctionType::Param, 8> func1Params;
  func1Params.append(func1->getParams().begin(), func1->getParams().end());

  SmallVector<AnyFunctionType::Param, 8> func2Params;
  func2Params.append(func2->getParams().begin(), func2->getParams().end());

  // Support conversion from `nonisolated(nonsending)` to a function type
  // with an isolated parameter.
  if (subKind == ConstraintKind::Subtype &&
      func1->getIsolation().isNonisolatedNonsending() &&
      func2->getIsolation().isParameter()) {
    // `nonisolated(nonsending)` function gets an implicit isolation parameter
    // introduced during SILGen and thunk is going to forward an isolation from
    // the caller to it.
    // Let's remove the isolated parameter from consideration, function
    // types have to match on everything else.
    llvm::erase_if(func2Params, [](const AnyFunctionType::Param &param) {
      return param.isIsolated();
    });
  }

  // Add a very narrow exception to SE-0110 by allowing functions that
  // take multiple arguments to be passed as an argument in places
  // that expect a function that takes a single tuple (of the same
  // arity);
  auto canImplodeParams = [&](ArrayRef<AnyFunctionType::Param> params,
                              const FunctionType *destFn) {
    if (params.size() == 1)
      return false;

    // We do not support imploding into a @differentiable function.
    if (destFn->isDifferentiable())
      return false;

    for (auto &param : params) {
      // We generally cannot handle parameter flags, though we can carve out an
      // exception for ownership flags such as __owned, which we can thunk, and
      // flags that can freely dropped from a function type such as
      // @_nonEphemeral. Note that @noDerivative can also be freely dropped, as
      // we've already ensured that the destination function is not
      // @differentiable.
      auto flags = param.getParameterFlags();
      flags = flags.withOwnershipSpecifier(
          param.isInOut() ? ParamSpecifier::InOut : ParamSpecifier::Default);
      flags = flags.withNonEphemeral(false)
                   .withNoDerivative(false);
      if (!flags.isNone())
        return false;
    }
    return true;
  };

  auto implodeParams = [&](SmallVectorImpl<AnyFunctionType::Param> &params) {
    // Form an imploded tuple type, dropping the parameter flags as although
    // canImplodeParams makes sure we're not dealing with vargs, inout, etc,
    // we may still have e.g ownership flags left over, which we can drop.
    auto input = AnyFunctionType::composeTuple(
        getASTContext(), params, ParameterFlagHandling::IgnoreNonEmpty);
    params.clear();
    // If fixes are disabled let's do an easy thing and implode
    // tuple directly into parameters list.
    if (!shouldAttemptFixes()) {
      params.emplace_back(input);
      return;
    }

    // Synthesize new argument and bind it to tuple formed from existing
    // arguments, this makes it easier to diagnose cases where we attempt
    // a single tuple element formed when no arguments were present.
    auto argLoc = argumentLocator.withPathElement(
        LocatorPathElt::SynthesizedArgument(0));
    auto *typeVar = createTypeVariable(getConstraintLocator(argLoc),
                                       TVO_CanBindToNoEscape);
    params.emplace_back(typeVar);
    assignFixedType(typeVar, input);
  };

  {
    SmallVector<LocatorPathElt, 4> path;
    locator.getLocatorParts(path);

    // Find the last path element, skipping OptionalInjection elements
    // so that we allow this exception in cases of optional injection.
    auto last = std::find_if(
        path.rbegin(), path.rend(), [](LocatorPathElt &elt) -> bool {
          return elt.getKind() != ConstraintLocator::OptionalInjection;
        });

    auto &ctx = getASTContext();
    if (last != path.rend()) {
      if (last->getKind() == ConstraintLocator::ApplyArgToParam) {
        if (isSingleTupleParam(ctx, func2Params) &&
            canImplodeParams(func1Params, /*destFn*/ func2)) {
          implodeParams(func1Params);
          increaseScore(SK_FunctionConversion, locator);
        } else if (!ctx.isLanguageModeAtLeast(LanguageMode::v5) &&
                   isSingleTupleParam(ctx, func1Params) &&
                   canImplodeParams(func2Params,  /*destFn*/ func1)) {
          auto *simplified = locator.trySimplifyToExpr();
          // We somehow let tuple unsplatting function conversions
          // through in some cases in Swift 4, so let's let that
          // continue to work, but only for Swift 4.
          if (simplified &&
              (isa<DeclRefExpr>(simplified) ||
               isa<OverloadedDeclRefExpr>(simplified) ||
               isa<UnresolvedDeclRefExpr>(simplified))) {
            implodeParams(func2Params);
            increaseScore(SK_FunctionConversion, locator);
          }
        }
      } else if (last->is<LocatorPathElt::PatternMatch>() &&
                 isa<EnumElementPattern>(
                     last->castTo<LocatorPathElt::PatternMatch>()
                         .getPattern())) {
        // A single paren pattern becomes a labeled tuple pattern
        // e.g. `case .test(let value):` should be able to match
        // `case test(result: Int)`. Note that it also means that:
        // `cast test(result: (String, Int))` would be matched against
        // e.g. `case .test((let x, let y))` but that fails during
        // pattern coercion (behavior consistent with what happens in
        // `TypeCheckPattern`).
        if (func1Params.size() == 1 && !func1Params.front().hasLabel() &&
            func2Params.size() == 1 && func2Params.front().hasLabel()) {
          auto param = func1Params.front();
          auto label = func2Params.front().getLabel();

          auto labeledParam = FunctionType::Param(param.getPlainType(), label,
                                                  param.getParameterFlags());

          func1Params.clear();
          func1Params.push_back(labeledParam);
        }

        // Consider following example:
        //
        // enum E {
        //   case foo((x: Int, y: Int))
        //   case bar(x: Int, y: Int)
        // }
        //
        // func test(e: E) {
        //   if case .foo(let x, let y) = e {}
        //   if case .bar(let tuple) = e {}
        // }
        //
        // Both of `if case` expressions have to be supported:
        //
        // 1. `case .foo(let x, let y) = e` allows a single tuple
        //    parameter to be "destructured" into multiple arguments.
        //
        // 2. `case .bar(let tuple) = e` allows to match multiple
        //    parameters with a single tuple argument.
        if (isSingleTupleParam(ctx, func1Params) &&
            canImplodeParams(func2Params, /*destFn*/ func1)) {
          implodeParams(func2Params);
          increaseScore(SK_FunctionConversion, locator);
        } else if (isSingleTupleParam(ctx, func2Params) &&
                   canImplodeParams(func1Params, /*destFn*/ func2)) {
          implodeParams(func1Params);
          increaseScore(SK_FunctionConversion, locator);
        }
      }
    }

    if (shouldAttemptFixes()) {
      auto *anchor = locator.trySimplifyToExpr();
      if (isa_and_nonnull<ClosureExpr>(anchor) &&
          isSingleTupleParam(ctx, func2Params) &&
          canImplodeParams(func1Params, /*destFn*/ func2)) {
        auto *fix = AllowClosureParamDestructuring::create(
            *this, func2, getConstraintLocator(anchor));
        if (recordFix(fix))
          return SolutionKind::Error;

        implodeParams(func1Params);
      }
    }
  }

  // https://github.com/apple/swift/issues/49345
  // Add a super-narrow hack to allow '(()) -> T' to be passed in place
  // of '() -> T'.
  if (getASTContext().isLanguageModeAtLeast(LanguageMode::v4) &&
      !getASTContext().isLanguageModeAtLeast(LanguageMode::v5)) {
    SmallVector<LocatorPathElt, 4> path;
    locator.getLocatorParts(path);

    // Find the last path element, skipping GenericArgument elements
    // so that we allow this exception in cases of optional types, and
    // skipping OptionalInjection elements so that we allow this
    // exception in cases of optional injection.
    auto last = std::find_if(
        path.rbegin(), path.rend(), [](LocatorPathElt &elt) -> bool {
          return elt.getKind() != ConstraintLocator::GenericArgument &&
                 elt.getKind() != ConstraintLocator::OptionalInjection;
        });

    if (last != path.rend()) {
      if (last->getKind() == ConstraintLocator::ApplyArgToParam) {
        if (isSingleTupleParam(getASTContext(), func1Params) &&
            func1Params[0].getOldType()->isVoid()) {
          if (func2Params.empty()) {
            func2Params.emplace_back(getASTContext().TheEmptyTupleType);
          }
        }
      }
    }
  }

  // FIXME: ParamPackMatcher should completely replace the non-variadic
  // case too eventually.
  if (containsPackExpansionType(func1Params) ||
      containsPackExpansionType(func2Params)) {
    ParamPackMatcher matcher(func1Params, func2Params, getASTContext(),
                             isPackExpansionType);
    if (matcher.match())
      return SolutionKind::Error;

    for (auto pair : matcher.pairs) {
      // Compare the parameter types, taking contravariance into account.
      auto result = matchTypes(pair.rhs, pair.lhs, subKind, subflags,
                               (func1Params.size() == 1
                                ? argumentLocator
                                : argumentLocator.withPathElement(
                                  LocatorPathElt::TupleElement(pair.lhsIdx))));
      if (result == SolutionKind::Error)
        return result;
    }
  } else {
    int diff = func1Params.size() - func2Params.size();
    if (diff != 0) {
      if (!shouldAttemptFixes())
        return SolutionKind::Error;

      auto *loc = getConstraintLocator(locator);

      // If this is conversion between optional (or IUO) parameter
      // and argument, let's drop the last path element so locator
      // could be simplified down to an argument expression.
      //
      // func foo(_: ((Int, Int) -> Void)?) {}
      // _ = foo { _ in } <- missing second closure parameter.
      if (loc->isLastElement<LocatorPathElt::OptionalInjection>()) {
        auto path = loc->getPath();
        loc = getConstraintLocator(loc->getAnchor(), path.drop_back());
      }

      auto anchor = simplifyLocatorToAnchor(loc);
      if (!anchor)
        return SolutionKind::Error;

      // The param diff is in a function type coercion context
      //
      // func fn(_: Int) {}
      // let i: Int = 0
      // (fn as (Int, Int) -> Void)(i, i)
      //
      // Since we are not in a function argument application, simply record
      // a function type mismatch instead of an argument fix.
      // Except for when a closure is a subexpr because closure expr parameters
      // syntax can be added or removed by missing/extraneous arguments fix.
      if (loc->isForCoercion() && !isExpr<ClosureExpr>(anchor)) {
        auto *fix = ContextualMismatch::create(*this, func1, func2, loc);
        if (recordFix(fix))
          return SolutionKind::Error;
      } else {
        // If there are missing arguments, let's add them
        // using parameter as a template.
        if (diff < 0) {
          if (fixMissingArguments(*this, anchor, func1Params, func2Params,
                                  abs(diff), loc))
            return SolutionKind::Error;
        } else {
          // If there are extraneous arguments, let's remove
          // them from the list.
          if (fixExtraneousArguments(*this, func2, func1Params, diff, loc))
            return SolutionKind::Error;
        }
      }

      if (diff > 0) {
        // Drop all of the extraneous arguments.
        auto numParams = func2Params.size();
        func1Params.erase(func1Params.begin() + numParams, func1Params.end());
      }
    }

    bool hasLabelingFailures = false;
    for (unsigned i : indices(func1Params)) {
      auto func1Param = func1Params[i];
      auto func2Param = func2Params[i];

      // Increase the score if matching an autoclosure parameter to an function
      // type, so we enforce that non-autoclosure overloads are preferred.
      //
      //   func autoclosure(f: () -> Int) { }
      //   func autoclosure(f: @autoclosure () -> Int) { }
      //
      //   let _ = autoclosure as (() -> (Int)) -> () // non-autoclosure preferred
      //
      auto isAutoClosureFunctionMatch = [](AnyFunctionType::Param &param1,
                                           AnyFunctionType::Param &param2) {
        return param1.isAutoClosure() &&
               (!param2.isAutoClosure() &&
                param2.getPlainType()->is<FunctionType>());
      };

      if (isAutoClosureFunctionMatch(func1Param, func2Param) ||
          isAutoClosureFunctionMatch(func2Param, func1Param)) {
        increaseScore(SK_FunctionToAutoClosureConversion, locator);
      }

      // Variadic bit must match.
      if (func1Param.isVariadic() != func2Param.isVariadic()) {
        if (!(shouldAttemptFixes() && func2Param.isVariadic()))
          return SolutionKind::Error;

        auto argType =
            getFixedTypeRecursive(func1Param.getPlainType(), /*wantRValue=*/true);
        auto varargsType = func2Param.getPlainType();

        // Delay solving this constraint until argument is resolved.
        if (argType->is<TypeVariableType>()) {
          addUnsolvedConstraint(Constraint::create(
              *this, kind, func1, func2, getConstraintLocator(locator)));
          return SolutionKind::Solved;
        }

        auto *fix = ExpandArrayIntoVarargs::attempt(
            *this, argType, varargsType,
            argumentLocator.withPathElement(LocatorPathElt::ApplyArgToParam(
                i, i, func2Param.getParameterFlags())));

        if (!fix || recordFix(fix))
          return SolutionKind::Error;

        continue;
      }

      // Labels must match.
      //
      // FIXME: We should not end up with labels here at all, but we do
      // from invalid code in diagnostics, and as a result of code completion
      // directly building constraint systems.
      if (func1Param.getLabel() != func2Param.getLabel()) {
        if (!shouldAttemptFixes())
          return SolutionKind::Error;

        // If we are allowed to attempt fixes, let's ignore labeling
        // failures, and create a fix to re-label arguments if types
        // line up correctly.
        hasLabelingFailures = true;
      }

      // "isolated" can be added as a subtype relation, but otherwise must match.
      if (func1Param.isIsolated() != func2Param.isIsolated() &&
          !(func2Param.isIsolated() && subKind >= ConstraintKind::Subtype)) {
        return SolutionKind::Error;
      }

      // If functions are differentiable, ensure that @noDerivative is not
      // discarded.
      if (func1->isDifferentiable() && func2->isDifferentiable() &&
          func1Param.isNoDerivative() && !func2Param.isNoDerivative()) {
        return SolutionKind::Error;
      }

      // Do not allow for functions that expect a sending parameter to match
      // with a function that expects a non-sending parameter.
      if (func1Param.getParameterFlags().isSending() &&
          !func2Param.getParameterFlags().isSending()) {
        auto *fix = AllowSendingMismatch::create(
            *this, func1, func2, getConstraintLocator(argumentLocator));
        if (recordFix(fix))
          return SolutionKind::Error;
      }

      // FIXME: We should check value ownership too, but it's not completely
      // trivial because of inout-to-pointer conversions.

      // Compare the parameter types, taking contravariance into account.
      auto result = matchTypes(
          func2Param.getOldType(), func1Param.getOldType(), subKind, subflags,
          (func1Params.size() == 1 ? argumentLocator
                                   : argumentLocator.withPathElement(
                                         LocatorPathElt::TupleElement(i))));
      if (result == SolutionKind::Error)
        return result;
    }

    if (hasLabelingFailures && !hasFixFor(loc)) {
      ConstraintFix *fix =
          loc->isLastElement<LocatorPathElt::ApplyArgToParam>()
              ? AllowArgumentMismatch::create(*this, func1, func2, loc)
              : ContextualMismatch::create(*this, func1, func2, loc);

      if (recordFix(fix))
        return SolutionKind::Error;
    }
  }

  // Result type can be covariant (or equal).
  return matchTypes(func1->getResult(), func2->getResult(), subKind,
                    subflags,
                    locator.withPathElement(ConstraintLocator::FunctionResult));
}

ConstraintSystem::SolutionKind
ConstraintSystem::matchSuperclassTypes(Type type1, Type type2,
                                       TypeMatchOptions flags,
                                       ConstraintLocatorBuilder locator) {
  TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);

  auto classDecl2 = type2->getClassOrBoundGenericClass();
  SmallPtrSet<ClassDecl *, 4> superclasses1;
  for (auto super1 = type1->getSuperclass();
       super1;
       super1 = super1->getSuperclass()) {
    auto superclass1 = super1->getClassOrBoundGenericClass();
    if (superclass1 != classDecl2) {
      // Break if we have circular inheritance.
      if (superclass1 && !superclasses1.insert(superclass1).second)
        break;

      continue;
    }

    return matchTypes(super1, type2, ConstraintKind::Bind,
                      subflags, locator);
  }

  return SolutionKind::Error;
}

static ConstraintSystem::SolutionKind matchDeepTypeArguments(
    ConstraintSystem &cs, ConstraintSystem::TypeMatchOptions subflags,
    ArrayRef<Type> args1, ArrayRef<Type> args2,
    ConstraintLocatorBuilder locator,
    llvm::function_ref<void(unsigned)> recordMismatch = [](unsigned) {}) {
  if (args1.size() != args2.size()) {
    return ConstraintSystem::SolutionKind::Error;
  }

  auto allMatch = ConstraintSystem::SolutionKind::Solved;
  for (unsigned i = 0, n = args1.size(); i != n; ++i) {
    auto result = cs.matchTypes(
        args1[i], args2[i], ConstraintKind::Bind, subflags,
        locator.withPathElement(LocatorPathElt::GenericArgument(i)));

    if (result == ConstraintSystem::SolutionKind::Error) {
      recordMismatch(i);
      allMatch = result;
    }
  }

  return allMatch;
}

/// Allow `any Sendable` to match `Any` constraint while matching
/// generic arguments i.e. `[any Sendable]` -> `[Any]` when `any Sendable`
/// type comes from context that involves `@preconcurrency` declarations
/// in non-strict concurrency compiler mode.
///
/// Note that it's currently impossible to figure out precisely
/// where `any Sendable` type came from.
static bool matchSendableExistentialToAnyInGenericArgumentPosition(
    ConstraintSystem &cs, Type lhs, Type rhs,
    ConstraintLocatorBuilder locator) {
  // Avoid heavier checks if are not `any Sendable` and `Any`.
  if (!(lhs->isSendableExistential() || lhs->isAny()) ||
      !(rhs->isSendableExistential() || rhs->isAny()))
    return false;

  auto last = locator.last();
  // `any Sendable` -> `Any` conversion is allowed for generic arguments
  // and for function argument/result positions if generic argument is
  // bound to a function type.
  if (!last || !(last->is<LocatorPathElt::GenericArgument>() ||
                 last->is<LocatorPathElt::FunctionArgument>() ||
                 last->is<LocatorPathElt::FunctionResult>()))
    return false;

  SmallVector<LocatorPathElt, 4> path;
  auto anchor = locator.getLocatorParts(path);

  {
    std::optional<unsigned> dropFromIdx;
    bool inGenericArgumentContext = false;

    for (unsigned i = 0, n = path.size(); i < n; ++i) {
      const auto &elt = path[i];
      if (elt.is<LocatorPathElt::GenericType>() ||
          elt.is<LocatorPathElt::LValueConversion>()) {
        if (!dropFromIdx)
          dropFromIdx = i;
        continue;
      }

      if (elt.is<LocatorPathElt::GenericArgument>()) {
        inGenericArgumentContext = true;
        continue;
      }

      // For example: `[(any Sendable) -> Void]` -> `[(Any) -> Void]`
      if (elt.is<LocatorPathElt::FunctionArgument>()) {
        if (inGenericArgumentContext) {
          // `matchFunctionTypes` accounts for contravariance even under
          // equality constraint (because it shouldn't matter), but it does
          // in this case.
          std::swap(lhs, rhs);
        }
      }
    }

    // If we are not in generic argument context,
    // this conversion don't apply.
    if (!inGenericArgumentContext || !dropFromIdx)
      return false;

    // Drop all of the elements that would get in a way of
    // finding the underlying declaration reference first.
    path.pop_back_n(path.size() - *dropFromIdx);
  }

  if (!(lhs->isSendableExistential() && rhs->isAny()))
    return false;

  std::function<bool(ConstraintLocator *)> isPreconcurrencyContext =
      [&](ConstraintLocator *locator) {
        if (locator->isLastElement<LocatorPathElt::ApplyArgToParam>())
          return isPreconcurrencyContext(
              cs.getConstraintLocator(simplifyLocatorToAnchor(locator)));

        if (locator->directlyAt<InOutExpr>()) {
          auto *IOE = castToExpr<InOutExpr>(locator->getAnchor());
          return isPreconcurrencyContext(
              cs.getConstraintLocator(IOE->getSubExpr()));
        }

        auto *calleeLoc = cs.getCalleeLocator(locator);
        if (!calleeLoc)
          return false;

        auto selectedOverload = cs.findSelectedOverloadFor(calleeLoc);
        if (!(selectedOverload && selectedOverload->choice.isDecl()))
          return false;

        if (!selectedOverload->choice.getDecl()->preconcurrency()) {
          // If the member is not preconcurrency, its base could be.
          if (auto *UDE =
                  getAsExpr<UnresolvedDotExpr>(calleeLoc->getAnchor())) {
            return isPreconcurrencyContext(
                cs.getConstraintLocator(UDE->getBase()));
          }
          if (auto *SE = getAsExpr<SubscriptExpr>(calleeLoc->getAnchor())) {
            return isPreconcurrencyContext(
                cs.getConstraintLocator(SE->getBase()));
          }
          return false;
        }

        return true;
      };

  if (!isPreconcurrencyContext(cs.getConstraintLocator(anchor, path)))
    return false;

  // Increase the score to make sure that if there is an overload that
  // uses `any Sendable` it would be preferred.
  cs.increaseScore(SK_EmptyExistentialConversion, locator);
  return true;
}

ConstraintSystem::SolutionKind
ConstraintSystem::matchDeepEqualityTypes(Type type1, Type type2,
                                         ConstraintLocatorBuilder locator) {
  TypeMatchOptions subflags = TMF_GenerateConstraints;

  // Handle opaque archetypes.
  if (auto opaque1 = type1->getAs<OpaqueTypeArchetypeType>()) {
    auto opaque2 = type2->castTo<OpaqueTypeArchetypeType>();
    assert(opaque1->getDecl() == opaque2->getDecl());

    // It's possible to declare a generic requirement like Self == Self.Iterator
    // where both types are going to be opaque.
    if (!opaque1->getInterfaceType()->isEqual(opaque2->getInterfaceType()))
      return SolutionKind::Error;

    auto args1 = opaque1->getSubstitutions().getReplacementTypes();
    auto args2 = opaque2->getSubstitutions().getReplacementTypes();

    if (!shouldAttemptFixes()) {
      // Match up the replacement types of the respective substitution maps.
      return matchDeepTypeArguments(*this, subflags, args1, args2, locator);
    }

    unsigned numMismatches = 0;
    auto result =
        matchDeepTypeArguments(*this, subflags, args1, args2, locator,
                               [&numMismatches](unsigned) { ++numMismatches; });

    if (numMismatches > 0) {
      auto anchor = locator.getAnchor();
      // TODO(diagnostics): Only assignments are supported at the moment.
      if (!isExpr<AssignExpr>(anchor))
        return SolutionKind::Error;

      auto *fix = IgnoreAssignmentDestinationType::create(
          *this, type1, type2, getConstraintLocator(locator));

      if (recordFix(fix, /*impact=*/FixImpact::Mismatch * numMismatches))
        return SolutionKind::Error;

      return SolutionKind::Solved;
    }

    return result;
  }

  // Handle opened archetype types.
  if (auto opened1 = type1->getAs<ExistentialArchetypeType>()) {
    auto opened2 = type2->castTo<ExistentialArchetypeType>();
    assert(opened1->getInterfaceType()->isEqual(opened2->getInterfaceType()) &&
           opened1->getGenericEnvironment()->getOpenedExistentialID() ==
               opened2->getGenericEnvironment()->getOpenedExistentialID());

    auto args1 = opened1->getGenericEnvironment()
                     ->getOuterSubstitutions()
                     .getReplacementTypes();
    auto args2 = opened2->getGenericEnvironment()
                     ->getOuterSubstitutions()
                     .getReplacementTypes();

    return matchDeepTypeArguments(*this, subflags, args1, args2, locator);
  }

  // `any Sendable` -> `Any`
  if (matchSendableExistentialToAnyInGenericArgumentPosition(*this, type1,
                                                             type2, locator))
    return SolutionKind::Solved;

  // Handle existential types.
  if (auto *existential1 = type1->getAs<ExistentialType>()) {
    auto existential2 = type2->castTo<ExistentialType>();

    auto result = matchTypes(
        existential1->getConstraintType(), existential2->getConstraintType(),
        ConstraintKind::Bind, subflags,
        locator.withPathElement(ConstraintLocator::ExistentialConstraintType));

    if (result == SolutionKind::Error)
      return result;

    return SolutionKind::Solved;
  }

  // Arguments of parameterized protocol types have to match on the nose.
  if (auto ppt1 = type1->getAs<ParameterizedProtocolType>()) {
    auto ppt2 = type2->castTo<ParameterizedProtocolType>();

    auto result = matchTypes(ppt1->getBaseType(),
                             ppt2->getBaseType(),
                             ConstraintKind::Bind, subflags,
                             locator.withPathElement(
                               ConstraintLocator::ParentType));

    if (result == SolutionKind::Error)
      return result;

    return matchDeepTypeArguments(*this, subflags,
                                  ppt1->getArgs(),
                                  ppt2->getArgs(),
                                  locator);
  }

  // Members of protocol compositions have to match.
  if (auto pct1 = type1->getAs<ProtocolCompositionType>()) {
    auto pct2 = type2->castTo<ProtocolCompositionType>();

    auto members1 = pct1->getMembers();
    auto members2 = pct2->getMembers();
    if (members1.size() != members2.size())
      return SolutionKind::Error;
    if (pct1->getInverses() != pct2->getInverses())
      return SolutionKind::Error;
    if (pct1->hasExplicitAnyObject() != pct2->hasExplicitAnyObject())
      return SolutionKind::Error;
    for (unsigned i = 0, e = members1.size(); i < e; ++i) {
      auto member1 = members1[i];
      auto member2 = members2[i];
      auto subLocator = locator.withPathElement(
                          LocatorPathElt::ProtocolCompositionMemberType(i));
      auto result = matchTypes(member1, member2, ConstraintKind::Bind, subflags,
                               subLocator);
      if (result == SolutionKind::Error)
        return result;
    }

    return SolutionKind::Solved;
  }

  // Handle nominal types that are not directly generic.
  if (auto nominal1 = type1->getAs<NominalType>()) {
    auto nominal2 = type2->castTo<NominalType>();

    assert((bool)nominal1->getParent() == (bool)nominal2->getParent() &&
           "Mismatched parents of nominal types");

    if (!nominal1->getParent())
      return SolutionKind::Solved;

    // Match up the parents, exactly.
    return matchTypes(nominal1->getParent(), nominal2->getParent(),
                      ConstraintKind::Bind, subflags,
                      locator.withPathElement(ConstraintLocator::ParentType));
  }

  auto bound1 = type1->castTo<BoundGenericType>();
  auto bound2 = type2->castTo<BoundGenericType>();

  // Match up the parents, exactly, if there are parents.
  assert((bool)bound1->getParent() == (bool)bound2->getParent() &&
         "Mismatched parents of bound generics");
  if (bound1->getParent()) {
    auto result = matchTypes(bound1->getParent(), bound2->getParent(),
                             ConstraintKind::Bind, subflags,
                             locator.withPathElement(
                                                ConstraintLocator::ParentType));
    if (result == SolutionKind::Error)
      return result;
  }

  auto args1 = bound1->getGenericArgs();
  auto args2 = bound2->getGenericArgs();

  // Match up the generic arguments, exactly.

  if (shouldAttemptFixes()) {
    auto *baseLoc =
      getConstraintLocator(locator, {LocatorPathElt::GenericType(type1),
          LocatorPathElt::GenericType(type2)});

    // Optionals have a lot of special diagnostics and only one
    // generic argument so if we're dealing with one, let's allow
    // `repairFailures` to take care of it.
    if (bound1->getDecl()->isOptionalDecl())
      return matchDeepTypeArguments(*this, subflags, args1, args2, baseLoc);

    auto argMatchingFlags = subflags;
    // Allow the solver to produce separate fixes while matching
    // key path's root/value to a contextual type instead of the
    // standard one fix for all mismatched generic arguments
    // because at least one side of such a relation would be resolved.
    if (!isExpr<KeyPathExpr>(locator.trySimplifyToExpr())) {
      argMatchingFlags |= TMF_ApplyingFix;
      argMatchingFlags |= TMF_MatchingGenericArguments;
    }

    SmallVector<unsigned, 4> mismatches;
    auto result = matchDeepTypeArguments(
        *this, argMatchingFlags, args1, args2, baseLoc,
        [&mismatches](unsigned position) { mismatches.push_back(position); });

    if (mismatches.empty())
      return result;

    auto *loc = getConstraintLocator(locator);

    auto path = loc->getPath();
    if (!path.empty()) {
      // If we have something like ... -> type req # -> pack element #, we're
      // solving a requirement of the form T : P where T is a type parameter pack
      if (path.back().is<LocatorPathElt::PackElement>())
        path = path.drop_back();

      if (path.back().is<LocatorPathElt::AnyRequirement>()) {
        if (auto *fix = fixRequirementFailure(*this, type1, type2, locator)) {
          if (recordFix(fix))
            return SolutionKind::Error;

          increaseScore(SK_Fix, loc, mismatches.size());
          return SolutionKind::Solved;
        }
      }
    }

    auto impact = FixImpact::Mismatch;

    if (type1->getAnyPointerElementType() &&
        type2->getAnyPointerElementType()) {
      // If this is a pointer <-> pointer conversion of different kind,
      // there is a dedicated restriction/fix for that in some cases.
      // To accommodate that, let's increase the impact of this fix.
      impact += FixImpact::TypeMismatch;
    } else {
      // Increase the solution's score for each mismatch this fixes.
      impact += mismatches.size() - 1;
    }

    auto *fix = GenericArgumentsMismatch::create(
        *this, type1, type2, mismatches, loc);

    if (!recordFix(fix, impact))
      return SolutionKind::Solved;

    return result;
  }
  return matchDeepTypeArguments(*this, subflags, args1, args2, locator);
}

ConstraintSystem::SolutionKind
ConstraintSystem::matchExistentialTypes(Type type1, Type type2,
                                        ConstraintKind kind,
                                        TypeMatchOptions flags,
                                        ConstraintLocatorBuilder locator) {
  // If the first type is a type variable or member thereof, there's nothing
  // we can do now.
  if (type1->isTypeVariableOrMember()) {
    if (flags.contains(TMF_GenerateConstraints)) {
      addUnsolvedConstraint(
        Constraint::create(*this, kind, type1, type2,
                           getConstraintLocator(locator)));
      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  }

  // FIXME: Feels like a hack.
  if (type1->is<InOutType>())
    return SolutionKind::Error;

  // FIXME; Feels like a hack...nothing actually "conforms" here, and
  // we need to disallow conversions from types containing @noescape
  // functions to Any.

  // FIXME: special case for nonescaping functions and tuples containing them
  // shouldn't be needed, as functions have conformances to Escapable/Copyable.
  if (type2->isAny() && type1->isNoEscape()) {
    if (shouldAttemptFixes()) {
      auto *fix = MarkExplicitlyEscaping::create(*this, type1, type2,
                                                 getConstraintLocator(locator));
      if (!recordFix(fix))
        return SolutionKind::Solved;
    }

    return SolutionKind::Error;
  }

  TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);

  // Handle existential metatypes.
  if (auto meta1 = type1->getAs<MetatypeType>()) {
    ExistentialMetatypeType *meta2;
    if (auto existential = type2->getAs<ExistentialType>()) {
      meta2 = existential->getConstraintType()->getAs<ExistentialMetatypeType>();
    } else {
      meta2 = type2->getAs<ExistentialMetatypeType>();
    }
    if (meta2) {
      return matchExistentialTypes(meta1->getInstanceType(),
                                   meta2->getInstanceType(), kind, subflags,
                                   locator.withPathElement(
                                     ConstraintLocator::InstanceType));
    }
  }

  if (!type2->isExistentialType())
    return SolutionKind::Error;

  auto layout = type2->getExistentialLayout();

  if (auto layoutConstraint = layout.getLayoutConstraint()) {
    if (layoutConstraint->isClass()) {
      if (kind == ConstraintKind::ConformsTo ||
          kind == ConstraintKind::NonisolatedConformsTo) {
        if (!type1->satisfiesClassConstraint()) {
          if (shouldAttemptFixes()) {
            if (auto last = locator.last()) {
              // If solver is in diagnostic mode and type1 is a hole, or if this
              // is a superclass requirement, let's consider `AnyObject`
              // conformance solved. The actual superclass requirement
              // will also fail (because type can't satisfy it), and it's
              // more interesting for diagnostics.
              auto req = last->getAs<LocatorPathElt::AnyRequirement>();
              if (!req)
                return SolutionKind::Error;

              // Superclass constraints are never satisfied by existentials,
              // even those that contain the superclass a la `any C & P`.
              if (!type1->isExistentialType() &&
                  (type1->isPlaceholder() ||
                  req->getRequirementKind() == RequirementKind::Superclass))
                return SolutionKind::Solved;

              auto *fix = fixRequirementFailure(*this, type1, type2, locator);
              if (fix && !recordFix(fix)) {
                recordFixedRequirement(getConstraintLocator(locator), type2);
                return SolutionKind::Solved;
              }
            }
          }

          return SolutionKind::Error;
        }
      } else {
        // Subtype relation to AnyObject also allows class-bound
        // existentials that are not @objc and therefore carry
        // witness tables.
        if (!type1->isClassExistentialType() &&
            !type1->satisfiesClassConstraint()) {
          if (shouldAttemptFixes()) {
            llvm::SmallVector<LocatorPathElt, 4> path;
            if (auto anchor = locator.getLocatorParts(path)) {
              // Let's drop `optional` or `generic argument` bits from
              // locator because that helps to diagnose reference equality
              // operators ("===" and "!==") since there is always a
              // `value-to-optional` or `optional-to-optional` conversion
              // associated with them (expected argument is `AnyObject?`).
              if (!path.empty() &&
                  (path.back().is<LocatorPathElt::OptionalInjection>() ||
                   path.back().is<LocatorPathElt::GenericArgument>()))
                path.pop_back();

              auto *fixLoc = getConstraintLocator(anchor, path);
              // If after looking through optionals and generic arguments
              // we end up directly on assignment this is a source/destination
              // type mismatch.
              if (fixLoc->directlyAt<AssignExpr>()) {
                auto *fix = IgnoreAssignmentDestinationType::create(
                    *this, type1, type2, fixLoc);
                return recordFix(fix) ? SolutionKind::Error
                                      : SolutionKind::Solved;
              }

              auto *fix = AllowNonClassTypeToConvertToAnyObject::create(
                  *this, type1, fixLoc);

              return recordFix(fix) ? SolutionKind::Error
                                    : SolutionKind::Solved;
            }
          }

          return SolutionKind::Error;
        }
      }

      // Keep going.
    }
  }

  if (layout.explicitSuperclass) {
    auto result = matchTypes(type1, layout.explicitSuperclass,
                             ConstraintKind::Subtype,
                             subflags, locator);
    if (result == SolutionKind::Error)
      return result;
  }

  for (auto *protoDecl : layout.getProtocols()) {
    switch (simplifyConformsToConstraint(type1, protoDecl, kind, locator,
                                         subflags)) {
      case SolutionKind::Solved:
      case SolutionKind::Unsolved:
        break;

      case SolutionKind::Error: {
        if (!shouldAttemptFixes())
          return SolutionKind::Error;

        SmallVector<LocatorPathElt, 4> path;
        auto anchor = locator.getLocatorParts(path);

        // If the path ends at `optional payload` it means that this
        // check is part of an implicit value-to-optional conversion,
        // and it could be safely dropped.
        while (!path.empty() &&
               path.back().is<LocatorPathElt::OptionalInjection>())
          path.pop_back();

        // Determine whether this conformance mismatch is
        // associated with argument to a call, and if so
        // produce a tailored fix.
        if (!path.empty()) {
          auto last = path.back();

          if (last.is<LocatorPathElt::ApplyArgToParam>() ||
              last.is<LocatorPathElt::AutoclosureResult>()) {
            auto proto = protoDecl->getDeclaredInterfaceType();
            // Impact is 2 here because there are two failures
            // 1 - missing conformance and 2 - incorrect argument type.
            //
            // This would make sure that arguments with incorrect
            // conformances are not prioritized over general argument
            // mismatches.
            if (type1->isOptional()) {
              auto unwrappedType = type1->lookThroughAllOptionalTypes();
              auto result = simplifyConformsToConstraint(
                  unwrappedType, protoDecl, kind, locator,
                  subflags | TMF_ApplyingFix);
              if (result == SolutionKind::Solved) {
                auto fix = ForceOptional::create(*this, type1, proto,
                                                 getConstraintLocator(locator));
                if (recordFix(fix))
                  return SolutionKind::Error;
                break;
              }
            }
            auto fix = AllowArgumentMismatch::create(
                  *this, type1, proto, getConstraintLocator(anchor, path));
            if (recordFix(fix, FixImpact::TypeMismatch))
              return SolutionKind::Error;
            break;
          }

          if ((isExpr<ArrayExpr>(anchor) || isExpr<DictionaryExpr>(anchor)) &&
              last.is<LocatorPathElt::TupleElement>()) {
            auto *fix = CollectionElementContextualMismatch::create(
                *this, type1, type2, getConstraintLocator(anchor, path));
            if (recordFix(fix, FixImpact::TypeMismatch))
              return SolutionKind::Error;
            break;
          }

          // TODO(diagnostics): If there are any requirement failures associated
          // with result types which are part of a function type conversion,
          // let's record general conversion mismatch in order for it to capture
          // and display complete function types.
          //
          // Once either reacher locators or better diagnostic presentation for
          // nested type failures is available this check could be removed.
          if (last.is<LocatorPathElt::FunctionResult>())
            return SolutionKind::Error;

          // If instance types didn't line up correctly, let's produce a
          // diagnostic which mentions them together with their metatypes.
          if (last.is<LocatorPathElt::InstanceType>())
            return SolutionKind::Error;

          // Fail here and let the generic argument matching handle the failure
          // because we need to present complete types in the diagnostic and
          // point to the mismatching location(s).
          if (last.is<LocatorPathElt::GenericArgument>())
            return SolutionKind::Error;

          if (last.is<LocatorPathElt::CoercionOperand>()) {
            auto *fix = ContextualMismatch::create(
                *this, type1, type2, getConstraintLocator(anchor, path));
            if (recordFix(fix))
              return SolutionKind::Error;
            break;
          }

          if (last.is<LocatorPathElt::ContextualType>() ||
              last.is<LocatorPathElt::EnumPatternImplicitCastMatch>() ||
              last.is<LocatorPathElt::SequenceElementType>()) {
            auto proto = protoDecl->getDeclaredInterfaceType();
            auto *fix = MissingConformance::forContextual(
                *this, type1, proto, getConstraintLocator(anchor, path));

            if (recordFix(fix))
              return SolutionKind::Error;

            break;
          }
        } else { // There are no elements in the path
          if (isExpr<AssignExpr>(anchor)) {
            auto *fix = ContextualMismatch::create(
                *this, type1, type2, getConstraintLocator(anchor, path));
            if (recordFix(fix))
              return SolutionKind::Error;
            break;
          }
          return SolutionKind::Error;
        }

        return SolutionKind::Error;
      }
    }
  }

  // Finally, check parameterized protocol requirements.
  if (!layout.getParameterizedProtocols().empty()) {
    SmallVector<std::pair<Identifier, Type>, 4> fromReqs;

    if (type1->isExistentialType()) {
      auto fromLayout = type1->getExistentialLayout();
      for (auto *parameterizedType : fromLayout.getParameterizedProtocols()) {
        auto *protoDecl = parameterizedType->getProtocol();
        auto assocTypes = protoDecl->getPrimaryAssociatedTypes();
        auto argTypes = parameterizedType->getArgs();

        for (unsigned i : indices(argTypes)) {
          auto argType = argTypes[i];
          fromReqs.push_back(std::make_pair(assocTypes[i]->getName(), argType));
        }
      }
    }

    for (auto *parameterizedType : layout.getParameterizedProtocols()) {
      // With two parameterized protocols, we've already made sure conformance
      // constraints are satisfied. Try to match the arguments!
      if (type1->isExistentialType()) {
        auto *protoDecl = parameterizedType->getProtocol();
        auto assocTypes = protoDecl->getPrimaryAssociatedTypes();
        auto argTypes = parameterizedType->getArgs();

        for (unsigned i : indices(argTypes)) {
          auto argType = argTypes[i];
          bool found = false;
          for (auto fromReq : fromReqs) {
            if (fromReq.first == assocTypes[i]->getName()) {
              // FIXME: Extend the locator path to point to the argument
              // inducing the requirement.
              auto result = matchTypes(fromReq.second, argType,
                                       ConstraintKind::Bind,
                                       subflags, locator);
              if (result == SolutionKind::Error)
                return result;

              found = true;
              break;
            }
          }

          if (!found)
            return SolutionKind::Error;
        }
      } else {
        // The source type is a concrete type.
        //
        // Substitute the source into the requirements of the parameterized type
        // and discharge the requirements of the parameterized protocol.
        //
        // FIXME: Extend the locator path to point to the argument
        // inducing the requirement.
        SmallVector<Requirement, 2> reqs;
        parameterizedType->getRequirements(type1, reqs);
        for (const auto &req : reqs) {
          assert(req.getKind() == RequirementKind::SameType);
          if (req.hasError()) {
            if (shouldAttemptFixes()) {
              // Increase SK_Hole just to ensure the solution is marked invalid.
              increaseScore(SK_Hole, locator);
              continue;
            }

            return SolutionKind::Error;
          }

          auto result = matchTypes(req.getFirstType(), req.getSecondType(),
                                   ConstraintKind::Bind,
                                   subflags, locator);
          if (result == SolutionKind::Error)
            return result;
        }
      }
    }
  }

  return SolutionKind::Solved;
}

static bool isStringCompatiblePointerBaseType(ASTContext &ctx,
                                              Type baseType) {
  // Allow strings to be passed to pointer-to-byte or pointer-to-void types.
  if (baseType->isInt8())
    return true;
  if (baseType->isUInt8())
    return true;
  if (baseType->isVoid())
    return true;
  
  return false;
}

/// Determine whether the first type with the given number of optionals
/// is potentially more optional than the second type with its number of
/// optionals.
static bool isPotentiallyMoreOptionalThan(Type type1, Type type2) {
  SmallVector<Type, 2> optionals1;
  Type objType1 = type1->lookThroughAllOptionalTypes(optionals1);
  auto numOptionals1 = optionals1.size();

  SmallVector<Type, 2> optionals2;
  type2->lookThroughAllOptionalTypes(optionals2);
  auto numOptionals2 = optionals2.size();

  if (numOptionals1 <= numOptionals2 && !objType1->isTypeVariableOrMember())
    return false;

  return true;
}

/// Enumerate all of the applicable optional conversion restrictions
static void enumerateOptionalConversionRestrictions(
                    Type type1, Type type2,
                    ConstraintKind kind, ConstraintLocatorBuilder locator,
                    llvm::function_ref<void(ConversionRestrictionKind)> fn) {
  // Optional-to-optional.
  if (type1->getOptionalObjectType() && type2->getOptionalObjectType())
    fn(ConversionRestrictionKind::OptionalToOptional);

  // Inject a value into an optional.
  if (isPotentiallyMoreOptionalThan(type2, type1)) {
    fn(ConversionRestrictionKind::ValueToOptional);
  }
}

/// Determine whether we can bind the given type variable to the given
/// fixed type.
static bool isBindable(TypeVariableType *typeVar, Type type) {
  // Disallow recursive bindings.
  if (ConstraintSystem::typeVarOccursInType(typeVar, type))
    return false;

  // If type variable we are about to bind represents a pack
  // expansion type, allow the binding to happen regardless of
  // what the \c type is, because contextual type is just a hint
  // in this situation and type variable would be bound to its
  // opened type instead.
  //
  // Note that although inference doesn't allow direct bindings to
  // type variables, they can still get through via `matchTypes`
  // when type is a partially resolved pack expansion that simplifies
  // down to a type variable.
  return typeVar->getImpl().isPackExpansion() ||
         !(type->is<TypeVariableType>() || type->is<DependentMemberType>());
}

ConstraintSystem::SolutionKind
ConstraintSystem::matchTypesBindTypeVar(
    TypeVariableType *typeVar, Type origType, ConstraintKind kind,
    TypeMatchOptions flags, ConstraintLocatorBuilder locator,
    llvm::function_ref<SolutionKind()> formUnsolvedResult) {
  assert(typeVar->is<TypeVariableType>() && "Expected a type variable!");
  assert(!origType->is<TypeVariableType>() && "Expected a non-type variable!");

  // Simplify the right-hand type and perform the "occurs" check.
  typeVar = getRepresentative(typeVar);
  auto type = simplifyType(origType, flags);
  if (!isBindable(typeVar, type)) {
    if (shouldAttemptFixes()) {
      // If type variable is allowed to be a hole and it can't be bound to
      // a particular (full resolved) type, just ignore this binding
      // instead of re-trying it and failing later.
      if (typeVar->getImpl().canBindToHole() && !type->hasTypeVariable())
        return SolutionKind::Solved;

      // Just like in cases where both sides are dependent member types
      // with resolved base that can't be simplified to a concrete type
      // let's ignore this mismatch and mark affected type variable as a hole
      // because something else has to be fixed already for this to happen.
      if (type->is<DependentMemberType>() && !type->hasTypeVariable()) {
        // Since the binding couldn't be performed, the type variable is a
        // hole regardless whether it would be bound later to some other
        // type or not. If this is not reflected in constraint system
        // it would let the solver to form a _valid_ solution as if the
        // constraint between the type variable and the unresolved dependent
        // member type never existed.
        increaseScore(SK_Hole, locator);
        recordPotentialHole(typeVar);
        return SolutionKind::Solved;
      }
    }

    return formUnsolvedResult();
  }

  // Since member lookup doesn't check requirements
  // it might sometimes return types which are not
  // visible in the current context e.g. typealias
  // defined in constrained extension, substitution
  // of which might produce error type for base, so
  // assignment should tread lightly and just fail
  // if it encounters such types.
  if (type->hasError())
    return SolutionKind::Error;

  // Equal constraints allow mixed LValue/RValue bindings, but
  // if we bind a type to a type variable that can bind to
  // LValues as part of simplifying the Equal constraint we may
  // later block a binding of the opposite "LValue-ness" to the
  // same type variable that happens as part of simplifying
  // another constraint.
  if (kind == ConstraintKind::Equal) {
    if (typeVar->getImpl().canBindToLValue())
      return formUnsolvedResult();

    type = type->getRValueType();
  }

  // Prevent generic arguments from being assigned `any Sendable`
  // directly, that should only happen through inference. This is
  // required because we allow `any Sendable` -> `Any` conversion
  // in modes without strict concurrency enabled to maintain source
  // compatibility and let the developers annotate existing APIs
  // with `any Sendable` and other concurrency attributes.
  if (typeVar->getImpl().getGenericParameter() &&
      !flags.contains(TMF_BindingTypeVariable) &&
      type->isSendableExistential()) {
    return formUnsolvedResult();
  }

  // Attempt to fix situations where type variable can't be bound
  // to a particular type e.g. `l-value` or `inout`.
  auto fixReferenceMismatch = [&](TypeVariableType *typeVar,
                                  Type type) -> bool {
    if (locator.endsWith<LocatorPathElt::ContextualType>()) {
      auto *fix = IgnoreContextualType::create(*this, typeVar, type,
                                               getConstraintLocator(locator));
      return !recordFix(fix);
    }

    return false;
  };

  // If the left-hand type variable cannot bind to an lvalue,
  // but we still have an lvalue, fail.
  if (!typeVar->getImpl().canBindToLValue() && type->hasLValueType()) {
    if (shouldAttemptFixes() && fixReferenceMismatch(typeVar, type))
      return SolutionKind::Solved;

    return SolutionKind::Error;
  }

  // If the left-hand type variable cannot bind to an inout,
  // but we still have an inout, fail.
  if (!typeVar->getImpl().canBindToInOut() && type->is<InOutType>()) {
    if (shouldAttemptFixes() && fixReferenceMismatch(typeVar, type))
      return SolutionKind::Solved;

    return SolutionKind::Error;
  }

  // If the left-hand type variable cannot bind to a non-escaping type,
  // but we still have a non-escaping type, fail.
  if (!typeVar->getImpl().canBindToNoEscape() && type->isNoEscape()) {
    if (shouldAttemptFixes()) {
      auto *fix = MarkExplicitlyEscaping::create(*this, typeVar, type,
                                                 getConstraintLocator(locator));
      if (recordFix(fix))
        return SolutionKind::Error;

      // Allow no-escape function to be bound with recorded fix.
    } else {
      return SolutionKind::Error;
    }
  }

  if (typeVar->getImpl().isPackExpansion()) {
    if (!flags.contains(TMF_BindingTypeVariable))
      return formUnsolvedResult();

    return resolvePackExpansion(typeVar, origType)
               ? SolutionKind::Solved
               : SolutionKind::Error;
  }

  // If we're attempting to bind a PackType or PackArchetypeType to a type
  // variable that doesn't support it, we have a pack reference outside of a
  // pack expansion expression.
  if (!typeVar->getImpl().canBindToPack() &&
      (type->is<PackArchetypeType>() || type->is<PackType>())) {
    if (!shouldAttemptFixes())
      return SolutionKind::Error;

    auto *fix = AllowInvalidPackReference::create(
        *this, type, getConstraintLocator(locator));
    if (recordFix(fix))
      return SolutionKind::Error;

    // Don't allow the invalid pack reference to propagate to other
    // bindings.
    type = PlaceholderType::get(typeVar->getASTContext(), typeVar);
  }

  // Binding to a pack expansion type is always an error in Swift 6 mode.
  // This indicates that a pack expansion expression was used in a context
  // that doesn't support it.
  //
  // In Swift 5 and earlier initializer references are handled in a special
  // way that uses a type variable to represent a type of the parameter
  // list. Such type variables should be allowed to bind to a pack expansion
  // type to support cases where initializer has a single unlabeled variadic
  // generic parameter - `init(_ data: repeat each T)`.
  //
  // See BindTupleOfFunctionParams constraint for more details.
  if (!typeVar->getImpl().isPackExpansion() && type->is<PackExpansionType>()) {
    bool representsParameterList =
        typeVar->getImpl()
            .getLocator()
            ->isLastElement<LocatorPathElt::ApplyArgument>();

    if (!(typeVar->getImpl().canBindToPack() && representsParameterList) ||
        getASTContext().isLanguageModeAtLeast(LanguageMode::v6)) {
      if (!shouldAttemptFixes())
        return SolutionKind::Error;

      auto *fix = AllowInvalidPackExpansion::create(
          *this, getConstraintLocator(locator));
      if (recordFix(fix))
        return SolutionKind::Error;

      // Don't allow the pack expansion type to propagate to other
      // bindings.
      type = PlaceholderType::get(typeVar->getASTContext(), typeVar);
    }
  }

  // We do not allow keypaths to go through AnyObject. Let's create a fix
  // so this can be diagnosed later.
  if (auto loc = typeVar->getImpl().getLocator()) {
    auto locPath = loc->getPath();

    if (!locPath.empty() &&
        locPath.back().getKind() == ConstraintLocator::KeyPathRoot &&
        type->isAnyObject()) {
      auto *fix = AllowAnyObjectKeyPathRoot::create(
          *this, getConstraintLocator(locator));

      if (recordFix(fix))
        return SolutionKind::Error;
    }
  }

  // Okay. Bind below.

  // A constraint that binds any pointer to a void pointer is
  // ineffective, since any pointer can be converted to a void pointer.
  if (kind == ConstraintKind::BindToPointerType && type->isVoid()) {
    // Bind type1 to Void only as a last resort.
    addConstraint(ConstraintKind::Defaultable, typeVar, type,
                  getConstraintLocator(locator));
    return SolutionKind::Solved;
  }

  // When binding a fixed type to a type variable that cannot contain
  // lvalues or noescape types, any type variables within the fixed
  // type cannot contain lvalues or noescape types either.
  if (type->hasTypeVariable()) {
    type.visit([&](Type t) {
      if (auto *tvt = dyn_cast<TypeVariableType>(t.getPointer())) {
        if (!typeVar->getImpl().canBindToLValue()) {
          tvt->getImpl().setCanBindToLValue(getTrail(),
                                            /*enabled=*/false);
        }
        if (!typeVar->getImpl().canBindToNoEscape()) {
          tvt->getImpl().setCanBindToNoEscape(getTrail(),
                                              /*enabled=*/false);
        }
      }
    });
  }

  if (typeVar->getImpl().isClosureType()) {
    return resolveClosure(typeVar, type, locator)
               ? SolutionKind::Solved
               : SolutionKind::Error;
  }

  if (typeVar->getImpl().isTapType()) {
    return resolveTapBody(typeVar, type, locator)
               ? SolutionKind::Solved
               : SolutionKind::Error;
  }

  if (typeVar->getImpl().isKeyPathType()) {
    return resolveKeyPath(typeVar, type, flags, locator)
               ? SolutionKind::Solved
               : SolutionKind::Error;
  }

  // If a type assigned to a synthesized argument has type variables
  // it should be possible to assign holes to them because it's not
  // guaranteed that they could always be resolved from the context.
  //
  // For example, when a parameter has a function type that returns
  // a generic parameter, if it's not connected to anything else i.e.
  // doesn't appear in other parameter positions or there is no
  // contextual type for the call when it's a result, it won't be
  // possible to resolve.
  if (typeVar->getImpl().isSynthesizedArgument()) {
    recordAnyTypeVarAsPotentialHole(type);
  }

  assignFixedType(typeVar, type, /*updateState=*/true,
                  /*notifyInference=*/!flags.contains(TMF_BindingTypeVariable));

  return SolutionKind::Solved;
}

static ConstraintFix *fixRequirementFailure(ConstraintSystem &cs, Type type1,
                                            Type type2, ASTNode anchor,
                                            ArrayRef<LocatorPathElt> path) {
  // Can't fix not yet properly resolved types.
  if (type1->isTypeVariableOrMember() || type2->isTypeVariableOrMember())
    return nullptr;

  // If we have something like ... -> type req # -> pack element #, we're
  // solving a requirement of the form T : P where T is a type parameter pack
  if (path.back().is<LocatorPathElt::PackElement>())
    path = path.drop_back();

  auto req = path.back().castTo<LocatorPathElt::AnyRequirement>();
  if (req.isConditionalRequirement()) {
    // path is - ... -> open generic -> type req # -> cond req #,
    // to identify type requirement we only need `open generic -> type req #`
    // part, because that's how fixes for type requirements are recorded.
    auto reqPath = path.drop_back();
    // If underlying conformance requirement has been fixed,
    // then there is no reason to fix up conditional requirements.
    if (cs.hasFixFor(cs.getConstraintLocator(anchor, reqPath)))
      return nullptr;
  }

  auto *reqLoc = cs.getConstraintLocator(anchor, path);

  switch (req.getRequirementKind()) {
  case RequirementKind::SameType:
    return SkipSameTypeRequirement::create(cs, type1, type2, reqLoc);

  case RequirementKind::SameShape:
    return SkipSameShapeRequirement::create(cs, type1, type2, reqLoc);

  case RequirementKind::Superclass:
    return SkipSuperclassRequirement::create(cs, type1, type2, reqLoc);

  case RequirementKind::Layout:
  case RequirementKind::Conformance:
    return MissingConformance::forRequirement(cs, type1, type2, reqLoc);
  }
  llvm_unreachable("covered switch");
}

static ConstraintFix *fixPropertyWrapperFailure(
    ConstraintSystem &cs, Type baseTy, ConstraintLocator *locator,
    llvm::function_ref<bool(SelectedOverload, VarDecl *, Type)> attemptFix,
    std::optional<Type> toType = std::nullopt) {
  // Don't attempt this fix if this is a key path dynamic member
  // lookup which produced no results. Unwrapping or wrapping
  // the base type is not going to produce desired results.
  if (locator->isForKeyPathDynamicMemberLookup())
    return nullptr;

  Expr *baseExpr = nullptr;
  if (auto *anchor = getAsExpr(locator->getAnchor())) {
    if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor))
      baseExpr = UDE->getBase();
    else if (auto *SE = dyn_cast<SubscriptExpr>(anchor))
      baseExpr = SE->getBase();
    else if (auto *MRE = dyn_cast<MemberRefExpr>(anchor))
      baseExpr = MRE->getBase();
    else if (auto anchor = simplifyLocatorToAnchor(locator))
      baseExpr = getAsExpr(anchor);
  }

  if (!baseExpr)
    return nullptr;

  auto resolvedOverload = cs.findSelectedOverloadFor(baseExpr);
  if (!resolvedOverload)
    return nullptr;

  auto *decl = resolvedOverload->choice.getDeclOrNull();
  auto *VD = dyn_cast_or_null<VarDecl>(decl);
  if (!VD)
    return nullptr;

  enum class Fix : uint8_t {
    ProjectedValue,
    PropertyWrapper,
    WrappedValue,
  };

  auto applyFix = [&](Fix fix, Type type) -> ConstraintFix * {
    if (!VD->hasInterfaceType() || !type)
      return nullptr;

    if (baseTy->isEqual(type))
      return nullptr;

    if (baseTy->is<TypeVariableType>() || type->is<TypeVariableType>())
      return nullptr;

    if (!attemptFix(*resolvedOverload, VD, type))
      return nullptr;

    switch (fix) {
    case Fix::ProjectedValue:
    case Fix::PropertyWrapper:
      return UsePropertyWrapper::create(cs, VD, fix == Fix::ProjectedValue,
                                        baseTy, toType.value_or(type), locator);

    case Fix::WrappedValue:
      return UseWrappedValue::create(cs, VD, baseTy, toType.value_or(type),
                                     locator);
    }
    llvm_unreachable("Unhandled Fix type in switch");
  };

  if (auto projectTy = cs.getPropertyWrapperProjectionType(*resolvedOverload)) {
    if (auto *fix = applyFix(Fix::ProjectedValue, projectTy))
      return fix;
  }

  if (auto backingTy = cs.getPropertyWrapperBackingType(*resolvedOverload)) {
    if (auto *fix = applyFix(Fix::PropertyWrapper, backingTy))
      return fix;
  }

  if (auto wrappedTy = cs.getWrappedPropertyType(*resolvedOverload)) {
    if (auto *fix = applyFix(Fix::WrappedValue, wrappedTy))
      return fix;
  }

  return nullptr;
}

static bool canBridgeThroughCast(ConstraintSystem &cs, Type fromType,
                                 Type toType) {
  // If we have a value of type AnyObject that we're trying to convert to
  // a class, force a downcast.
  // FIXME: Also allow types bridged through Objective-C classes.
  if (fromType->isAnyObject() && toType->getClassOrBoundGenericClass())
    return true;

  auto bridged = TypeChecker::getDynamicBridgedThroughObjCClass(cs.DC,
                                                                fromType, toType);
  if (!bridged)
    return false;

  // Note: don't perform this recovery for NSNumber;
  if (auto classType = bridged->getAs<ClassType>()) {
    SmallString<16> scratch;
    if (classType->getDecl()->isObjC() &&
        classType->getDecl()->getObjCRuntimeName(scratch) == "NSNumber")
      return false;
  }

  return true;
}

static bool
repairViaBridgingCast(ConstraintSystem &cs, Type fromType, Type toType,
                      SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
                      ConstraintLocatorBuilder locator) {
  // Don't check if any of the types have type variables or placeholders,
  // `typeCheckCheckedCast` doesn't support checking solver-allocated types.
  if (fromType->hasTypeVariableOrPlaceholder() ||
      toType->hasTypeVariableOrPlaceholder()) {
    return false;
  }

  auto objectType1 = fromType->getOptionalObjectType();
  auto objectType2 = toType->getOptionalObjectType();

  if (objectType1 && !objectType2) {
    auto *anchor = locator.trySimplifyToExpr();
    if (!anchor)
      return false;

    if (auto overload = cs.findSelectedOverloadFor(anchor)) {
      auto *decl = overload->choice.getDeclOrNull();
      if (decl && decl->isImplicitlyUnwrappedOptional())
        fromType = objectType1;
    }
  }

  if (!canBridgeThroughCast(cs, fromType, toType))
    return false;

  if (!TypeChecker::checkedCastMaySucceed(fromType, toType, cs.DC))
    return false;

  conversionsOrFixes.push_back(ForceDowncast::create(
      cs, fromType, toType, cs.getConstraintLocator(locator)));
  return true;
}

/// Return tuple of type and number of optionals on that type.
static std::pair<Type, unsigned> getObjectTypeAndNumUnwraps(Type type) {
  SmallVector<Type, 2> optionals;
  Type objType = type->lookThroughAllOptionalTypes(optionals);
  return std::make_pair(objType, optionals.size());
}

static bool
repairViaOptionalUnwrap(ConstraintSystem &cs, Type fromType, Type toType,
                        ConstraintKind matchKind,
                        SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
                        ConstraintLocatorBuilder locator) {
  fromType = fromType->getWithoutSpecifierType();

  if (!fromType->getOptionalObjectType() || toType->is<TypeVariableType>())
    return false;

  // If we have an optional type, try to force-unwrap it.
  // FIXME: Should we also try '?'?
  auto *anchor = locator.trySimplifyToExpr();
  if (!anchor)
    return false;

  // If this is a conversion to a non-optional contextual type e.g.
  // `let _: Bool = try? foo()` and `foo()` produces `Int`
  // we should diagnose it as type mismatch instead of missing unwrap.
  bool possibleContextualMismatch = [&]() {
    if (!(locator.endsWith<LocatorPathElt::ContextualType>() ||
          locator.endsWith<LocatorPathElt::Condition>()))
      return false;

    // If the contextual type is optional as well, it's definitely a
    // missing unwrap.
    if (toType->getOptionalObjectType())
      return false;

    // If this is a leading-dot syntax member chain with `?.`
    // notation, it wouldn't be possible to infer the base type
    // without the contextual type, so we have to treat it as
    // a missing unwrap.
    if (auto *OEE = getAsExpr<OptionalEvaluationExpr>(anchor)) {
      if (isExpr<UnresolvedMemberChainResultExpr>(OEE->getSubExpr()))
        return false;
    }

    return true;
  }();

  // `OptionalEvaluationExpr` doesn't add a new level of
  // optionality but it could be hiding concrete types
  // behind itself which we can use to better understand
  // how many levels of optionality have to be unwrapped.
  if (auto *OEE = dyn_cast<OptionalEvaluationExpr>(anchor)) {
    auto *subExpr = OEE->getSubExpr();

    // First, let's check whether it has been determined that
    // it was incorrect to use `?` in this position.
    if (cs.hasFixFor(cs.getConstraintLocator(subExpr), FixKind::RemoveUnwrap)) {
      if (auto *typeVar =
              fromType->getOptionalObjectType()->getAs<TypeVariableType>()) {
        // If the optional chain is invalid let's unwrap optional and
        // re-introduce the constraint to be solved later once both sides
        // are sufficiently resolved, this would allow to diagnose not only
        // the invalid unwrap but an invalid conversion (if any) as well.
        cs.addConstraint(matchKind, typeVar, toType,
                         cs.getConstraintLocator(locator));
      }
      return true;
    }

    auto type = cs.getType(subExpr);
    // If the type of sub-expression is optional, type of the
    // `OptionalEvaluationExpr` could be safely ignored because
    // it doesn't add any type information.
    if (type->getOptionalObjectType())
      fromType = type;

    // Don't attempt the fix until sub-expression is resolved
    // if chain is not using leading-dot syntax. This is better
    // than attempting to propagate type information down optional
    // chain which is hard to diagnose.
    if (type->isTypeVariableOrMember() &&
        !isa<UnresolvedMemberChainResultExpr>(subExpr))
      return false;

    // If this is a conversion from optional chain to some
    // other type e.g. contextual type or a parameter type,
    // let's use `Bind` to match object types because
    // object type of the optional chain is a type variable.
    //
    // One exception is contextual conversion - in such cases
    // let's give optional chain a chance to infer its inner type
    // first, that makes it much easier to diagnose contextual
    // mismatch vs. missing optional unwrap.
    if (!possibleContextualMismatch && matchKind >= ConstraintKind::Conversion)
      matchKind = ConstraintKind::Bind;
  }

  if (auto *DRE = dyn_cast<DeclRefExpr>(anchor)) {
    if (DRE->getDecl()->isImplicit()) {
      // The expression that provides the first type is implicit and never
      // spelled out in source code, e.g. $match in an expression pattern.
      // Thus we cannot force unwrap the first type
      return false;
    }
  }

  if (auto *optTryExpr = dyn_cast<OptionalTryExpr>(anchor)) {
    auto subExprType = cs.getType(optTryExpr->getSubExpr());
    const bool isSwift5OrGreater =
        cs.getASTContext().isLanguageModeAtLeast(LanguageMode::v5);

    if (subExprType->getOptionalObjectType()) {
      if (isSwift5OrGreater) {
        // For 'try?' expressions, a ForceOptional fix converts 'try?'
        // to 'try!'. If the sub-expression is optional, then a force-unwrap
        // won't change anything in Swift 5+ because 'try?' already avoids
        // adding an additional layer of Optional there.
        return false;
      }
    } else {
      // In cases when sub-expression isn't optional, 'try?'
      // always adds one level of optionality regardless of
      // language mode, so we can safely try to bind its
      // object type to contextual type without risk of
      // causing more optionality mismatches down the road.
      //
      // For contextual conversions let's give `try?` a chance to
      // infer inner type which, if incorrect, should result in
      // contextual conversion failure instead of optional unwrap.
      matchKind = possibleContextualMismatch ? ConstraintKind::Conversion
                                             : ConstraintKind::Bind;
    }
  }

  Type fromObjectType, toObjectType;
  unsigned fromUnwraps, toUnwraps;

  std::tie(fromObjectType, fromUnwraps) = getObjectTypeAndNumUnwraps(fromType);
  std::tie(toObjectType, toUnwraps) = getObjectTypeAndNumUnwraps(toType);

  // Since equality is symmetric and it decays into a `Bind`, eagerly
  // unwrapping optionals from either side might be incorrect since
  // there is not enough information about what is expected e.g.
  // `Int?? equal T0?` just like `T0? equal Int??` allows `T0` to be
  // bound to `Int?` and there is no need to unwrap. Solver has to wait
  // until more information becomes available about what `T0` is expected
  // to be before taking action.
  if (matchKind == ConstraintKind::Equal &&
      (fromObjectType->is<TypeVariableType>() ||
       toObjectType->is<TypeVariableType>())) {
    return false;
  }

  // If `from` is not less optional than `to`, force unwrap is
  // not going to help here. In case of object type of `from`
  // is a type variable, let's assume that it might be optional.
  if (fromUnwraps <= toUnwraps && !fromObjectType->is<TypeVariableType>())
    return false;

  // If the result of optional chaining is converted to
  // an optional contextual type represented by a type
  // variable e.g. `T?`, there can be no optional mismatch
  // because `T` could be bound to an optional of any depth.
  if (isa<OptionalEvaluationExpr>(anchor) && toUnwraps > 0) {
    if (locator.endsWith<LocatorPathElt::ContextualType>() &&
        toObjectType->is<TypeVariableType>())
      return false;
  }

  auto result =
      cs.matchTypes(fromObjectType, toObjectType, matchKind,
                    ConstraintSystem::TypeMatchFlags::TMF_ApplyingFix, locator);
  if (result != ConstraintSystem::SolutionKind::Solved)
    return false;

  conversionsOrFixes.push_back(ForceOptional::create(
      cs, fromType, toType, cs.getConstraintLocator(locator)));
  return true;
}

static bool repairArrayLiteralUsedAsDictionary(
    ConstraintSystem &cs, Type arrayType, Type dictType,
    ConstraintKind matchKind,
    SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
    ConstraintLocator *loc) {
  if (auto *fix = TreatArrayLiteralAsDictionary::attempt(cs, dictType,
                                                         arrayType, loc)) {
    // Ignore any attempts at promoting the value to an optional as even after
    // stripping off all optionals above the underlying types won't match (array
    // vs dictionary).
    conversionsOrFixes.erase(
        llvm::remove_if(conversionsOrFixes,
                        [&](RestrictionOrFix &E) {
                          if (auto restriction = E.getRestriction())
                            return *restriction == ConversionRestrictionKind::
                                                       ValueToOptional ||
                                   *restriction == ConversionRestrictionKind::
                                                       OptionalToOptional;
                          return false;
                        }),
        conversionsOrFixes.end());

    conversionsOrFixes.push_back(fix);
    return true;
  }
  return false;
}

/// Let's check whether this is an out-of-order argument in binary
/// operator/function with concrete type parameters e.g.
/// `func ^^(x: Int, y: String)` called as `"" ^^ 42` instead of
/// `42 ^^ ""` and repair it by using out-of-order fix on the
/// parent locator.
static bool repairOutOfOrderArgumentsInBinaryFunction(
    ConstraintSystem &cs, SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
    ConstraintLocator *locator) {
  if (!locator->isLastElement<LocatorPathElt::ApplyArgToParam>())
    return false;

  auto path = locator->getPath();
  auto *parentLoc =
      cs.getConstraintLocator(locator->getAnchor(), path.drop_back());

  if (cs.hasFixFor(parentLoc, FixKind::MoveOutOfOrderArgument))
    return true;

  auto *calleeLoc = cs.getCalleeLocator(locator);
  if (!calleeLoc)
    return false;

  auto overload = cs.findSelectedOverloadFor(calleeLoc);
  if (!(overload && overload->choice.isDecl()))
    return false;

  auto *fnType = overload->adjustedOpenedType->getAs<FunctionType>();
  if (!(fnType && fnType->getNumParams() == 2))
    return false;

  auto argument = simplifyLocatorToAnchor(locator);
  // Argument could be synthesized.
  if (!argument)
    return false;

  auto argLoc = locator->castLastElementTo<LocatorPathElt::ApplyArgToParam>();
  auto currArgIdx = argLoc.getArgIdx();
  auto currParamIdx = argLoc.getParamIdx();

  // Argument is extraneous and has been re-ordered to match one
  // of two parameter types.
  if (currArgIdx >= 2 || currArgIdx != currParamIdx)
    return false;

  auto otherArgIdx = currArgIdx == 0 ? 1 : 0;

  auto argType = cs.getType(argument);
  auto paramType = fnType->getParams()[otherArgIdx].getOldType();

  bool isOperatorRef = overload->choice.getDecl()->isOperator();

  // If one of the parameters is `inout`, we can't flip the arguments.
  {
    auto params = fnType->getParams();
    if (params[0].isInOut() != params[1].isInOut())
      return false;
  }

  auto getReorderedArgumentLocator = [&](unsigned argIdx) {
    auto paramIdx = argIdx == 0 ? 1 : 0;
    return cs.getConstraintLocator(
        parentLoc, LocatorPathElt::ApplyArgToParam(
                       argIdx, paramIdx,
                       fnType->getParams()[paramIdx].getParameterFlags()));
  };

  auto matchArgToParam = [&](Type argType, Type paramType, unsigned argIdx)
      -> ConstraintSystem::SolutionKind {
    // If argument (and/or parameter) is a generic type let's not even try this
    // fix because it would be impossible to match given types  without delaying
    // until more context becomes available.
    if (argType->hasTypeVariable() || paramType->hasTypeVariable())
      return ConstraintSystem::SolutionKind::Error;

    // FIXME: There is currently no easy way to avoid attempting
    // fixes, matchTypes do not propagate `TMF_ApplyingFix` flag.
    llvm::SaveAndRestore<ConstraintSystemOptions> options(
        cs.Options, cs.Options - ConstraintSystemFlags::AllowFixes);

    // Check optionality, if argument is more optional than parameter
    // they are not going to match. This saves us one disjunction because
    // optionals are matched as deep-equality and optional-to-optional.
    {
      unsigned numArgUnwraps;
      unsigned numParamUnwraps;

      std::tie(argType, numArgUnwraps) = getObjectTypeAndNumUnwraps(argType);
      std::tie(paramType, numParamUnwraps) =
          getObjectTypeAndNumUnwraps(paramType);

      if (numArgUnwraps > numParamUnwraps)
        return ConstraintSystem::SolutionKind::Error;
    }

    auto *loc = getReorderedArgumentLocator(argIdx);
    return cs.matchTypes(
        argType, paramType,
        isOperatorRef ? ConstraintKind::OperatorArgumentConversion
                      : ConstraintKind::ArgumentConversion,
        ConstraintSystem::TypeMatchFlags::TMF_ApplyingFix, loc);
  };

  auto result = matchArgToParam(argType, paramType, currArgIdx);
  if (result == ConstraintSystem::SolutionKind::Solved) {
    // Let's check whether other argument matches current parameter type,
    // if it does - it's definitely out-of-order arguments issue.
    auto *otherArgLoc = getReorderedArgumentLocator(otherArgIdx);
    auto otherArg = simplifyLocatorToAnchor(otherArgLoc);
    // Argument could be synthesized.
    if (!otherArg)
      return false;

    argType = cs.getType(otherArg);
    paramType = fnType->getParams()[currArgIdx].getOldType();

    result = matchArgToParam(argType, paramType, otherArgIdx);
    if (result == ConstraintSystem::SolutionKind::Solved) {
      conversionsOrFixes.push_back(MoveOutOfOrderArgument::create(
          cs, otherArgIdx, currArgIdx, {{0}, {1}}, parentLoc));
      return true;
    }
  }

  return false;
}

/// Attempt to repair typing failures and record fixes if needed.
/// \return true if at least some of the failures has been repaired
/// successfully, which allows type matcher to continue.
bool ConstraintSystem::repairFailures(
    Type lhs, Type rhs, ConstraintKind matchKind, TypeMatchOptions flags,
    SmallVectorImpl<RestrictionOrFix> &conversionsOrFixes,
    ConstraintLocatorBuilder locator) {
  SmallVector<LocatorPathElt, 4> path;
  auto anchor = locator.getLocatorParts(path);

  // If there is a missing explicit call it could be:
  //
  // a). Contextual e.g. `let _: R = foo`
  // b). Argument is a function value passed to parameter
  //     which expects its result type e.g. `foo(bar)`
  // c). Assignment destination type matches return type of
  //     of the function value e.g. `foo = bar` or `foo = .bar`
  auto repairByInsertingExplicitCall = [&](Type srcType, Type dstType) -> bool {
    auto fnType = srcType->getAs<FunctionType>();
    if (!fnType)
      return false;

    // If the locator isn't anchored at an expression, or the expression is
    // implicit, don't try to insert an explicit call into the source code.
    auto *loc = getConstraintLocator(locator);
    auto *anchor = getAsExpr(simplifyLocatorToAnchor(loc));
    if (!anchor || anchor->isImplicit())
      return false;

    if (isArgumentOfPatternMatchingOperator(loc))
      return false;

    // Don't attempt this fix for trailing closures.
    if (auto elt = loc->getLastElementAs<LocatorPathElt::ApplyArgToParam>()) {
      auto argumentList = getArgumentList(loc);
      if (argumentList->isTrailingClosureIndex(elt->getArgIdx()))
        return false;
    }

    // If argument is a function type and all of its parameters have
    // default values, let's see whether error is related to missing
    // explicit call.
    if (fnType->getNumParams() > 0) {
      auto overload = findSelectedOverloadFor(anchor);
      if (!(overload && overload->choice.isDecl()))
        return false;

      const auto &choice = overload->choice;
      ParameterListInfo info(fnType->getParams(), choice.getDecl(),
                             hasAppliedSelf(*this, choice));

      if (llvm::any_of(indices(fnType->getParams()),
                       [&info](const unsigned idx) {
                         return !info.hasDefaultArgument(idx);
                       }))
        return false;
    }

    auto resultType = fnType->getResult();
    // If this is situation like `x = { ... }` where closure results in
    // `Void`, let's not suggest to call the closure, because it's most
    // likely not intended.
    if (auto *assignment = getAsExpr<AssignExpr>(anchor)) {
      if (isa<ClosureExpr>(assignment->getSrc()) && resultType->isVoid())
        return false;
    }

    // If left-hand side is a function type but right-hand
    // side isn't, let's check it would be possible to fix
    // this by forming an explicit call.
    auto convertTo = dstType->lookThroughAllOptionalTypes();

    // If the RHS is a function type, the source must be a function-returning
    // function.
    if (convertTo->is<FunctionType>() && !resultType->is<FunctionType>())
      return false;

    // Right-hand side can't be a type variable or dependent member, or `Any`
    // (if function conversion to `Any` didn't succeed there is something else
    // going on e.g. problem with escapiness).
    if (convertTo->isTypeVariableOrMember() || convertTo->isAny())
      return false;

    ConstraintKind matchKind;
    if (resultType->is<TypeVariableType>()) {
      matchKind = ConstraintKind::Equal;
    } else {
      matchKind = ConstraintKind::Conversion;
    }

    // FIXME: There is currently no easy way to avoid attempting
    // fixes, matchTypes do not propagate `TMF_ApplyingFix` flag.
    llvm::SaveAndRestore<ConstraintSystemOptions> options(
        Options, Options - ConstraintSystemFlags::AllowFixes);

    auto result = matchTypes(resultType, dstType, matchKind,
                             TypeMatchFlags::TMF_ApplyingFix, locator);

    if (result == SolutionKind::Solved) {
      conversionsOrFixes.push_back(
          InsertExplicitCall::create(*this, getConstraintLocator(locator)));
      return true;
    }

    return false;
  };

  auto repairByAnyToAnyObjectCast = [&](Type lhs, Type rhs) -> bool {
    if (!(lhs->isAny() && rhs->isAnyObject()))
      return false;

    conversionsOrFixes.push_back(MissingConformance::forContextual(
        *this, lhs, rhs, getConstraintLocator(locator)));
    return true;
  };

  auto repairByTreatingRValueAsLValue = [&](Type lhs, Type rhs) -> bool {
    if (!lhs->is<LValueType>() &&
        (rhs->is<LValueType>() || rhs->is<InOutType>())) {
      // Conversion from l-value to inout in an operator argument
      // position (which doesn't require explicit `&`) decays into
      // a `Bind` of involved object types, same goes for explicit
      // `&` conversion from l-value to inout type.
      //
      // In case of regular argument conversion although explicit `&`
      // is required we still want to diagnose the problem as one
      // about mutability instead of suggesting to add `&` which wouldn't
      // be correct.
      auto kind = (isExpr<InOutExpr>(anchor) ||
                   (rhs->is<InOutType>() &&
                    (matchKind == ConstraintKind::ArgumentConversion ||
                     matchKind == ConstraintKind::OperatorArgumentConversion)))
                      ? ConstraintKind::Bind
                      : matchKind;

      auto result = matchTypes(lhs, rhs->getWithoutSpecifierType(), kind,
                               TMF_ApplyingFix, locator);

      if (result == SolutionKind::Solved) {
        // If left side is a hole, let's not record a fix since hole can
        // assume any type and already represents a problem elsewhere in
        // the expression.
        if (lhs->isPlaceholder())
          return true;

        auto *loc = getConstraintLocator(locator);
        // If this `inout` is in incorrect position, it should be diagnosed
        // by other fixes.
        if (loc->directlyAt<InOutExpr>()) {
          if (!getArgumentLocator(castToExpr(anchor))) {
            conversionsOrFixes.push_back(
                RemoveAddressOf::create(*this, lhs, rhs, loc));
            return true;
          }
        }

        conversionsOrFixes.push_back(TreatRValueAsLValue::create(*this, loc));
        return true;
      }
    }

    return false;
  };

  // Check whether given `value` type matches a `RawValue` type of
  // a given raw representable type.
  auto isValueOfRawRepresentable = [&](Type valueType,
                                       Type rawReprType) -> bool {
    // diagnostic is going to suggest failable initializer anyway.
    if (auto objType = rawReprType->getOptionalObjectType())
      rawReprType = objType;

    // If value is optional diagnostic would suggest using `Optional.map` in
    // combination with `<Type>(rawValue: ...)` initializer.
    if (auto objType = valueType->getOptionalObjectType())
      valueType = objType;

    if (rawReprType->isTypeVariableOrMember() || rawReprType->isPlaceholder())
      return false;

    auto rawValue = isRawRepresentable(*this, rawReprType);
    if (!rawValue)
      return false;

    auto result = matchTypes(valueType, rawValue, ConstraintKind::Conversion,
                             TMF_ApplyingFix, locator);
    return result != SolutionKind::Error;
  };

  // Check whether given `rawReprType` does indeed conform to `RawRepresentable`
  // and if so check that given `expectedType` matches its `RawValue` type. If
  // that condition holds add a tailored fix which is going to suggest to
  // explicitly construct a raw representable type from a given value type.
  auto repairByConstructingRawRepresentableType =
      [&](Type expectedType, Type rawReprType) -> bool {
    if (!isValueOfRawRepresentable(expectedType, rawReprType))
      return false;

    conversionsOrFixes.push_back(ExplicitlyConstructRawRepresentable::create(
        *this, rawReprType, expectedType, getConstraintLocator(locator)));
    return true;
  };

  // Check whether given `rawReprType` does indeed conform to `RawRepresentable`
  // and if so check that given `expectedType` matches its `RawValue` type. If
  // that condition holds add a tailored fix which is going to suggest to
  // use `.rawValue` associated with given raw representable type to match
  // given expected type.
  auto repairByUsingRawValueOfRawRepresentableType =
      [&](Type rawReprType, Type expectedType) -> bool {
    if (!isValueOfRawRepresentable(expectedType, rawReprType))
      return false;

    conversionsOrFixes.push_back(UseRawValue::create(
        *this, rawReprType, expectedType, getConstraintLocator(locator)));
    return true;
  };

  auto hasConversionOrRestriction = [&](ConversionRestrictionKind kind) {
    return llvm::any_of(conversionsOrFixes,
                        [kind](const RestrictionOrFix correction) {
                          if (auto restriction = correction.getRestriction())
                            return restriction == kind;
                          return false;
                        });
  };

  auto hasAnyRestriction = [&]() {
    return llvm::any_of(conversionsOrFixes,
                        [](const RestrictionOrFix &correction) {
                          return bool(correction.getRestriction());
                        });
  };

  // Check whether this is a tuple with a single unlabeled element
  // i.e. `(_: Int)` and return type of that element if so. Note that
  // if the element is pack expansion type the tuple is significant.
  auto isSingleUnlabeledElementTuple = [](Type type) -> Type {
    if (auto *tuple = type->getAs<TupleType>()) {
      if (tuple->getNumElements() == 1 && !tuple->getElement(0).hasName()) {
        auto eltType = tuple->getElement(0).getType();
        return isPackExpansionType(eltType) ? Type() : eltType;
      }
    }
    return Type();
  };

  if (repairArrayLiteralUsedAsDictionary(*this, lhs, rhs, matchKind,
                                         conversionsOrFixes,
                                         getConstraintLocator(locator)))
    return true;

  if (locator.endsWith<LocatorPathElt::ThrownErrorType>()) {
    conversionsOrFixes.push_back(
        IgnoreThrownErrorMismatch::create(*this, lhs, rhs,
                                          getConstraintLocator(locator)));
    return true;
  }

  auto maybeRepairKeyPathResultFailure = [&](KeyPathExpr *kpExpr) {
    if (lhs->isPlaceholder() || rhs->isPlaceholder())
      return true;
    if (lhs->isTypeVariableOrMember() || rhs->isTypeVariableOrMember())
      return false;

    if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) ||
        hasConversionOrRestriction(ConversionRestrictionKind::ValueToOptional))
      return false;

    auto i = kpExpr->getComponents().size() - 1;
    auto lastCompLoc =
        getConstraintLocator(kpExpr, LocatorPathElt::KeyPathComponent(i));
    if (hasFixFor(lastCompLoc, FixKind::AllowTypeOrInstanceMember))
      return true;

    auto *keyPathLoc = getConstraintLocator(anchor);

    if (hasFixFor(keyPathLoc))
      return true;

    if (auto contextualInfo = getContextualTypeInfo(anchor)) {
      if (hasFixFor(getConstraintLocator(
              keyPathLoc,
              LocatorPathElt::ContextualType(contextualInfo->purpose))))
        return true;
    }

    conversionsOrFixes.push_back(IgnoreContextualType::create(
        *this, lhs, rhs,
        getConstraintLocator(keyPathLoc, ConstraintLocator::KeyPathValue)));
    return true;
  };

  // Propagate a hole from one type to another. This is useful for contextual
  // types since type resolution forms a top-level ErrorType for types with
  // nested errors, e.g `S<@error_type>` becomes `@error_type`. As such, when
  // matching `S<$T0> == $T1` where `$T1` is a hole from a contextual type, we
  // want to eagerly turn `$T0` into a hole since it's likely that `$T1` would
  // have provided the contextual info for it.
  auto tryPropagateHole = [&](Type from, Type to) {
    if (from->isPlaceholder() && to->hasTypeVariable())
      recordTypeVariablesAsHoles(to);
  };

  if (path.empty()) {
    if (!anchor)
      return false;

    // If we have an ErrorExpr anchor, we don't need to do any more fixes.
    if (isExpr<ErrorExpr>(anchor))
      return true;

    if (getAsPattern<ExprPattern>(anchor)) {
      if (lhs->isPlaceholder() || rhs->isPlaceholder())
        return true;
    }

    // This could be:
    // - `InOutExpr` used with r-value e.g. `foo(&x)` where `x` is a `let`.
    // - `ForceValueExpr` e.g. `foo.bar! = 42` where `bar` or `foo` are
    //   immutable or a subscript e.g. `foo["bar"]! = 42`.
    if (repairByTreatingRValueAsLValue(lhs, rhs))
      return true;

    // If method reference forms a value type of the key path,
    // there is going to be a constraint to match result of the
    // member lookup to the generic parameter `V` of *KeyPath<R, V>
    // type associated with key path expression, which we need to
    // fix-up here unless last component has already a invalid type or
    // instance fix recorded.
    if (isExpr<KeyPathExpr>(anchor)) {
      if (lhs->isKnownKeyPathType() && rhs->isKnownKeyPathType()) {
        // If we have a conversion happening here, we should let fix happen in
        // simplifyRestrictedConstraint.
        if (hasAnyRestriction())
          return false;
      }

      conversionsOrFixes.push_back(IgnoreContextualType::create(
          *this, lhs, rhs, getConstraintLocator(locator)));
      return true;
    }

    if (isExpr<OverloadedDeclRefExpr>(anchor)) {
      if (lhs->is<LValueType>()) {
        conversionsOrFixes.push_back(
            TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
        return true;
      }
    }

    if (auto *OEE = getAsExpr<OptionalEvaluationExpr>(anchor)) {
      // If concrete type of the sub-expression can't be converted to the
      // type associated with optional evaluation result it could only be
      // contextual mismatch where type of the top-level expression
      // comes from contextual type or its parent expression.
      //
      // Because result type of the optional evaluation is supposed to
      // represent the type of its sub-expression with added level of
      // optionality if needed.
      auto contextualTy = simplifyType(rhs)->getOptionalObjectType();
      if (!lhs->getOptionalObjectType() && !lhs->hasTypeVariable() &&
          contextualTy && !contextualTy->isTypeVariableOrMember()) {
        auto *fixLocator = getConstraintLocator(OEE->getSubExpr());
        // If inner expression already has a fix, consider this two-way
        // mismatch as un-salvageable.
        if (hasFixFor(fixLocator))
          return false;

        conversionsOrFixes.push_back(
              IgnoreContextualType::create(*this, lhs, rhs, fixLocator));
        return true;
      }
    }

    if (auto *AE = getAsExpr<AssignExpr>(anchor)) {
      if (repairByInsertingExplicitCall(lhs, rhs))
        return true;

      if (auto *inoutExpr = dyn_cast<InOutExpr>(AE->getSrc())) {
        auto *loc = getConstraintLocator(inoutExpr);

        // Remove all of the restrictions because none of them
        // are going to succeed.
        conversionsOrFixes.erase(
            llvm::remove_if(
                conversionsOrFixes,
                [](const auto &entry) { return bool(entry.getRestriction()); }),
            conversionsOrFixes.end());

        if (hasFixFor(loc, FixKind::RemoveAddressOf))
          return true;

        conversionsOrFixes.push_back(
            RemoveAddressOf::create(*this, lhs, rhs, loc));
        return true;
      }

      if (repairByAnyToAnyObjectCast(lhs, rhs))
        return true;

      if (repairViaBridgingCast(*this, lhs, rhs, conversionsOrFixes, locator))
        return true;

      if (hasAnyRestriction())
        return false;

      // If destination is `AnyObject` it means that source doesn't conform.
      if (rhs->getWithoutSpecifierType()
              ->lookThroughAllOptionalTypes()
              ->isAnyObject()) {
        conversionsOrFixes.push_back(IgnoreAssignmentDestinationType::create(
            *this, lhs, rhs, getConstraintLocator(locator)));
        return true;
      }

      auto *destExpr = AE->getDest();
      // Literal expression as well as call/operator application can't be
      // used as an assignment destination because resulting type is immutable.
      if (isa<ApplyExpr>(destExpr) || isa<LiteralExpr>(destExpr)) {
        conversionsOrFixes.push_back(
            TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
        return true;
      }

      // If destination has a function type, it might either be
      // a property with a function type or a method reference,
      // e.g. `foo.bar = 42` neither can be used if the destination
      // is not l-value.
      auto destType = getType(destExpr);
      auto destTypeVar = destType->getAs<TypeVariableType>();
      bool destIsOrCanBindToLValue =
          destType->is<LValueType>() ||
          (destTypeVar && destTypeVar->getImpl().canBindToLValue());
      if (!destIsOrCanBindToLValue && rhs->is<FunctionType>()) {
        conversionsOrFixes.push_back(
            TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
        return true;
      }

      if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind,
                                  conversionsOrFixes, locator))
        return true;

      // `rhs` - is an assignment destination and `lhs` is its source.
      if (repairByConstructingRawRepresentableType(lhs, rhs))
        return true;

      if (repairByUsingRawValueOfRawRepresentableType(lhs, rhs))
        return true;

      // If either side is a placeholder then let's consider this
      // assignment correctly typed.
      if (lhs->isPlaceholder() || rhs->isPlaceholder())
        return true;

      // Let's try to match source and destination types one more
      // time to see whether they line up, if they do - the problem is
      // related to immutability, otherwise it's a type mismatch.
      auto result = matchTypes(lhs, rhs, ConstraintKind::Conversion,
                               TMF_ApplyingFix, locator);
      
      auto *loc = getConstraintLocator(locator);
      if (destIsOrCanBindToLValue || result == SolutionKind::Error) {
        // Let this assignment failure be diagnosed by the
        // AllowTupleTypeMismatch fix already recorded.
        if (hasFixFor(loc, FixKind::AllowTupleTypeMismatch))
          return true;

        conversionsOrFixes.push_back(
            IgnoreAssignmentDestinationType::create(*this, lhs, rhs, loc));
      } else {
        conversionsOrFixes.push_back(TreatRValueAsLValue::create(*this, loc));
      }

      return true;
    }

    return false;
  }

  if (auto *VD = getAsDecl<ValueDecl>(anchor)) {
    // Matching a witness to an protocol requirement.
    if (auto witnessElt = path[0].getAs<LocatorPathElt::Witness>()) {
      if (isa<ProtocolDecl>(VD->getDeclContext()) &&
          VD->isProtocolRequirement()) {
        auto *witness = witnessElt->getDecl();
        if ((VD->preconcurrency() || witness->preconcurrency()) &&
            // Note that the condition below is very important,
            // we need to wait until the very last moment to strip
            // the concurrency annotations from the innermost type.
            conversionsOrFixes.empty()) {
          // Allow requirements/witnesses to introduce `swift_attr` and other
          // concurrency related annotations (e.g. `& Sendable` or `@Sendable`)
          // (note that `swift_attr` in type contexts weren't supported
          // before) and for witnesses to adopt them gradually by matching
          // with a warning in non-strict concurrency mode.
          if (!(Context.isLanguageModeAtLeast(LanguageMode::v6) ||
                Context.LangOpts.StrictConcurrencyLevel ==
                    StrictConcurrency::Complete)) {
            auto strippedLHS = lhs->stripConcurrency(/*recursive=*/true,
                                                     /*dropGlobalActor=*/true);
            auto strippedRHS = rhs->stripConcurrency(/*recursive=*/true,
                                                     /*dropGlobalActor=*/true);

            // If nothing got stripped there is no reason to re-match
            // the types.
            if (!strippedLHS->isEqual(lhs) || !strippedRHS->isEqual(rhs)) {
              auto result = matchTypes(strippedLHS, strippedRHS, matchKind,
                                       flags | TMF_ApplyingFix, locator);
              if (result != SolutionKind::Error) {
                increaseScore(SK_MissingSynthesizableConformance, locator);
                return true;
              }
            }
          }
        }
      }
    }
  }

  // If there is a conversion associated with an existential member access
  // along the path, the problem is that the constraint system does not support
  // the (formally sane) upcast required to access the member.
  if (llvm::find_if(path, [](const LocatorPathElt &elt) -> bool {
        return elt.is<LocatorPathElt::ExistentialMemberAccessConversion>();
      }) != path.end()) {
    if (auto overload = findSelectedOverloadFor(castToExpr(anchor))) {
      auto &choice = overload->choice;
      conversionsOrFixes.push_back(AllowMemberRefOnExistential::create(
          *this, choice.getBaseType(), choice.getDecl(),
          DeclNameRef(choice.getDecl()->getName()),
          getConstraintLocator(locator)));

      return true;
    }
  }

  auto elt = path.back();
  switch (elt.getKind()) {
  case ConstraintLocator::LValueConversion: {
    // Ignore l-value conversion element since it has already
    // played its role.
    path.pop_back();
    // If this is a contextual mismatch between l-value types e.g.
    // `@lvalue String vs. @lvalue Int`, let's pretend that it's okay.
    if (!path.empty()) {
      if (path.back().is<LocatorPathElt::ContextualType>()) {
        auto *locator = getConstraintLocator(anchor, path.back());
        conversionsOrFixes.push_back(
            IgnoreContextualType::create(*this, lhs, rhs, locator));
        break;
      }

      // There is no subtyping between object types of inout argument/parameter.
      if (auto argConv = path.back().getAs<LocatorPathElt::ApplyArgToParam>()) {
        // Attempt conversions first.
        if (hasAnyRestriction())
          break;

        // Unwraps are allowed to preserve l-valueness so we can suggest
        // them here.
        if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind,
                                    conversionsOrFixes, locator))
          return true;

        auto *loc = getConstraintLocator(locator);

        auto result = matchTypes(lhs, rhs, ConstraintKind::Conversion,
                                 TMF_ApplyingFix, locator);

        ConstraintFix *fix = nullptr;
        if (result == SolutionKind::Error) {
          fix = AllowArgumentMismatch::create(*this, lhs, rhs, loc);
        } else {
          fix = AllowInOutConversion::create(*this, lhs, rhs, loc);
        }

        conversionsOrFixes.push_back(fix);
        break;
      }

      // If this is a problem with result type of a subscript setter,
      // let's re-attempt to repair without l-value conversion in the
      // locator to fix underlying type mismatch.
      if (path.back().is<LocatorPathElt::FunctionResult>()) {
        return repairFailures(lhs, rhs, matchKind, flags, conversionsOrFixes,
                              getConstraintLocator(anchor, path));
      }

      // If this is a function type param type mismatch in any position,
      // the mismatch we want to report is for the whole structural type.
      auto last = std::find_if(
          path.rbegin(), path.rend(), [](LocatorPathElt &elt) -> bool {
            return elt.is<LocatorPathElt::FunctionArgument>();
          });

      if (last != path.rend())
        break;
    }

    break;
  }

  case ConstraintLocator::ApplyArgToParam: {
    auto loc = getConstraintLocator(locator);

    // If this type mismatch is associated with a synthesized argument,
    // let's just ignore it because the main problem is the absence of
    // the argument.
    if (auto applyLoc = elt.getAs<LocatorPathElt::ApplyArgToParam>()) {
      if (auto *argumentList = getArgumentList(loc)) {
        // This is either synthesized argument or a default value.
        if (applyLoc->getArgIdx() >= argumentList->size()) {
          auto *calleeLoc = getCalleeLocator(loc);
          auto overload = findSelectedOverloadFor(calleeLoc);
          // If this cannot be a default value matching, let's ignore.
          if (!(overload && overload->choice.isDecl()))
            return true;

          // Ignore decls that don't have meaningful parameter lists - this
          // matches variables and parameters with function types.
          auto *paramList = overload->choice.getDecl()->getParameterList();
          if (!paramList)
            return true;

          if (!paramList->get(applyLoc->getParamIdx())->getTypeOfDefaultExpr())
            return true;
        }
      }
    }

    // Don't attempt to fix an argument being passed to a
    // _OptionalNilComparisonType parameter. Such an overload should only take
    // effect when a nil literal is used in valid code, and doesn't offer any
    // useful fixes for invalid code.
    if (auto *nominal = rhs->getAnyNominal()) {
      if (nominal->isStdlibDecl() &&
          nominal->getName() == getASTContext().Id_OptionalNilComparisonType) {
        return false;
      }
    }

    if (isForCodeCompletion()) {
      // If the argument contains the code completion location, the user has not
      // finished typing out this argument yet. Treat the mismatch as valid so
      // we don't penalize this solution.
      if (auto *arg = getAsExpr(simplifyLocatorToAnchor(loc))) {
        // Ignore synthesized args like $match in implicit pattern match
        // operator calls. Their source location is usually the same as the
        // other (explicit) argument's so source range containment alone isn't
        // sufficient.
        bool isSynthesizedArg = arg->isImplicit() && isa<DeclRefExpr>(arg);
        if (!isSynthesizedArg && isForCodeCompletion() &&
            containsIDEInspectionTarget(arg) && !lhs->isVoid() &&
            !lhs->isUninhabited())
          return true;
      }
    }

    if (repairByInsertingExplicitCall(lhs, rhs))
      break;

    bool isPatternMatching = isArgumentOfPatternMatchingOperator(loc);
    // Let's not suggest force downcasts in pattern-matching context.
    if (!isPatternMatching &&
        repairViaBridgingCast(*this, lhs, rhs, conversionsOrFixes, locator))
      break;

    // Argument is a r-value but parameter expects an l-value e.g.
    //
    // func foo(_ x: inout Int) {}
    // let x: Int = 42
    // foo(x) // `x` can't be converted to `inout Int`.
    //
    // This has to happen before checking for optionality mismatch
    // because otherwise `Int? arg conv inout Int` is going to get
    // fixed as 2 fixes - "force unwrap" + r-value -> l-value mismatch.
    if (repairByTreatingRValueAsLValue(lhs, rhs))
      break;

    // If argument in l-value type and parameter is `inout` or a pointer,
    // let's see if it's generic parameter matches and suggest adding explicit
    // `&`.
    if (lhs->is<LValueType>() &&
        (rhs->is<InOutType>() || rhs->getAnyPointerElementType())) {
      auto baseType = rhs->is<InOutType>() ? rhs->getInOutObjectType()
                                           : rhs->getAnyPointerElementType();

      // Let's use `BindToPointer` constraint here to match up base types
      // of implied `inout` argument and `inout` or pointer parameter.
      // This helps us to avoid implicit conversions associated with
      // `ArgumentConversion` constraint.
      auto result = matchTypes(lhs->getRValueType(), baseType,
                               ConstraintKind::BindToPointerType,
                               TypeMatchFlags::TMF_ApplyingFix, locator);

      if (result == SolutionKind::Solved) {
        conversionsOrFixes.push_back(AddAddressOf::create(
            *this, lhs, rhs, getConstraintLocator(locator)));
        break;
      }
    }

    // If the argument is inout and the parameter is not inout or a pointer,
    // suggest removing the &.
    if (lhs->is<InOutType>() && !rhs->is<InOutType>()) {
      auto objectType = rhs->lookThroughAllOptionalTypes();
      if (!objectType->getAnyPointerElementType()) {
        auto result = matchTypes(lhs->getInOutObjectType(), rhs,
                                 ConstraintKind::ArgumentConversion,
                                 TypeMatchFlags::TMF_ApplyingFix, locator);

        if (result == SolutionKind::Solved) {
          conversionsOrFixes.push_back(RemoveAddressOf::create(
              *this, lhs, rhs, getConstraintLocator(locator)));
          break;
        }
      }
    }

    // If parameter type is `Any` the problem might be related to
    // invalid escapiness of the argument.
    if (rhs->isAny())
      break;

    // If there are any restrictions here we need to wait and let
    // `simplifyRestrictedConstraintImpl` handle them.
    if (hasAnyRestriction())
      break;

    if (auto *fix = fixPropertyWrapperFailure(
            *this, lhs, loc,
            [&](SelectedOverload overload, VarDecl *decl, Type newBase) {
              // FIXME: There is currently no easy way to avoid attempting
              // fixes, matchTypes do not propagate `TMF_ApplyingFix` flag.
              llvm::SaveAndRestore<ConstraintSystemOptions> options(
                  Options, Options - ConstraintSystemFlags::AllowFixes);

              TypeMatchOptions flags;
              return matchTypes(newBase, rhs, ConstraintKind::Subtype, flags,
                                getConstraintLocator(locator))
                  == SolutionKind::Solved;
            },
            rhs)) {
      conversionsOrFixes.push_back(fix);
      break;
    }

    // If this is an implicit 'something-to-pointer' conversion
    // it's going to be diagnosed by specialized fix which deals
    // with generic argument mismatches.
    if (matchKind == ConstraintKind::BindToPointerType) {
      if (!rhs->isPlaceholder())
        break;
    }

    // If this is a ~= operator implicitly generated by pattern matching
    // let's not try to fix right-hand side of the operator because it's
    // a correct contextual type.
    if (isPatternMatching &&
        elt.castTo<LocatorPathElt::ApplyArgToParam>().getParamIdx() == 1)
      break;

    if (auto *fix = ExpandArrayIntoVarargs::attempt(*this, lhs, rhs, locator)) {
      conversionsOrFixes.push_back(fix);
      break;
    }

    // If parameter is a collection but argument is not, let's try
    // to try and match collection element type to the argument to
    // produce better diagnostics e.g.:
    //
    // ```
    // func foo<T>(_: [T]) {}
    // foo(1) // expected '[Int]', got 'Int'
    // ```
    if (rhs->isKnownStdlibCollectionType()) {
      std::function<Type(Type)> getArrayOrSetType = [&](Type type) -> Type {
        if (auto eltTy = type->getArrayElementType())
          return getArrayOrSetType(eltTy);

        if (auto eltTy = isSetType(type))
          return getArrayOrSetType(*eltTy);

        return type;
      };

      // Let's ignore any optional types associated with element e.g. `[T?]`
      auto rhsEltTy = getArrayOrSetType(rhs)->lookThroughAllOptionalTypes();
      (void)matchTypes(lhs, rhsEltTy, ConstraintKind::Equal, TMF_ApplyingFix,
                       locator);
    }

    // If either type has a placeholder, consider this fixed.
    if (lhs->hasPlaceholder() || rhs->hasPlaceholder())
      return true;

    // `lhs` - is an argument and `rhs` is a parameter type.
    if (repairByConstructingRawRepresentableType(lhs, rhs))
      break;

    if (repairByUsingRawValueOfRawRepresentableType(lhs, rhs))
      break;

    if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes,
                                locator))
      return true;

    {
      auto *calleeLocator = getCalleeLocator(loc);
      if (hasFixFor(calleeLocator, FixKind::AddQualifierToAccessTopLevelName)) {
        if (auto overload = findSelectedOverloadFor(calleeLocator)) {
          if (auto choice = overload->choice.getDeclOrNull()) {
            // If this is an argument of a symmetric function/operator let's
            // not fix any position rather than first because we'd just end
            // up with ambiguity instead of reporting an actual problem with
            // mismatched type since each argument can have district bindings.
            if (auto *AFD = dyn_cast<AbstractFunctionDecl>(choice)) {
              auto *paramList = AFD->getParameters();
              auto firstParamType = paramList->get(0)->getInterfaceType();
              if (elt.castTo<LocatorPathElt::ApplyArgToParam>().getParamIdx() >
                      0 &&
                  llvm::all_of(*paramList, [&](const ParamDecl *param) -> bool {
                    return param->getInterfaceType()->isEqual(firstParamType);
                  }))
                return true;
            }
          }
        }
      }
    }

    if (repairOutOfOrderArgumentsInBinaryFunction(*this, conversionsOrFixes,
                                                  loc))
      return true;

    // There is already a remove extraneous arguments fix recorded for this
    // apply arg to param locator, so let's skip the default argument mismatch.
    if (hasFixFor(loc, FixKind::RemoveExtraneousArguments))
      return true;

    // If parameter is a pack, let's see if we have already recorded
    // either synthesized or extraneous argument fixes.
    if (rhs->is<PackType>()) {
      ArrayRef tmpPath(path);

      // Ignore argument/parameter type conversion mismatch if we already
      // detected a tuple splat issue.
      if (hasFixFor(loc,
                    FixKind::DestructureTupleToMatchPackExpansionParameter))
        return true;

      // Path would end with `ApplyArgument`.
      auto *argsLoc = getConstraintLocator(anchor, tmpPath.drop_back());
      if (hasFixFor(argsLoc, FixKind::RemoveExtraneousArguments) ||
          hasFixFor(argsLoc, FixKind::AddMissingArguments))
        return true;
    }

    // If the argument couldn't be found, this could be a default value
    // type mismatch.
    if (!simplifyLocatorToAnchor(loc)) {
      auto *calleeLocator = getCalleeLocator(loc);
      unsigned paramIdx =
          loc->castLastElementTo<LocatorPathElt::ApplyArgToParam>()
              .getParamIdx();

      if (auto overload = findSelectedOverloadFor(calleeLocator)) {
        if (auto *decl = overload->choice.getDeclOrNull()) {
          if (auto paramList = decl->getParameterList()) {
            if (paramList->get(paramIdx)->getTypeOfDefaultExpr()) {
              conversionsOrFixes.push_back(
                  IgnoreDefaultExprTypeMismatch::create(*this, lhs, rhs, loc));
              break;
            }
          }
        }
      }
    }

    conversionsOrFixes.push_back(
        AllowArgumentMismatch::create(*this, lhs, rhs, loc));
    break;
  }

  case ConstraintLocator::KeyPathRoot: {
    // The root mismatch is from base U? to U or a subtype of U in keypath 
    // application so let's suggest an unwrap the optional fix.
    if (auto unwrapFix = UnwrapOptionalBaseKeyPathApplication::attempt(
            *this, lhs, rhs, getConstraintLocator(locator))) {
      conversionsOrFixes.push_back(unwrapFix);
      break;
    }

    conversionsOrFixes.push_back(AllowKeyPathRootTypeMismatch::create(
        *this, lhs, rhs, getConstraintLocator(locator)));

    break;
  }

  case ConstraintLocator::WrappedValue: {
    conversionsOrFixes.push_back(AllowWrappedValueMismatch::create(
            *this, lhs, rhs, getConstraintLocator(locator)));
    break;
  }

  case ConstraintLocator::FunctionArgument: {
    // Let's drop the last element which points to a single argument
    // and see if this is a contextual mismatch.
    path.pop_back();
    if (path.empty() ||
        !(path.back().getKind() == ConstraintLocator::ApplyArgToParam ||
          path.back().getKind() == ConstraintLocator::ContextualType))
      return false;

    if (auto argToParamElt =
            path.back().getAs<LocatorPathElt::ApplyArgToParam>()) {
      auto loc = getConstraintLocator(anchor, path);

      if (auto closure = getAsExpr<ClosureExpr>(simplifyLocatorToAnchor(loc))) {
        auto closureTy = getClosureType(closure);
        // What we have here is a form or tuple splat with no arguments
        // demonstrated by following example:
        //
        // func foo<T: P>(_: T, _: (T.Element) -> Int) {}
        // foo { 42 }
        //
        // In cases like this `T.Element` might be resolved to `Void`
        // which means that we have to try a single empty tuple argument
        // as a narrow exception to SE-0110, see `matchFunctionTypes`.
        //
        // But if `T.Element` didn't get resolved to `Void` we'd like
        // to diagnose this as a missing argument which can't be ignored or
        // a tuple is trying to be inferred as a tuple for destructuring but
        // contextual argument does not match(in this case we remove the extra
        // closure arguments).
        if (closureTy->getNumParams() == 0) {
          conversionsOrFixes.push_back(AddMissingArguments::create(
              *this, {SynthesizedArg{0, AnyFunctionType::Param(lhs)}}, loc));
          break;
        }

        // Since this is a problem with `FunctionArgument` we know that the
        // contextual type only has one parameter, if closure has more than
        // that the fix is to remove extraneous ones.
        if (closureTy->getNumParams() > 1) {
          auto callee = getCalleeLocator(loc);
          if (auto overload = findSelectedOverloadFor(callee)) {
            auto fnType = simplifyType(overload->adjustedOpenedType)
                              ->castTo<FunctionType>();
            auto paramIdx = argToParamElt->getParamIdx();
            auto paramType = fnType->getParams()[paramIdx].getParameterType();
            if (auto paramFnType = paramType->getAs<FunctionType>()) {
              conversionsOrFixes.push_back(RemoveExtraneousArguments::create(
                  *this, paramFnType, {}, loc));
              break;
            }
          }
        }
      }
    }

    auto *parentLoc = getConstraintLocator(anchor, path);

    if (lhs->is<InOutType>() != rhs->is<InOutType>()) {
      // Since `FunctionArgument` as a last locator element represents
      // a single parameter of the function type involved in a conversion
      // to another function type, see `matchFunctionTypes`. If there is already
      // a fix for the this conversion, we can just ignore individual function
      // argument in-out mismatch failure by considered this fixed.
      if (hasFixFor(parentLoc))
        return true;

      // We want to call matchTypes with the default decomposition options
      // in case there are type variables that we couldn't bind due to the
      // inout attribute mismatch.
      auto result = matchTypes(lhs->getInOutObjectType(),
                               rhs->getInOutObjectType(), matchKind,
                               getDefaultDecompositionOptions(TMF_ApplyingFix),
                               locator);

      if (result == SolutionKind::Solved) {
        conversionsOrFixes.push_back(AllowInOutConversion::create(*this, lhs,
            rhs, getConstraintLocator(locator)));
        break;
      }
    }

    // In cases like this `FunctionArgument` as a last locator element
    // represents a single parameter of the function type involved in
    // a conversion to another function type, see `matchFunctionTypes`.
    if (parentLoc->isForContextualType() ||
        parentLoc->isLastElement<LocatorPathElt::ApplyArgToParam>()) {
      // If either type has a placeholder, consider this fixed.
      if (lhs->hasPlaceholder() || rhs->hasPlaceholder())
        return true;

      // If there is a fix associated with contextual conversion or
      // a function type itself, let's ignore argument failure but
      // increase a score.
      if (hasFixFor(parentLoc)) {
        increaseScore(SK_Fix, locator);
        return true;
      }

      // Since there is only one parameter let's give it a chance to diagnose
      // a more specific error in some situations.
      if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) ||
          hasConversionOrRestriction(ConversionRestrictionKind::Existential) ||
          hasConversionOrRestriction(ConversionRestrictionKind::Superclass))
        break;

      conversionsOrFixes.push_back(AllowFunctionTypeMismatch::create(
          *this, lhs, rhs, parentLoc, /*index=*/0));
      break;
    }

    break;
  }

  case ConstraintLocator::TypeParameterRequirement:
  case ConstraintLocator::ConditionalRequirement: {
    // If either type has a placeholder, consider this fixed.
    if (lhs->hasPlaceholder() || rhs->hasPlaceholder())
      return true;

    // If requirement is something like `T == [Int]` let's let
    // type matcher a chance to match generic parameters before
    // recording a fix, because then we'll know exactly how many
    // generic parameters did not match.
    if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality))
      break;

    auto *reqLoc = getConstraintLocator(locator);

    if (isFixedRequirement(reqLoc, rhs))
      return true;

    if (auto *fix = fixRequirementFailure(*this, lhs, rhs, anchor, path)) {
      recordFixedRequirement(reqLoc, rhs);
      conversionsOrFixes.push_back(fix);
    }
    break;
  }

  case ConstraintLocator::ExistentialConstraintType: {
    if (lhs->hasPlaceholder() || rhs->hasPlaceholder())
      return true;

    // If there are any restrictions/conversions left to attempt, wait.
    if (hasAnyRestriction())
      break;

    // Drop the element introduced by DeepEquality matcher.
    path.pop_back();

    // Presence of DeepEquality conversion delayed repair but since the
    // constraint types didn't match easier, let's retry it.
    return repairFailures(ExistentialType::get(lhs), ExistentialType::get(rhs),
                          matchKind, flags, conversionsOrFixes,
                          getConstraintLocator(anchor, path));
  }

  case ConstraintLocator::ClosureBody:
  case ConstraintLocator::ClosureResult: {
    // If either type is a placeholder, consider this fixed, eagerly propagating
    // a hole from the contextual type.
    if (lhs->isPlaceholder() || rhs->isPlaceholder()) {
      tryPropagateHole(rhs, lhs);
      return true;
    }

    if (repairByInsertingExplicitCall(lhs, rhs))
      break;

    if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes,
                                locator))
      return true;

    // If we could record a generic arguments mismatch instead of this fix,
    // don't record a contextual type mismatch here.
    if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality))
      break;

    auto *fix = IgnoreContextualType::create(*this, lhs, rhs,
                                             getConstraintLocator(locator));
    conversionsOrFixes.push_back(fix);
    break;
  }

  case ConstraintLocator::ContextualType: {
    // If either type is a placeholder, consider this fixed, eagerly propagating
    // a hole from the contextual type.
    if (lhs->isPlaceholder() || rhs->isPlaceholder()) {
      tryPropagateHole(rhs, lhs);
      return true;
    }

    // If either side is not yet resolved, it's too early for this fix.
    if (lhs->isTypeVariableOrMember() || rhs->isTypeVariableOrMember())
      break;

    // If there is already a fix for contextual failure, let's not
    // record a duplicate one.
    if (hasFixFor(getConstraintLocator(locator)))
      return true;

    auto purpose = getContextualTypePurpose(anchor);
    if (rhs->isVoid() && purpose == CTP_ReturnStmt) {
      conversionsOrFixes.push_back(
          RemoveReturn::create(*this, lhs, getConstraintLocator(locator)));
      return true;
    }

    if (repairByInsertingExplicitCall(lhs, rhs))
      break;

    if (repairByAnyToAnyObjectCast(lhs, rhs))
      break;

    if (repairViaBridgingCast(*this, lhs, rhs, conversionsOrFixes, locator))
      break;

    if (lhs->is<FunctionType>() && !rhs->is<AnyFunctionType>() &&
        isExpr<ClosureExpr>(anchor)) {
      auto *fix = ContextualMismatch::create(*this, lhs, rhs,
                                             getConstraintLocator(locator));
      conversionsOrFixes.push_back(fix);
    }

    // Solver can unwrap contextual type in an unlabeled one-element tuple
    // while matching type to a tuple that contains one or more pack expansion
    // types (because such tuples can loose their elements under substitution),
    // if that's the case, let's just produce a regular contextual mismatch fix.
    if (auto contextualType = isSingleUnlabeledElementTuple(rhs)) {
      rhs = contextualType;
    }

    if (purpose == CTP_Initialization && lhs->is<TupleType>() &&
        rhs->is<TupleType>()) {
      auto *fix = AllowTupleTypeMismatch::create(*this, lhs, rhs,
                                                 getConstraintLocator(locator));
      conversionsOrFixes.push_back(fix);
      break;
    }

    if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes,
                                locator))
      return true;

    // Let's wait until both sides are of the same optionality before
    // attempting `.rawValue` fix.
    if (hasConversionOrRestriction(ConversionRestrictionKind::ValueToOptional))
      break;

    if (repairByUsingRawValueOfRawRepresentableType(lhs, rhs))
      break;

    // If there are any restrictions here we need to wait and let
    // `simplifyRestrictedConstraintImpl` handle them.
    if (hasAnyRestriction())
      break;

    // `lhs` - is an result type and `rhs` is a contextual type.
    if (repairByConstructingRawRepresentableType(lhs, rhs))
      break;

    conversionsOrFixes.push_back(IgnoreContextualType::create(
        *this, lhs, rhs, getConstraintLocator(locator)));
    break;
  }

  case ConstraintLocator::FunctionResult: {
    if (lhs->isPlaceholder() || rhs->isPlaceholder()) {
      recordAnyTypeVarAsPotentialHole(lhs);
      recordAnyTypeVarAsPotentialHole(rhs);
      return true;
    }

    if (auto *kpExpr = getAsExpr<KeyPathExpr>(anchor)) {
      return maybeRepairKeyPathResultFailure(kpExpr);
    }

    auto *loc = getConstraintLocator(anchor, {path.begin(), path.end() - 1});
    // If this is a mismatch between contextual type and (trailing)
    // closure with explicitly specified result type let's record it
    // as contextual type mismatch.
    if (loc->isLastElement<LocatorPathElt::ContextualType>() ||
        loc->isLastElement<LocatorPathElt::ApplyArgToParam>()) {
      auto argument = simplifyLocatorToAnchor(loc);
      if (isExpr<ClosureExpr>(argument)) {
        auto *locator =
            getConstraintLocator(argument, ConstraintLocator::ClosureResult);

        if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind,
                                    conversionsOrFixes, locator))
          return true;

        conversionsOrFixes.push_back(
            IgnoreContextualType::create(*this, lhs, rhs, locator));
        break;
      }
    }

    // Handle function result coerce expression wrong type conversion.
    if (isExpr<CoerceExpr>(anchor)) {
      auto *fix =
          ContextualMismatch::create(*this, lhs, rhs, loc);
      conversionsOrFixes.push_back(fix);
      break;
    }
    LLVM_FALLTHROUGH;
  }

  case ConstraintLocator::Member:
  case ConstraintLocator::DynamicLookupResult: {
    // Most likely this is an attempt to use get-only subscript as mutating,
    // or assign a value of a result of function/member ref e.g. `foo() = 42`
    // or `foo.bar = 42`, or `foo.bar()! = 42`.
    if (repairByTreatingRValueAsLValue(rhs, lhs))
      break;

    // `apply argument` -> `arg/param compare` ->
    // `@autoclosure result` -> `function result`
    if (path.size() > 3) {
      const auto &elt = path[path.size() - 2];
      if (elt.getKind() == ConstraintLocator::AutoclosureResult &&
          repairByInsertingExplicitCall(lhs, rhs))
        return true;
    }
    break;
  }

  case ConstraintLocator::AutoclosureResult: {
    if (repairByInsertingExplicitCall(lhs, rhs))
      return true;

    auto isPointerType = [](Type type) -> bool {
      return bool(
          type->lookThroughAllOptionalTypes()->getAnyPointerElementType());
    };

    // Let's see whether this is an implicit conversion to a pointer type
    // which is invalid in @autoclosure context e.g. from `inout`, Array
    // or String.
    if (!isPointerType(lhs) && isPointerType(rhs)) {
      auto result = matchTypes(
          lhs, rhs, ConstraintKind::ArgumentConversion,
          TypeMatchFlags::TMF_ApplyingFix,
          locator.withPathElement(ConstraintLocator::FunctionArgument));

      if (result == SolutionKind::Solved)
        conversionsOrFixes.push_back(AllowAutoClosurePointerConversion::create(
            *this, lhs, rhs, getConstraintLocator(locator)));
    }

    // In situations like this:
    //
    // struct S<T> {}
    // func foo(_: @autoclosure () -> S<Int>) {}
    // foo(S<String>())
    //
    // Generic type conversion mismatch is a better fix which is going to
    // point to the generic arguments that did not align properly.
    if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality))
      break;

    conversionsOrFixes.push_back(AllowArgumentMismatch::create(
        *this, lhs, rhs, getConstraintLocator(locator)));
    break;
  }

  case ConstraintLocator::TupleElement: {
    if (lhs->isPlaceholder() || rhs->isPlaceholder()) {
      recordAnyTypeVarAsPotentialHole(lhs);
      recordAnyTypeVarAsPotentialHole(rhs);
      return true;
    }

    if (hasAnyRestriction())
      return false;

    if (isExpr<ArrayExpr>(anchor) || isExpr<DictionaryExpr>(anchor)) {
      // If we could record a generic arguments mismatch instead of this fix,
      // don't record a ContextualMismatch here.
      if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality))
        break;

      // We already have a fix for trying to initialize/assign an array literal
      // to a dictionary type. In this case elements mismatch only add extra
      // verbosity to the diagnostic. So let's skip the fix and only increase
      // the score to focus on suggesting using dictionary literal instead.
      path.pop_back();
      auto loc = getConstraintLocator(anchor, path);
      if (hasFixFor(loc, FixKind::TreatArrayLiteralAsDictionary)) {
        increaseScore(SK_Fix, loc);
        return true;
      }

      if (loc->directlyAt<ArrayExpr>() && lhs->isArray() &&
          (rhs->isInlineArray() || rhs->is_InlineArray())) {
        auto literalCount = castToExpr<ArrayExpr>(anchor)->getNumElements();
        if (auto inlineCount = rhs->castTo<BoundGenericStructType>()
                                   ->getGenericArgs()[0]
                                   ->getAs<IntegerType>()) {
          if (inlineCount->getValue() != literalCount) {
            conversionsOrFixes.push_back(
                AllowInlineArrayLiteralCountMismatch::create(
                    *this, inlineCount->getValue().getSExtValue(), literalCount,
                    loc));
            return true;
          }
        }
      }

      conversionsOrFixes.push_back(CollectionElementContextualMismatch::create(
          *this, lhs, rhs, getConstraintLocator(locator)));
      break;
    }

    // Drop the `tuple element` locator element so that all tuple element
    // mismatches within the same tuple type can be coalesced later.
    auto index = elt.getAs<LocatorPathElt::TupleElement>()->getIndex();
    path.pop_back();

    // Drop the tuple type path elements too, but extract each tuple type first.
    if (!path.empty() && path.back().is<LocatorPathElt::TupleType>()) {
      rhs = path.back().getAs<LocatorPathElt::TupleType>()->getType();
      path.pop_back();
      lhs = path.back().getAs<LocatorPathElt::TupleType>()->getType();
      path.pop_back();
    }

    auto *tupleLocator = getConstraintLocator(locator.getAnchor(), path);

    // Let this fail if it's a contextual mismatch with sequence element types,
    // as there's a special fix for that.
    if (tupleLocator->isLastElement<LocatorPathElt::SequenceElementType>())
      break;

    // Generic argument/requirement failures have a more general fix which
    // is attached to a parent type and aggregates all argument failures
    // into a single fix.
    if (tupleLocator->isLastElement<LocatorPathElt::AnyRequirement>() ||
        tupleLocator->isLastElement<LocatorPathElt::GenericArgument>())
      break;

    // If the mismatch is a part of either optional-to-optional or
    // value-to-optional conversions, let's allow fix refer to a complete
    // top level type and not just a part of it.
    if (tupleLocator->findLast<LocatorPathElt::OptionalInjection>())
      break;

    if (tupleLocator->isForContextualType()) {
      if (auto contextualTy = isSingleUnlabeledElementTuple(rhs)) {
        return repairFailures(lhs, contextualTy, matchKind, flags,
                              conversionsOrFixes, tupleLocator);
      }
    }

    ConstraintFix *fix;
    if (tupleLocator->isLastElement<LocatorPathElt::FunctionArgument>()) {
      fix = AllowFunctionTypeMismatch::create(*this, lhs, rhs, tupleLocator, index);
    } else if (tupleLocator->isLastElement<LocatorPathElt::ApplyArgToParam>()) {
      fix = AllowArgumentMismatch::create(*this, lhs, rhs, tupleLocator);
    } else {
      fix = AllowTupleTypeMismatch::create(*this, lhs, rhs, tupleLocator, index);
    }
    conversionsOrFixes.push_back(fix);
    break;
  }

  case ConstraintLocator::PackElement: {
    path.pop_back();

    if (!path.empty() && path.back().is<LocatorPathElt::PackType>())
      path.pop_back();

    if (!path.empty() && path.back().is<LocatorPathElt::PackType>())
      path.pop_back();

    return repairFailures(lhs, rhs, matchKind, flags, conversionsOrFixes,
                          getConstraintLocator(anchor, path));
  }

  case ConstraintLocator::PackShape: {
    auto *shapeLocator = getConstraintLocator(locator);

    // FIXME: If the anchor isn't a pack expansion, this shape requirement
    // came from a same-shape generic requirement, which will fail separately
    // with an applied requirement fix. Currently, pack shapes can themselves be
    // pack types with pack expansions, so matching shape types can recursively
    // add ShapeOf constraints. For now, skip fixing the nested ones to avoid
    // cascading diagnostics.
    if (!isExpr<PackExpansionExpr>(shapeLocator->getAnchor()))
      return true;

    auto *fix = SkipSameShapeRequirement::create(*this, lhs, rhs, shapeLocator);
    conversionsOrFixes.push_back(fix);
    break;
  }

  case ConstraintLocator::SequenceElementType: {
    if (lhs->isPlaceholder() || rhs->isPlaceholder()) {
      recordAnyTypeVarAsPotentialHole(lhs);
      recordAnyTypeVarAsPotentialHole(rhs);
      return true;
    }

    // This is going to be diagnosed as `missing conformance`,
    // so no need to create duplicate fixes.
    if (rhs->isExistentialType())
      break;

    conversionsOrFixes.push_back(CollectionElementContextualMismatch::create(
        *this, lhs, rhs, getConstraintLocator(locator)));
    break;
  }

  case ConstraintLocator::SubscriptMember: {
    if (repairByTreatingRValueAsLValue(lhs, rhs))
      break;

    break;
  }

  case ConstraintLocator::Condition: {
    // If the condition is already a hole, we're done.
    if (lhs->isPlaceholder())
      return true;

    if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes,
                                locator))
      return true;

    if (repairByInsertingExplicitCall(lhs, rhs))
      return true;

    conversionsOrFixes.push_back(IgnoreContextualType::create(
        *this, lhs, rhs, getConstraintLocator(locator)));
    break;
  }

  case ConstraintLocator::UnresolvedMemberChainResult: {
    // Ignore this mismatch if base or result is already a hole.
    if (lhs->isPlaceholder() || rhs->isPlaceholder())
      return true;

    if (hasAnyRestriction())
      return false;

    if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes,
                                locator))
      return true;

    if (repairByTreatingRValueAsLValue(lhs, rhs))
      break;

    // If there is a type mismatch here it's contextual e.g.,
    // `let x: E = .foo(42)`, where `.foo` is a member of `E`
    // but produces an incorrect type.
    auto *fix = IgnoreContextualType::create(*this, lhs, rhs,
                                             getConstraintLocator(locator));
    conversionsOrFixes.push_back(fix);
    break;
  }

  case ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice: {
    // If this is an attempt to use readonly IUO as a destination
    // of an assignment e.g.
    //
    // let x: Int! = 0
    // x = 42 <- `x` can be either `Int?` or `Int` but it can't be an l-value.
    if (lhs->is<LValueType>() && !rhs->is<LValueType>()) {
      auto result = matchTypes(lhs->getWithoutSpecifierType(), rhs, matchKind,
                               TMF_ApplyingFix, locator);

      if (result == SolutionKind::Solved) {
        conversionsOrFixes.push_back(
            TreatRValueAsLValue::create(*this, getConstraintLocator(locator)));
      }
    }
    break;
  }

  case ConstraintLocator::InstanceType: {
    if (lhs->hasPlaceholder() || rhs->hasPlaceholder())
      return true;

    break;
  }

  case ConstraintLocator::OptionalInjection: {
    if (lhs->isPlaceholder() || rhs->isPlaceholder())
      return true;

    if (hasAnyRestriction())
      return false;

    if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes,
                                locator))
      return true;

    if (path.size() > 1) {
      path.pop_back();
      if (path.back().is<LocatorPathElt::SequenceElementType>()) {
        conversionsOrFixes.push_back(
            CollectionElementContextualMismatch::create(
                *this, lhs, rhs, getConstraintLocator(anchor, path)));
        return true;
      }
      if (path.back().is<LocatorPathElt::ApplyArgToParam>()) {
        conversionsOrFixes.push_back(
            AllowArgumentMismatch::create(*this, lhs, rhs,
                                          getConstraintLocator(anchor, path)));
        return true;
      }
    }
    break;
  }

  case ConstraintLocator::TernaryBranch:
  case ConstraintLocator::SingleValueStmtResult: {
    if (lhs->hasPlaceholder() || rhs->hasPlaceholder())
      return true;

    // If there's a contextual type, let's consider it the source of truth and
    // produce a contextual mismatch instead of  per-branch failure, because
    // it's a better pointer than potential then-to-else type mismatch.
    if (auto contextualType =
            getContextualType(anchor, /*forConstraint=*/false)) {
      auto purpose = getContextualTypePurpose(anchor);
      if (contextualType->isEqual(rhs)) {
        auto *loc = getConstraintLocator(
            anchor, LocatorPathElt::ContextualType(purpose));
        if (hasFixFor(loc, FixKind::IgnoreContextualType))
          return true;

        if (contextualType->isVoid() && purpose == CTP_ReturnStmt) {
          conversionsOrFixes.push_back(RemoveReturn::create(*this, lhs, loc));
          break;
        }

        conversionsOrFixes.push_back(
            IgnoreContextualType::create(*this, lhs, rhs, loc));
        break;
      }
    }

    // If there is no contextual type, this is most likely a contextual type
    // mismatch between the branches.
    conversionsOrFixes.push_back(ContextualMismatch::create(
        *this, lhs, rhs, getConstraintLocator(locator)));
    break;
  }

  case ConstraintLocator::EnumPatternImplicitCastMatch: {
    // If either type is a placeholder, consider this fixed.
    if (lhs->isPlaceholder() || rhs->isPlaceholder())
      return true;

    // If we're converting to an existential, we'll diagnose failures in
    // the conformance constraint.
    if (hasConversionOrRestriction(ConversionRestrictionKind::Existential))
      return false;

    conversionsOrFixes.push_back(ContextualMismatch::create(
        *this, lhs, rhs, getConstraintLocator(locator)));
    break;
  }

  case ConstraintLocator::PatternMatch: {
    // Let's let the matching (i.e. deep equality) happen before attempting any
    // fixes for patterns.
    if (hasAnyRestriction())
      return false;

    auto *pattern = elt.castTo<LocatorPathElt::PatternMatch>().getPattern();

    // TODO: We ought to introduce a new locator element for this.
    bool isMemberMatch =
        lhs->is<FunctionType>() && isa<EnumElementPattern>(pattern);

    // If member reference couldn't be resolved, let's allow pattern
    // to have holes.
    if (rhs->isPlaceholder() && isMemberMatch) {
      recordAnyTypeVarAsPotentialHole(lhs);
      return true;
    }

    // If either type is a placeholder, consider this fixed.
    if (lhs->isPlaceholder() || rhs->isPlaceholder())
      return true;

    if (isMemberMatch) {
      conversionsOrFixes.push_back(AllowAssociatedValueMismatch::create(
          *this, lhs, rhs, getConstraintLocator(locator)));
      break;
    }

    // `weak` declaration with an explicit non-optional type e.g.
    // `weak x: X = ...` where `X` is a class.
    if (auto *TP = dyn_cast<TypedPattern>(pattern)) {
      if (auto *NP = dyn_cast<NamedPattern>(TP->getSubPattern())) {
        auto *var = NP->getDecl();

        auto ROK = ReferenceOwnership::Strong;
        if (auto *OA = var->getAttrs().getAttribute<ReferenceOwnershipAttr>())
          ROK = OA->get();

        if (!lhs->getOptionalObjectType() &&
            optionalityOf(ROK) == ReferenceOwnershipOptionality::Required) {
          conversionsOrFixes.push_back(
              AllowNonOptionalWeak::create(*this, getConstraintLocator(NP)));
          break;
        }
      }
    }

    conversionsOrFixes.push_back(ContextualMismatch::create(
        *this, lhs, rhs, getConstraintLocator(locator)));

    break;
  }

  case ConstraintLocator::GenericArgument: {
    // If any of the types is a placeholder, consider it fixed.
    if (lhs->isPlaceholder() || rhs->isPlaceholder())
      return true;

    // Ignoring the generic argument because we may have a generic requirement
    // failure e.g. `String bind T.Element`, so let's drop the generic argument
    // path element and recurse in repairFailures to check and potentially
    // record the requirement failure fix.
    auto genericArgElt =
        path.pop_back_val().castTo<LocatorPathElt::GenericArgument>();

    // If we have something like ... -> type req # -> pack element #, we're
    // solving a requirement of the form T : P where T is a type parameter pack
    if (!path.empty() && path.back().is<LocatorPathElt::PackElement>())
      path.pop_back();

    if (!path.empty()) {
      if (path.back().is<LocatorPathElt::AnyRequirement>()) {
        return repairFailures(lhs, rhs, matchKind, flags, conversionsOrFixes,
                              getConstraintLocator(anchor, path));
      }

      if (auto argConv = path.back().getAs<LocatorPathElt::ApplyArgToParam>()) {
        auto argIdx = argConv->getArgIdx();
        auto paramIdx = argConv->getParamIdx();

        auto *argLoc = getConstraintLocator(anchor, path);
        if (auto overload = findSelectedOverloadFor(getCalleeLocator(argLoc))) {
          auto *overloadTy =
              simplifyType(overload->boundType)->castTo<FunctionType>();
          auto *argList = getArgumentList(argLoc);
          // If the argument was synthesized by the solver, let's fail. This can
          // happen when a parameter type is optional and (currently) inference
          // would try both optional and non-optional bindings for the new
          // argument which cases a mismatch in one of the conversion cases.
          if (!argList || argList->size() <= argIdx)
            return false;

          conversionsOrFixes.push_back(AllowArgumentMismatch::create(
              *this, getType(argList->getExpr(argIdx)),
              overloadTy->getParams()[paramIdx].getPlainType(), argLoc));
          return true;
        }
      }
    }

    // When the solver sets `TMF_MatchingGenericArguments` it means
    // that it's matching generic argument pairs to identify any mismatches
    // as part of larger matching of two generic types. Letting this
    // fail results in a single fix that aggregates all mismatch locations.
    //
    // Types are not always resolved enough to enable that which means
    // that the comparison should be delayed, which brings us here - a
    // standalone constraint that represents such a match, in such cases
    // we create a fix per mismatch location and coalesce them during
    // diagnostics.
    if (flags.contains(TMF_MatchingGenericArguments))
      break;

    if (hasAnyRestriction())
      break;

    Type fromType;
    Type toType;

    if (path.size() >= 2) {
      if (path[path.size() - 2].is<LocatorPathElt::GenericType>()) {
        fromType = path[path.size() - 2]
                       .castTo<LocatorPathElt::GenericType>()
                       .getType();
      }

      if (path[path.size() - 1].is<LocatorPathElt::GenericType>()) {
        toType = path[path.size() - 1]
                     .castTo<LocatorPathElt::GenericType>()
                     .getType();
      }
    }

    if (!fromType || !toType)
      break;

    Type fromObjectType, toObjectType;
    unsigned fromUnwraps, toUnwraps;

    std::tie(fromObjectType, fromUnwraps) = getObjectTypeAndNumUnwraps(lhs);
    std::tie(toObjectType, toUnwraps) = getObjectTypeAndNumUnwraps(rhs);

    // If the bound contextual type is more optional than the binding type, then
    // propogate binding type to contextual type and attempt to solve.
    if (fromUnwraps < toUnwraps) {
      (void)matchTypes(fromObjectType, toObjectType, ConstraintKind::Bind,
                       TMF_ApplyingFix, locator);
    }

    // Drop both `GenericType` elements.
    path.pop_back();
    path.pop_back();

    ConstraintFix *fix = nullptr;
    auto *fixLoc = getConstraintLocator(anchor, path);

    if (fixLoc->isLastElement<LocatorPathElt::AnyRequirement>()) {
      fix = fixRequirementFailure(*this, fromType, toType, anchor, path);
    } else if (fixLoc->isLastElement<LocatorPathElt::TupleElement>()) {
      return repairFailures(lhs, rhs, matchKind, flags, conversionsOrFixes,
                            fixLoc);
    } else if (!lhs->mayHaveSuperclass() && rhs->isAnyObject()) {
      fix = AllowNonClassTypeToConvertToAnyObject::create(*this, fromType,
                                                          fixLoc);
    } else {
      fix = GenericArgumentsMismatch::create(
          *this, fromType, toType, {genericArgElt.getIndex()}, fixLoc);
    }

    if (!fix)
      break;

    conversionsOrFixes.push_back(fix);
    return true;
  }

  case ConstraintLocator::GlobalActorType: {
    // Drop global actor element as it servers only to indentify the global
    // actor matching.
    path.pop_back();

    conversionsOrFixes.push_back(AllowGlobalActorMismatch::create(
        *this, lhs, rhs, getConstraintLocator(anchor, path)));
    break;
  }

  case ConstraintLocator::CoercionOperand: {
    // If either type is a placeholder, consider this fixed, eagerly propagating
    // a hole from the contextual type.
    if (lhs->isPlaceholder() || rhs->isPlaceholder()) {
      tryPropagateHole(rhs, lhs);
      return true;
    }

    auto *coercion = castToExpr<CoerceExpr>(anchor);

    // Coercion from T.Type to T.Protocol.
    if (hasConversionOrRestriction(
            ConversionRestrictionKind::MetatypeToExistentialMetatype))
      return false;

    if (hasConversionOrRestriction(ConversionRestrictionKind::Superclass))
      return false;

    // Let's check whether the sub-expression is an optional type which
    // is possible to unwrap (either by force or `??`) to satisfy the cast,
    // otherwise we'd have to fallback to force downcast.
    if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind,
                                conversionsOrFixes,
                                getConstraintLocator(coercion->getSubExpr())))
      return true;

    // If the result type of the coercion has an value to optional conversion
    // we can instead suggest the conditional downcast as it is safer in
    // situations like conditional binding.
    auto useConditionalCast =
        llvm::any_of(ConstraintRestrictions, [&](const auto &restriction) {
          Type type1, type2;
          std::tie(type1, type2) = restriction.first;
          auto restrictionKind = restriction.second;

          if (restrictionKind != ConversionRestrictionKind::ValueToOptional)
            return false;

          return rhs->isEqual(type1);
        });

    // Repair a coercion ('as') with a runtime checked cast ('as!' or 'as?').
    if (auto *coerceToCheckCastFix =
            CoerceToCheckedCast::attempt(*this, lhs, rhs, useConditionalCast,
                                         getConstraintLocator(locator))) {
      conversionsOrFixes.push_back(coerceToCheckCastFix);
      return true;
    }

    // If it has a deep equality restriction, defer the diagnostic to
    // GenericMismatch.
    if (hasConversionOrRestriction(ConversionRestrictionKind::DeepEquality) &&
        !hasConversionOrRestriction(
            ConversionRestrictionKind::OptionalToOptional)) {
      return false;
    }

    if (hasConversionOrRestriction(ConversionRestrictionKind::Existential))
      return false;

    auto *fix = ContextualMismatch::create(*this, lhs, rhs,
                                           getConstraintLocator(locator));
    conversionsOrFixes.push_back(fix);
    return true;
  }
  case ConstraintLocator::KeyPathValue: {
    if (maybeRepairKeyPathResultFailure(getAsExpr<KeyPathExpr>(anchor)))
      return true;

    break;
  }

  case ConstraintLocator::KeyPathDynamicMember: {
    if (lhs->isPlaceholder() || rhs->isPlaceholder())
      return true;
    break;
  }

  default:
    break;
  }

  return !conversionsOrFixes.empty();
}

static bool isTupleWithUnresolvedPackExpansion(Type type) {
  if (auto *tuple = type->getAs<TupleType>()) {
    return llvm::any_of(tuple->getElements(), [&](const TupleTypeElt &elt) {
      if (auto typeVar = elt.getType()->getAs<TypeVariableType>())
        return typeVar->getImpl().isPackExpansion();
      return false;
    });
  }
  return false;
}

static bool isDependentMemberTypeWithBaseThatContainsUnresolvedPackExpansions(
    ConstraintSystem &cs, Type type) {
  if (!type->is<DependentMemberType>())
    return false;

  // FIXME: It's really unfortunate we need to use `simplifyType` here since
  // this is called from `matchTypes`. We need to completely simplify the type
  // though since pack expansions can be present in fixed types for nested
  // type vars.
  auto baseTy = cs.simplifyType(type->getDependentMemberRoot());
  llvm::SmallPtrSet<TypeVariableType *, 2> typeVars;
  baseTy->getTypeVariables(typeVars);
  return llvm::any_of(typeVars, [](const TypeVariableType *typeVar) {
    return typeVar->getImpl().isPackExpansion();
  });
}

ConstraintSystem::ImpliedResultConversionKind
ConstraintSystem::getImpliedResultConversionKind(ConstraintLocator *locator) {
  if (locator->isLastElement<LocatorPathElt::ClosureResult>())
    return ImpliedResultConversionKind::ToVoid;

  if (locator->isLastElement<LocatorPathElt::ClosureBody>() ||
      locator->isForContextualType(CTP_ReturnStmt) ||
      locator->isForContextualType(CTP_ClosureResult) ||
      locator->isForSingleValueStmtBranch()) {
    // Implied results can occur for closure bodies, returns, and if/switch
    // expression branches.
    //
    // We only allow the Void conversion for implied results in a closure
    // context. In the more general case, we only allow the Never conversion.
    // For explicit branches, no conversions are allowed, unless this is for
    // a single expression body closure, in which case we still allow the
    // Never conversion.
    if (auto *E = getAsExpr(simplifyLocatorToAnchor(locator))) {
      if (auto kind = isImpliedResult(E)) {
        switch (*kind) {
        case ImpliedResultKind::Regular:
          return ImpliedResultConversionKind::FromNever;
        case ImpliedResultKind::ForClosure:
          return ImpliedResultConversionKind::ToVoid;
        }
      } else if (locator->isLastElement<LocatorPathElt::ClosureBody>()) {
        // Even if explicit, we always allow uninhabited types in single
        // expression closures.
        return ImpliedResultConversionKind::FromNever;
      }
    }
  }

  return ImpliedResultConversionKind::None;
}

ConstraintSystem::SolutionKind
ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind,
                             TypeMatchOptions flags,
                             ConstraintLocatorBuilder locator) {
  auto origType1 = type1;
  auto origType2 = type2;

  // If we have type variables that have been bound to fixed types, look through
  // to the fixed type.
  type1 = getFixedTypeRecursive(type1, flags, kind == ConstraintKind::Equal);
  type2 = getFixedTypeRecursive(type2, flags, kind == ConstraintKind::Equal);

  auto desugar1 = type1->getDesugaredType();
  auto desugar2 = type2->getDesugaredType();

  // Refuse to match types with errors. We ought to consider making this an
  // assert (clients should instead use holes), but for now let's bail out of
  // solving.
  if (desugar1->hasError() || desugar2->hasError())
    return SolutionKind::Error;

  // If both sides are dependent members without type variables, it's
  // possible that base type is incorrect e.g. `Foo.Element` where `Foo`
  // is a concrete type substituted for generic parameter,
  // so checking equality here would lead to incorrect behavior,
  // let's defer it until later proper check.
  if (!(desugar1->is<DependentMemberType>() &&
        desugar2->is<DependentMemberType>())) {
    // If the types are obviously equivalent, we're done.
    if (desugar1->isEqual(desugar2) && !isa<InOutType>(desugar2)) {
      return SolutionKind::Solved;
    }
  }

  // Local function that should be used to produce the return value whenever
  // this function was unable to resolve the constraint. It should be used
  // within \c matchTypes() as
  //
  //   return formUnsolvedResult();
  //
  // along any unsolved path. No other returns should produce
  // SolutionKind::Unsolved or inspect TMF_GenerateConstraints.
  auto formUnsolvedResult = [&](bool useOriginalTypes = false) {
    // If we're supposed to generate constraints (i.e., this is a
    // newly-generated constraint), do so now.
    if (flags.contains(TMF_GenerateConstraints)) {
      // Add a new constraint between these types. We consider the current
      // type-matching problem to the "solved" by this addition, because
      // this new constraint will be solved at a later point.
      // Obviously, this must not happen at the top level, or the
      // algorithm would not terminate.
      if (useOriginalTypes) {
        addUnsolvedConstraint(Constraint::create(
            *this, kind, origType1, origType2, getConstraintLocator(locator)));
      } else {
        addUnsolvedConstraint(Constraint::create(
            *this, kind, type1, type2, getConstraintLocator(locator)));
      }
      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  };

  auto *typeVar1 = dyn_cast<TypeVariableType>(desugar1);
  auto *typeVar2 = dyn_cast<TypeVariableType>(desugar2);

  // If either (or both) types are type variables, unify the type variables.
  if (typeVar1 || typeVar2) {
    // Handle the easy case of both being type variables, and being
    // identical, first.
    if (typeVar1 && typeVar2) {
      auto rep1 = getRepresentative(typeVar1);
      auto rep2 = getRepresentative(typeVar2);
      if (rep1 == rep2) {
        // We already merged these two types, so this constraint is
        // trivially solved.
        return SolutionKind::Solved;
      }
    }

    switch (kind) {
    case ConstraintKind::Bind:
    case ConstraintKind::BindToPointerType:
    case ConstraintKind::Equal: {
      if (typeVar1 && typeVar2) {
        auto rep1 = getRepresentative(typeVar1);
        auto rep2 = getRepresentative(typeVar2);

        // Pack expansion variables cannot be merged because
        // they involve other type variables.
        if (rep1->getImpl().isPackExpansion() ||
            rep2->getImpl().isPackExpansion())
          return formUnsolvedResult();

        // If exactly one of the type variables can bind to an lvalue, we
        // can't merge these two type variables.
        if (kind == ConstraintKind::Equal &&
            rep1->getImpl().canBindToLValue()
              != rep2->getImpl().canBindToLValue())
          return formUnsolvedResult();

        // Merge the equivalence classes corresponding to these two variables.
        mergeEquivalenceClasses(rep1, rep2, /*updateWorkList=*/true);
        return SolutionKind::Solved;
      }

      // If type variable represents a key path value type, defer binding it to
      // contextual type in diagnostic mode. We want it to be bound from the
      // last key path component to help with diagnostics.
      if (shouldAttemptFixes()) {
        if (typeVar1 && typeVar1->getImpl().isKeyPathValue() &&
            !flags.contains(TMF_BindingTypeVariable))
          return formUnsolvedResult();
      }

      assert((type1->is<TypeVariableType>() != type2->is<TypeVariableType>()) &&
             "Expected a type variable and a non type variable!");

      auto *typeVar = typeVar1 ? typeVar1 : typeVar2;
      auto type = typeVar1 ? type2 : type1;

      return matchTypesBindTypeVar(typeVar, type, kind, flags, locator,
                                   formUnsolvedResult);
    }

    case ConstraintKind::BindParam: {
      if (typeVar2 && !typeVar1) {
        // Simplify the left-hand type and perform the "occurs" check.
        auto rep2 = getRepresentative(typeVar2);
        type1 = simplifyType(type1, flags);
        if (!isBindable(typeVar2, type1))
          return formUnsolvedResult();

        if (auto *iot = type1->getAs<InOutType>()) {
          if (!rep2->getImpl().canBindToLValue())
            return SolutionKind::Error;
          assignFixedType(rep2, LValueType::get(iot->getObjectType()));
        } else {
          assignFixedType(rep2, type1);
        }
        return SolutionKind::Solved;
      } else if (typeVar1 && !typeVar2) {
        // Simplify the right-hand type and perform the "occurs" check.
        auto rep1 = getRepresentative(typeVar1);
        type2 = simplifyType(type2, flags);
        if (!isBindable(rep1, type2))
          return formUnsolvedResult();

        if (auto *lvt = type2->getAs<LValueType>()) {
          if (!rep1->getImpl().canBindToInOut())
            return SolutionKind::Error;
          assignFixedType(rep1, InOutType::get(lvt->getObjectType()));
        } else {
          assignFixedType(rep1, type2);
        }
        return SolutionKind::Solved;
      } if (typeVar1 && typeVar2) {
        auto rep1 = getRepresentative(typeVar1);
        auto rep2 = getRepresentative(typeVar2);

        // Pack expansion variables cannot be merged because
        // they involve other type variables.
        if (rep1->getImpl().isPackExpansion() ||
            rep2->getImpl().isPackExpansion())
          return formUnsolvedResult();

        if (!rep1->getImpl().canBindToInOut() ||
            !rep2->getImpl().canBindToLValue()) {
          // Merge the equivalence classes corresponding to these two variables.
          mergeEquivalenceClasses(rep1, rep2, /*updateWorkList=*/true);
          return SolutionKind::Solved;
        }
      }

      return formUnsolvedResult();
    }

    case ConstraintKind::Subtype:
    case ConstraintKind::Conversion:
    case ConstraintKind::ArgumentConversion:
    case ConstraintKind::OperatorArgumentConversion: {
      if (typeVar1) {
        // Performance optimization: Propagate fully or partially resolved
        // contextual type down into the body of result builder transformed
        // closure by eagerly binding intermediate body result type to the
        // contextual one. This helps to determine when closure body could be
        // solved early.
        //
        // TODO: This could be extended to cover all multi-statement closures.
        //
        // See \c BindingSet::favoredOverConjunction for more details.
        if (!typeVar2 && locator.endsWith<LocatorPathElt::FunctionResult>()) {
          SmallVector<LocatorPathElt> path;
          auto anchor = locator.getLocatorParts(path);

          // Drop `FunctionResult` element.
          path.pop_back();

          ClosureExpr *closure = nullptr;
          {
            // This avoids a new locator allocation.
            SourceRange range;
            ArrayRef<LocatorPathElt> scratchPath(path);
            simplifyLocator(anchor, scratchPath, range);

            if (scratchPath.empty())
              closure = getAsExpr<ClosureExpr>(anchor);
          }

          if (closure && !closure->hasExplicitResultType() &&
              getAppliedResultBuilderTransform(closure)) {
            return matchTypesBindTypeVar(typeVar1, type2, ConstraintKind::Equal,
                                         flags, locator, formUnsolvedResult);
          }
        }
      }

      return formUnsolvedResult();
    }

    case ConstraintKind::ApplicableFunction:
    case ConstraintKind::DynamicCallableApplicableFunction:
    case ConstraintKind::BindOverload:
    case ConstraintKind::BridgingConversion:
    case ConstraintKind::CheckedCast:
    case ConstraintKind::SubclassOf:
    case ConstraintKind::NonisolatedConformsTo:
    case ConstraintKind::ConformsTo:
    case ConstraintKind::TransitivelyConformsTo:
    case ConstraintKind::Defaultable:
    case ConstraintKind::Disjunction:
    case ConstraintKind::Conjunction:
    case ConstraintKind::DynamicTypeOf:
    case ConstraintKind::EscapableFunctionOf:
    case ConstraintKind::OpenedExistentialOf:
    case ConstraintKind::KeyPath:
    case ConstraintKind::KeyPathApplication:
    case ConstraintKind::LiteralConformsTo:
    case ConstraintKind::ForEachElement:
    case ConstraintKind::OptionalObject:
    case ConstraintKind::UnresolvedValueMember:
    case ConstraintKind::ValueMember:
    case ConstraintKind::OneWayEqual:
    case ConstraintKind::FallbackType:
    case ConstraintKind::UnresolvedMemberChainBase:
    case ConstraintKind::PropertyWrapper:
    case ConstraintKind::SyntacticElement:
    case ConstraintKind::BindTupleOfFunctionParams:
    case ConstraintKind::PackElementOf:
    case ConstraintKind::ShapeOf:
    case ConstraintKind::ExplicitGenericArguments:
    case ConstraintKind::SameShape:
    case ConstraintKind::MaterializePackExpansion:
    case ConstraintKind::LValueObject:
      llvm_unreachable("Not a relational constraint");
    }
  }

  // If one of the types is a member type of a type variable type,
  // there's nothing we can do.
  if (desugar1->isTypeVariableOrMember() ||
      desugar2->isTypeVariableOrMember()) {
    return formUnsolvedResult();
  }

  // If the original type on one side consisted of a tuple type with
  // unresolved pack expansion(s), let's make sure that both sides are
  // tuples to enable proper pack matching for situations like:
  //
  // `Int <conversion> (_: $T3)`
  // where `$T3` is pack expansion of pattern type `$T2`
  //
  // `Int` should be wrapped in a one-element tuple to make sure
  // that tuple matcher can form a pack expansion type that would
  // match `$T3` and propagate `Pack{Int}` to `$T2`.
  //
  // This is also important for situations like: `$T2 conv (Int, $T_exp)`
  // because expansion could be defaulted to an empty pack which means
  // that under substitution that element would disappear and the type
  // would be just `(Int)`.
  //
  // Notable exceptions here are: `Any` which doesn't require wrapping and
  // would be handled by an existential promotion in cases where it's allowed,
  // and `Optional<T>` which would be handled by optional injection.
  //
  // This logic is intended for concrete types, so `PackExpansionType` must be
  // avoided to not tuple-wrap pack arguments.
  //
  // `LValueType`s are also ignored at this stage to avoid accidentally wrapping
  // them. If they
  //  are valid wrapping targets, they will be tuple-wrapped after the lvalue is
  //  converted.
  if (isTupleWithUnresolvedPackExpansion(origType1) ||
      isTupleWithUnresolvedPackExpansion(origType2)) {
    auto isTypeVariableWrappedInOptional = [](Type type) {
      if (type->getOptionalObjectType()) {
        return type->lookThroughAllOptionalTypes()->isTypeVariableOrMember();
      }
      return false;
    };
    if (isa<TupleType>(desugar1) != isa<TupleType>(desugar2) &&
        !isa<PackExpansionType>(desugar1) &&
        !isa<PackExpansionType>(desugar2) &&
        !isa<InOutType>(desugar1) && !isa<InOutType>(desugar2) &&
        !isa<LValueType>(desugar1) && !isa<LValueType>(desugar2) &&
        !isTypeVariableWrappedInOptional(desugar1) &&
        !isTypeVariableWrappedInOptional(desugar2) &&
        !desugar1->isAny() &&
        !desugar2->isAny()) {
      return matchTypes(
          desugar1->is<TupleType>() ? type1
                                    : TupleType::get({type1}, getASTContext()),
          desugar2->is<TupleType>() ? type2
                                    : TupleType::get({type2}, getASTContext()),
          kind, flags, locator);
    }
  }

  // Dependent members cannot be simplified if base type contains unresolved
  // pack expansion type variables because they don't give enough information
  // to substitution logic to form a correct type. For example:
  //
  // ```
  // protocol P { associatedtype V }
  // struct S<each T> : P { typealias V = (repeat (each T)?) }
  // ```
  //
  // If pack expansion is represented as `$T1` and its pattern is `$T2`, a
  // reference to `V` would get a type `S<Pack{$T}>.V` and simplified version
  // would be `Optional<Pack{$T1}>` instead of `Pack{repeat Optional<$T2>}`
  // because `$T1` is treated as a substitution for `each T` until bound.
  if (isDependentMemberTypeWithBaseThatContainsUnresolvedPackExpansions(
          *this, origType1) ||
      isDependentMemberTypeWithBaseThatContainsUnresolvedPackExpansions(
          *this, origType2)) {
    // It's important to preserve the original types here because any attempt
    // at simplification or canonicalization wouldn't produce a correct type
    // util pack expansion type variables are bound.
    return formUnsolvedResult(/*useOriginalTypes=*/true);
  }

  llvm::SmallVector<RestrictionOrFix, 4> conversionsOrFixes;

  // Decompose parallel structure.
  TypeMatchOptions subflags =
    getDefaultDecompositionOptions(flags) - TMF_ApplyingFix;
  if (desugar1->getKind() == desugar2->getKind()) {
    switch (desugar1->getKind()) {
#define SUGARED_TYPE(id, parent) case TypeKind::id:
#define ARTIFICIAL_TYPE(id, parent) case TypeKind::id:
#define TYPE(id, parent)
#include "swift/AST/TypeNodes.def"
    case TypeKind::BuiltinTuple:
    case TypeKind::Join:
    case TypeKind::Meet:
    case TypeKind::Error:
    case TypeKind::GenericTypeParam:
    case TypeKind::TypeVariable:
    case TypeKind::GenericFunction:
    case TypeKind::UnboundGeneric:
      ABORT([&](llvm::raw_ostream &out) {
        out << "Bad type in matchTypes():\n";
        desugar1->dump(out);
      });

    // Mismatched builtin types
#define BUILTIN_GENERIC_TYPE(id, parent)
#define BUILTIN_TYPE(id, parent) case TypeKind::id:
#define TYPE(id, parent)
#include "swift/AST/TypeNodes.def"
      return SolutionKind::Error;

    // BuiltinGenericType subclasses
    case TypeKind::BuiltinBorrow:
    case TypeKind::BuiltinFixedArray: {
      auto *fixed1 = cast<BuiltinGenericType>(desugar1);
      auto *fixed2 = cast<BuiltinGenericType>(desugar2);
      if (fixed1->getBuiltinTypeKind() != fixed2->getBuiltinTypeKind()) {
        return SolutionKind::Error;
      }

      auto result = SolutionKind::Solved;
      for (unsigned i
            : indices(fixed1->getSubstitutions().getReplacementTypes())) {
        result = matchTypes(fixed1->getSubstitutions().getReplacementTypes()[i],
                            fixed2->getSubstitutions().getReplacementTypes()[i],
                            ConstraintKind::Bind, subflags,
                            locator.withPathElement(
                                   LocatorPathElt::GenericArgument(i)));
        if (result == SolutionKind::Error) {
          return result;
        }
      }

      return result;
    }

    case TypeKind::Placeholder: {
      // If it's allowed to attempt fixes, let's delegate
      // decision to `repairFailures`, since depending on
      // locator we might either ignore such a mismatch,
      // or record a specialized fix.
      if (shouldAttemptFixes())
        break;

      return SolutionKind::Error;
    }

    case TypeKind::DependentMember: {
      // If types are identical, let's consider this constraint solved
      // even though they are dependent members, they would be resolved
      // to the same concrete type.
      if (desugar1->isEqual(desugar2))
        return SolutionKind::Solved;

      if (shouldAttemptFixes()) {
        if (!desugar1->hasTypeVariable() && !desugar2->hasTypeVariable()) {
          auto *loc = getConstraintLocator(locator);

          auto *fix =
              loc->isLastElement<LocatorPathElt::TypeParameterRequirement>()
                  ? fixRequirementFailure(*this, type1, type2, loc->getAnchor(),
                                          loc->getPath())
                  : ContextualMismatch::create(*this, type1, type2, loc);

          if (!fix || recordFix(fix))
            return SolutionKind::Error;

          return SolutionKind::Solved;
        }
      }

      // If one of the dependent member types has no type variables,
      // this comparison is effectively illformed, because dependent
      // member couldn't be simplified down to the actual type, and
      // we wouldn't be able to solve this constraint, so let's just fail.
      // This should only happen outside of diagnostic mode, as otherwise the
      // member is replaced by a placeholder in simplifyType.
      if (!desugar1->hasTypeVariable() || !desugar2->hasTypeVariable())
        return SolutionKind::Error;

      // Nothing we can solve yet, since we need to wait until
      // type variables will get resolved.
      return formUnsolvedResult();
    }

    case TypeKind::Module:
    case TypeKind::PrimaryArchetype:
    case TypeKind::PackArchetype:
    case TypeKind::ElementArchetype: {
      // Give `repairFailures` a chance to fix the problem.
      if (shouldAttemptFixes())
        break;

      // If two module types or archetypes were not already equal, there's
      // nothing more we can do.
      return SolutionKind::Error;
    }

    case TypeKind::Tuple: {
      // FIXME: TuplePackMatcher doesn't correctly handle matching two
       // abstract contextual tuple types in a generic context.
       if (simplifyType(desugar1)->isEqual(simplifyType(desugar2)))
         return SolutionKind::Solved;

      // If the tuple has consecutive pack expansions, packs must be
      // resolved before matching.
      auto delayMatching = [](TupleType *tuple) {
        bool afterPack = false;
        for (auto element : tuple->getElements()) {
          if (afterPack && !element.hasName()) {
            SmallPtrSet<TypeVariableType *, 2> typeVars;
            element.getType()->getTypeVariables(typeVars);

            bool hasUnresolvedPack = llvm::any_of(typeVars, [](auto *tv) {
              return tv->getImpl().canBindToPack();
            });

            if (hasUnresolvedPack)
              return true;
          }

          // Delay matching if one of the elements is unresolved pack
          // expansion represented by a type variable.
          if (auto *typeVar = element.getType()->getAs<TypeVariableType>()) {
            if (typeVar->getImpl().isPackExpansion())
              return true;
          }

          afterPack = element.getType()->is<PackExpansionType>();
        }

        return false;
      };


      auto *tuple1 = cast<TupleType>(desugar1);
      auto *tuple2 = cast<TupleType>(desugar2);
      if (delayMatching(tuple1) || delayMatching(tuple2)) {
        return formUnsolvedResult();
      }

      // Closure result is allowed to convert to Void in certain circumstances,
      // let's forego tuple matching because it is guaranteed to fail and jump
      // to `() -> T` to `() -> Void` rule.
      if (locator.endsWith<LocatorPathElt::ClosureBody>()) {
        if (containsPackExpansionType(tuple1) && tuple2->isVoid())
          break;
      }

      // Add each tuple type to the locator before matching the element types.
      // This is useful for diagnostics, because the error message can use the
      // full tuple type for several element mismatches. Use the original types
      // to preserve sugar such as typealiases.
      auto tmpTupleLoc = locator.withPathElement(LocatorPathElt::TupleType(type1));
      auto tupleLoc = tmpTupleLoc.withPathElement(LocatorPathElt::TupleType(type2));
      auto result = matchTupleTypes(cast<TupleType>(desugar1),
                                    cast<TupleType>(desugar2),
                                    kind, subflags, tupleLoc);
      if (result != SolutionKind::Error)
        return result;

      // FIXME: All cases in this switch should go down to the fix logic
      // to give repairFailures() a chance to run, but this breaks stuff
      // right now.
      break;
    }

    case TypeKind::Enum:
    case TypeKind::Struct:
    case TypeKind::Class: {
      auto nominal1 = cast<NominalType>(desugar1);
      auto nominal2 = cast<NominalType>(desugar2);
      if (nominal1->getDecl() == nominal2->getDecl())
        conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality);

      // Check for CF <-> ObjectiveC bridging.
      if (isa<ClassType>(desugar1) &&
          kind >= ConstraintKind::Subtype) {
        auto class1 = cast<ClassDecl>(nominal1->getDecl());
        auto class2 = cast<ClassDecl>(nominal2->getDecl());

        // CF -> Objective-C via toll-free bridging.
        if (class1->getForeignClassKind() == ClassDecl::ForeignKind::CFType &&
            class2->getForeignClassKind() != ClassDecl::ForeignKind::CFType &&
            class1->getAttrs().hasAttribute<ObjCBridgedAttr>()) {
          conversionsOrFixes.push_back(
            ConversionRestrictionKind::CFTollFreeBridgeToObjC);
        }

        // Objective-C -> CF via toll-free bridging.
        if (class2->getForeignClassKind() == ClassDecl::ForeignKind::CFType &&
            class1->getForeignClassKind() != ClassDecl::ForeignKind::CFType &&
            class2->getAttrs().hasAttribute<ObjCBridgedAttr>()) {
          conversionsOrFixes.push_back(
            ConversionRestrictionKind::ObjCTollFreeBridgeToCF);
        }
      }

      if (kind >= ConstraintKind::Subtype &&
          nominal1->getDecl() != nominal2->getDecl() &&
          ((nominal1->isCGFloat() || nominal2->isCGFloat()) &&
           (nominal1->isDouble() || nominal2->isDouble()))) {
        ConstraintLocatorBuilder location{locator};
        // Look through all value-to-optional promotions to allow
        // conversions like Double -> CGFloat?? and vice versa.
        // T -> Optional<T>
        if (location.endsWith<LocatorPathElt::OptionalInjection>() ||
            location.endsWith<LocatorPathElt::GenericArgument>()) {
          SmallVector<LocatorPathElt, 4> path;
          auto anchor = location.getLocatorParts(path);

          // Drop all of the applied `value-to-optional` and
          // `optional-to-optional` conversions.
          path.erase(llvm::remove_if(
                         path,
                         [](const LocatorPathElt &elt) {
                           return elt.is<LocatorPathElt::OptionalInjection>() ||
                                  elt.is<LocatorPathElt::GenericArgument>();
                         }),
                     path.end());

          location = getConstraintLocator(anchor, path);
        }

        // Support implicit Double<->CGFloat conversions only for
        // something which could be directly represented in the AST
        // e.g. argument-to-parameter, contextual conversions etc.
        if (!location.trySimplifyToExpr()) {
          return SolutionKind::Error;
        }

        SmallVector<LocatorPathElt, 4> path;
        auto anchor = location.getLocatorParts(path);

        // Try implicit CGFloat conversion only if:
        // - This is not:
        //     - an explicit call to a CGFloat initializer;
        //     - an explicit coercion;
        //     - a runtime type check (via `is` expression);
        //     - a checked or conditional cast;
        // - This is a first type such conversion is attempted for
        //   for a given path (AST element).

        auto isCGFloatInit = [&](ASTNode location) {
          if (auto *call = getAsExpr<CallExpr>(location)) {
            if (auto *typeExpr = dyn_cast<TypeExpr>(call->getFn())) {
              return getInstanceType(typeExpr)->isCGFloat();
            }
          }
          return false;
        };

        auto isCoercionOrCast = [](ASTNode anchor,
                                   ArrayRef<LocatorPathElt> path) {
          // E.g. contextual conversion from coercion/cast
          // to some other type.
          if (!(path.empty() ||
                path.back().is<LocatorPathElt::CoercionOperand>()))
            return false;

          return isExpr<CoerceExpr>(anchor) || isExpr<IsExpr>(anchor) ||
                 isExpr<ConditionalCheckedCastExpr>(anchor) ||
                 isExpr<ForcedCheckedCastExpr>(anchor);
        };

        if (!isCGFloatInit(anchor) && !isCoercionOrCast(anchor, path)) {
          conversionsOrFixes.push_back(
              desugar1->isCGFloat()
                  ? ConversionRestrictionKind::CGFloatToDouble
                  : ConversionRestrictionKind::DoubleToCGFloat);
        }
      }

      break;
    }

    case TypeKind::DynamicSelf:
      // FIXME: Deep equality? What is the rule between two DynamicSelfs?
      break;
       
    case TypeKind::Protocol:
      // Nothing to do here; try existential and user-defined conversions below.
      break;

    case TypeKind::Metatype:
    case TypeKind::ExistentialMetatype: {
      auto meta1 = cast<AnyMetatypeType>(desugar1);
      auto meta2 = cast<AnyMetatypeType>(desugar2);

      // A.Type < B.Type if A < B and both A and B are classes.
      // P.Type < Q.Type if P < Q, both P and Q are protocols, and P.Type
      // and Q.Type are both existential metatypes
      auto subKind = std::min(kind, ConstraintKind::Subtype);
      // If instance types can't have a subtype relationship
      // it means that such types can be simply equated.
      auto instanceType1 = meta1->getInstanceType();
      auto instanceType2 = meta2->getInstanceType();
      if (isa<MetatypeType>(meta1) &&
          !(instanceType1->mayHaveSuperclass() &&
            instanceType2->getClassOrBoundGenericClass())) {
        subKind = ConstraintKind::Bind;
      }

      auto result =
          matchTypes(instanceType1, instanceType2, subKind, subflags,
                     locator.withPathElement(ConstraintLocator::InstanceType));

      // If matching of the instance types resulted in the failure make sure
      // to give `repairFailure` a chance to run to attempt to fix the issue.
      if (shouldAttemptFixes() && result == SolutionKind::Error)
        break;

      return result;
    }

    case TypeKind::Function: {
      auto func1 = cast<FunctionType>(desugar1);
      auto func2 = cast<FunctionType>(desugar2);

      auto result = matchFunctionTypes(func1, func2, kind, flags, locator);

      if (shouldAttemptFixes() && result == SolutionKind::Error)
        break;

      return result;
    }

    case TypeKind::Existential:
    case TypeKind::ProtocolComposition:
    case TypeKind::ParameterizedProtocol:
      switch (kind) {
      case ConstraintKind::Equal:
      case ConstraintKind::Bind:
      case ConstraintKind::BindParam:
        // If we are matching types for equality, we might still have
        // type variables inside the protocol composition's superclass
        // constraint.
        if (desugar1->getKind() == desugar2->getKind())
          conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality);
        break;

      default:
        // Subtype constraints where the RHS is an existential type are
        // handled below.
        break;
      }

      break;

    case TypeKind::LValue:
      if (kind == ConstraintKind::BindParam)
        return SolutionKind::Error;
      return matchTypes(cast<LValueType>(desugar1)->getObjectType(),
                        cast<LValueType>(desugar2)->getObjectType(),
                        ConstraintKind::Bind, subflags,
                        locator.withPathElement(
                          ConstraintLocator::LValueConversion));
    
    case TypeKind::InOut:
      if (kind == ConstraintKind::BindParam)
        return SolutionKind::Error;
      
      if (kind == ConstraintKind::OperatorArgumentConversion) {
        conversionsOrFixes.push_back(
            RemoveAddressOf::create(*this, type1, type2,
                                    getConstraintLocator(locator)));
        break;
      }

      return matchTypes(cast<InOutType>(desugar1)->getObjectType(),
                        cast<InOutType>(desugar2)->getObjectType(),
                        ConstraintKind::Bind, subflags,
                  locator.withPathElement(ConstraintLocator::LValueConversion));

    case TypeKind::BoundGenericClass:
    case TypeKind::BoundGenericEnum:
    case TypeKind::BoundGenericStruct: {
      auto bound1 = cast<BoundGenericType>(desugar1);
      auto bound2 = cast<BoundGenericType>(desugar2);
      
      if (bound1->getDecl() == bound2->getDecl())
        conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality);
      break;
    }

    // Opaque archetypes are globally bound, so we can match them for deep
    // equality.
    case TypeKind::OpaqueTypeArchetype: {
      auto opaque1 = cast<OpaqueTypeArchetypeType>(desugar1);
      auto opaque2 = cast<OpaqueTypeArchetypeType>(desugar2);
      
      if (opaque1->getDecl() == opaque2->getDecl()) {
        conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality);
      }
      break;
    }

    case TypeKind::ExistentialArchetype: {
      auto opened1 = cast<ExistentialArchetypeType>(desugar1);
      auto opened2 = cast<ExistentialArchetypeType>(desugar2);
      // If they have the same interface type and ID, two ExistentialArchetypeTypes
      // match if their generic arguments do as well.
      if (opened1->getInterfaceType()->isEqual(opened2->getInterfaceType()) &&
          opened1->getGenericEnvironment()->getOpenedExistentialID() ==
              opened2->getGenericEnvironment()->getOpenedExistentialID()) {
        conversionsOrFixes.push_back(ConversionRestrictionKind::DeepEquality);
      }
      break;
    }

    case TypeKind::Pack: {
      auto tmpPackLoc = locator.withPathElement(LocatorPathElt::PackType(type1));
      auto packLoc = tmpPackLoc.withPathElement(LocatorPathElt::PackType(type2));

      auto result =
          matchPackTypes(cast<PackType>(desugar1), cast<PackType>(desugar2),
                         kind, subflags, packLoc);

      // Let `repairFailures` attempt to "fix" this.
      if (shouldAttemptFixes() && result == SolutionKind::Error)
        break;

      return result;
    }
    case TypeKind::PackExpansion: {
      auto expansion1 = cast<PackExpansionType>(desugar1);
      auto expansion2 = cast<PackExpansionType>(desugar2);

      return matchPackExpansionTypes(expansion1, expansion2, kind, subflags,
                                     locator);
    }

    case TypeKind::PackElement: {
      auto pack1 = cast<PackElementType>(desugar1)->getPackType();
      auto pack2 = cast<PackElementType>(desugar2)->getPackType();

      return matchTypes(pack1, pack2, kind, subflags, locator);
    }

    case TypeKind::ErrorUnion:
      break;

    case TypeKind::Integer:
      if (shouldAttemptFixes())
        break;

      // If we're asking if two integer types are the same, then we know they
      // aren't.
      return SolutionKind::Error;

    case TypeKind::Hidden:
      // Two HiddenTypes match only if they have the same mangled name.
      if (cast<HiddenType>(desugar1)->getMangledName() ==
          cast<HiddenType>(desugar2)->getMangledName())
        break;
      return SolutionKind::Error;
    }
  }

  if (kind == ConstraintKind::BindToPointerType) {
    if (desugar2->isEqual(getASTContext().TheEmptyTupleType))
      return SolutionKind::Solved;
  }

  if (kind == ConstraintKind::BindParam) {
    if (auto *iot = dyn_cast<InOutType>(desugar1)) {
      if (auto *lvt = dyn_cast<LValueType>(desugar2)) {
        return matchTypes(iot->getObjectType(), lvt->getObjectType(),
                          ConstraintKind::Bind, subflags,
                          locator.withPathElement(
                            ConstraintLocator::LValueConversion));
      }
    }
  }

  if (kind >= ConstraintKind::Conversion) {
    // An lvalue of type T1 can be converted to a value of type T2 so long as
    // T1 is convertible to T2 (by loading the value).  Note that we cannot get
    // a value of inout type as an lvalue though.
    if (type1->is<LValueType>() && !type2->is<InOutType>()) {
      auto result = matchTypes(type1->getWithoutSpecifierType(), type2, kind,
                               subflags, locator);
      if (result == SolutionKind::Solved || !shouldAttemptFixes())
        return result;
    }
  }

  if (kind >= ConstraintKind::Subtype) {
    // Subclass-to-superclass conversion.
    if (type1->mayHaveSuperclass() &&
        type2->getClassOrBoundGenericClass() &&
        type1->getClassOrBoundGenericClass()
          != type2->getClassOrBoundGenericClass()) {
      conversionsOrFixes.push_back(ConversionRestrictionKind::Superclass);
    }

    // Existential-to-superclass conversion.
    if (type1->isClassExistentialType() &&
        type2->getClassOrBoundGenericClass()) {
      conversionsOrFixes.push_back(ConversionRestrictionKind::Superclass);
    }

    // Metatype-to-existential-metatype conversion.
    //
    // Equivalent to a conformance relation on the instance types.
    if (type1->is<MetatypeType>() &&
        type2->is<ExistentialMetatypeType>()) {
      conversionsOrFixes.push_back(
        ConversionRestrictionKind::MetatypeToExistentialMetatype);
    }

    // Existential-metatype-to-superclass-metatype conversion.
    if (type2->is<MetatypeType>()) {
      if (auto *meta1 = type1->getAs<ExistentialMetatypeType>()) {
        if (meta1->getInstanceType()->isClassExistentialType()) {
          conversionsOrFixes.push_back(
            ConversionRestrictionKind::ExistentialMetatypeToMetatype);
        }
      }
    }

    // Concrete value to existential conversion.
    if (!type1->is<LValueType>() &&
        type2->isExistentialType()) {

      // Penalize conversions to Any.
      if (kind >= ConstraintKind::Conversion && type2->isAny())
        increaseScore(ScoreKind::SK_EmptyExistentialConversion, locator);

      conversionsOrFixes.push_back(ConversionRestrictionKind::Existential);
    }

    // T -> AnyHashable.
    if (desugar2->isAnyHashable()) {
      // Don't allow this in operator contexts or we'll end up allowing
      // 'T() == U()' for unrelated T and U that just happen to be Hashable.
      // We can remove this special case when we implement operator hiding.
      if (!type1->is<LValueType>() &&
          kind != ConstraintKind::OperatorArgumentConversion) {
        conversionsOrFixes.push_back(
                              ConversionRestrictionKind::HashableToAnyHashable);
      }
    }

    // Metatype to object conversion.
    //
    // Class and protocol metatypes are interoperable with certain Objective-C
    // runtime classes, but only when ObjC interop is enabled.

    if (getASTContext().LangOpts.EnableObjCInterop) {
      // These conversions are between concrete types that don't need further
      // resolution, so we can consider them immediately solved.
      auto addSolvedRestrictedConstraint
        = [&](ConversionRestrictionKind restriction) -> SolutionKind {
          addRestrictedConstraint(ConstraintKind::Subtype, restriction,
                                  type1, type2, locator);
          return SolutionKind::Solved;
        };
      
      if (auto meta1 = type1->getAs<MetatypeType>()) {
        if (meta1->getInstanceType()->mayHaveSuperclass()
            && type2->isAnyObject()) {
          increaseScore(ScoreKind::SK_UserConversion, locator);
          return addSolvedRestrictedConstraint(
                           ConversionRestrictionKind::ClassMetatypeToAnyObject);
        }
        // Single @objc protocol value metatypes can be converted to the ObjC
        // Protocol class type.
        auto isProtocolClassType = [&](Type t) -> bool {
          if (auto classDecl = t->getClassOrBoundGenericClass())
            if (classDecl->getName() == getASTContext().Id_Protocol
                && classDecl->getModuleContext()->getName()
                    == getASTContext().Id_ObjectiveC)
              return true;
          return false;
        };

        auto constraintType = meta1->getInstanceType();
        if (auto existential = constraintType->getAs<ExistentialType>())
          constraintType = existential->getConstraintType();
        
        if (auto protoTy = constraintType->getAs<ProtocolType>()) {
          if (protoTy->getDecl()->isObjC()
              && isProtocolClassType(type2)) {
            increaseScore(ScoreKind::SK_UserConversion, locator);
            return addSolvedRestrictedConstraint(
                    ConversionRestrictionKind::ProtocolMetatypeToProtocolClass);
          }
        }
      }
      if (auto meta1 = type1->getAs<ExistentialMetatypeType>()) {
        // Class-constrained existential metatypes can be converted to AnyObject.
        if (meta1->getInstanceType()->isClassExistentialType()
            && type2->isAnyObject()) {
          increaseScore(ScoreKind::SK_UserConversion, locator);
          return addSolvedRestrictedConstraint(
                     ConversionRestrictionKind::ExistentialMetatypeToAnyObject);
        }
      }
    }

    // Special implicit nominal conversions.
    if (!type1->is<LValueType>()) {
      if (desugar1->isArray() && desugar2->isArray()) {
        // Array -> Array.
        auto elementType = type1->getArrayElementType();
        if (!elementType->isForeignReferenceType())
          conversionsOrFixes.push_back(ConversionRestrictionKind::ArrayUpcast);
      } else if (isDictionaryType(desugar1) && isDictionaryType(desugar2)) {
        // Dictionary -> Dictionary.
        auto keyValueTypes = *isDictionaryType(desugar1);
        if (!keyValueTypes.first->isForeignReferenceType() &&
            !keyValueTypes.second->isForeignReferenceType())
          conversionsOrFixes.push_back(
              ConversionRestrictionKind::DictionaryUpcast);
      } else if (isSetType(desugar1) && isSetType(desugar2)) {
        // Set -> Set.
        auto elementType = *isSetType(desugar1);
        if (!elementType->isForeignReferenceType())
          conversionsOrFixes.push_back(ConversionRestrictionKind::SetUpcast);
      }
    }
  }

  // Pointer arguments can be converted from pointer-compatible types.
  if (kind >= ConstraintKind::ArgumentConversion) {
    // It is never legal to form an autoclosure that results in these
    // implicit conversions to pointer types.
    bool isAutoClosureArgument = locator.isForAutoclosureResult();

    Type unwrappedType2 = type2;
    bool type2IsOptional = false;
    if (Type unwrapped = type2->getOptionalObjectType()) {
      type2IsOptional = true;
      unwrappedType2 = unwrapped;
    }
    PointerTypeKind pointerKind;
    if (Type pointeeTy =
            unwrappedType2->getAnyPointerElementType(pointerKind)) {
      switch (pointerKind) {
      case PTK_UnsafeRawPointer:
      case PTK_UnsafeMutableRawPointer:
      case PTK_UnsafePointer:
      case PTK_UnsafeMutablePointer:
        // UnsafeMutablePointer can be converted from an inout reference to a
        // scalar or array.
        if (auto inoutType1 = dyn_cast<InOutType>(desugar1)) {
          if (!isAutoClosureArgument) {
            auto inoutBaseType = getFixedTypeRecursive(
                inoutType1->getInOutObjectType(), /*wantRValue=*/true);

            // Wait until the base type of `inout` is sufficiently resolved
            // before making any assessments regarding conversions.
            if (inoutBaseType->isTypeVariableOrMember())
              return formUnsolvedResult();

            auto baseIsArray = inoutBaseType->isArray();

            if (baseIsArray)
              conversionsOrFixes.push_back(
                  ConversionRestrictionKind::ArrayToPointer);

            // Only try an inout-to-pointer conversion if we know it's not
            // an array being converted to a raw pointer type. Such
            // conversions can only use array-to-pointer.
            if (!baseIsArray || !isRawPointerKind(pointerKind)) {
              conversionsOrFixes.push_back(
                  ConversionRestrictionKind::InoutToPointer);

              // If regular inout-to-pointer conversion doesn't work,
              // let's try C pointer conversion that has special semantics
              // for imported declarations.
              if (isArgumentOfImportedDecl(locator)) {
                conversionsOrFixes.push_back(
                    baseIsArray ? ConversionRestrictionKind::ArrayToCPointer
                                : ConversionRestrictionKind::InoutToCPointer);
              }
            }
          }
        }

        // Operators cannot use these implicit conversions.
        if (kind == ConstraintKind::ArgumentConversion) {
          // We can potentially convert from an UnsafeMutablePointer
          // of a different type, if we're a void pointer.
          Type unwrappedType1 = type1;
          bool type1IsOptional = false;
          if (Type unwrapped = type1->getOptionalObjectType()) {
            type1IsOptional = true;
            unwrappedType1 = unwrapped;
          }

          // Don't handle normal optional-related conversions here.
          if (unwrappedType1->isEqual(unwrappedType2))
            break;

          PointerTypeKind type1PointerKind;
          bool type1IsPointer{
              unwrappedType1->getAnyPointerElementType(type1PointerKind)};
          bool optionalityMatches = !type1IsOptional || type2IsOptional;
          if (type1IsPointer && optionalityMatches) {
            if (type1PointerKind == PTK_UnsafeMutablePointer) {
              // Favor an UnsafeMutablePointer-to-UnsafeMutablePointer
              // conversion.
              if (type1PointerKind != pointerKind)
                increaseScore(ScoreKind::SK_ValueToPointerConversion,
                              locator);
              conversionsOrFixes.push_back(
                ConversionRestrictionKind::PointerToPointer);
            }
            // UnsafeMutableRawPointer -> UnsafeRawPointer
            else if (type1PointerKind == PTK_UnsafeMutableRawPointer &&
                     pointerKind == PTK_UnsafeRawPointer) {
              if (type1PointerKind != pointerKind)
                increaseScore(ScoreKind::SK_ValueToPointerConversion,
                              locator);
              conversionsOrFixes.push_back(
                ConversionRestrictionKind::PointerToPointer);
            }
          }
          // UnsafePointer and UnsafeRawPointer can also be converted from an
          // array or string value, or a UnsafePointer or
          // AutoreleasingUnsafeMutablePointer.
          if (pointerKind == PTK_UnsafePointer
              || pointerKind == PTK_UnsafeRawPointer) {
            if (!isAutoClosureArgument) {
              if (type1->isArray()) {
                conversionsOrFixes.push_back(
                    ConversionRestrictionKind::ArrayToPointer);

                // If regular array-to-pointer conversion doesn't work,
                // let's try C pointer conversion that has special semantics
                // for imported declarations.
                if (isArgumentOfImportedDecl(locator)) {
                  conversionsOrFixes.push_back(
                      ConversionRestrictionKind::ArrayToCPointer);
                }
              }

              // The pointer can be converted from a string, if the element
              // type is compatible.
              auto &ctx = getASTContext();
              if (type1->isString()) {
                auto baseTy = getFixedTypeRecursive(pointeeTy, false);

                if (baseTy->isTypeVariableOrMember() ||
                    isStringCompatiblePointerBaseType(ctx, baseTy))
                  conversionsOrFixes.push_back(
                      ConversionRestrictionKind::StringToPointer);
              }
            }

            if (type1IsPointer && optionalityMatches &&
                (type1PointerKind == PTK_UnsafePointer ||
                 type1PointerKind == PTK_AutoreleasingUnsafeMutablePointer)) {
              conversionsOrFixes.push_back(
                                 ConversionRestrictionKind::PointerToPointer);
            }
          }

          // If both sides are non-optional pointers, let's check whether
          // this argument supports Swift -> C pointer conversions.
          //
          // Do some light verification before recording restriction to
          // avoid allocating constraints for obviously invalid cases.
          if (type1IsPointer && !type1IsOptional && !type2IsOptional &&
              (shouldAttemptFixes() || isArgumentOfImportedDecl(locator))) {
            // UnsafeRawPointer -> UnsafePointer<[U]Int8>
            if (type1PointerKind == PTK_UnsafeRawPointer &&
                pointerKind == PTK_UnsafePointer) {
              conversionsOrFixes.push_back(
                  ConversionRestrictionKind::PointerToCPointer);
            }

            // UnsafeMutableRawPointer -> Unsafe[Mutable]Pointer<[U]Int8>
            if (type1PointerKind == PTK_UnsafeMutableRawPointer &&
                (pointerKind == PTK_UnsafePointer ||
                 pointerKind == PTK_UnsafeMutablePointer)) {
              conversionsOrFixes.push_back(
                  ConversionRestrictionKind::PointerToCPointer);
            }

            // Unsafe[Mutable]Pointer -> Unsafe[Mutable]Pointer
            if (type1PointerKind == PTK_UnsafePointer &&
                pointerKind == PTK_UnsafePointer) {
              conversionsOrFixes.push_back(
                  ConversionRestrictionKind::PointerToCPointer);
            }

            if (type1PointerKind == PTK_UnsafeMutablePointer &&
                (pointerKind == PTK_UnsafePointer ||
                 pointerKind == PTK_UnsafeMutablePointer)) {
              conversionsOrFixes.push_back(
                  ConversionRestrictionKind::PointerToCPointer);
            }
          }
        }
        break;

      case PTK_AutoreleasingUnsafeMutablePointer:
        // PTK_AutoreleasingUnsafeMutablePointer can be converted from an
        // inout reference to a scalar.
        if (!isAutoClosureArgument && type1->is<InOutType>()) {
          conversionsOrFixes.push_back(
                                   ConversionRestrictionKind::InoutToPointer);
        }
        break;
      }
    }
  }

  if (kind >= ConstraintKind::OperatorArgumentConversion) {
    // If the RHS is an inout type, the LHS must be an @lvalue type.
    if (auto *lvt = type1->getAs<LValueType>()) {
      if (auto *iot = type2->getAs<InOutType>()) {
        return matchTypes(lvt->getObjectType(), iot->getObjectType(),
                          ConstraintKind::Bind, subflags,
                          locator.withPathElement(
                                  ConstraintLocator::LValueConversion));
      }
    }
  }

  // A value of type T! can be converted to type U if T is convertible
  // to U by force-unwrapping the source value.
  // A value of type T, T?, or T! can be converted to type U? or U! if
  // T is convertible to U.
  if (!type1->is<LValueType>() && kind >= ConstraintKind::Subtype) {
    enumerateOptionalConversionRestrictions(
        type1, type2, kind, locator,
        [&](ConversionRestrictionKind restriction) {
      conversionsOrFixes.push_back(restriction);
    });
  }

  // Allow '() -> T' to '() -> ()' and '() -> Never' to '() -> T' for closure
  // literals and expressions representing an implied result of closures and
  // if/switch expressions.
  if (auto elt = locator.last()) {
    if (kind >= ConstraintKind::Subtype &&
        (type1->isUninhabited() || type2->isVoid())) {
      auto *loc = getConstraintLocator(locator);
      bool allowConversion = false;
      switch (getImpliedResultConversionKind(loc)) {
      case ImpliedResultConversionKind::None:
        break;
      case ImpliedResultConversionKind::FromNever:
        allowConversion = type1->isUninhabited();
        break;
      case ImpliedResultConversionKind::ToVoid:
        allowConversion = true;
        break;
      }

      if (allowConversion) {
        increaseScore(SK_FunctionConversion, locator);
        return SolutionKind::Solved;
      }
    }
  }

  // Matching types where one side is a pack expansion and the other is not
  // means a pack expansion was used where it isn't supported.
  if (type1->is<PackExpansionType>() != type2->is<PackExpansionType>()) {
    if (!shouldAttemptFixes())
      return SolutionKind::Error;

    if (type1->isPlaceholder() || type2->isPlaceholder())
      return SolutionKind::Solved;

    // If we are applying args, we may be able to emit a tailored fix.
    auto *loc = getConstraintLocator(locator);
    if (loc->isLastElement<LocatorPathElt::ApplyArgToParam>()) {
      // If parameter pack expansion contains more than one element and the
      // other side is a tuple, record a fix.
      if (auto packExpansion = type2->getAs<PackExpansionType>()) {
        auto countType = simplifyType(packExpansion->getCountType(), flags);
        if (auto paramPack = countType->getAs<PackType>()) {
          if (type1->is<TupleType>() && paramPack->getNumElements() >= 1) {
            if (recordFix(DestructureTupleToMatchPackExpansionParameter::create(
                    *this, paramPack, loc))) {
              return SolutionKind::Error;
            }
            return SolutionKind::Solved;
          }
        }
      }
      // If the other side contains a pack expansion within a tuple, record a
      // fix and wrap the parameter pack in a tuple.
      if (auto tuple = type2->getAs<TupleType>()) {
        if (type1->is<PackExpansionType>() &&
            containsPackExpansionType(tuple)) {
          if (recordFix(AllowInvalidPackExpansion::create(*this, loc))) {
            return SolutionKind::Error;
          }
          return matchTypes(TupleType::get({type1}, type1->getASTContext()),
                            type2, kind, flags, locator);
        }
      }
    }
    if (recordFix(AllowInvalidPackExpansion::create(*this, loc)))
      return SolutionKind::Error;

    return SolutionKind::Solved;
  }

  // Attempt fixes iff it's allowed, both types are concrete and
  // we are not in the middle of attempting one already.
  if (shouldAttemptFixes() && !flags.contains(TMF_ApplyingFix)) {
    if (repairFailures(type1, type2, kind, flags, conversionsOrFixes,
                       locator)) {
      if (conversionsOrFixes.empty())
        return SolutionKind::Solved;
    }
  }

  if (conversionsOrFixes.empty())
    return SolutionKind::Error;

  // Where there is more than one potential conversion, create a disjunction
  // so that we'll explore all of the options.
  if (conversionsOrFixes.size() > 1) {
    auto fixedLocator = getConstraintLocator(locator);
    SmallVector<Constraint *, 2> constraints;

    for (auto potential : conversionsOrFixes) {
      auto constraintKind = kind;

      if (auto restriction = potential.getRestriction()) {
        // Determine the constraint kind. For a deep equality constraint, only
        // perform equality.
        if (*restriction == ConversionRestrictionKind::DeepEquality)
          constraintKind = ConstraintKind::Bind;

        constraints.push_back(
          Constraint::createRestricted(*this, constraintKind, *restriction,
                                       type1, type2, fixedLocator));

        if (constraints.back()->getKind() == ConstraintKind::Bind)
          constraints.back()->setFavored();

        continue;
      }

      auto fix = *potential.getFix();
      constraints.push_back(
        Constraint::createFixed(*this, constraintKind, fix, type1, type2,
                                fixedLocator));
    }

    // Sort favored constraints first.
    std::sort(constraints.begin(), constraints.end(),
              [&](Constraint *lhs, Constraint *rhs) -> bool {
                if (lhs->isFavored() == rhs->isFavored())
                  return false;

                return lhs->isFavored();
              });

    addDisjunctionConstraint(constraints, fixedLocator);
    return SolutionKind::Solved;
  }

  // For a single potential conversion, directly recurse, so that we
  // don't allocate a new constraint or constraint locator.

  // Handle restrictions.
  if (auto restriction = conversionsOrFixes[0].getRestriction()) {
    return simplifyRestrictedConstraint(*restriction, type1,
                                        type2, kind,
                                        subflags, locator);
  }

  // Handle fixes.
  auto fix = *conversionsOrFixes[0].getFix();
  return simplifyFixConstraint(fix, type1, type2, kind,
                               subflags, locator);
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyConstructionConstraint(
    Type valueType, FunctionType *fnType, TypeMatchOptions flags,
    DeclContext *useDC,
    FunctionRefInfo functionRefInfo, ConstraintLocator *locator) {

  // Desugar the value type.
  auto desugarValueType = valueType->getDesugaredType();

  switch (desugarValueType->getKind()) {
#define SUGARED_TYPE(id, parent) case TypeKind::id:
#define ARTIFICIAL_TYPE(id, parent) case TypeKind::id:
#define TYPE(id, parent)
#include "swift/AST/TypeNodes.def"
  case TypeKind::BuiltinTuple:
  case TypeKind::GenericFunction:
  case TypeKind::GenericTypeParam:
  case TypeKind::UnboundGeneric:
  case TypeKind::Integer:
  case TypeKind::Hidden:
  case TypeKind::Join:
  case TypeKind::Meet:
    ABORT([&](llvm::raw_ostream &out) {
      out << "Bad type in simplifyConstructionConstraint():\n";
      desugarValueType->dump(out);
    });
    
  case TypeKind::Error:
  case TypeKind::Placeholder:
    return SolutionKind::Error;

  case TypeKind::TypeVariable:
  case TypeKind::DependentMember:
    return SolutionKind::Unsolved;

  case TypeKind::Tuple: {
    // If this is an attempt to construct `Void` with arguments,
    // let's diagnose it.
    if (shouldAttemptFixes()) {
      if (valueType->isVoid() && fnType->getNumParams() > 0) {
        auto contextualType = FunctionType::get({}, fnType->getResult());
        if (fixExtraneousArguments(
                *this, contextualType, fnType->getParams(),
                fnType->getNumParams(),
                getConstraintLocator(locator,
                                     ConstraintLocator::FunctionArgument)))
          return SolutionKind::Error;

        fnType = contextualType;
      }
    }
    SmallVector<AnyFunctionType::Param, 4> args;
    for (auto idx : indices(fnType->getParams())) {
      auto &arg = fnType->getParams()[idx];

      // We can disregard '_const', it's not applicable for tuple construction.
      auto flags = arg.getParameterFlags().withCompileTimeLiteral(false);

      // We cannot handle inout for tuple construction.
      if (flags.isInOut()) {
        if (!shouldAttemptFixes())
          return SolutionKind::Error;

        auto *argLoc = getConstraintLocator(locator, {
          LocatorPathElt::ApplyArgument(),
          LocatorPathElt::ApplyArgToParam(idx, idx, ParameterTypeFlags())
        });
        auto argTy = arg.getParameterType();
        if (recordFix(RemoveAddressOf::create(*this, argTy, argTy, argLoc)))
          return SolutionKind::Error;

        flags = flags.withInOut(false);
      }
      args.push_back(arg.withFlags(flags));
    }

    // Tuple construction is simply tuple conversion. We should have already
    // handled the parameter flags. If any future parameter flags are added,
    // they should also be verified above.
    Type argType = AnyFunctionType::composeTuple(
        getASTContext(), args, ParameterFlagHandling::AssertEmpty);
    Type resultType = fnType->getResult();

    ConstraintLocatorBuilder builder(locator);
    if (matchTypes(resultType, desugarValueType, ConstraintKind::Bind, flags,
                   builder.withPathElement(ConstraintLocator::ApplyFunction))
            == SolutionKind::Error)
      return SolutionKind::Error;

    return matchTypes(argType, valueType, ConstraintKind::Conversion,
                      getDefaultDecompositionOptions(flags), locator);
  }

  case TypeKind::Enum:
  case TypeKind::Struct:
  case TypeKind::Class:
  case TypeKind::BoundGenericClass:
  case TypeKind::BoundGenericEnum:
  case TypeKind::BoundGenericStruct:
  case TypeKind::PrimaryArchetype:
  case TypeKind::ExistentialArchetype:
  case TypeKind::OpaqueTypeArchetype:
  case TypeKind::PackArchetype:
  case TypeKind::ElementArchetype:
  case TypeKind::DynamicSelf:
  case TypeKind::ProtocolComposition:
  case TypeKind::ParameterizedProtocol:
  case TypeKind::Protocol:
  case TypeKind::Existential:
  case TypeKind::ErrorUnion:
    // Break out to handle the actual construction below.
    break;

#define BUILTIN_TYPE(id, parent) case TypeKind::id:
#define TYPE(id, parent)
#include "swift/AST/TypeNodes.def"
  case TypeKind::ExistentialMetatype:
  case TypeKind::Metatype:
  case TypeKind::Function:
  case TypeKind::LValue:
  case TypeKind::InOut:
  case TypeKind::Module:
  case TypeKind::Pack:
  case TypeKind::PackExpansion:
  case TypeKind::PackElement: {
    // If solver is in the diagnostic mode and this is an invalid base,
    // let's give solver a chance to repair it to produce a good diagnostic.
    if (shouldAttemptFixes())
      break;

    return SolutionKind::Error;
  }
  }

  auto fnLocator = getConstraintLocator(locator,
                                        ConstraintLocator::ApplyFunction);
  auto memberTypeLoc =
      getConstraintLocator(fnLocator, LocatorPathElt::ConstructorMemberType(
                                          /*shortFormOrSelfDelegating*/ true));

  auto memberType = createTypeVariable(memberTypeLoc, TVO_CanBindToNoEscape);

  // The constructor will have function type T -> T2, for a fresh type
  // variable T. T2 is the result type provided via the construction
  // constraint itself.
  addValueMemberConstraint(MetatypeType::get(valueType, getASTContext()),
                           // OK: simplifyConstructionConstraint() is only used
                           // for `T(...)` init calls, not `T.init(...)` calls,
                           // so there's no module selector on `init`.
                           DeclNameRef::createConstructor(),
                           memberType,
                           useDC, functionRefInfo,
                           /*outerAlternatives=*/{},
                           getConstraintLocator(
                             fnLocator, 
                             ConstraintLocator::ConstructorMember));

  // HACK: Bind the function's parameter list as a tuple to a type variable.
  // This only exists to preserve compatibility with rdar://85263844, as it can
  // affect the prioritization of bindings, which can affect behavior for tuple
  // matching as tuple subtyping is currently a *weaker* constraint than tuple
  // conversion.
  if (!getASTContext().isLanguageModeAtLeast(LanguageMode::v6)) {
    auto paramTypeVar = createTypeVariable(
        getConstraintLocator(locator, ConstraintLocator::ApplyArgument),
        TVO_CanBindToLValue | TVO_CanBindToInOut | TVO_CanBindToNoEscape |
            TVO_CanBindToPack);
    addConstraint(ConstraintKind::BindTupleOfFunctionParams, memberType,
                  paramTypeVar, locator);
  }

  addApplicationConstraint(fnType, memberType,
                           /*trailingClosureMatching=*/std::nullopt, useDC,
                           fnLocator);

  return SolutionKind::Solved;
}

ConstraintSystem::SolutionKind ConstraintSystem::simplifySubclassOfConstraint(
                                 Type type,
                                 Type classType,
                                 ConstraintLocatorBuilder locator,
                                 TypeMatchOptions flags) {
  if (!classType->getClassOrBoundGenericClass())
    return SolutionKind::Error;

  // Dig out the fixed type to which this type refers.
  type = getFixedTypeRecursive(type, flags, /*wantRValue=*/true);
  if (shouldAttemptFixes() && type->isPlaceholder()) {
    // If the type associated with this subclass check is a "hole" in the
    // constraint system, let's consider this check a success without recording
    // a fix, because it's just a consequence of the other failure, e.g.
    //
    // func foo<T: NSObject>(_: T) {}
    // foo(Foo.bar) <- if `Foo` doesn't have `bar` there is
    //                 no reason to complain the subclass.
    return SolutionKind::Solved;
  }

  auto formUnsolved = [&]() {
    // If we're supposed to generate constraints, do so.
    if (flags.contains(TMF_GenerateConstraints)) {
      auto *subclassOf = Constraint::create(
          *this, ConstraintKind::SubclassOf, type, classType,
          getConstraintLocator(locator));

      addUnsolvedConstraint(subclassOf);
      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  };

  // If we hit a type variable without a fixed type, we can't
  // solve this yet.
  if (type->isTypeVariableOrMember())
    return formUnsolved();

  // SubclassOf constraints are generated when opening a generic
  // signature with a RequirementKind::Superclass requirement, so
  // we must handle pack types on the left by splitting up into
  // smaller constraints.
  if (auto *packType = type->getAs<PackType>()) {
    for (unsigned i = 0, e = packType->getNumElements(); i < e; ++i) {
      auto eltType = packType->getElementType(i);
      if (auto *packExpansionType = eltType->getAs<PackExpansionType>()) {
        auto patternLoc =
            locator.withPathElement(ConstraintLocator::PackExpansionPattern);
        addConstraint(ConstraintKind::SubclassOf, packExpansionType->getPatternType(),
                      classType, patternLoc);
      } else {
        addConstraint(ConstraintKind::SubclassOf, eltType,
                      classType, locator.withPathElement(LocatorPathElt::PackElement(i)));
      }
    }

    return SolutionKind::Solved;
  }

  // A class-constrained existential like 'C & P' does not satisfy an
  // AnyObject requirement, if 'P' is not self-conforming.
  //
  // While matchSuperclassTypes() will still match here because 'C & P'
  // satisfies a Subtype constraint with 'C', 'C & P' cannot satisfy a
  // superclass requirement in a generic signature, so rule that out here.
  if (type->satisfiesClassConstraint()) {
    // If we have an exact match of class declarations, ensure the
    // generic arguments match.
    if (type->getClassOrBoundGenericClass() ==
        classType->getClassOrBoundGenericClass()) {
      auto result = matchTypes(type, classType, ConstraintKind::Bind,
                               flags, locator);
      if (result != SolutionKind::Error)
        return SolutionKind::Solved;

    // Otherwise, ensure the left hand side is a proper subclass of the
    // right hand side.
    } else {
      auto result = matchSuperclassTypes(type, classType, flags, locator);
      if (result != SolutionKind::Error)
        return SolutionKind::Solved;
    }
  }

  // Record a fix if we didn't match one of the two cases above.
  if (shouldAttemptFixes()) {
    if (auto *fix = fixRequirementFailure(*this, type, classType, locator)) {
      if (recordFix(fix))
        return SolutionKind::Error;
      return SolutionKind::Solved;
    }
  }

  return SolutionKind::Error;
}

ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
                                 Type type,
                                 Type protocol,
                                 ConstraintKind kind,
                                 ConstraintLocatorBuilder locator,
                                 TypeMatchOptions flags) {

  if (auto proto = protocol->getAs<ProtocolType>()) {
    return simplifyConformsToConstraint(type, proto->getDecl(), kind,
                                        locator, flags);
  }

  auto conformsToSubKind = (kind == ConstraintKind::NonisolatedConformsTo)
      ? kind
      : ConstraintKind::ConformsTo;

  // Dig out the fixed type to which this type refers.
  type = getFixedTypeRecursive(type, flags, /*wantRValue=*/true);

  // ConformsTo constraints are generated when opening a generic
  // signature with a RequirementKind::Conformance requirement, so
  // we must handle pack types on the left by splitting up into
  // smaller constraints.
  if (auto *packType = type->getAs<PackType>()) {
    for (unsigned i = 0, e = packType->getNumElements(); i < e; ++i) {
      auto eltType = packType->getElementType(i);
      if (auto *packExpansionType = eltType->getAs<PackExpansionType>()) {
        auto patternLoc =
            locator.withPathElement(ConstraintLocator::PackExpansionPattern);
        addConstraint(conformsToSubKind,
                      packExpansionType->getPatternType(), protocol,
                      patternLoc);
      } else {
        addConstraint(conformsToSubKind, eltType, protocol,
                      locator.withPathElement(LocatorPathElt::PackElement(i)));
      }
    }

    return SolutionKind::Solved;
  }

  auto result = matchExistentialTypes(type, protocol, kind, flags, locator);

  if (shouldAttemptFixes() && result == SolutionKind::Error) {
    auto *loc = getConstraintLocator(locator);

    ArrayRef<LocatorPathElt> path = loc->getPath();
    while (!path.empty()) {
      if (!path.back().is<LocatorPathElt::InstanceType>())
        break;

      path = path.drop_back();
    }

    if (path.size() != loc->getPath().size()) {
      loc = getConstraintLocator(loc->getAnchor(), path);
    }

    ConstraintFix *fix = nullptr;
    if (loc->isLastElement<LocatorPathElt::ApplyArgToParam>()) {
      fix = AllowArgumentMismatch::create(*this, type, protocol, loc);
    } else if (loc->isLastElement<LocatorPathElt::ContextualType>()) {
      fix = ContextualMismatch::create(*this, type, protocol, loc);
    }

    if (fix) {
      return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
    }
  }

  return result;
}

void ConstraintSystem::recordSynthesizedConformance(
                                           ConstraintLocator *locator,
                                           ProtocolDecl *proto) {
  bool inserted = SynthesizedConformances.insert({locator, proto}).second;
  ASSERT(inserted);

  if (solverState)
    recordChange(SolverTrail::Change::RecordedSynthesizedConformance(locator));
}

ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
                                 Type type,
                                 ProtocolDecl *protocol,
                                 ConstraintKind kind,
                                 ConstraintLocatorBuilder locator,
                                 TypeMatchOptions flags) {
  const auto rawType = type;
  auto *typeVar = type->getAs<TypeVariableType>();

  // Dig out the fixed type to which this type refers.
  type = getFixedTypeRecursive(type, flags, /*wantRValue=*/true);
  if (shouldAttemptFixes() && type->isPlaceholder()) {
    // If the type associated with this conformance check is a "hole" in the
    // constraint system, let's consider this check a success without recording
    // a fix, because it's just a consequence of the other failure, e.g.
    //
    // func foo<T: BinaryInteger>(_: T) {}
    // foo(Foo.bar) <- if `Foo` doesn't have `bar` there is
    //                 no reason to complain about missing conformance.
    return SolutionKind::Solved;
  }

  auto formUnsolved = [&]() {
    // If we're supposed to generate constraints, do so.
    if (flags.contains(TMF_GenerateConstraints)) {
      auto *conformance = Constraint::create(
          *this, kind, type, protocol->getDeclaredInterfaceType(),
          getConstraintLocator(locator));

      addUnsolvedConstraint(conformance);
      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  };

  // If we hit a type variable without a fixed type, we can't
  // solve this yet.
  if (type->isTypeVariableOrMember())
    return formUnsolved();

  // If we have a function type and are checking for Sendable conformance we
  // need to delay if we have Sendable dependence.
  if (auto *fnTy = type->getAs<FunctionType>()) {
    if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable)) {
      if (fnTy->getSendableDependentType()) {
        fnTy = simplifyType(fnTy)->castTo<FunctionType>();
        type = fnTy;
      }
      if (fnTy->getSendableDependentType())
        return formUnsolved();
    }
  }

  auto conformsToSubKind = kind;
  if (kind != ConstraintKind::NonisolatedConformsTo)
    conformsToSubKind = ConstraintKind::ConformsTo;

  // ConformsTo constraints are generated when opening a generic
  // signature with a RequirementKind::Conformance requirement, so
  // we must handle pack types on the left by splitting up into
  // smaller constraints.
  if (auto *packType = type->getAs<PackType>()) {
    for (unsigned i = 0, e = packType->getNumElements(); i < e; ++i) {
      auto eltType = packType->getElementType(i);
      if (auto *packExpansionType = eltType->getAs<PackExpansionType>()) {
        auto patternLoc =
            locator.withPathElement(ConstraintLocator::PackExpansionPattern);
        addConstraint(conformsToSubKind,
                      packExpansionType->getPatternType(),
                      protocol->getDeclaredInterfaceType(),
                      patternLoc);
      } else {
        addConstraint(conformsToSubKind, eltType,
                      protocol->getDeclaredInterfaceType(),
                      locator.withPathElement(LocatorPathElt::PackElement(i)));
      }
    }

    return SolutionKind::Solved;
  }

  // We sometimes get a pack expansion type here.
  if (auto *expansionType = type->getAs<PackExpansionType>()) {
    addConstraint(
        conformsToSubKind, expansionType->getPatternType(),
        protocol->getDeclaredInterfaceType(),
        locator.withPathElement(LocatorPathElt::PackExpansionPattern()));

    return SolutionKind::Solved;
  }

  auto *loc = getConstraintLocator(locator);

  /// Record the given conformance as the result, adding any conditional
  /// requirements if necessary.
  auto recordConformance = [&](ProtocolConformanceRef conformance) {
    if (isConformanceUnavailable(conformance, loc)) {
      if (conformance.getProtocol()->isSpecificProtocol(KnownProtocolKind::Sendable)) {
        increaseScore(SK_MissingSynthesizableConformance, locator);
      } else {
        increaseScore(SK_Unavailable, locator);
      }
    }

    unsigned numMissing = 0;
    conformance.forEachMissingConformance([&numMissing](auto *missing) {
                                            ++numMissing;
                                            return false;
                                          });

    if (numMissing > 0)
      increaseScore(SK_MissingSynthesizableConformance, locator, numMissing);

    // If we aren't allowed to have an isolated conformance, check for any
    // isolated conformances here.
    if (kind == ConstraintKind::NonisolatedConformsTo &&
        !conformance.getProtocol()->isMarkerProtocol()) {
      // Grab the first isolated conformance, if there is one.
      ProtocolConformanceRef isolatedConformance;
      conformance.forEachIsolatedConformance([&](ProtocolConformanceRef conf) {
        if (!isolatedConformance)
          isolatedConformance = conf;
        return true;
      });

      if (isolatedConformance && isolatedConformance.isConcrete()) {
        auto fix = IgnoreIsolatedConformance::create(
            *this, getConstraintLocator(locator),
            isolatedConformance.getConcrete());
        if (recordFix(fix)) {
          return SolutionKind::Error;
        }
      }
    }

    // This conformance may be conditional, in which case we need to consider
    // those requirements as constraints too.
    if (conformance.isConcrete()) {
      unsigned index = 0;
      auto *conformanceLoc = getConstraintLocator(
          loc,
          LocatorPathElt::ConformanceRequirement(conformance.getConcrete()));

      for (auto req : conformance.getConditionalRequirements()) {
        // Replace any ErrorTypes with holes if needed in case of substitution
        // failure.
        req = req.tranformSubjectTypes([&](Type ty) {
          return replaceInferableTypesWithTypeVars(ty, loc);
        });
        addConstraint(
            req, getConstraintLocator(conformanceLoc,
                                      LocatorPathElt::ConditionalRequirement(
                                          index++, req.getKind())),
            /*isFavored=*/false, kind == ConstraintKind::NonisolatedConformsTo);
      }
    }

    return SolutionKind::Solved;
  };

  // For purposes of argument type matching, existential types don't need to
  // conform -- they only need to contain the protocol, so check that
  // separately.
  switch (kind) {
  case ConstraintKind::Subtype: {
    auto pair = TypeChecker::containsProtocol(
        type, protocol, /*allowMissing=*/true);
    if (pair.first)
      return SolutionKind::Solved;
    if (pair.second)
      return recordConformance(pair.second);
  } break;
  case ConstraintKind::NonisolatedConformsTo:
  case ConstraintKind::ConformsTo:
  case ConstraintKind::LiteralConformsTo: {
    // Check whether this type conforms to the protocol.
    auto conformance = lookupConformance(type, protocol);
    if (conformance) {
      return recordConformance(conformance);
    }

    // Account for ad-hoc requirements on some distributed actor
    // requirements.
    if (auto witnessInfo = locator.isForWitnessGenericParameterRequirement()) {
      auto *GP = witnessInfo->second;

      // Conformance requirement between on `Res` and `SerializationRequirement`
      // of `DistributedActorSystem.remoteCall` are not expressible at the moment
      // but they are verified by Sema so it's okay to omit them here and lookup
      // dynamically during IRGen.
      if (auto *witness = dyn_cast<FuncDecl>(witnessInfo->first)) {
        auto synthesizeConformance = [&]() {
          auto witnessLoc = getConstraintLocator(
              locator.getAnchor(), LocatorPathElt::Witness(witness));
          // FIXME: Why are we recording the same locator more than once here?
          if (SynthesizedConformances.count(witnessLoc) == 0)
            recordSynthesizedConformance(witnessLoc, protocol);
          return SolutionKind::Solved;
        };

        if (witness->hasGenericParamList()) {
          // `DistributedActorSystem.remoteCall`
          if (witness->isDistributedActorSystemRemoteCall(/*isVoidReturn=*/false)) {
            if (GP->isEqual(cast<FuncDecl>(witness)->getResultInterfaceType()))
              return synthesizeConformance();
          }

          // `DistributedTargetInvocationEncoder.record{Argument, ResultType}`
          // `DistributedTargetInvocationDecoder.decodeNextArgument`
          // `DistributedTargetInvocationResultHandler.onReturn`
          if (witness->isDistributedTargetInvocationEncoderRecordArgument() ||
              witness->isDistributedTargetInvocationEncoderRecordReturnType() ||
              witness
                  ->isDistributedTargetInvocationDecoderDecodeNextArgument() ||
              witness->isDistributedTargetInvocationResultHandlerOnReturn()) {
            auto genericParams = witness->getGenericParams()->getParams();
            if (GP->isEqual(genericParams.front()->getDeclaredInterfaceType()))
              return synthesizeConformance();
          }
        }
      }
    }

    auto arrayLiteralProto =
      getASTContext().getProtocol(KnownProtocolKind::ExpressibleByArrayLiteral);
    auto anchor = loc->getAnchor();
    auto arrayLiteral = getAsExpr<ArrayExpr>(anchor);

    // If we're attempting to bind an array literal to an 'InlineArray'
    // parameter, then check if the counts are equal and solve.
    if (kind == ConstraintKind::LiteralConformsTo &&
        protocol == arrayLiteralProto &&
        (type->isInlineArray() || type->is_InlineArray()) &&
        arrayLiteral) {
      auto iaTy = type->castTo<BoundGenericStructType>();

      // <let count: Int, Element>
      // Attempt to bind the number of elements in the literal with the
      // contextual count. This will diagnose if the literal does not enough
      // or too many elements.
      auto inlineArrayCountParam = iaTy->getGenericArgs()[0];
      // If our contextual count is not known, e.g., InlineArray<_, Int> = [1, 2],
      //then just eagerly bind the count to what the literal count is.
      if (inlineArrayCountParam->isTypeVariableOrMember()) {
        addConstraint(
            ConstraintKind::Bind, inlineArrayCountParam,
            IntegerType::get(std::to_string(arrayLiteral->getNumElements()),
                             /* isNegative */ false, iaTy->getASTContext()),
            locator);
        return SolutionKind::Solved;
      }

      unsigned inlineArrayCount = 0;
      if (auto *intCount = inlineArrayCountParam->getAs<IntegerType>()) {
        inlineArrayCount = intCount->getValue().getZExtValue();
      } else {
        return SolutionKind::Error;
      }

      // If the counts are already equal, '2' == '2', then we're done.
      if (inlineArrayCount == arrayLiteral->getNumElements()) {
        return SolutionKind::Solved;
      }

      // Otherwise this is an error and the counts aren't equal to each other.
      if (!shouldAttemptFixes())
        return SolutionKind::Error;

      auto fix = AllowInlineArrayLiteralCountMismatch::create(
          *this, inlineArrayCount, arrayLiteral->getNumElements(), loc);
      return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
    }
  } break;

  default:
    llvm_unreachable("bad constraint kind");
  }
  
  if (!shouldAttemptFixes())
    return SolutionKind::Error;

  auto protocolTy = protocol->getDeclaredInterfaceType();

  // If this conformance has been fixed already, let's just consider this done.
  if (isFixedRequirement(loc, protocolTy))
    return SolutionKind::Solved;

  // If this is a generic requirement let's try to record that
  // conformance is missing and consider this a success, which
  // makes it much easier to diagnose problems like that.
  {
    SmallVector<LocatorPathElt, 4> path;
    auto anchor = locator.getLocatorParts(path);

    // If this is a `nil` literal, it would be a contextual failure.
    if (auto *Nil = getAsExpr<NilLiteralExpr>(anchor)) {
      auto *fixLocator = getConstraintLocator(
          getContextualType(Nil, /*forConstraint=*/false)
              ? locator.withPathElement(LocatorPathElt::ContextualType(
                    getContextualTypePurpose(Nil)))
              : locator);

      // Only requirement placed directly on `nil` literal is
      // `ExpressibleByNilLiteral`, so if `nil` is an argument
      // to an application, let's update locator accordingly to
      // diagnose possible ambiguities with multiple mismatched
      // overload choices.
      if (fixLocator->directlyAt<NilLiteralExpr>()) {
        if (auto *loc = getArgumentLocator(castToExpr(fixLocator->getAnchor())))
          fixLocator = loc;
      }

      // Here the roles are reversed - `nil` is something we are trying to
      // convert to `type` by making sure that it conforms to a specific
      // protocol.
      auto *fix =
          ContextualMismatch::create(*this, protocolTy, type, fixLocator);

      return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
    }

    // If there is a missing conformance between source and destination
    // of the assignment, let's ignore current the types and instead use
    // source/destination types directly to make it possible to diagnose
    // protocol compositions.
    if (auto *assignment = getAsExpr<AssignExpr>(anchor)) {
      // If the locator's last element points to the function result,
      // let's check whether there is a problem with function argument
      // as well, and if so, avoid producing a fix here, because
      // contextual mismatch mentions the source/destination
      // types of the assignment.
      if (locator.endsWith<LocatorPathElt::FunctionResult>() &&
          hasFixFor(
              getConstraintLocator(anchor, LocatorPathElt::FunctionArgument())))
        return SolutionKind::Solved;

      auto srcType = getType(assignment->getSrc());
      auto dstType = getType(assignment->getDest());

      auto *fix = IgnoreAssignmentDestinationType::create(
          *this, srcType, dstType, loc);
      return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
    }

    if (path.empty())
      return SolutionKind::Error;

    // If this is a conformance failure related to a contextual type
    // let's record it as a "contextual mismatch" because diagnostic
    // is going to be dependent on other contextual information.
    if (path.back().is<LocatorPathElt::ContextualType>()) {
      auto *fix = ContextualMismatch::create(*this, type, protocolTy, loc);
      return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
    }

    // Conditional conformance requirements could produce chains of
    // `path element -> pack expansion pattern -> pack element`.
    while (!path.empty()) {
      // If we have something like ... -> type req # -> pack element #, we're
      // solving a requirement of the form T : P where T is a type parameter pack
      if (path.back().is<LocatorPathElt::PackElement>()) {
        path.pop_back();
        continue;
      }

      // This is similar to `PackElement` but locator points to the requirement
      // associated with pack expansion pattern (i.e. `repeat each T: P`) where
      // the path is something like:
      // `... -> type req # -> pack expansion pattern`.
      if (path.back().is<LocatorPathElt::PackExpansionPattern>()) {
        path.pop_back();
        continue;
      }

      // Matching existentials could introduce constraints with `instance type`
      // element at the end if the confirming type wasn't fully resolved.
      if (path.back().is<LocatorPathElt::InstanceType>()) {
        path.pop_back();
        continue;
      }

      break;
    }

    if (auto req = path.back().getAs<LocatorPathElt::AnyRequirement>()) {
      // If this is a requirement associated with `Self` which is bound
      // to `Any`, let's consider this "too incorrect" to continue.
      //
      // This helps us to filter out cases like operator overloads where
      // `Self` type comes from e.g. default for collection element -
      // `[1, "hello"].map { $0 + 1 }`. Main problem here is that
      // collection type couldn't be determined without unification to
      // `Any` and `+` failing for all numeric overloads is just a consequence.
      if (typeVar && type->isAny()) {
        if (auto *GP = typeVar->getImpl().getGenericParameter()) {
          if (auto *GPD = GP->getDecl()) {
            auto *DC = GPD->getDeclContext();
            if (DC->isTypeContext() && DC->getSelfInterfaceType()->isEqual(GP))
              return SolutionKind::Error;
          }
        }
      }

      if (auto rawValue = isRawRepresentable(*this, type)) {
        if (!rawValue->isTypeVariableOrMember() &&
            lookupConformance(rawValue, protocol)) {
          auto *fix = UseRawValue::create(*this, type, protocolTy, loc);
          // Since this is a conformance requirement failure (where the
          // source is most likely an argument), let's increase its impact
          // to disambiguate vs. conversion failure of the same kind.
          return recordFix(fix, FixImpact::TypeMismatch) ? SolutionKind::Error
                                                         : SolutionKind::Solved;
        }
      }

      auto anchor = locator.getAnchor();

      if (isExpr<UnresolvedMemberExpr>(anchor) &&
          req->is<LocatorPathElt::TypeParameterRequirement>()) {
        auto *memberLoc = getConstraintLocator(anchor, path.front());

        if (hasFixFor(memberLoc))
          return SolutionKind::Solved;

        auto signature = path[path.size() - 2]
                             .castTo<LocatorPathElt::OpenedGeneric>()
                             .getSignature();
        auto requirement = signature.getRequirements()[req->getIndex()];

        auto attemptInvalidStaticMemberRefOnMetatypeFix = [&]() {
          // If the failed requirement isn't the first generic parameter,
          // it can't be a static member reference on a protocol metatype.
          if (!requirement.getFirstType()->isEqual(getASTContext().TheSelfType))
            return false;

          // If we don't know the overload yet, conservatively assume it's
          // a static member reference on a protocol metatype.
          auto overload = findSelectedOverloadFor(memberLoc);
          if (!overload)
            return true;

          auto *decl = overload->choice.getDeclOrNull();
          if (!decl)
            return true;

          // Otherwise, we can do a precise check.
          if (!decl->isStatic())
            return false;

          return decl->getDeclContext()->getSelfProtocolDecl() != nullptr;
        };

        if (attemptInvalidStaticMemberRefOnMetatypeFix()) {
          auto *fix = AllowInvalidStaticMemberRefOnProtocolMetatype::create(
              *this, memberLoc);

          return recordFix(fix, FixImpact::InvalidReference)
                     ? SolutionKind::Error
                     : SolutionKind::Solved;
        }
      }

      if (auto *fix =
              fixRequirementFailure(*this, type, protocolTy, anchor, path)) {
        auto impact = assessRequirementFailureImpact(*this, rawType, locator);
        if (!recordFix(fix, impact)) {
          // Record this conformance requirement as "fixed".
          recordFixedRequirement(getConstraintLocator(anchor, path),
                                 protocolTy);
          return SolutionKind::Solved;
        }
      }
    }

    if (path.back().is<LocatorPathElt::MemberRefBase>()) {
      auto *fix = ContextualMismatch::create(*this, protocolTy, type, loc);
      if (!recordFix(fix))
        return SolutionKind::Solved;
    }

    // Conformance constraint that is introduced by an implicit conversion
    // for example to `AnyHashable`.
    if ((kind == ConstraintKind::ConformsTo ||
         kind == ConstraintKind::NonisolatedConformsTo) &&
        path.back().is<LocatorPathElt::ApplyArgToParam>()) {
      auto *fix = AllowArgumentMismatch::create(*this, type, protocolTy, loc);
      return recordFix(fix, FixImpact::TypeMismatch) ? SolutionKind::Error
                                                     : SolutionKind::Solved;
    }

    // If this is an implicit Hashable conformance check generated for each
    // index argument of the keypath subscript component, we could just treat
    // it as though it conforms.
    if (loc->isResultOfKeyPathDynamicMemberLookup() ||
        loc->isKeyPathSubscriptComponent() ||
        loc->isKeyPathMemberComponent() ||
        loc->isKeyPathApplyComponent()) {
      if (protocol ==
          getASTContext().getProtocol(KnownProtocolKind::Hashable)) {
        auto *fix =
            TreatKeyPathSubscriptIndexAsHashable::create(*this, type, loc);
        if (!recordFix(fix))
          return SolutionKind::Solved;
      }
    }
  }

  // There's nothing more we can do; fail.
  return SolutionKind::Error;
}

ConstraintSystem::SolutionKind ConstraintSystem::simplifyTransitivelyConformsTo(
    Type type, Type protocolTy, ConstraintLocatorBuilder locator,
    TypeMatchOptions flags) {
  auto &ctx = getASTContext();

  // Since this is a performance optimization, let's ignore it
  // in diagnostic mode.
  if (shouldAttemptFixes())
    return SolutionKind::Solved;

  auto formUnsolved = [&]() {
    // If we're supposed to generate constraints, do so.
    if (flags.contains(TMF_GenerateConstraints)) {
      auto *conformance =
          Constraint::create(*this, ConstraintKind::TransitivelyConformsTo,
                             type, protocolTy, getConstraintLocator(locator));

      addUnsolvedConstraint(conformance);
      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  };

  auto resolvedTy = getFixedTypeRecursive(type, /*wantRValue=*/true);
  if (resolvedTy->isTypeVariableOrMember())
    return formUnsolved();

  // If the composition consists of a class + protocol,
  // we can't check conformance of the argument because
  // parameter could pick one of the components.
  if (resolvedTy.findIf(
          [](Type type) { return type->is<ProtocolCompositionType>(); }))
    return SolutionKind::Solved;

  // All bets are off for pointers, there are multiple combinations
  // to check and it doesn't see worth to do that upfront.
  {
    PointerTypeKind pointerKind;
    if (resolvedTy->getAnyPointerElementType(pointerKind))
      return SolutionKind::Solved;
  }

  auto *protocol = protocolTy->castTo<ProtocolType>()->getDecl();

  // First, let's check whether the type itself conforms,
  // if it does - we are done.
  if (lookupConformance(resolvedTy, protocol))
    return SolutionKind::Solved;

  // If the type doesn't conform, let's check whether
  // an Optional or Unsafe{Mutable}Pointer from it would.

  // If the current score is equal to the best score, fail without checking
  // implicit conversions, because an implicit conversion would lead to a
  // worse score anyway.
  if (solverState && solverState->BestScore && CurrentScore == *solverState->BestScore)
    return SolutionKind::Error;

  SmallVector<Type, 4> typesToCheck;

  // T -> Optional<T>
  if (!resolvedTy->getOptionalObjectType())
    typesToCheck.push_back(OptionalType::get(resolvedTy));

  // AnyHashable
  if (auto *anyHashable = ctx.getAnyHashableDecl())
    typesToCheck.push_back(anyHashable->getDeclaredInterfaceType());

  // Rest of the implicit conversions depend on the resolved type.
  {
    auto getPointerFor = [&ctx](PointerTypeKind ptrKind,
                                std::optional<Type> elementTy =
                                    std::nullopt) -> Type {
      switch (ptrKind) {
      case PTK_UnsafePointer:
        assert(elementTy);
        return BoundGenericType::get(ctx.getUnsafePointerDecl(),
                                     /*parent=*/Type(), {*elementTy});
      case PTK_UnsafeMutablePointer:
        assert(elementTy);
        return BoundGenericType::get(ctx.getUnsafeMutablePointerDecl(),
                                     /*parent=*/Type(), {*elementTy});

      case PTK_UnsafeRawPointer:
        return ctx.getUnsafeRawPointerDecl()->getDeclaredInterfaceType();

      case PTK_UnsafeMutableRawPointer:
        return ctx.getUnsafeMutableRawPointerDecl()->getDeclaredInterfaceType();

      case PTK_AutoreleasingUnsafeMutablePointer:
        llvm_unreachable("no implicit conversion");
      }
    };

    // String -> UnsafePointer<Void>
    if (auto *string = ctx.getStringDecl()) {
      if (resolvedTy->isEqual(string->getDeclaredInterfaceType())) {
        typesToCheck.push_back(
            getPointerFor(PTK_UnsafePointer, ctx.TheEmptyTupleType));
      }
    }

    // Array<T> -> Unsafe{Raw}Pointer<T>
    if (auto elt = resolvedTy->getArrayElementType()) {
      typesToCheck.push_back(getPointerFor(PTK_UnsafePointer, elt));
      typesToCheck.push_back(getPointerFor(PTK_UnsafeRawPointer, elt));
    }

    // inout argument -> UnsafePointer<T>, UnsafeMutablePointer<T>,
    //                   UnsafeRawPointer, UnsafeMutableRawPointer.
    if (type->is<InOutType>()) {
      typesToCheck.push_back(getPointerFor(PTK_UnsafePointer, resolvedTy));
      typesToCheck.push_back(getPointerFor(PTK_UnsafeMutablePointer, resolvedTy));
      typesToCheck.push_back(getPointerFor(PTK_UnsafeRawPointer));
      typesToCheck.push_back(getPointerFor(PTK_UnsafeMutableRawPointer));
    }
  }

  return llvm::any_of(
             typesToCheck,
             [&](Type type) { return bool(lookupConformance(type, protocol)); })
             ? SolutionKind::Solved
             : SolutionKind::Error;
}

/// Determine the kind of checked cast to perform from the given type to
/// the given type.
///
/// This routine does not attempt to check whether the cast can actually
/// succeed; that's the caller's responsibility.
static CheckedCastKind getCheckedCastKind(ConstraintSystem *cs,
                                          Type fromType,
                                          Type toType) {
  // Array downcasts are handled specially.
  if (fromType->isArray() && toType->isArray()) {
    return CheckedCastKind::ArrayDowncast;
  }

  // Dictionary downcasts are handled specially.
  if (cs->isDictionaryType(fromType) && cs->isDictionaryType(toType)) {
    return CheckedCastKind::DictionaryDowncast;
  }

  // Set downcasts are handled specially.
  if (cs->isSetType(fromType) && cs->isSetType(toType)) {
    return CheckedCastKind::SetDowncast;
  }

  return CheckedCastKind::ValueCast;
}

// Optional types always conform to `ExpressibleByNilLiteral`.
static bool isCastToExpressibleByNilLiteral(ConstraintSystem &cs, Type fromType,
                                            Type toType) {
  auto &ctx = cs.getASTContext();
  auto *nilLiteral = ctx.getProtocol(KnownProtocolKind::ExpressibleByNilLiteral);
  if (!nilLiteral)
    return false;

  return toType->isEqual(nilLiteral->getDeclaredExistentialType()) &&
         fromType->getOptionalObjectType();
}

static ConstraintFix *maybeWarnAboutExtraneousCast(
    ConstraintSystem &cs, Type origFromType, Type origToType, Type fromType,
    Type toType, SmallVector<Type, 4> fromOptionals,
    SmallVector<Type, 4> toOptionals,
    ConstraintSystem::TypeMatchOptions flags,
    ConstraintLocatorBuilder locator) {
  if (locator.endsWith<LocatorPathElt::GenericArgument>())
    return nullptr;

  // Both types have to be resolved, `typeCheckCheckedCast` doesn't support
  // checking solver-allocated types.
  if (fromType->hasTypeVariableOrPlaceholder() ||
      toType->hasTypeVariableOrPlaceholder()) {
    return nullptr;
  }

  SmallVector<LocatorPathElt, 4> path;
  auto anchor = locator.getLocatorParts(path);

  auto *castExpr = getAsExpr<ExplicitCastExpr>(anchor);
  if (!castExpr)
    return nullptr;

  // "from" could be less optional than "to" e.g. `0 as Any?`, so
  // we need to store the difference as a signed integer.
  int extraOptionals = fromOptionals.size() - toOptionals.size();

  // "from" expression could be a type variable wrapped in an optional e.g.
  // Optional<$T0>. So when that is the case we have to add this additional
  // optionality levels to from type.
  const auto subExprType = cs.getType(castExpr->getSubExpr());
  if (subExprType->getOptionalObjectType()) {
    SmallVector<Type, 4> subExprOptionals;
    const auto unwrappedSubExprType =
        subExprType->lookThroughAllOptionalTypes(subExprOptionals);
    if (unwrappedSubExprType->is<TypeVariableType>()) {
      extraOptionals += subExprOptionals.size();
      for (size_t i = 0; i != subExprOptionals.size(); ++i) {
        origFromType = OptionalType::get(origFromType);
      }
    }
  }

  // Removing the optionality from to type when the force cast expr is an IUO.
  const auto *const TR = castExpr->getCastTypeRepr();
  if (isExpr<ForcedCheckedCastExpr>(anchor) && TR &&
      TR->getKind() == TypeReprKind::ImplicitlyUnwrappedOptional) {
    extraOptionals++;
  }

  // In cases of 'try?' where origFromType isn't optional that meaning
  // sub-expression isn't optional, always adds one level of optionality
  // because the result of the expression is always an optional type
  // regardless of language mode.
  auto *sub = castExpr->getSubExpr()->getSemanticsProvidingExpr();
  if (isExpr<OptionalTryExpr>(sub) && !origFromType->getOptionalObjectType()) {
    origFromType = OptionalType::get(fromType);
    extraOptionals++;
  }

  // Except for forced cast expressions, if optionals are more than a single
  // level difference or there is a single level between the types but an extra
  // level of optional is added to subexpr via OptionalEvaluationExpr, we don't
  // need to record any fix.
  if (!isExpr<ForcedCheckedCastExpr>(anchor) &&
      (extraOptionals > 1 ||
       isExpr<OptionalEvaluationExpr>(castExpr->getSubExpr())))
    return nullptr;

  // Always succeed
  if (isCastToExpressibleByNilLiteral(cs, origFromType, toType)) {
    return AllowNoopCheckedCast::create(cs, fromType, toType,
                                        CheckedCastKind::Coercion,
                                        cs.getConstraintLocator(locator));
  }

  // If both original are metatypes we have to use them because most of the
  // logic on how correctly handle metatypes casting is on
  // typeCheckCheckedCast.
  if (origFromType->is<AnyMetatypeType>() &&
      origToType->is<AnyMetatypeType>()) {
    fromType = origFromType;
    toType = origToType;
  }

  auto castKind = TypeChecker::typeCheckCheckedCast(
      fromType, toType, CheckedCastContextKind::None, cs.DC);

  if (castKind == CheckedCastKind::Unresolved) {
    return AllowCheckedCastToUnrelated::attempt(
        cs, origFromType, origToType, castKind,
        cs.getConstraintLocator(locator));
  }

  if (castKind == CheckedCastKind::ValueCast) {
    // https://github.com/apple/swift/issues/44221
    // Special 'is' case diagnostics for CFTypes.
    return AllowNoopExistentialToCFTypeCheckedCast::attempt(
        cs, origFromType, origToType, castKind,
        cs.getConstraintLocator(locator));
  }

  if (!(castKind == CheckedCastKind::Coercion ||
        castKind == CheckedCastKind::BridgingCoercion))
    return nullptr;

  if (auto *fix = AllowUnsupportedRuntimeCheckedCast::attempt(
          cs, fromType, toType, castKind, cs.getConstraintLocator(locator))) {
    return fix;
  }
  if (extraOptionals > 0) {
    // Conditional cast in this case can be an attempt to just unwrap a nil
    // value.
    if (isExpr<ConditionalCheckedCastExpr>(anchor) &&
        castKind == CheckedCastKind::BridgingCoercion) {
      return nullptr;
    }

    return AllowCheckedCastCoercibleOptionalType::create(
        cs, origFromType, origToType, castKind,
        cs.getConstraintLocator(locator));
  } else {
    // No optionals, just a trivial cast that always succeeds.
    return AllowNoopCheckedCast::create(cs, origFromType, origToType, castKind,
                                        cs.getConstraintLocator(locator));
  }
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyCheckedCastConstraint(
                    Type fromType, Type toType,
                    TypeMatchOptions flags,
                    ConstraintLocatorBuilder locator) {
  TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);

  /// Form an unresolved result.
  auto formUnsolved = [&] {
    if (flags.contains(TMF_GenerateConstraints)) {
      addUnsolvedConstraint(
        Constraint::create(*this, ConstraintKind::CheckedCast, fromType,
                           toType, getConstraintLocator(locator)));
      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  };

  Type origFromType =
      getFixedTypeRecursive(fromType, flags, /*wantRValue=*/true);
  Type origToType = getFixedTypeRecursive(toType, flags, /*wantRValue=*/true);

  SmallVector<Type, 4> fromOptionals;
  SmallVector<Type, 4> toOptionals;

  do {
    // Dig out the fixed type this type refers to.
    fromType = getFixedTypeRecursive(fromType, flags, /*wantRValue=*/true);

    // If we hit a type variable without a fixed type, we can't
    // solve this yet.
    if (fromType->isTypeVariableOrMember())
      return formUnsolved();

    // Dig out the fixed type this type refers to.
    toType = getFixedTypeRecursive(toType, flags, /*wantRValue=*/true);

    // If we hit a type variable without a fixed type, we can't
    // solve this yet.
    if (toType->isTypeVariableOrMember())
      return formUnsolved();

    Type origFromType = fromType;
    Type origToType = toType;

    // Peel off optionals metatypes from the types, because we might cast through
    // them.
    toType = toType->lookThroughAllOptionalTypes(toOptionals);
    fromType = fromType->lookThroughAllOptionalTypes(fromOptionals);

    // Peel off metatypes, since if we can cast two types, we can cast their
    // metatypes.
    while (auto toMetatype = toType->getAs<MetatypeType>()) {
      auto fromMetatype = fromType->getAs<MetatypeType>();
      if (!fromMetatype)
        break;
      toType = toMetatype->getInstanceType();
      fromType = fromMetatype->getInstanceType();
    }

    // Peel off a potential layer of existential<->concrete metatype conversion.
    if (auto toMetatype = toType->getAs<AnyMetatypeType>()) {
      if (auto fromMetatype = fromType->getAs<MetatypeType>()) {
        toType = toMetatype->getInstanceType();
        fromType = fromMetatype->getInstanceType();
      }
    }

    // Peel off marker protocol requirements if this is an existential->concrete
    // cast. Handles cases like `WritableKeyPath<...> & Sendable as KeyPath`
    // that require inference which is only attempted if both sides are classes.
    if (fromType->isExistentialType() && !toType->isExistentialType()) {
      if (auto *existential = fromType->getAs<ExistentialType>()) {
        if (auto *PCT = existential->getConstraintType()
                            ->getAs<ProtocolCompositionType>()) {
          auto newConstraintTy = PCT->withoutMarkerProtocols();
          if (!newConstraintTy->isEqual(PCT)) {
            fromType = newConstraintTy->getClassOrBoundGenericClass()
                           ? newConstraintTy
                           : ExistentialType::get(newConstraintTy);
          }
        }
      }
    }

    // We've decomposed the types further, so adopt the subflags.
    flags = subflags;

    // If nothing changed, we're done.
    if (fromType.getPointer() == origFromType.getPointer() &&
        toType.getPointer() == origToType.getPointer())
      break;
  } while (true);

  auto attemptRecordCastFixIfSolved = [&](SolutionKind result) {
    if (result != SolutionKind::Solved)
      return;

    if (auto *fix = maybeWarnAboutExtraneousCast(
            *this, origFromType, origToType, fromType, toType, fromOptionals,
            toOptionals, flags, locator)) {
      (void)recordFix(fix);
    }
  };

  auto kind = getCheckedCastKind(this, fromType, toType);
  switch (kind) {
  case CheckedCastKind::ArrayDowncast: {
    auto fromBaseType = fromType->getArrayElementType();
    auto toBaseType = toType->getArrayElementType();

    auto elementLocator =
        locator.withPathElement(LocatorPathElt::GenericArgument(0));
    auto result = simplifyCheckedCastConstraint(fromBaseType, toBaseType,
                                                subflags, elementLocator);
    attemptRecordCastFixIfSolved(result);
    return result;
  }

  case CheckedCastKind::DictionaryDowncast: {
    Type fromKeyType, fromValueType;
    std::tie(fromKeyType, fromValueType) = *isDictionaryType(fromType);

    Type toKeyType, toValueType;
    std::tie(toKeyType, toValueType) = *isDictionaryType(toType);

    auto keyLocator =
        locator.withPathElement(LocatorPathElt::GenericArgument(0));
    if (simplifyCheckedCastConstraint(fromKeyType, toKeyType, subflags,
                                      keyLocator) == SolutionKind::Error)
      return SolutionKind::Error;

    auto valueLocator =
        locator.withPathElement(LocatorPathElt::GenericArgument(1));
    auto result = simplifyCheckedCastConstraint(fromValueType, toValueType,
                                                subflags, valueLocator);
    attemptRecordCastFixIfSolved(result);
    return result;
  }

  case CheckedCastKind::SetDowncast: {
    auto fromBaseType = *isSetType(fromType);
    auto toBaseType = *isSetType(toType);
    
    auto elementLocator =
        locator.withPathElement(LocatorPathElt::GenericArgument(0));
    auto result = simplifyCheckedCastConstraint(fromBaseType, toBaseType,
                                                subflags, elementLocator);
    attemptRecordCastFixIfSolved(result);
    return result;
  }

  case CheckedCastKind::ValueCast: {
    // If casting among classes, and there are open
    // type variables remaining, introduce a subtype constraint to help resolve
    // them.
    if (fromType->getClassOrBoundGenericClass()
        && toType->getClassOrBoundGenericClass()
        && (fromType->hasTypeVariable() || toType->hasTypeVariable())) {
      addConstraint(ConstraintKind::Subtype, toType, fromType,
                    getConstraintLocator(locator));
    }

    // Attempts to record warning fixes when both types are known by the
    // compiler and we can infer that the runtime checked cast will always
    // succeed or fail.
    if (auto *fix = maybeWarnAboutExtraneousCast(
            *this, origFromType, origToType, fromType, toType, fromOptionals,
            toOptionals, flags, locator)) {
      (void)recordFix(fix);
    }

    return SolutionKind::Solved;
  }

  case CheckedCastKind::Coercion:
  case CheckedCastKind::BridgingCoercion:
  case CheckedCastKind::Unresolved:
    llvm_unreachable("Not a valid result");
  }

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

Type
ConstraintSystem::lookupDependentMember(Type base, AssociatedTypeDecl *assocTy,
                                        bool openExistential,
                                        ConstraintLocatorBuilder locator) {
  /// TODO: This should become the basis for a new "type witness" constraint to
  /// replace the use of DependentMemberType in the constraint system.

  ASSERT(!base->isTypeVariableOrMember() && "Must simplify first");

  GenericEnvironment *openedEnv = nullptr;
  if (openExistential && base->isExistentialType()) {
    auto openedTy = ExistentialArchetypeType::get(base->getCanonicalType());
    openedEnv = openedTy->getGenericEnvironment();
    base = openedTy;
  }

  // Add a conformance constraint since this handles things like conditional
  // constraints and unavailable conformances.
  auto *proto = assocTy->getProtocol();
  addConstraint(ConstraintKind::ConformsTo, base,
                proto->getDeclaredInterfaceType(), locator);

  // Then lookup the conformance to dig out the witness. If it's missing, we'll
  // have recorded a fix in the ConformsTo constraint so can bail.
  auto conformance = lookupConformance(base, proto);
  if (!conformance) {
    // Increase SK_Hole just to ensure the solution is marked invalid.
    increaseScore(SK_Hole, locator);
    return Type();
  }

  auto result = conformance.getTypeWitness(assocTy);
  if (result->hasError()) {
    // If the type witness is invalid we'll have emitted an error, record a
    // fix to ensure the solution is marked invalid.
    auto *loc = getConstraintLocator(locator);
    recordFix(IgnoreInvalidASTNode::create(*this, loc), FixImpact::InvalidAST);
    return Type();
  }

  // If we opened an archetype, erase the result.
  if (openedEnv)
    result = typeEraseOpenedArchetypesFromEnvironment(result, openedEnv);

  return result;
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyForEachElementConstraint(
    Type first, Type second, TypeMatchOptions flags,
    ConstraintLocatorBuilder locator) {
  /// TODO: Once we have a "type witness" constraint to express a
  /// DependentMemberType, we ought to see if this constraint can be removed
  /// and expressed in terms of member types instead.

  Type seqTy = getFixedTypeRecursive(first, flags, /*wantRValue=*/true);
  if (seqTy->isTypeVariableOrMember()) {
    if (flags.contains(TMF_GenerateConstraints)) {
      addUnsolvedConstraint(
          Constraint::create(*this, ConstraintKind::ForEachElement, first,
                             second, getConstraintLocator(locator)));
      return SolutionKind::Solved;
    }
    return SolutionKind::Unsolved;
  }

  if (seqTy->isPlaceholder()) {
    recordTypeVariablesAsHoles(second);
    return SolutionKind::Solved;
  }

  auto anchor = locator.getAnchor();
  auto &ctx = getASTContext();

  // We can retrieve the corresponding sequence protocol from the contextual
  // type set.
  auto contextualTy = getContextualTypeInfo(anchor)->getType();
  auto *seqProto = contextualTy->castTo<ProtocolType>()->getDecl();
  auto isAsync = seqProto->isSpecificProtocol(KnownProtocolKind::AsyncSequence);
  auto isBorrowing =
      shouldUseIterable(ctx, seqTy, isAsync, anchor.getStartLoc(), DC);

  if (isBorrowing) {
    seqProto = ctx.getProtocol(KnownProtocolKind::Iterable);
  }

  auto *contextualLoc = getConstraintLocator(
      anchor, LocatorPathElt::ContextualType(CTP_ForEachSequence));

  // To lookup the corresponding element type we first need to lookup the
  // type witness for Iterator, opening an existential if needed.
  //
  // We can't directly use `Sequence.Element` since that may be not be
  // representable through the erased Iterator type, e.g for:
  //
  // protocol Q: Sequence where Self.Element: P {}
  //
  // The erased element type is `any P`, but `makeIterator` can only produce
  // `any IteratorProtocol`, so the actual element type is `Any`.
  auto *iteratorAssocTy = seqProto->getAssociatedType(
      isAsync ? ctx.Id_AsyncIterator
              : (isBorrowing ? ctx.Id_BorrowingIterator : ctx.Id_Iterator));
  auto iterTy = lookupDependentMember(seqTy, iteratorAssocTy,
                                      /*openExistential*/ true, contextualLoc);
  if (!iterTy) {
    // Already recorded fix.
    recordTypeVariablesAsHoles(second);
    return SolutionKind::Solved;
  }

  // Now we have the Iterator type, do the same lookup for Element, opening
  // an existential if needed.
  auto *iterProto = ctx.getProtocol(
      isAsync ? KnownProtocolKind::AsyncIteratorProtocol
              : (isBorrowing ? KnownProtocolKind::BorrowingIteratorProtocol
                             : KnownProtocolKind::IteratorProtocol));
  auto *eltAssocTy = iterProto->getAssociatedType(Context.Id_Element);
  auto eltTy = lookupDependentMember(iterTy, eltAssocTy,
                                     /*openExistential*/ true, contextualLoc);
  if (!eltTy) {
    // Already recorded fix.
    recordTypeVariablesAsHoles(second);
    return SolutionKind::Solved;
  }

  // Desugar the element type if necessary, since types like
  // `Iter<Seq>.Element` aren't helpful.
  eltTy = eltTy->getDesugaredType();

  addConstraint(ConstraintKind::Conversion, eltTy, second, locator);
  return SolutionKind::Solved;
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyOptionalObjectConstraint(
                                           Type first, Type second,
                                           TypeMatchOptions flags,
                                           ConstraintLocatorBuilder locator) {
  // Resolve the optional type.
  Type optLValueTy = getFixedTypeRecursive(first, flags, /*wantRValue=*/false);
  Type optTy = optLValueTy->getRValueType();
  if (optTy.getPointer() != optLValueTy.getPointer())
    optTy = getFixedTypeRecursive(optTy, /*wantRValue=*/false);

  if (optTy->isTypeVariableOrMember()) {
    if (flags.contains(TMF_GenerateConstraints)) {
      addUnsolvedConstraint(
        Constraint::create(*this, ConstraintKind::OptionalObject, optLValueTy,
                           second, getConstraintLocator(locator)));
      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  }

  if (optTy->isPlaceholder()) {
    if (auto *typeVar = second->getAs<TypeVariableType>())
      recordPotentialHole(typeVar);
    return SolutionKind::Solved;
  }

  Type objectTy = optTy->getOptionalObjectType();
  // If the base type is not optional, let's attempt a fix (if possible)
  // and assume that `!` is just not there.
  if (!objectTy) {
    if (!shouldAttemptFixes())
      return SolutionKind::Error;
    
    // Let's see if we can apply a specific fix here.
    if (optTy->isPlaceholder())
      return SolutionKind::Solved;
    
    auto fnType = optTy->getAs<FunctionType>();
    if (fnType && fnType->getNumParams() == 0) {
      // For function types with no parameters, let's try to
      // offer a "make it a call" fix if possible.
      auto optionalResultType = fnType->getResult()->getOptionalObjectType();
      if (optionalResultType) {
        if (matchTypes(optionalResultType, second, ConstraintKind::Bind,
                       flags | TMF_ApplyingFix, locator)
                == SolutionKind::Solved) {
          auto *fix =
              InsertExplicitCall::create(*this, getConstraintLocator(locator));

          return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
        }
      }
    }

    auto *fix =
        RemoveUnwrap::create(*this, optTy, getConstraintLocator(locator));

    if (recordFix(fix))
      return SolutionKind::Error;

    // If the fix was successful let's record
    // "fixed" object type and continue.
    objectTy = optTy;
  }

  // The object type is an lvalue if the optional was.
  if (optLValueTy->is<LValueType>())
    objectTy = LValueType::get(objectTy);

  // Equate it to the other type in the constraint.
  addConstraint(ConstraintKind::Bind, objectTy, second, locator);
  return SolutionKind::Solved;
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyBindTupleOfFunctionParamsConstraint(
    Type first, Type second, TypeMatchOptions flags,
    ConstraintLocatorBuilder locator) {
  auto simplified = simplifyType(first);
  auto simplifiedCopy = simplified;

  unsigned unwrapCount = 0;
  if (shouldAttemptFixes()) {
    while (auto objectTy = simplified->getOptionalObjectType()) {
      simplified = objectTy;

      // Track how many times we do this so that we can record a fix for each.
      ++unwrapCount;
    }

    if (simplified->isPlaceholder()) {
      if (auto *typeVar = second->getAs<TypeVariableType>())
        recordPotentialHole(typeVar);
      return SolutionKind::Solved;
    }
  }

  if (simplified->isTypeVariableOrMember()) {
    if (!flags.contains(TMF_GenerateConstraints))
      return SolutionKind::Unsolved;

    addUnsolvedConstraint(
        Constraint::create(*this, ConstraintKind::BindTupleOfFunctionParams,
                           simplified, second, getConstraintLocator(locator)));
    return SolutionKind::Solved;
  }

  auto *funcTy = simplified->getAs<FunctionType>();
  if (!funcTy)
    return SolutionKind::Error;

  auto tupleTy =
      AnyFunctionType::composeTuple(getASTContext(), funcTy->getParams(),
                                    ParameterFlagHandling::IgnoreNonEmpty);

  addConstraint(ConstraintKind::Bind, tupleTy, second,
                locator.withPathElement(ConstraintLocator::FunctionArgument));

  if (unwrapCount > 0) {
    auto *fix = ForceOptional::create(*this, simplifiedCopy, second,
                                      getConstraintLocator(locator));
    if (recordFix(fix, FixImpact::Mismatch * unwrapCount))
      return SolutionKind::Error;
  }
  return SolutionKind::Solved;
}

ConstraintSystem::SolutionKind
ConstraintSystem::matchPackElementType(Type elementType, Type patternType,
                                       ConstraintLocatorBuilder locator) {
  auto tryFix = [&](llvm::function_ref<ConstraintFix *(void)> fix) {
    if (!shouldAttemptFixes())
      return SolutionKind::Error;

    if (recordFix(fix()))
      return SolutionKind::Error;

    recordAnyTypeVarAsPotentialHole(elementType);
    return SolutionKind::Solved;
  };

  auto *loc = getConstraintLocator(locator);
  ASSERT(loc->directlyAt<PackExpansionExpr>());
  auto *packExpansion = castToExpr<PackExpansionExpr>(loc->getAnchor());

  ASSERT(!patternType->hasTypeVariable());
  auto shapeClass = patternType->getReducedShape();

  // `each` was applied to a concrete type.
  if (!shapeClass->is<PackArchetypeType>()) {
    return tryFix([&]() {
      return AllowInvalidPackElement::create(*this, patternType, loc);
    });
  }

  auto shapeParam = CanGenericTypeParamType(cast<GenericTypeParamType>(
      shapeClass->mapTypeOutOfEnvironment()->getCanonicalType()));

  auto *genericEnv = getPackExpansionEnvironment(packExpansion);
  if (genericEnv) {
    if (shapeParam != genericEnv->getOpenedElementShapeClass()) {
      return tryFix([&]() {
        auto envShape = genericEnv->mapTypeIntoEnvironment(
            genericEnv->getOpenedElementShapeClass());
        if (auto *pack = dyn_cast<PackType>(envShape.getPointer()))
          envShape = pack->unwrapSingletonPackExpansion()->getPatternType();

        return SkipSameShapeRequirement::create(
            *this, envShape, shapeClass,
            getConstraintLocator(loc, ConstraintLocator::PackShape));
      });
    }
  } else {
    genericEnv = createPackExpansionEnvironment(packExpansion, shapeParam);
  }

  auto expectedElementTy =
      genericEnv->mapContextualPackTypeIntoElementContext(patternType);
  assert(!expectedElementTy->is<PackType>());

  addConstraint(ConstraintKind::Equal, elementType, expectedElementTy, locator);
  return SolutionKind::Solved;
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyPackElementOfConstraint(Type first, Type second,
                                                  TypeMatchOptions flags,
                                                  ConstraintLocatorBuilder locator) {
  auto elementType = simplifyType(first, flags);
  auto patternType = simplifyType(second, flags);

  auto formUnsolved = [&]() {
    if (!flags.contains(TMF_GenerateConstraints))
      return SolutionKind::Unsolved;

    addUnsolvedConstraint(
        Constraint::create(*this, ConstraintKind::PackElementOf, first, second,
                           getConstraintLocator(locator)));

    return SolutionKind::Solved;
  };

  // If neither side is fully resolved yet, there is nothing we can do.
  if (elementType->hasTypeVariable() && patternType->hasTypeVariable())
    return formUnsolved();

  if (shouldAttemptFixes()) {
    if (elementType->isPlaceholder() || patternType->isPlaceholder())
      return SolutionKind::Solved;
  }

  if (isSingleUnlabeledPackExpansionTuple(patternType)) {
    auto *packVar = addMaterializePackExpansionConstraint(patternType, locator);
    addConstraint(ConstraintKind::PackElementOf, elementType, packVar, locator);
    return SolutionKind::Solved;
  }

  // Let's try to resolve element type based on the pattern type.
  if (!patternType->hasTypeVariable())
    return matchPackElementType(elementType, patternType, locator);

  // Otherwise we are inferred or checking pattern type.

  auto *packEnv = DC->getGenericEnvironmentOfContext();

  // Map element archetypes to the pack context to check for equality.
  if (elementType->hasElementArchetype())
    elementType = packEnv->mapElementTypeIntoPackContext(elementType);

  addConstraint(ConstraintKind::Equal, elementType, patternType, locator);
  return SolutionKind::Solved;
}

static bool isForKeyPathSubscript(ConstraintSystem &cs,
                                  ConstraintLocator *locator) {
  if (!locator || !locator->getAnchor())
    return false;

  if (auto *SE = getAsExpr<SubscriptExpr>(locator->getAnchor())) {
    return SE->getArgs()->isUnary() &&
           SE->getArgs()->getLabel(0) == cs.getASTContext().Id_keyPath;
  }
  return false;
}

static bool mayBeForKeyPathSubscriptWithoutLabel(ConstraintSystem &cs,
                                                 ConstraintLocator *locator) {
  if (!locator || !locator->getAnchor())
    return false;

  if (auto *SE = getAsExpr<SubscriptExpr>(locator->getAnchor())) {
    if (auto *unary = SE->getArgs()->getUnlabeledUnaryExpr())
      return isa<KeyPathExpr>(unary) || isa<CodeCompletionExpr>(unary);
  }
  return false;
}

/// Determine whether all of the given candidate overloads
/// found through conditional conformances of a given base type.
/// This is useful to figure out whether it makes sense to
/// perform dynamic member lookup or not.
static bool
allFromConditionalConformances(ConstraintSystem &cs, Type baseTy,
                               ArrayRef<OverloadChoice> candidates) {
  auto *NTD = baseTy->getAnyNominal();
  if (!NTD)
    return false;

  return llvm::all_of(candidates, [&](const OverloadChoice &choice) {
    auto *decl = choice.getDeclOrNull();
    if (!decl)
      return false;

    auto *candidateDC = decl->getDeclContext();

    if (auto *extension = dyn_cast<ExtensionDecl>(candidateDC)) {
      if (extension->isConstrainedExtension())
        return true;
    }

    if (auto *protocol = candidateDC->getSelfProtocolDecl()) {
      auto conformance = cs.lookupConformance(baseTy, protocol);
      if (!conformance.isConcrete())
        return false;

      return !conformance.getConcrete()->getConditionalRequirements().empty();
    }

    return false;
  });
}

// Check whether given key path dynamic member lookup is self-recursive,
// which happens when root type of the key path is the same as base type
// of the member and lookup is attempted on non-existing property e.g.
//
// @dynamicMemberLookup
// struct Recurse<T> {
//   subscript<U>(dynamicMember member: KeyPath<Recurse<T>, U>) -> Int {
//     return 1
//   }
// }
//
// If we going to lookup any no-existent property or member on `Recursive`
// using key path dynamic member lookup it would attempt to lookup such
// member on root type which is also `Recursive` which leads to an infinite
// recursion.
static bool isSelfRecursiveKeyPathDynamicMemberLookup(
    ConstraintSystem &cs, Type keyPathRootTy, ConstraintLocator *locator) {
  // Let's check whether this is a recursive call to keypath
  // dynamic member lookup on the same type.
  if (!locator ||
      !locator->isLastElement<LocatorPathElt::KeyPathDynamicMember>())
    return false;

  auto path = locator->getPath();
  auto *choiceLoc =
      cs.getConstraintLocator(locator->getAnchor(), path.drop_back());

  if (auto overload = cs.findSelectedOverloadFor(choiceLoc)) {
    auto baseTy = overload->choice.getBaseType();

    // If it's `Foo<Int>` vs. `Foo<String>` it doesn't really matter
    // for dynamic lookup because it's going to be performed on `Foo`.
    if (baseTy->is<BoundGenericType>() &&
        keyPathRootTy->is<BoundGenericType>()) {
      auto *baseDecl = baseTy->castTo<BoundGenericType>()->getDecl();
      auto *keyPathRootDecl =
          keyPathRootTy->castTo<BoundGenericType>()->getDecl();
      return baseDecl == keyPathRootDecl;
    }

    // Previous base type could be r-value because that could be
    // a base type of subscript "as written" for which we attempt
    // a dynamic member lookup.
    auto baseTy1 = baseTy->getRValueType();
    // Root type of key path is always wrapped in an l-value
    // before lookup is performed, so we need to unwrap that.
    auto baseTy2 = keyPathRootTy->getRValueType();

    if (baseTy1->isEqual(baseTy2))
      return true;
  }

  return false;
}

/// Given a ValueMember, UnresolvedValueMember, or TypeMember constraint,
/// perform a lookup into the specified base type to find a candidate list.
/// The list returned includes the viable candidates as well as the unviable
/// ones (along with reasons why they aren't viable).
///
/// If includeInaccessibleMembers is set to true, this burns compile time to
/// try to identify and classify inaccessible members that may be being
/// referenced.
MemberLookupResult ConstraintSystem::
performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
                    Type baseTy, FunctionRefInfo functionRefInfo,
                    ConstraintLocator *memberLocator,
                    bool includeInaccessibleMembers) {
  Type baseObjTy = baseTy->getRValueType();
  Type instanceTy = baseObjTy;

  auto &ctx = getASTContext();
  auto memberNode = simplifyLocatorToAnchor(memberLocator);
  auto memberLoc = memberNode ? memberNode.getStartLoc() : SourceLoc();

  if (auto baseObjMeta = baseObjTy->getAs<AnyMetatypeType>()) {
    instanceTy = baseObjMeta->getInstanceType();
  }

  MemberLookupResult result;

  if (instanceTy->isTypeVariableOrMember()) {
    result.OverallResult = MemberLookupResult::Unsolved;
    return result;
  }

  // Delay member lookup until single-element tuple with pack expansion
  // is sufficiently resolved.
  if (isSingleUnlabeledPackExpansionTuple(instanceTy)) {
    auto elementTy = instanceTy->castTo<TupleType>()->getElementType(0);
    if (elementTy->is<TypeVariableType>()) {
      result.OverallResult = MemberLookupResult::Unsolved;
      return result;
    }
  }

  // Okay, start building up the result list.
  result.OverallResult = MemberLookupResult::HasResults;

  // Add key path result.
  // If we are including inaccessible members, check for the use of a keypath
  // subscript without a `keyPath:` label. Add it to the result so that it
  // can be caught by the missing argument label checking later.
  if (isForKeyPathSubscript(*this, memberLocator) ||
      (mayBeForKeyPathSubscriptWithoutLabel(*this, memberLocator) &&
       includeInaccessibleMembers)) {
    if (baseTy->isAnyObject()) {
      result.addUnviable(
          OverloadChoice(baseTy, OverloadChoiceKind::KeyPathApplication),
          MemberLookupResult::UR_KeyPathWithAnyObjectRootType);
    } else {
      result.ViableCandidates.push_back(
          OverloadChoice(baseTy, OverloadChoiceKind::KeyPathApplication));
    }
  }

  // If the base type is a tuple type, look for the named or indexed member
  // of the tuple.
  if (auto baseTuple = baseObjTy->getAs<TupleType>()) {
    if (!memberName.isSpecial()) {
      StringRef nameStr = memberName.getBaseIdentifier().str();
      // Accessing `.element` on an abstract tuple materializes a pack.
      // (deprecated behavior)
      if (nameStr == "element" && baseTuple->getNumElements() == 1 &&
          isPackExpansionType(baseTuple->getElementType(0))) {
        auto elementType = baseTuple->getElementType(0);

        if (elementType->is<PackExpansionType>()) {
          result.ViableCandidates.push_back(
            OverloadChoice(baseTy, OverloadChoiceKind::MaterializePack));
        } else {
          assert(elementType->is<TypeVariableType>());
          result.OverallResult = MemberLookupResult::Unsolved;
        }
        return result;
      }

      int fieldIdx = -1;
      // Resolve a number reference into the tuple type.
      unsigned Value = 0;
      if (!nameStr.getAsInteger(10, Value) &&
          Value < baseTuple->getNumElements()) {
        fieldIdx = Value;
      } else {
        fieldIdx = baseTuple->getNamedElementId(memberName.getBaseIdentifier());
      }

      if (fieldIdx != -1) {
        // Add an overload set that selects this field.
        result.ViableCandidates.push_back(OverloadChoice(baseTy, fieldIdx));
        return result;
      }
    }
  }

  if (auto *selfTy = instanceTy->getAs<DynamicSelfType>())
    instanceTy = selfTy->getSelfType();

  // Dynamically isolated function types have a magic '.isolation'
  // member that extracts the isolation value.
  if (auto *fn = instanceTy->getAs<FunctionType>()) {
    if (fn->getIsolation().isErased() &&
        memberName.isSimpleName(Context.Id_isolation)) {
      result.ViableCandidates.push_back(
        OverloadChoice(baseTy, OverloadChoiceKind::ExtractFunctionIsolation));
    }
  }

  if (!instanceTy->mayHaveMembers())
    return result;

  // If we have a simple name, determine whether there are argument
  // labels we can use to restrict the set of lookup results.
  if (baseObjTy->isAnyObject() && memberName.isSimpleName()) {
    // If we're referencing AnyObject and we have argument labels, put
    // the argument labels into the name: we don't want to look for
    // anything else, because the cost of the general search is so
    // high.
    if (auto *args = getArgumentList(memberLocator)) {
      SmallVector<Identifier, 4> scratch;
      memberName = memberName.withArgumentLabels(
                                  ctx, args->getArgumentLabels(scratch));
    }
  }

  DeclNameRef lookupName = memberName;
  if (memberName.isCompoundName()) {
    auto &context = getASTContext();

    // Remove any $ prefixes for lookup
    SmallVector<Identifier, 4> lookupLabels;
    for (auto label : memberName.getArgumentNames()) {
      if (label.hasDollarPrefix()) {
        auto unprefixed = label.str().drop_front();
        lookupLabels.push_back(context.getIdentifier(unprefixed));
      } else {
        lookupLabels.push_back(label);
      }
    }

    lookupName = lookupName.withArgumentLabels(context, lookupLabels);
  }

  // Look for members within the base.
  LookupResult &lookup = lookupMember(instanceTy, lookupName, memberLoc);

  // If this is true, we're using type construction syntax (Foo()) rather
  // than an explicit call to `init` (Foo.init()).
  bool isImplicitInit = false;
  if (memberName.isSimpleName(DeclBaseName::createConstructor())) {
    SmallVector<LocatorPathElt, 2> parts;
    if (auto anchor = memberLocator->getAnchor()) {
      auto path = memberLocator->getPath();
      if (!path.empty())
        if (path.back().getKind() == ConstraintLocator::ConstructorMember)
          isImplicitInit = true;
    }
  }

  // If we are pattern-matching an enum element and we found any enum elements,
  // ignore anything that isn't an enum element.
  bool onlyAcceptEnumElements = false;
  if (memberLocator &&
      memberLocator->isLastElement<LocatorPathElt::PatternMatch>() &&
      isa<EnumElementPattern>(
          memberLocator->getLastElementAs<LocatorPathElt::PatternMatch>()
            ->getPattern())) {
    for (const auto &result: lookup) {
      if (isa<EnumElementDecl>(result.getValueDecl())) {
        onlyAcceptEnumElements = true;
        break;
      }
    }
  }

  // If the instance type is String bridged to NSString, compute
  // the type we'll look in for bridging.
  Type bridgedType;
  if (baseObjTy->isString()) {
    if (Type classType = ctx.getBridgedToObjC(DC, instanceTy)) {
      bridgedType = classType;
    }
  }

  // Exclude some of the dynamic member choices from results
  // because using such choices would result in a self-recursive reference.
  //
  // This is required because if there are no viable/unviable choices
  // `performMemberLookup` is going to attempt to lookup inaccessible
  // members and results would include dynamic member subscripts which
  // have already been excluded.
  llvm::SmallPtrSet<ValueDecl *, 2> excludedDynamicMembers;

  // Local function that adds the given declaration if it is a
  // reasonable choice.
  auto addChoice = [&](OverloadChoice candidate) {
    auto decl = candidate.getDecl();

    // Reject circular references immediately.
    if (decl->isRecursiveValidation())
      return;

    // If the result is invalid, skip it unless solving for code completion
    // For code completion include the result because we can partially match
    // against function types that only have one parameter with error type.
    if (decl->isInvalid() && !isForCodeCompletion()) {
      // Clang import failures emit notes rather than errors, so the error
      // hasn't actually been emitted yet.
      if (!decl->hasClangNode())
        result.markErrorAlreadyDiagnosed();
      return;
    }

    // If we only accept enum elements but this isn't one, ignore it.
    if (onlyAcceptEnumElements && !isa<EnumElementDecl>(decl))
      return;

    // Dig out the instance type and figure out what members of the instance type
    // we are going to see.
    auto baseTy = candidate.getBaseType();
    const auto baseObjTy = baseTy->getRValueType();

    bool hasInstanceMembers = false;
    bool hasInstanceMethods = false;
    bool hasStaticMembers = false;
    Type instanceTy = baseObjTy;
    if (baseObjTy->is<ModuleType>()) {
      hasStaticMembers = true;
    } else if (auto baseObjMeta = baseObjTy->getAs<AnyMetatypeType>()) {
      instanceTy = baseObjMeta->getInstanceType();
      if (baseObjMeta->is<ExistentialMetatypeType>()) {
        // An instance of an existential metatype is a concrete type conforming
        // to the existential, say Self. Instance members of the concrete type
        // have type Self -> T -> U, but we don't know what Self is at compile
        // time so we cannot refer to them. Static methods are fine, on the other
        // hand -- we already know that they do not have Self or associated type
        // requirements, since otherwise we would not be able to refer to the
        // existential metatype in the first place.
        hasStaticMembers = true;
      } else if (instanceTy->isExistentialType()) {
        // A protocol metatype has instance methods with type P -> T -> U, but
        // not instance properties or static members, unless result type of a
        // member conforms to this protocol -- the metatype value itself
        // doesn't give us a witness so there's no static method to bind.
        hasInstanceMethods = true;
        hasStaticMembers |=
            memberLocator->isLastElement<LocatorPathElt::UnresolvedMember>();
      } else {
        // Metatypes of nominal types and archetypes have instance methods and
        // static members, but not instance properties.
        // FIXME: partial application of properties
        hasInstanceMethods = true;
        hasStaticMembers = true;
      }

      // If we're at the root of an unevaluated context, we can
      // reference instance members on the metatype.
      if (memberLocator &&
          UnevaluatedRootExprs.count(getAsExpr(memberLocator->getAnchor()))) {
        hasInstanceMembers = true;
      }
    } else {
      // Otherwise, we can access all instance members.
      hasInstanceMembers = true;
      hasInstanceMethods = true;
    }

    const auto isUnsupportedExistentialMemberAccess = [&] {
      if (!instanceTy->isExistentialType())
        return false;

      // If the base type is composed with marker protocol(s) i.e.
      // `<<Type>> & Sendable`, let's skip this check because such
      // compositions are always opened and simplified down to a
      // superclass bound post-Sema.
      if (auto *existential = instanceTy->getAs<ExistentialType>()) {
        auto *compositionTy =
            existential->getConstraintType()->getAs<ProtocolCompositionType>();
        if (compositionTy &&
            !compositionTy->withoutMarkerProtocols()->isExistentialType())
          return false;
      }

      // We may not be able to derive a well defined type for an existential
      // member access if the member's signature references 'Self'.
      switch (isMemberAvailableOnExistential(instanceTy, decl)) {
      case ExistentialMemberAccessLimitation::Unsupported:
      // TODO: Write-only accesses are not supported yet.
      case ExistentialMemberAccessLimitation::WriteOnly:
        return true;

      case ExistentialMemberAccessLimitation::ReadOnly:
      case ExistentialMemberAccessLimitation::None:
        return false;
      }
    };

    // Metatype extension members are only accessible on the protocol
    // metatype itself, not on conforming types. Keep the declaration as an
    // unviable candidate so diagnostics can explain that distinction.
    if (decl->getDeclContext()->isMetatypeExtension() &&
        !instanceTy->isExistentialType()) {
      result.addUnviable(
          candidate,
          MemberLookupResult::UR_MetatypeExtensionMemberOnConformingType);
      return;
    }

    // See if we have an instance method, instance member or static method,
    // and check if it can be accessed on our base type.

    if (decl->isInstanceMember()) {
      if (baseObjTy->is<AnyMetatypeType>()) {
        // Metatype extension instance members are instance members of the
        // metatype type itself.  They are accessed directly on the protocol
        // metatype value (e.g. P.value), not on an instance of the protocol.
        if (decl->getDeclContext()->isMetatypeExtension()) {
          result.addViable(candidate);
          return;
        }

        // `AnyObject` has special semantics, so let's just let it be.
        // Otherwise adjust base type and reference kind to make it
        // look as if lookup was done on the instance, that helps
        // with diagnostics.
        auto choice =
            instanceTy->isAnyObject()
                ? candidate
                : OverloadChoice::getDecl(instanceTy, decl,
                                          functionRefInfo);

        const bool invalidMethodRef = isa<FuncDecl>(decl) && !hasInstanceMethods;
        const bool invalidMemberRef = !isa<FuncDecl>(decl) && !hasInstanceMembers;

        if (invalidMethodRef || invalidMemberRef) {
          // If this is definitely an invalid way to reference a method or member
          // on the metatype, let's stop here.
          result.addUnviable(choice,
                             MemberLookupResult::UR_InstanceMemberOnType);
          return;
        } else if (isUnsupportedExistentialMemberAccess()) {
          // If the member reference itself is legal, but it turns out to be an
          // unsupported existential member access, do not make further
          // assumptions about the correctness of a potential call -- let
          // the unsupported member access error prevail.
          result.addUnviable(candidate,
                             MemberLookupResult::UR_UnavailableInExistential);
          return;
        } else {
          // Otherwise, still add an unviable result to the set, because it
          // could be an invalid call that was supposed to be performed on an
          // instance of the type.
          //
          // New candidate shouldn't affect performance because such
          // choice would only be attempted when solver is in diagnostic mode.
          result.addUnviable(choice,
                             MemberLookupResult::UR_InstanceMemberOnType);

        }
      }

      if (auto *UDE =
              getAsExpr<UnresolvedDotExpr>(memberLocator->getAnchor())) {
        auto *base = UDE->getBase();
        if (auto *accessor = DC->getInnermostPropertyAccessorContext()) {
          if (accessor->isInitAccessor() && isa<DeclRefExpr>(base) &&
              accessor->getImplicitSelfDecl() ==
                  cast<DeclRefExpr>(base)->getDecl()) {
            bool isValidReference = false;

            // If name doesn't appear in either `initializes` or `accesses`
            // then it's invalid instance member.

            isValidReference |= llvm::any_of(
                accessor->getInitializedProperties(), [&](VarDecl *prop) {
                  return prop->createNameRef() == memberName;
                });

            isValidReference |= llvm::any_of(
                accessor->getAccessedProperties(), [&](VarDecl *prop) {
                  return prop->createNameRef() == memberName;
                });

            if (!isValidReference) {
              result.addUnviable(
                  candidate,
                  MemberLookupResult::UR_UnavailableWithinInitAccessor);
              return;
            }
          }
        }
      }

    // If the underlying type of a typealias is fully concrete, it is legal
    // to access the type with a protocol metatype base.
    } else if (instanceTy->isExistentialType() &&
               isa<TypeAliasDecl>(decl) &&
               !cast<TypeAliasDecl>(decl)
                  ->getUnderlyingType()->getCanonicalType()
                    ->hasTypeParameter()) {

      /* We're OK */
    } else if (instanceTy->isExistentialType() &&
               decl->getDeclContext()->isMetatypeExtension()) {
      // Metatype extension members are directly accessible on the
      // protocol metatype without requiring Self to be bound.
    } else if (hasStaticMembers && baseObjTy->is<MetatypeType>() &&
               instanceTy->isExistentialType()) {
      // Static member lookup on protocol metatype in generic context
      // requires `Self` of the protocol to be bound to some concrete
      // type via same-type requirement, otherwise it would be
      // impossible to find a witness for this member.

      if (!isa<ExtensionDecl>(decl->getDeclContext())) {
        result.addUnviable(candidate,
                           MemberLookupResult::UR_TypeMemberOnInstance);
        return;
      }

      if (getConcreteReplacementForProtocolSelfType(decl)) {
        result.addViable(candidate);
        return;
      }

      // Cannot instantiate a protocol or reference a member on
      // protocol composition type.
      if (!memberLocator->isLastElement<LocatorPathElt::UnresolvedMember>()) {
        if (isa<ConstructorDecl>(decl) ||
            instanceTy->is<ProtocolCompositionType>()) {
          result.addUnviable(candidate,
                             MemberLookupResult::UR_TypeMemberOnInstance);
          return;
        }
      }
      result.addUnviable(
            candidate,
            MemberLookupResult::UR_InvalidStaticMemberOnProtocolMetatype);
      return;

    } else {
      if (!hasStaticMembers) {
        result.addUnviable(candidate,
                           MemberLookupResult::UR_TypeMemberOnInstance);
        return;
      }
    }

    if (isUnsupportedExistentialMemberAccess()) {
      result.addUnviable(candidate,
                         MemberLookupResult::UR_UnavailableInExistential);
      return;
    }

    // If we have an rvalue base, make sure that the result isn't 'mutating'
    // (only valid on lvalues).
    if (!baseTy->is<AnyMetatypeType>() &&
        !baseTy->is<LValueType>() &&
        decl->isInstanceMember()) {
      if (auto *FD = dyn_cast<FuncDecl>(decl))
        if (FD->isMutating()) {
          result.addUnviable(candidate,
                             MemberLookupResult::UR_MutatingMemberOnRValue);
          return;
        }

      // Subscripts and computed properties are ok on rvalues so long
      // as the getter is nonmutating.
      if (auto storage = dyn_cast<AbstractStorageDecl>(decl)) {
        if (storage->isGetterMutating()) {
          result.addUnviable(candidate,
                             MemberLookupResult::UR_MutatingGetterOnRValue);
          return;
        }
      }
    }

    // Check whether this is overload choice found via keypath
    // based dynamic member lookup. Since it's unknown upfront
    // what kind of declaration lookup is going to find, let's
    // double check here that given keypath is appropriate for it.
    if (memberLocator) {
      using KPDynamicMemberElt = LocatorPathElt::KeyPathDynamicMember;
      if (auto kpElt = memberLocator->getLastElementAs<KPDynamicMemberElt>()) {
        auto *keyPath = kpElt->getKeyPathDecl();
        if (isSelfRecursiveKeyPathDynamicMemberLookup(*this, baseTy,
                                                      memberLocator)) {
          excludedDynamicMembers.insert(candidate.getDecl());
          return;
        }

        if (auto *storage = dyn_cast<AbstractStorageDecl>(decl)) {
          // If this is an attempt to access read-only member via
          // writable key path, let's fail this choice early.
          auto &ctx = getASTContext();
          if (isReadOnlyKeyPathComponent(storage, SourceLoc()) &&
              (keyPath == ctx.getWritableKeyPathDecl() ||
               keyPath == ctx.getReferenceWritableKeyPathDecl())) {
            result.addUnviable(
                candidate,
                MemberLookupResult::UR_WritableKeyPathOnReadOnlyMember);
            return;
          }

          // A nonmutating setter indicates a reference-writable base,
          // on the other hand if setter is mutating there is no point
          // of attempting `ReferenceWritableKeyPath` overload.
          if (storage->isSetterMutating() &&
              keyPath == ctx.getReferenceWritableKeyPathDecl()) {
            result.addUnviable(candidate,
                               MemberLookupResult::
                                   UR_ReferenceWritableKeyPathOnMutatingMember);
            return;
          }
        }
      }
    }

    // Otherwise, we're good, add the candidate to the list.
    result.addViable(candidate);
  };

  // Local function that turns a ValueDecl into a properly configured
  // OverloadChoice.
  auto getOverloadChoice =
      [&](ValueDecl *cand, bool isBridged, bool isUnwrappedOptional,
          bool isFallbackUnwrap = false) -> OverloadChoice {
    // If we're looking into an existential type, check whether this
    // result was found via dynamic lookup.
    if (instanceTy->isAnyObject()) {
      assert(cand->getDeclContext()->isTypeContext() && "Dynamic lookup bug");
      
      // We found this declaration via dynamic lookup, record it as such.
      return OverloadChoice::getDeclViaDynamic(baseTy, cand, functionRefInfo);
    }
    
    // If we have a bridged type, we found this declaration via bridging.
    if (isBridged)
      return OverloadChoice::getDeclViaBridge(bridgedType, cand,
                                              functionRefInfo);
    
    // If we got the choice by unwrapping an optional type, unwrap the base
    // type.
    if (isUnwrappedOptional) {
      auto ovlBaseTy = MetatypeType::get(baseTy->castTo<MetatypeType>()
                                             ->getInstanceType()
                                             ->getOptionalObjectType());
      return OverloadChoice::getDeclViaUnwrappedOptional(
          ovlBaseTy, cand,
          /*isFallback=*/isFallbackUnwrap, functionRefInfo);
    }

    // While looking for subscript choices it's possible to find
    // `subscript(dynamicMember: {Reference{Writable}}KeyPath, ...)` on types
    // marked as `@dynamicMemberLookup`; let's mark this candidate as
    // representing "dynamic lookup" unless it's a direct call to such subscript
    // (in that case label is expected to match).
    if (auto *SD = dyn_cast<SubscriptDecl>(cand)) {
      if (memberLocator && instanceTy->hasDynamicMemberLookupAttribute()) {
        if (SD->getDynamicMemberLookupKind() ==
            SubscriptDecl::DynamicMemberLookupKind::KeyPath) {
          auto *args = getArgumentList(memberLocator);
          auto isDirectCallToSubscript =
              args && args->isUnary() &&
              args->getLabel(0) == getASTContext().Id_dynamicMember;
          if (!isDirectCallToSubscript) {
            return OverloadChoice::getDynamicMemberLookup(
                baseTy, SD, ctx.getIdentifier("subscript"),
                /*isKeyPathBased=*/true);
          }
        }
      }
    }

    return OverloadChoice::getDecl(baseTy, cand, functionRefInfo);
  };

  // Add all results from this lookup.
  for (auto result : lookup)
    addChoice(getOverloadChoice(result.getValueDecl(),
                                /*isBridged=*/false,
                                /*isUnwrappedOptional=*/false));

  // Backward compatibility hack. In Swift 4, `init` and init were
  // the same name, so you could write "foo.init" to look up a
  // method or property named `init`.
  if (!ctx.isLanguageModeAtLeast(LanguageMode::v5) &&
      memberName.getBaseName().isConstructor() && !isImplicitInit) {
    auto &compatLookup = lookupMember(instanceTy,
                                      DeclNameRef(ctx.getIdentifier("init")),
                                      memberLoc);
    for (auto result : compatLookup)
      addChoice(getOverloadChoice(result.getValueDecl(),
                                  /*isBridged=*/false,
                                  /*isUnwrappedOptional=*/false));
  }

  // If the instance type is a bridged to an Objective-C type, perform
  // a lookup into that Objective-C type.
  if (bridgedType) {
    LookupResult &bridgedLookup = lookupMember(bridgedType, memberName,
                                               memberLoc);
    ModuleDecl *foundationModule = nullptr;
    for (auto result : bridgedLookup) {
      // Ignore results from the Objective-C "Foundation"
      // module. Those core APIs are explicitly provided by the
      // Foundation module overlay.
      auto module = result.getValueDecl()->getModuleContext();
      if (foundationModule) {
        if (module == foundationModule)
          continue;
      } else if (ClangModuleUnit::hasClangModule(module) &&
                 module->getName().str() == "Foundation") {
        // Cache the foundation module name so we don't need to look
        // for it again.
        foundationModule = module;
        continue;
      }
      
      addChoice(getOverloadChoice(result.getValueDecl(),
                                  /*isBridged=*/true,
                                  /*isUnwrappedOptional=*/false));
    }
  }

  // If we have candidates, and we're doing a member lookup for a pattern
  // match, unwrap optionals and try again to allow implicit creation of
  // optional "some" patterns (spelled "?").
  if (result.ViableCandidates.empty() && result.UnviableCandidates.empty() &&
      memberLocator &&
      memberLocator->isLastElement<LocatorPathElt::PatternMatch>() &&
      instanceTy->getOptionalObjectType() &&
      baseObjTy->is<AnyMetatypeType>()) {
    SmallVector<Type, 2> optionals;
    Type instanceObjectTy = instanceTy->lookThroughAllOptionalTypes(optionals);
    Type metaObjectType = MetatypeType::get(instanceObjectTy);
    auto result = performMemberLookup(
        constraintKind, memberName, metaObjectType,
        functionRefInfo, memberLocator, includeInaccessibleMembers);
    result.numImplicitOptionalUnwraps = optionals.size();
    result.actualBaseType = metaObjectType;
    return result;
  }

  // If we're looking into a metatype for an unresolved member lookup, look
  // through optional types.
  //
  // FIXME: Unify with the above code path.
  if (baseObjTy->is<AnyMetatypeType>() &&
      constraintKind == ConstraintKind::UnresolvedValueMember) {
    if (auto objectType = instanceTy->getOptionalObjectType()) {
      // If we don't have a wrapped type yet, we can't look through the optional
      // type.
      if (objectType->getAs<TypeVariableType>() && result.ViableCandidates.empty()) {
        MemberLookupResult result;
        result.OverallResult = MemberLookupResult::Unsolved;
        return result;
      }

      if (objectType->mayHaveMembers()) {
        // If there are viable members directly on `Optional`, let's
        // prioritize them and mark any results found on wrapped type
        // as a fallback results.
        bool isFallback = !result.ViableCandidates.empty();
        LookupResult &optionalLookup = lookupMember(objectType, memberName,
                                                    memberLoc);
        for (auto result : optionalLookup)
          addChoice(getOverloadChoice(result.getValueDecl(),
                                      /*bridged*/ false,
                                      /*isUnwrappedOptional=*/true,
                                      /*isUnwrapFallback=*/isFallback));
      }
    }
  }

  // If we're about to fail lookup because there are no viable candidates
  // or if all of the candidates come from conditional conformances (which
  // might not be applicable), and we are looking for members in a type with
  // the @dynamicMemberLookup attribute, then we resolve a reference to a
  // `subscript(dynamicMember:...)` method and pass the member name as a string
  // parameter.
  if (constraintKind == ConstraintKind::ValueMember &&
      memberName.isSimpleName() && !memberName.isSpecial() &&
      instanceTy->hasDynamicMemberLookupAttribute()) {
    const auto &candidates = result.ViableCandidates;

    if ((candidates.empty() ||
         allFromConditionalConformances(*this, instanceTy, candidates)) &&
        !isSelfRecursiveKeyPathDynamicMemberLookup(*this, baseTy,
                                                   memberLocator)) {

      auto subscripts = performMemberLookup(
          constraintKind, DeclNameRef::createSubscript(), baseTy,
          functionRefInfo, memberLocator, includeInaccessibleMembers);

      // Reflect the candidates found as `DynamicMemberLookup` results.
      auto name = memberName.getBaseIdentifier();
      for (const auto &candidate : subscripts.ViableCandidates) {
        auto *SD = cast<SubscriptDecl>(candidate.getDecl());
        auto kind = SD->getDynamicMemberLookupKind();
        if (!kind) {
          continue;
        }

        result.addViable(OverloadChoice::getDynamicMemberLookup(
            baseTy, SD, name,
            /*isKeyPathBased=*/kind ==
                SubscriptDecl::DynamicMemberLookupKind::KeyPath));
      }

      for (auto index : indices(subscripts.UnviableCandidates)) {
        auto *SD =
            cast<SubscriptDecl>(subscripts.UnviableCandidates[index].getDecl());
        auto kind = SD->getDynamicMemberLookupKind();
        if (!kind) {
          continue;
        }

        result.addUnviable(
            OverloadChoice::getDynamicMemberLookup(
                baseTy, SD, name,
                /*isKeyPathBased=*/kind ==
                    SubscriptDecl::DynamicMemberLookupKind::KeyPath),
            subscripts.UnviableReasons[index]);
      }
    }
  }

  // If we have no viable or unviable candidates, and we're generating,
  // diagnostics, rerun the query with various excluded members included, so we
  // can include them in the unviable candidates list.
  if (result.ViableCandidates.empty() && result.UnviableCandidates.empty() &&
      includeInaccessibleMembers) {
    NLOptions lookupOptions =
        defaultConstraintSolverMemberLookupOptions;

    // Local function that looks up additional candidates using the given lookup
    // options, recording the results as unviable candidates.
    auto lookupUnviable =
        [&](DeclNameRef memberName,
            NLOptions lookupOptions,
            MemberLookupResult::UnviableReason reason) -> bool {
      auto lookup = TypeChecker::lookupMember(DC, instanceTy, memberName,
                                              memberLoc, lookupOptions);
      for (auto entry : lookup) {
        auto *cand = entry.getValueDecl();

        // If the result is invalid, skip it.
        if (cand->isInvalid()) {
          if (!cand->hasClangNode())
            result.markErrorAlreadyDiagnosed();
          break;
        }

        if (excludedDynamicMembers.count(cand))
          continue;

        result.addUnviable(getOverloadChoice(cand, /*isBridged=*/false,
                                             /*isUnwrappedOptional=*/false),
                           reason);
      }

      return !lookup.empty();
    };

    // Look for members that were excluded because of a module selector.
    if (memberName.hasModuleSelector()) {
      DeclNameRef unqualifiedMemberName{memberName.getFullName()};

      if (lookupUnviable(unqualifiedMemberName,
                         lookupOptions,
                         MemberLookupResult::UR_WrongModule))
        return result;
    }

    // Ignore access control so we get candidates that might have been missed
    // before.
    if (lookupUnviable(memberName,
                       lookupOptions | NLFlags::IgnoreAccessControl,
                       MemberLookupResult::UR_Inaccessible))
      return result;
  }
  
  return result;
}

/// Determine whether the given type refers to a non-final class (or
/// dynamic self of one).
static bool isNonFinalClass(Type type) {
  if (auto dynamicSelf = type->getAs<DynamicSelfType>())
    type = dynamicSelf->getSelfType();

  if (auto classDecl = type->getClassOrBoundGenericClass())
    return !classDecl->isSemanticallyFinal();

  if (auto archetype = type->getAs<ArchetypeType>())
    if (auto super = archetype->getSuperclass())
      return isNonFinalClass(super);

  return type->isExistentialType();
}

/// Determine whether given constructor reference is valid or does it require
/// any fixes e.g. when base is a protocol metatype.
static ConstraintFix *validateInitializerRef(ConstraintSystem &cs,
                                             ConstructorDecl *init,
                                             ConstraintLocator *locator) {
  auto anchor = locator->getAnchor();
  if (!anchor)
    return nullptr;

  auto getType = [&cs](Expr *expr) -> Type {
    return cs.simplifyType(cs.getType(expr))->getRValueType();
  };

  Expr *baseExpr = nullptr;
  Type baseType;

  // Explicit initializer reference e.g. `T.init(...)` or `T.init`.
  if (auto *UDE = getAsExpr<UnresolvedDotExpr>(anchor)) {
    baseExpr = UDE->getBase();
    baseType = getType(baseExpr);
    if (baseType->is<MetatypeType>()) {
      auto instanceType = baseType->getAs<MetatypeType>()->getInstanceType();
      if (!cs.isTypeReference(baseExpr) && instanceType->isExistentialType()) {
        return AllowInvalidInitRef::onProtocolMetatype(
            cs, baseType, init, /*isStaticallyDerived=*/true,
            baseExpr->getSourceRange(), locator);
      }
    }
    // Initializer call e.g. `T(...)`
  } else if (auto *CE = getAsExpr<CallExpr>(anchor)) {
    baseExpr = CE->getFn();
    baseType = getType(baseExpr);
    // FIXME: Historically, UnresolvedMemberExprs have allowed implicit
    // construction through a metatype value, but this should probably be
    // illegal.
    if (!isa<UnresolvedMemberExpr>(baseExpr)) {
      // If this is an initializer call without explicit mention
      // of `.init` on metatype value.
      if (auto *AMT = baseType->getAs<AnyMetatypeType>()) {
        auto instanceType = AMT->getInstanceType();
        if (!cs.isTypeReference(baseExpr)) {
          if (baseType->is<MetatypeType>() &&
              instanceType->isAnyExistentialType()) {
            return AllowInvalidInitRef::onProtocolMetatype(
                cs, baseType, init, cs.isStaticallyDerivedMetatype(baseExpr),
                baseExpr->getSourceRange(), locator);
          }

          if (!instanceType->isExistentialType() ||
              instanceType->isAnyExistentialType()) {
            return AllowInvalidInitRef::onNonConstMetatype(cs, baseType, init,
                                                           locator);
          }
        }
      }
    }
    // Initializer reference which requires contextual base type e.g.
    // `.init(...)`. Could also be a nested type or typealias being constructed
    // via implicit member syntax, e.g., `let _: Base = .Nested()` where
    // `Base.Nested: Base`.
  } else if (auto *UME = getAsExpr<UnresolvedMemberExpr>(anchor)) {
    // If we're accessing a nested type to perform the construction implicitly,
    // then the type we're constructing may not actually be the base of the
    // UnresolvedMemberExpr--instead, it will be the type of the nested type
    // member.
    // We need to find type variable which represents contextual base.
    auto *baseLocator = cs.getConstraintLocator(
        UME, locator->isLastElement<LocatorPathElt::ConstructorMember>()
                 ? ConstraintLocator::UnresolvedMember
                 : ConstraintLocator::MemberRefBase);

    // FIXME: Type variables responsible for contextual base could be cached
    // in the constraint system to speed up lookup.
    auto result = llvm::find_if(
        cs.getTypeVariables(), [&baseLocator](const TypeVariableType *typeVar) {
          return typeVar->getImpl().getLocator() == baseLocator;
        });

    assert(result != cs.getTypeVariables().end());
    baseType = cs.simplifyType(*result)->getRValueType();
    // Constraint for member base is formed as '$T.Type[.<member] = ...`
    // which means MetatypeType has to be added after finding a type variable.
    if (baseLocator->isLastElement<LocatorPathElt::MemberRefBase>())
      baseType = MetatypeType::get(baseType);
  } else if (getAsExpr<KeyPathExpr>(anchor)) {
    // Key path can't refer to initializers e.g. `\Type.init`
    return AllowInvalidRefInKeyPath::forRef(cs, baseType, init, locator);
  }

  if (!baseType)
    return nullptr;

  if (!baseType->is<AnyMetatypeType>()) {
    bool applicable = false;
    // Special case -- in a protocol extension initializer with a class
    // constrained Self type, 'self' has archetype type, and only
    // required initializers can be called.
    if (baseExpr && !baseExpr->isSuperExpr()) {
      auto &ctx = cs.getASTContext();
      if (auto *DRE =
              dyn_cast<DeclRefExpr>(baseExpr->getSemanticsProvidingExpr())) {
        if (DRE->getDecl()->getName() == ctx.Id_self) {
          if (getType(DRE)->is<ArchetypeType>())
            applicable = true;
        }
      }
    }

    if (!applicable)
      return nullptr;
  }

  auto instanceType = baseType->getMetatypeInstanceType();
  bool isStaticallyDerived = true;
  // If this is expression like `.init(...)` where base type is
  // determined by a contextual type.
  if (!baseExpr) {
    isStaticallyDerived = !(instanceType->is<DynamicSelfType>() ||
                            instanceType->is<ArchetypeType>());
  // Otherwise this is something like `T.init(...)`
  } else {
    isStaticallyDerived = cs.isStaticallyDerivedMetatype(baseExpr);
  }

  auto baseRange = baseExpr ? baseExpr->getSourceRange() : SourceRange();
  // FIXME: The "hasClangNode" check here is a complete hack.
  if (isNonFinalClass(instanceType) && !isStaticallyDerived &&
      !init->hasClangNode() &&
      !(init->isRequired() || init->getDeclContext()->getSelfProtocolDecl())) {
    return AllowInvalidInitRef::dynamicOnMetatype(cs, baseType, init, baseRange,
                                                  locator);
    // Constructors cannot be called on a protocol metatype, because there is no
    // metatype to witness it.
  } else if (baseType->is<MetatypeType>() &&
             instanceType->isExistentialType()) {
    return AllowInvalidInitRef::onProtocolMetatype(
        cs, baseType, init, isStaticallyDerived, baseRange, locator);
  }

  return nullptr;
}

static ConstraintFix *fixMemberRef(
    ConstraintSystem &cs, Type baseTy, DeclNameRef memberName,
    const OverloadChoice &choice, ConstraintLocator *locator,
    std::optional<MemberLookupResult::UnviableReason> reason = std::nullopt) {
  // Not all of the choices handled here are going
  // to refer to a declaration.
  if (auto *decl = choice.getDeclOrNull()) {
    if (auto *CD = dyn_cast<ConstructorDecl>(decl)) {
      if (auto *fix = validateInitializerRef(cs, CD, locator))
        return fix;
    }

    if (locator->isForKeyPathDynamicMemberLookup() ||
        locator->isForKeyPathComponent() ||
        locator->isKeyPathSubscriptComponent()) {
      if (auto *fix =
              AllowInvalidRefInKeyPath::forRef(cs, baseTy, decl, locator))
        return fix;
    }
  }

  if (reason) {
    switch (*reason) {
    case MemberLookupResult::UR_InstanceMemberOnType:
    case MemberLookupResult::UR_TypeMemberOnInstance: {
      return choice.isDecl()
                 ? AllowTypeOrInstanceMember::create(
                       cs, baseTy, choice.getDecl(), memberName, locator)
                 : nullptr;
    }

    case MemberLookupResult::UR_MetatypeExtensionMemberOnConformingType:
      return choice.isDecl()
                 ? AllowMetatypeExtensionMemberOnConformingType::create(
                       cs, baseTy, choice.getDecl(), memberName, locator)
                 : nullptr;

    case MemberLookupResult::UR_WrongModule:
      ASSERT(choice.isDecl());
      return AllowMemberFromWrongModule::create(cs, baseTy, choice.getDecl(),
                                                memberName, locator);

    case MemberLookupResult::UR_Inaccessible:
      assert(choice.isDecl());
      return AllowInaccessibleMember::create(cs, baseTy, choice.getDecl(),
                                             memberName, locator);

    case MemberLookupResult::UR_UnavailableInExistential: {
      return choice.isDecl()
                 ? AllowMemberRefOnExistential::create(
                       cs, baseTy, choice.getDecl(), memberName, locator)
                 : nullptr;
    }

    case MemberLookupResult::UR_MutatingMemberOnRValue:
    case MemberLookupResult::UR_MutatingGetterOnRValue: {
      return choice.isDecl()
                 ? AllowMutatingMemberOnRValueBase::create(
                       cs, baseTy, choice.getDecl(), memberName, locator)
                 : nullptr;
    }

    // TODO(diagnostics): Add a new fix that is suggests to
    // add `subscript(dynamicMember: {Writable}KeyPath<T, U>)`
    // overload here, that would help if such subscript has
    // not been provided.
    case MemberLookupResult::UR_WritableKeyPathOnReadOnlyMember:
      return TreatRValueAsLValue::create(cs, cs.getConstraintLocator(locator));
    case MemberLookupResult::UR_ReferenceWritableKeyPathOnMutatingMember:
      return IgnoreClassRequirementForDynamicMemberLookup::create(
          cs, baseTy, choice.getDecl(), cs.getConstraintLocator(locator));
    case MemberLookupResult::UR_KeyPathWithAnyObjectRootType:
      return AllowAnyObjectKeyPathRoot::create(cs, locator);

    case MemberLookupResult::UR_InvalidStaticMemberOnProtocolMetatype:
      return AllowInvalidStaticMemberRefOnProtocolMetatype::create(cs, locator);

    case MemberLookupResult::UR_UnavailableWithinInitAccessor:
      return AllowInvalidMemberReferenceInInitAccessor::create(cs, memberName,
                                                               locator);
    }
  }

  return nullptr;
}

/// Convert the given enum element pattern into an expression pattern
/// and synthesize ~= operator application to find the type of the
/// element.
static bool inferEnumMemberThroughTildeEqualsOperator(
    ConstraintSystem &cs, EnumElementPattern *pattern, Type enumTy,
    Type elementTy, ConstraintLocator *locator) {
  if (!pattern->hasUnresolvedOriginalExpr())
    return true;

  auto &ctx = cs.getASTContext();

  // Retrieve a corresponding ExprPattern which we can solve with ~=.
  auto *EP = evaluateOrFatal(ctx.evaluator,
                             EnumElementExprPatternRequest{pattern});

  auto target = SyntacticElementTarget::forExprPattern(EP);

  DiagnosticTransaction diagnostics(ctx.Diags);
  {
    if (cs.preCheckTarget(target)) {
      // Skip diagnostics if they are disabled, otherwise it would result in
      // duplicate diagnostics, since this operation is going to be repeated
      // in diagnostic mode.
      if (!cs.shouldAttemptFixes())
        diagnostics.abort();

      return true;
    }
  }
  cs.setType(EP->getMatchVar(), enumTy);
  cs.setType(EP, enumTy);

  if (cs.generateConstraints(target))
    return true;

  // Sub-expression associated with expression pattern is the enum element
  // access which needs to be connected to the provided element type.
  cs.addConstraint(ConstraintKind::Conversion, cs.getType(EP->getSubExpr()),
                   elementTy, cs.getConstraintLocator(EP));

  cs.setTargetFor(pattern, target);
  return false;
}

ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
    ConstraintKind kind, Type baseTy, DeclNameRef member, Type memberTy,
    DeclContext *useDC, FunctionRefInfo functionRefInfo,
    ArrayRef<OverloadChoice> outerAlternatives, TypeMatchOptions flags,
    ConstraintLocatorBuilder locatorB) {
  // We'd need to record original base type because it might be a type
  // variable representing another missing member.
  auto origBaseTy = baseTy;
  // Resolve the base type, if we can. If we can't resolve the base type,
  // then we can't solve this constraint.
  baseTy = simplifyType(baseTy, flags);
  Type baseObjTy = baseTy->getRValueType();

  auto locator = getConstraintLocator(locatorB);

  auto formUnsolved = [&](bool activate = false) {
    // If requested, generate a constraint.
    if (flags.contains(TMF_GenerateConstraints)) {
      auto *memberRef = Constraint::createMemberOrOuterDisjunction(
          *this, kind, baseTy, memberTy, member, useDC, functionRefInfo,
          outerAlternatives, locator);

      addUnsolvedConstraint(memberRef);

      if (activate)
        activateConstraint(memberRef);

      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  };

  // If the base type of this member lookup is a "hole" there is no
  // reason to perform a lookup because it wouldn't return any results.
  if (shouldAttemptFixes()) {
    // If this is an unresolved member ref e.g. `.foo` and its contextual base
    // type has been determined to be a "hole", let's mark the resulting member
    // type as a potential hole and continue solving.
    if (kind == ConstraintKind::UnresolvedValueMember) {
      // Let's look through all metatypes to find "underlying" type
      // of this lookup.
      Type underlyingType = baseObjTy;
      while (auto *MT = underlyingType->getAs<AnyMetatypeType>()) {
        underlyingType = MT->getInstanceType();
      }

      // Let's delay solving this constraint in diagnostic
      // mode until it's certain that there is no way to
      // find out what the base type is.
      if (underlyingType->isTypeVariableOrMember())
        return formUnsolved();

      // Let's record a fix only if the hole originates either
      // at the result of the chain (that could happen since solving
      // of this constraint is delayed until base could be resolved),
      // or it is certain that base type can't be bound to any other
      // type but a hole.
      auto shouldRecordFixForHole = [&](PlaceholderType *baseType) {
        auto *originator =
            baseType->getOriginator().dyn_cast<TypeVariableType *>();

        if (!originator)
          return false;

        auto *originatorLoc = originator->getImpl().getLocator();

        // It could either be a hole associated directly with the base
        // or a hole which came from result type of the chain.
        if (originatorLoc->isLastElement<
                LocatorPathElt::UnresolvedMemberChainResult>()) {
          auto *UMCR = castToExpr<UnresolvedMemberChainResultExpr>(
              originatorLoc->getAnchor());
          return UMCR->getChainBase() == getAsExpr(locator->getAnchor());
        }

        return originatorLoc == locator;
      };

      if (auto *hole = underlyingType->getAs<PlaceholderType>()) {
        if (shouldRecordFixForHole(hole)) {
          auto *fix = SpecifyBaseTypeForContextualMember::create(*this, member,
                                                                 locator);
          if (recordFix(fix))
            return SolutionKind::Error;
        }

        recordTypeVariablesAsHoles(memberTy);
        return SolutionKind::Solved;
      }
    } else if (kind == ConstraintKind::ValueMember &&
               baseObjTy->getMetatypeInstanceType()->isPlaceholder()) {
      // If base type is a "hole" there is no reason to record any
      // more "member not found" fixes for chained member references.
      recordTypeVariablesAsHoles(memberTy);
      return SolutionKind::Solved;
    }
  }

  MemberLookupResult result =
      performMemberLookup(kind, member, baseTy, functionRefInfo, locator,
                          /*includeInaccessibleMembers*/ shouldAttemptFixes());

  switch (result.OverallResult) {
  case MemberLookupResult::Unsolved:
    return formUnsolved();

  case MemberLookupResult::ErrorAlreadyDiagnosed:
  case MemberLookupResult::HasResults:
    // Keep going!
    break;
  }

  SmallVector<Constraint *, 4> candidates;

  // If we found viable candidates, then we're done!
  if (!result.ViableCandidates.empty()) {
    // If we had to look in a different type, use that.
    if (result.actualBaseType)
      baseTy = result.actualBaseType;

    generateOverloadConstraints(
        candidates, memberTy, result.ViableCandidates, useDC, locator,
        /*requiresFix=*/false,
        [&](unsigned, const OverloadChoice &choice) {
          return fixMemberRef(*this, baseTy, member, choice, locator);
        });

    if (!outerAlternatives.empty()) {
      // If local scope has a single choice,
      // it should always be preferred.
      if (candidates.size() == 1)
        candidates.front()->setFavored();

      // We *might* include any non-members that we found in outer contexts in
      // some special cases, for backwards compatibility: first, we have to be
      // looking for one of the special names ('min' or 'max'), and second, all
      // of the inner (viable) results need to come from conditional
      // conformances. The second condition is how the problem here was
      // encountered: a type ('Range') was made to conditionally conform to a
      // new protocol ('Sequence'), which introduced some extra methods
      // ('min' and 'max') that shadowed global functions that people regularly
      // called within extensions to that type (usually adding 'clamp').
      bool treatAsViable =
          (member.isSimpleName("min") || member.isSimpleName("max")) &&
          allFromConditionalConformances(*this, baseTy,
                                         result.ViableCandidates);

      generateOverloadConstraints(
          candidates, memberTy, outerAlternatives, useDC, locator,
          /*requiresFix=*/!treatAsViable,
          [&](unsigned, const OverloadChoice &) {
            return treatAsViable ? nullptr
                                 : AddQualifierToAccessTopLevelName::create(
                                       *this, locator);
          });
    }
  }

  if (!result.UnviableCandidates.empty()) {
    // Generate constraints for unavailable choices if they have a fix,
    // and disable them by default, they'd get picked up in the "salvage" mode.
    generateOverloadConstraints(
        candidates, memberTy, result.UnviableCandidates, useDC, locator,
        /*requiresFix=*/true,
        [&](unsigned idx, const OverloadChoice &choice) {
          return fixMemberRef(*this, baseTy, member, choice, locator,
                              result.UnviableReasons[idx]);
        });
  }

  // Attempt to record a warning where the unresolved member could be
  // ambiguous with optional member. e.g.
  //  enum Foo {
  //    case none
  //  }
  //
  //  let _: Foo? = .none // Although base is inferred as Optional.none
  //  it could be also Foo.none.
  if (auto *fix = SpecifyBaseTypeForOptionalUnresolvedMember::attempt(
          *this, kind, baseObjTy, member, functionRefInfo, result, locator)) {
    (void)recordFix(fix);
  }

  // If there were no results from a direct enum lookup, let's attempt
  // to resolve this member via ~= operator application.
  if (candidates.empty()) {
    if (auto patternLoc =
            locator->getLastElementAs<LocatorPathElt::PatternMatch>()) {
      if (auto *enumElement =
              dyn_cast<EnumElementPattern>(patternLoc->getPattern())) {
        auto enumType = baseObjTy->getMetatypeInstanceType();

        // Optional base type does not trigger `~=` synthesis, but it tries
        // to find member on both `Optional` and its wrapped type.
        if (!enumType->getOptionalObjectType()) {
          // If the synthesis of ~= resulted in errors (i.e. broken stdlib)
          // that would be diagnosed inline, so let's just fall through and
          // let this situation be diagnosed as a missing member.
          auto hadErrors = inferEnumMemberThroughTildeEqualsOperator(
            *this, enumElement, enumType, memberTy, locator);

          // Let's consider current member constraint solved because it's
          // replaced by a new set of constraints that would resolve member
          // type.
          if (!hadErrors)
            return SolutionKind::Solved;
        }
      }
    }
  }

  if (!candidates.empty()) {
    addOverloadSet(candidates, locator);
    return SolutionKind::Solved;
  }

  // If the lookup found no hits at all (either viable or unviable), diagnose it
  // as such and try to recover in various ways.
  if (shouldAttemptFixes()) {
    auto fixMissingMember = [&](Type baseTy, Type memberTy,
                                ConstraintLocator *locator) -> SolutionKind {
      // Let's check whether there are any generic parameters associated with
      // base type, and record potential holes if so.
      simplifyType(baseTy).visit([&](Type type) {
        if (auto *typeVar = type->getAs<TypeVariableType>()) {
          if (!typeVar->getImpl().hasRepresentativeOrFixed())
            recordPotentialHole(typeVar);
        }
      });

      auto success = [&]() -> SolutionKind {
        // Record a hole for memberTy to make it possible to form solutions
        // when contextual result type cannot be deduced e.g. `let _ = x.foo`.
        if (auto *memberTypeVar = memberTy->getAs<TypeVariableType>()) {
          if (getFixedType(memberTypeVar)) {
            // If member has been bound before the base and the base was
            // incorrect at that (e.g. fallback to default `Any` type),
            // then we need to re-activate all of the constraints
            // associated with this member reference otherwise some of
            // the constraints could be left unchecked in inactive state.
            // This is especially important for key path expressions because
            // `key path` constraint can't be retired until all components
            // are simplified.
            addTypeVariableConstraintsToWorkList(memberTypeVar);
          } else {
            // Eagerly turn the member type variable into a hole since we know
            // this is where the issue is and we've recorded a fix for it. This
            // avoids producing unnecessary holes for e.g generic parameters.
            recordTypeVariablesAsHoles(memberTypeVar);
          }
        }

        return SolutionKind::Solved;
      };

      bool alreadyDiagnosed = (result.OverallResult ==
                               MemberLookupResult::ErrorAlreadyDiagnosed);
      auto *fix = DefineMemberBasedOnUse::create(*this, baseTy, member,
                                                 alreadyDiagnosed, locator);

      auto instanceTy = baseObjTy->getMetatypeInstanceType();
      auto impact = FixImpact::InvalidReference;
      // Impact is higher if the base type is any function type
      // because function types can't have any members other than self
      if (instanceTy->is<AnyFunctionType>()) {
        impact += FixImpact::InvalidAST;
      }

      if (recordFix(fix, impact))
        return SolutionKind::Error;

      return success();
    };

    if (auto unwrapTy = baseObjTy->getOptionalObjectType()) {
      // If the base type was an optional, look through it.

      // If the base type is optional because we haven't chosen to force an
      // implicit optional, don't try to fix it. The IUO will be forced instead.
      if (auto dotExpr = getAsExpr<UnresolvedDotExpr>(locator->getAnchor())) {
        auto baseExpr = dotExpr->getBase();
        if (auto overload = findSelectedOverloadFor(baseExpr)) {
          auto iuoKind = overload->choice.getIUOReferenceKind(*this);
          if (iuoKind == IUOReferenceKind::Value)
            return SolutionKind::Error;
        }
      }

      // Let's check whether the problem is related to optionality of base
      // type, or there is no member with a given name.
      result =
          performMemberLookup(kind, member, unwrapTy, functionRefInfo, locator,
                              /*includeInaccessibleMembers*/ true);

      if (result.OverallResult == MemberLookupResult::Unsolved)
        return formUnsolved();

      // If unwrapped type still couldn't find anything for a given name,
      // let's fallback to a "not such member" fix.
      if (result.ViableCandidates.empty() && result.UnviableCandidates.empty())
        return fixMissingMember(origBaseTy, memberTy, locator);

      // If we have a subscript dynamic member we can't attempt the member
      // constraint since the applicable function won't be on the correct
      // type var. Just unconditionally record the fix since we know we at
      // least have a candidate.
      if (locator->isSubscriptMemberRef() &&
          unwrapTy->getMetatypeInstanceType()
              ->hasDynamicMemberLookupAttribute()) {
        if (recordFix(UnwrapOptionalBase::create(*this, member, baseObjTy,
                                                 locator))) {
          return SolutionKind::Error;
        }
        recordTypeVariablesAsHoles(memberTy);
        return SolutionKind::Solved;
      }

      bool baseIsKeyPathRootType = [&]() {
        auto keyPathComponent =
            locator->getLastElementAs<LocatorPathElt::KeyPathComponent>();
        return keyPathComponent && keyPathComponent->getIndex() == 0;
      }();

      // The result of the member access can either be the expected member type
      // (for '!' or optional members with '?'), or the original member type
      // with one extra level of optionality ('?' with non-optional members).
      auto innerTV = createTypeVariable(locator,
                                        TVO_CanBindToLValue |
                                        TVO_CanBindToNoEscape);
      Type optTy = TypeChecker::getOptionalType(SourceLoc(), innerTV);
      assert(!optTy->hasError());
      SmallVector<Constraint *, 2> optionalities;
      auto nonoptionalResult = Constraint::createFixed(
          *this, ConstraintKind::Bind,
          UnwrapOptionalBase::create(*this, member, baseObjTy, locator),
          memberTy, innerTV, locator);
      optionalities.push_back(nonoptionalResult);

      if (!baseIsKeyPathRootType) {
        auto optionalResult = Constraint::createFixed(
            *this, ConstraintKind::Bind,
            UnwrapOptionalBase::createWithOptionalResult(*this, member,
                                                         baseObjTy, locator),
            optTy, memberTy, locator);
        optionalities.push_back(optionalResult);
      }

      addDisjunctionConstraint(optionalities, locator);

      // Look through one level of optional.
      addValueMemberConstraint(unwrapTy, member, innerTV, useDC,
                               functionRefInfo, outerAlternatives, locator);
      return SolutionKind::Solved;
    }

    auto solveWithNewBaseOrName = [&](Type baseType,
                                      DeclNameRef memberName) -> SolutionKind {
      return simplifyMemberConstraint(kind, baseType, memberName, memberTy,
                                      useDC, functionRefInfo, outerAlternatives,
                                      flags | TMF_ApplyingFix, locatorB);
    };

    // If this member reference is a result of a previous fix, let's not allow
    // any more fixes expect when base is optional, because it could also be
    // an IUO which requires a separate fix.
    if (flags.contains(TMF_ApplyingFix))
      return SolutionKind::Error;

    // Check if any property wrappers on the base of the member lookup have
    // matching members that we can fall back to, or if the type wraps any
    // properties that have matching members.
    if (auto *fix = fixPropertyWrapperFailure(
            *this, baseTy, locator,
            [&](SelectedOverload overload, VarDecl *decl, Type newBase) {
              return solveWithNewBaseOrName(newBase, member) ==
                     SolutionKind::Solved;
            })) {
      return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
    }

    // If base is an archetype or metatype of archetype, check for an unintended
    // extra generic parameter.
    if (auto archetype =
            baseTy->getMetatypeInstanceType()->getAs<ArchetypeType>()) {
      if (auto genericTy =
              archetype->mapTypeOutOfEnvironment()->getAs<GenericTypeParamType>()) {
        for (auto param : DC->getGenericSignatureOfContext()
                              .getGenericParams()) {
          // Find a param at the same depth and one index past the type we're
          // dealing with
          if (param->getDepth() != genericTy->getDepth() ||
              param->getIndex() != genericTy->getIndex() + 1)
            continue;
          auto paramDecl = param->getDecl();
          if (!paramDecl)
            continue;

          auto descriptor = UnqualifiedLookupDescriptor{
              DeclNameRef(param->getName()),
              paramDecl->getDeclContext()->getModuleScopeContext(),
              SourceLoc(),
              UnqualifiedLookupFlags::TypeLookup};
          auto lookup = evaluateOrDefault(
              Context.evaluator, UnqualifiedLookupRequest{descriptor}, {});
          for (auto &result : lookup) {
            if (auto proto =
                    dyn_cast_or_null<ProtocolDecl>(result.getValueDecl())) {
              auto result =
                  baseTy->is<MetatypeType>()
                      ? solveWithNewBaseOrName(ExistentialMetatypeType::get(
                                                   proto->getDeclaredInterfaceType()),
                                               member)
                      : solveWithNewBaseOrName(proto->getDeclaredInterfaceType(),
                                               member);
              if (result == SolutionKind::Solved)
                return recordFix(
                           DefineMemberBasedOnUnintendedGenericParam::create(
                               *this, baseTy, member, param->getName(),
                               locator))
                           ? SolutionKind::Error
                           : SolutionKind::Solved;
            }
          }
        }
      }
    }

    if (auto *funcType = baseTy->getAs<FunctionType>()) {
      // We can't really suggest anything useful unless
      // function takes no arguments, otherwise it
      // would make sense to report this a missing member.
      if (funcType->getNumParams() == 0) {
        auto result = solveWithNewBaseOrName(funcType->getResult(), member);
        // If there is indeed a member with given name in result type
        // let's return, otherwise let's fall-through and report
        // this problem as a missing member.
        if (result == SolutionKind::Solved)
          return recordFix(InsertExplicitCall::create(
                     *this, getConstraintLocator(
                                locator, ConstraintLocator::MemberRefBase)))
                     ? SolutionKind::Error
                     : SolutionKind::Solved;
      }
    }

    // Instead of using subscript operator spelled out `subscript` directly.
    if (member.getBaseName() == getTokenText(tok::kw_subscript)) {
      auto result =
          solveWithNewBaseOrName(baseTy, DeclNameRef::createSubscript());
      // Looks like it was indeed meant to be a subscript operator.
      if (result == SolutionKind::Solved)
        return recordFix(UseSubscriptOperator::create(*this, locator),
                         FixImpact::InvalidReference)
                   ? SolutionKind::Error
                   : SolutionKind::Solved;
    }

    // FIXME(diagnostics): This is more of a hack than anything.
    // Let's not try to suggest that there is no member related to an
    // obscure underscored type, the real problem would be somewhere
    // else. This helps to diagnose pattern matching cases.
    {
      if (auto *metatype = baseTy->getAs<MetatypeType>()) {
        auto instanceTy = metatype->getInstanceType();
        if (auto *NTD = instanceTy->getAnyNominal()) {
          if (NTD->getName() == getASTContext().Id_OptionalNilComparisonType)
            return SolutionKind::Error;
        }
      }
    }

    result = performMemberLookup(kind, member, baseTy, functionRefInfo, locator,
                                 /*includeInaccessibleMembers*/ true);

    // FIXME(diagnostics): If there were no viable results, but there are
    // unviable ones, we'd have to introduce fix for each specific problem.
    if (!result.UnviableCandidates.empty())
      return SolutionKind::Error;

    // Since member with given base and name doesn't exist, let's try to
    // fake its presence based on use, that makes it possible to diagnose
    // problems related to member lookup more precisely.

    return fixMissingMember(origBaseTy, memberTy, locator);
  }
  return SolutionKind::Error;
}

ConstraintSystem::SolutionKind ConstraintSystem::simplifyDefaultableConstraint(
    Type first, Type second, TypeMatchOptions flags,
    ConstraintLocatorBuilder locator) {
  first = getFixedTypeRecursive(first, flags, true);

  if (first->isTypeVariableOrMember()) {
    if (flags.contains(TMF_GenerateConstraints)) {
      addUnsolvedConstraint(
          Constraint::create(*this, ConstraintKind::Defaultable, first, second,
                             getConstraintLocator(locator)));
      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  }

  // Otherwise, any type is fine.
  return SolutionKind::Solved;
}

ConstraintSystem::SolutionKind ConstraintSystem::simplifyFallbackTypeConstraint(
    Type defaultableType, Type fallbackType,
    ArrayRef<TypeVariableType *> referencedVars, TypeMatchOptions flags,
    ConstraintLocatorBuilder locator) {
  defaultableType =
      getFixedTypeRecursive(defaultableType, flags, /*wantRValue=*/true);

  if (defaultableType->isTypeVariableOrMember()) {
    if (flags.contains(TMF_GenerateConstraints)) {
      addUnsolvedConstraint(Constraint::create(
          *this, ConstraintKind::FallbackType, defaultableType, fallbackType,
          getConstraintLocator(locator), referencedVars));
      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  }

  // Propagate placeholders into an inferred closure type. Without this
  // we'd produce superfluous diagnostics about parameter/result types.
  if (defaultableType->isPlaceholder() && locator.directlyAt<ClosureExpr>()) {
    recordTypeVariablesAsHoles(fallbackType);
  }

  // Otherwise, any type is fine.
  return SolutionKind::Solved;
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyPropertyWrapperConstraint(
    Type wrapperType, Type wrappedValueType, TypeMatchOptions flags,
    ConstraintLocatorBuilder locator) {
  wrapperType = getFixedTypeRecursive(wrapperType, flags, /*wantRValue=*/true);
  auto *loc = getConstraintLocator(locator);

  if (wrapperType->isTypeVariableOrMember()) {
    if (flags.contains(TMF_GenerateConstraints)) {
      addUnsolvedConstraint(Constraint::create(
          *this, ConstraintKind::PropertyWrapper, wrapperType, wrappedValueType, loc));

      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  }

  // If the wrapper type is a hole or a dependent member with no type variables,
  // don't record a fix, because this indicates that there was an error
  // elsewhere in the constraint system.
  if (wrapperType->isPlaceholder() || wrapperType->is<DependentMemberType>())
    return SolutionKind::Solved;

  auto *wrappedVar = getAsDecl<VarDecl>(locator.getAnchor());
  assert(wrappedVar && wrappedVar->hasAttachedPropertyWrapper());

  // The wrapper type must be a property wrapper.
  auto *nominal = wrapperType->getDesugaredType()->getAnyNominal();
  if (!(nominal && nominal->getAttrs().hasAttribute<PropertyWrapperAttr>())) {
    if (shouldAttemptFixes()) {
      auto *fix = AllowInvalidPropertyWrapperType::create(
          *this, wrapperType, getConstraintLocator(locator));
      // The impact here matches that of a missing member, because it's the same problem.
      if (!recordFix(fix, FixImpact::InvalidReference))
        return SolutionKind::Solved;
    }

    return SolutionKind::Error;
  }

  auto typeInfo = nominal->getPropertyWrapperTypeInfo();

  // Implicit property wrappers must support projected-value initialization.
  if (wrappedVar->hasImplicitPropertyWrapper() &&
      !(typeInfo.projectedValueVar && typeInfo.hasProjectedValueInit)) {
    if (shouldAttemptFixes()) {
      auto *fix = RemoveProjectedValueArgument::create(
          *this, wrapperType, cast<ParamDecl>(wrappedVar),
          getConstraintLocator(locator));
      // The impact here matches that of a missing member, because it's the same problem.
      if (!recordFix(fix, FixImpact::InvalidReference))
        return SolutionKind::Solved;
    }

    return SolutionKind::Error;
  }

  auto resolvedType = wrapperType->getTypeOfMember(typeInfo.valueVar);
  if (typeInfo.valueVar->isSettable(nullptr) && typeInfo.valueVar->isSetterAccessibleFrom(DC) &&
      !typeInfo.valueVar->isSetterMutating()) {
    resolvedType = LValueType::get(resolvedType);
  }

  addConstraint(ConstraintKind::Bind, wrappedValueType, resolvedType, locator);

  return SolutionKind::Solved;
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyOneWayConstraint(
    ConstraintKind kind,
    Type first, Type second, TypeMatchOptions flags,
    ConstraintLocatorBuilder locator) {
  // Determine whether the second type can be fully simplified. Only then
  // will this constraint be resolved.
  Type secondSimplified = simplifyType(second);
  if (secondSimplified->hasTypeVariable()) {
    if (flags.contains(TMF_GenerateConstraints)) {
      addUnsolvedConstraint(
        Constraint::create(*this, kind, first, second,
                           getConstraintLocator(locator)));
      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  }

  // Propagate holes through one-way constraints.
  if (secondSimplified->isPlaceholder()) {
    recordAnyTypeVarAsPotentialHole(first);
    return SolutionKind::Solved;
  }

  // Translate this constraint into an equality or bind-parameter constraint,
  // as appropriate.
  ASSERT(kind == ConstraintKind::OneWayEqual);
  return matchTypes(first, secondSimplified, ConstraintKind::Equal, flags,
                    locator);
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyUnresolvedMemberChainBaseConstraint(
    Type first, Type second, TypeMatchOptions flags,
    ConstraintLocatorBuilder locator) {
  auto resultTy = getFixedTypeRecursive(first, flags, /*wantRValue=*/true);
  auto baseTy = getFixedTypeRecursive(second, flags, /*wantRValue=*/true);

  if (baseTy->isTypeVariableOrMember() || resultTy->isTypeVariableOrMember()) {
    if (flags.contains(TMF_GenerateConstraints)) {
      addUnsolvedConstraint(
          Constraint::create(*this, ConstraintKind::UnresolvedMemberChainBase,
                             first, second, getConstraintLocator(locator)));
      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  }

  if (baseTy->is<ProtocolType, ProtocolCompositionType>()) {
    auto *baseExpr =
        castToExpr<UnresolvedMemberChainResultExpr>(locator.getAnchor())
            ->getChainBase();
    auto *memberLoc =
        getConstraintLocator(baseExpr, ConstraintLocator::UnresolvedMember);

    if (shouldAttemptFixes() && hasFixFor(memberLoc))
      return SolutionKind::Solved;

    return simplifyConformsToConstraint(
        resultTy, baseTy, ConstraintKind::ConformsTo,
        getConstraintLocator(memberLoc, ConstraintLocator::MemberRefBase),
        flags);
  }

  return matchTypes(baseTy, resultTy, ConstraintKind::Equal, flags, locator);
}

static Type getOpenedResultBuilderTypeFor(ConstraintSystem &cs,
                                            ConstraintLocatorBuilder locator) {
  auto lastElt = locator.last();
  if (!lastElt)
    return Type();

  auto argToParamElt = lastElt->getAs<LocatorPathElt::ApplyArgToParam>();
  if (!argToParamElt)
    return Type();

  auto *calleeLocator = cs.getCalleeLocator(cs.getConstraintLocator(locator));
  auto selectedOverload = cs.findSelectedOverloadFor(calleeLocator);
  if (!(selectedOverload &&
        (selectedOverload->choice.getKind() == OverloadChoiceKind::Decl ||
         selectedOverload->choice.getKind() ==
             OverloadChoiceKind::DeclViaUnwrappedOptional)))
    return Type();

  auto *choice = selectedOverload->choice.getDecl();
  bool skipCurriedSelf = hasAppliedSelf(cs, selectedOverload->choice);

  if (choice->hasCurriedSelf() && !skipCurriedSelf)
    return Type();

  if (!choice->hasParameterList())
    return Type();

  auto *PD = getParameterAt(choice, argToParamElt->getParamIdx());
  assert(PD);

  auto builderType = PD->getResultBuilderType();
  if (!builderType)
    return Type();

  // If the builder type has a type parameter, substitute in the type
  // variables.
  if (builderType->hasTypeParameter()) {
    // Find the opened type for this callee and substitute in the type
    // parameters.
    auto substitutions = cs.getOpenedTypes(calleeLocator);
    if (!substitutions.empty()) {
      builderType = cs.openType(builderType, substitutions, locator,
                                /*preparedOverload=*/nullptr);
    }

    assert(!builderType->hasTypeParameter());
  }
  return builderType;
}

void ConstraintSystem::recordIsolatedParam(ParamDecl *param) {
  bool inserted = isolatedParams.insert(param).second;
  ASSERT(inserted);

  if (solverState)
    recordChange(SolverTrail::Change::RecordedIsolatedParam(param));
}

void ConstraintSystem::removeIsolatedParam(ParamDecl *param) {
  bool erased = isolatedParams.erase(param);
  ASSERT(erased);
}

void ConstraintSystem::recordPreconcurrencyClosure(
    const ClosureExpr *closure) {
  bool inserted = preconcurrencyClosures.insert(closure).second;
  ASSERT(inserted);

  if (solverState) {
    recordChange(SolverTrail::Change::RecordedPreconcurrencyClosure(
      const_cast<ClosureExpr *>(closure)));
  }
}

void ConstraintSystem::removePreconcurrencyClosure(
    const ClosureExpr *closure) {
  bool erased = preconcurrencyClosures.erase(closure);
  ASSERT(erased);
}

bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar,
                                      Type contextualType,
                                      ConstraintLocatorBuilder locator) {
  auto *closureLocator = typeVar->getImpl().getLocator();
  auto *closure = castToExpr<ClosureExpr>(closureLocator->getAnchor());
  auto *inferredClosureType = getClosureType(closure);

  // Note if this closure is isolated by preconcurrency.
  if (hasPreconcurrencyCallee(locator))
    recordPreconcurrencyClosure(closure);

  // Let's look through all optionals associated with contextual
  // type to make it possible to infer parameter/result type of
  // the closure faster e.g.:
  //
  // func test(_: ((Int) -> Void)?) {
  //   ...
  // }
  //
  // test { $0 + ... }
  //
  // In this case dropping optionality from contextual type
  // `((Int) -> Void)?` allows `resolveClosure` to infer type
  // of `$0` directly (via `getContextualParamAt`) instead of
  // having to use type variable inference mechanism.
  contextualType = contextualType->lookThroughAllOptionalTypes();

  auto getContextualParamAt =
      [&contextualType, &inferredClosureType](
          unsigned index) -> std::optional<AnyFunctionType::Param> {
    auto *fnType = contextualType->getAs<FunctionType>();
    if (!fnType)
      return std::nullopt;

    auto numContextualParams = fnType->getNumParams();

    if (numContextualParams == 1) {
      const auto &param = fnType->getParams()[0];
      if (auto *tuple = param.getPlainType()->getAs<TupleType>()) {
        // If arity is the same it's a tuple splat which is allowed
        // for closures (see SE-0110 for more details):
        //
        // func test(_: ((Int, Int)) -> Void) {}
        // test { (arg, _) in
        //   ...
        // }
        if (tuple->getNumElements() == inferredClosureType->getNumParams() &&
            param.getParameterFlags().isNone()) {
          const auto &elt = tuple->getElement(index);
          return AnyFunctionType::Param(elt.getType(), elt.getName());
        }

        return std::nullopt;
      }
    }

    if (numContextualParams != inferredClosureType->getNumParams() ||
        numContextualParams <= index)
      return std::nullopt;

    return fnType->getParams()[index];
  };

  // Check whether given contextual parameter type could be
  // used to bind external closure parameter type.
  auto isSuitableContextualType = [](Type contextualTy) {
    // We need to wait until contextual type
    // is fully resolved before binding it.
    if (contextualTy->isTypeVariableOrMember())
      return false;

    // Cannot propagate pack expansion type from context,
    // it has to be handled by type matching logic.
    if (isPackExpansionType(contextualTy))
      return false;

    // If contextual type has an error, let's wait for inference,
    // otherwise contextual would interfere with diagnostics.
    if (contextualTy->hasError())
      return false;

    if (isa<TypeAliasType>(contextualTy.getPointer())) {
      auto underlyingTy = contextualTy->getDesugaredType();
      // FIXME: typealias pointing to an existential type is special
      // because if the typealias has type variables then we'd end up
      // opening existential from a type with unresolved generic
      // parameter(s), which CSApply can't currently simplify while
      // building type-checked AST because `ExistentialArchetypeType` doesn't
      // propagate flags. Example is as simple as `{ $0.description }`
      // where `$0` is `Error` that inferred from a (generic) typealias.
      if (underlyingTy->isExistentialType() && contextualTy->hasTypeVariable())
        return false;
    }

    return true;
  };

  // If the contextual type cannot possibly be a supertype of a function type,
  // and this closure is used as an argument, let's skip resolution.
  //
  // Doing so improves performance if closure is passed as an argument
  // to a (heavily) overloaded declaration, avoid unrelated errors,
  // propagate holes, and record a more impactful fix.
  if (!isPossibleSupertypeOfFunctionType(contextualType) &&
      locator.endsWith<LocatorPathElt::ApplyArgToParam>()) {
    if (!shouldAttemptFixes())
      return false;

    assignFixedType(typeVar, PlaceholderType::get(getASTContext(), typeVar));
    recordTypeVariablesAsHoles(inferredClosureType);

    return !recordFix(
        AllowArgumentMismatch::create(*this, typeVar, contextualType,
                                      getConstraintLocator(locator)),
        /*impact=*/FixImpact::InvalidAST + FixImpact::FunctionTypeMismatch);
  }

  // Determine whether a result builder will be applied.
  auto resultBuilderType = getOpenedResultBuilderTypeFor(*this, locator);

  auto *paramList = closure->getParameters();
  SmallVector<AnyFunctionType::Param, 4> parameters;
  bool hasIsolatedParam = false;
  for (unsigned i = 0, n = paramList->size(); i != n; ++i) {
    auto param = inferredClosureType->getParams()[i];
    auto *paramDecl = paramList->get(i);

    // In case of anonymous or name-only parameters, let's infer
    // inout/variadic/isolated flags from context, that helps to propagate
    // type information into the internal type of the parameter and reduces
    // inference solver has to make.
    if (!paramDecl->getTypeRepr()) {
      if (auto contextualParam = getContextualParamAt(i)) {
        auto flags = param.getParameterFlags();

        // Note when a parameter is inferred to be isolated.
        if (contextualParam->isIsolated() && !flags.isIsolated() && paramDecl)
          recordIsolatedParam(paramDecl);

        // Carry-over the ownership specifier from the contextual parameter.
        auto paramOwnership =
            contextualParam->getParameterFlags().getOwnershipSpecifier();

        // `sending` is already carried over; skip this related ownership kind.
        if (paramOwnership == ParamSpecifier::ImplicitlyCopyableConsuming)
          paramOwnership = flags.getOwnershipSpecifier();

        param = param.withFlags(flags.withInOut(contextualParam->isInOut())
                                    .withVariadic(contextualParam->isVariadic())
                                    .withIsolated(contextualParam->isIsolated())
                                    .withSending(contextualParam->isSending())
                                    .withOwnershipSpecifier(paramOwnership));
      }
    }

    if (paramDecl->hasAttachedPropertyWrapper()) {
      Type backingType;
      Type wrappedValueType;

      if (paramDecl->hasImplicitPropertyWrapper()) {
        if (auto contextualType = getContextualParamAt(i)) {
          backingType = contextualType->getPlainType();
        } else {
          // There may not be a contextual parameter type if the contextual
          // type is not a function type or if closure body declares too many
          // parameters.
          auto *paramLoc =
              getConstraintLocator(closure, LocatorPathElt::TupleElement(i));
          backingType = createTypeVariable(paramLoc, TVO_CanBindToHole);
        }

        wrappedValueType = createTypeVariable(getConstraintLocator(paramDecl),
                                              TVO_CanBindToHole | TVO_CanBindToLValue);
      } else {
        auto *wrapperAttr = paramDecl->getOutermostAttachedPropertyWrapper();
        auto wrapperType = paramDecl->getAttachedPropertyWrapperType(0);
        backingType = replaceInferableTypesWithTypeVars(
            wrapperType, getConstraintLocator(wrapperAttr->getTypeRepr()));
        wrappedValueType = computeWrappedValueType(paramDecl, backingType);
      }

      auto *backingVar = paramDecl->getPropertyWrapperBackingProperty();
      setType(backingVar, backingType);

      auto *localWrappedVar = paramDecl->getPropertyWrapperWrappedValueVar();
      setType(localWrappedVar, wrappedValueType);

      if (auto *projection = paramDecl->getPropertyWrapperProjectionVar()) {
        setType(projection, computeProjectedValueType(paramDecl, backingType));
      }

      if (!paramDecl->getName().hasDollarPrefix()) {
        if (generateWrappedPropertyTypeConstraints(paramDecl, backingType,
                                                   param.getParameterType()))
          return false;
      }

      auto result = applyPropertyWrapperToParameter(backingType, param.getParameterType(),
                                                    paramDecl, paramDecl->getName(),
                                                    ConstraintKind::Equal,
                                                    getConstraintLocator(closure),
                                                    getConstraintLocator(closure));
      if (result == SolutionKind::Error)
        return false;
    }

    Type internalType;
    if (paramDecl->getTypeRepr()) {
      // Internal type is the type used in the body of the closure,
      // so "external" type translates to it as follows:
      //  - `Int...` -> `[Int]`,
      //  - `inout Int` -> `@lvalue Int`.
      internalType = param.getParameterType();
    } else {
      auto *paramLoc =
          getConstraintLocator(closure, LocatorPathElt::TupleElement(i));

      auto *typeVar = createTypeVariable(paramLoc, TVO_CanBindToLValue |
                                                       TVO_CanBindToNoEscape);

      // If external parameter is variadic it translates into an array in
      // the body of the closure.
      internalType =
          param.isVariadic() ? VariadicSequenceType::get(typeVar) : Type(typeVar);

      auto externalType = param.getOldType();

      // Performance optimization.
      //
      // If there is a concrete contextual type we could use, let's bind
      // it to the external type right away because internal type has to
      // be equal to that type anyway (through `BindParam` on external type
      // i.e. <internal> bind param <external> conv <concrete contextual>).
      //
      // Note: it's correct to avoid doing this, but it would result
      // in (a lot) more checking since solver would have to re-discover,
      // re-attempt and fail parameter type while solving for overloaded
      // choices in the body.
      if (auto contextualParam = getContextualParamAt(i)) {
        auto paramTy = simplifyType(contextualParam->getOldType());
        if (isSuitableContextualType(paramTy))
          addConstraint(ConstraintKind::Bind, externalType, paramTy, paramLoc);
      }

      addConstraint(
          ConstraintKind::BindParam, externalType, typeVar, paramLoc);
    }

    hasIsolatedParam |= param.isIsolated();

    setType(paramDecl, internalType);
    parameters.push_back(param);
  }

  auto closureExtInfo = inferredClosureType->getExtInfo();
  if (auto contextualFnType = contextualType->getAs<FunctionType>()) {
    if (!closureExtInfo.isSendable()) {
      if (auto sendTy = contextualFnType->getSendableDependentType()) {
        closureExtInfo = closureExtInfo.withSendableDependentType(sendTy);
      } else if (contextualFnType->isSendable()) {
        closureExtInfo = closureExtInfo.withSendable();
      }
    }
  }

  // Propagate sending result from the contextual type to the closure.
  if (auto contextualFnType = contextualType->getAs<FunctionType>()) {
    if (contextualFnType->hasExtInfo() && contextualFnType->hasSendingResult())
      closureExtInfo = closureExtInfo.withSendingResult();
  }

  // Isolated parameters override any other kind of isolation we might infer.
  if (hasIsolatedParam) {
    closureExtInfo = closureExtInfo.withIsolation(
      FunctionTypeIsolation::forParameter());
  }

  auto closureType =
      FunctionType::get(parameters, inferredClosureType->getResult(),
                        closureExtInfo);
  assignFixedType(typeVar, closureType);

  // If there is a result builder to apply, do so now.
  if (resultBuilderType) {
    if (auto result = matchResultBuilder(
            closure, resultBuilderType, closureType->getResult(),
            ConstraintKind::Conversion, contextualType, locator)) {
      return (*result) == SolutionKind::Solved;
    }
  }

  SyntacticElementTarget target(closure, contextualType);
  setTargetFor(closure, target);

  // Generate constraints from the body of this closure.
  return !generateConstraints(AnyFunctionRef{closure}, closure->getBody());
}

bool ConstraintSystem::resolvePackExpansion(TypeVariableType *typeVar,
                                            Type contextualType) {
  assert(typeVar->getImpl().isPackExpansion());

  auto *locator = typeVar->getImpl().getLocator();

  Type openedExpansionType =
      locator->castLastElementTo<LocatorPathElt::PackExpansionType>()
          .getOpenedType();

  assignFixedType(typeVar, openedExpansionType);
  return true;
}

bool ConstraintSystem::resolveTapBody(TypeVariableType *typeVar,
                                      Type contextualType,
                                      ConstraintLocatorBuilder locator) {
  auto *tapLoc = typeVar->getImpl().getLocator();
  auto *tapExpr = castToExpr<TapExpr>(tapLoc->getAnchor());

  // Assign a type to tap expression itself.
  assignFixedType(typeVar, contextualType);
  // Set type to `$interpolation` variable declared in the body of tap
  // expression.
  setType(tapExpr->getVar(), contextualType);

  // With all of the contextual information recorded in the constraint system,
  // it's time to generate constraints for the body of this tap expression.
  return !generateConstraints(tapExpr);
}

bool ConstraintSystem::resolveKeyPath(TypeVariableType *typeVar,
                                      Type contextualType,
                                      TypeMatchOptions flags,
                                      ConstraintLocatorBuilder locator) {
  // Key path types recently gained Copyable, Escapable requirements.
  // The solver cannot account for that during inference because Root
  // and Value types are required to be only resolved enough to infer
  // a capability of a key path itself.
  if (auto *BGT = contextualType->getAs<BoundGenericType>()) {
    auto keyPathTy = openUnboundGenericType(
        BGT->getDecl(), BGT->getParent(), locator, /*isTypeResolution=*/false);

    assignFixedType(
        typeVar, keyPathTy, /*updateState=*/true,
        /*notifyInference=*/!flags.contains(TMF_BindingTypeVariable));
    addConstraint(ConstraintKind::Equal, keyPathTy, contextualType, locator);
    return true;
  }

  assignFixedType(typeVar, contextualType, /*updateState=*/true,
                  /*notifyInference=*/!flags.contains(TMF_BindingTypeVariable));
  return true;
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyDynamicTypeOfConstraint(
                                        Type type1, Type type2,
                                        TypeMatchOptions flags,
                                        ConstraintLocatorBuilder locator) {
  TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);

  // Local function to form an unsolved result.
  auto formUnsolved = [&] {
    if (flags.contains(TMF_GenerateConstraints)) {
      addUnsolvedConstraint(
        Constraint::create(*this, ConstraintKind::DynamicTypeOf, type1, type2,
                           getConstraintLocator(locator)));
      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  };

  // Solve forward.
  type2 = getFixedTypeRecursive(type2, flags, /*wantRValue=*/true);
  if (!type2->isTypeVariableOrMember()) {
    Type dynamicType2;
    if (type2->isAnyExistentialType()) {
      dynamicType2 = ExistentialMetatypeType::get(type2);
    } else {
      dynamicType2 = MetatypeType::get(type2);
    }
    return matchTypes(type1, dynamicType2, ConstraintKind::Bind, subflags,
                      locator);
  }

  // Okay, can't solve forward.  See what we can do backwards.
  type1 = getFixedTypeRecursive(type1, flags, /*wantRValue=*/true);
  if (type1->isTypeVariableOrMember())
    return formUnsolved();

  // If we have an existential metatype, that's good enough to solve
  // the constraint.
  if (auto metatype1 = type1->getAs<ExistentialMetatypeType>())
    return matchTypes(metatype1->getInstanceType(), type2,
                      ConstraintKind::Bind,
                      subflags, locator);

  // If we have a normal metatype, we can't solve backwards unless we
  // know what kind of object it is.
  if (auto metatype1 = type1->getAs<MetatypeType>()) {
    Type instanceType1 = getFixedTypeRecursive(metatype1->getInstanceType(),
                                               true);
    if (instanceType1->isTypeVariableOrMember())
      return formUnsolved();

    return matchTypes(instanceType1, type2, ConstraintKind::Bind, subflags,
                      locator);
  }

  // We don't have a non-metatype result, produce a fix.
  if (shouldAttemptFixes()) {
    // If we have a hole as a contextual type, eagerly produce holes in the
    // argument of `type(of:)`.
    if (type1->isPlaceholder()) {
      recordTypeVariablesAsHoles(type2);
      return SolutionKind::Solved;
    }

    // Otherwise we have some invalid contextual type, record a fix and let the
    // argument be turned into a hole if needed.
    recordAnyTypeVarAsPotentialHole(type2);

    recordFix(IgnoreNonMetatypeDynamicType::create(
                  *this, type2, type1, getConstraintLocator(locator)),
              FixImpact::TypeMismatch);
    return SolutionKind::Solved;
  }

  return SolutionKind::Error;
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyBridgingConstraint(Type type1,
                                             Type type2,
                                             TypeMatchOptions flags,
                                             ConstraintLocatorBuilder locator) {
  TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);

  /// Form an unresolved result.
  auto formUnsolved = [&] {
    if (flags.contains(TMF_GenerateConstraints)) {
      addUnsolvedConstraint(
        Constraint::create(*this, ConstraintKind::BridgingConversion, type1,
                           type2, getConstraintLocator(locator)));
      return SolutionKind::Solved;
    }
    
    return SolutionKind::Unsolved;
  };

  // Local function to look through optional types. It produces the
  // fully-unwrapped type and a count of the total # of optional types that were
  // unwrapped.
  auto unwrapType = [&](Type type) -> std::pair<Type, unsigned> {
    unsigned count = 0;
    while (Type objectType = type->getOptionalObjectType()) {
      ++count;

      TypeMatchOptions unusedOptions;
      type = getFixedTypeRecursive(objectType, unusedOptions, /*wantRValue=*/true);
    }

    return { type, count };
  };

  const auto rawType1 = type1;
  type1 = getFixedTypeRecursive(type1, flags, /*wantRValue=*/true);
  type2 = getFixedTypeRecursive(type2, flags, /*wantRValue=*/true);

  if (type1->isTypeVariableOrMember() || type2->isTypeVariableOrMember())
    return formUnsolved();

  // Noncopyable & Nonescapable types can't be involved in bridging conversions
  // since a bridged type assumes such abilities are granted.
  if (!type1->hasTypeVariable()
      && (type1->isNoncopyable() || !type1->isEscapable())) {
    return SolutionKind::Error;
  }

  Type unwrappedFromType;
  unsigned numFromOptionals;
  std::tie(unwrappedFromType, numFromOptionals) = unwrapType(type1);

  Type unwrappedToType;
  unsigned numToOptionals;
  std::tie(unwrappedToType, numToOptionals) = unwrapType(type2);

  if (unwrappedFromType->isTypeVariableOrMember() ||
      unwrappedToType->isTypeVariableOrMember())
    return formUnsolved();

  // Update the score.
  increaseScore(SK_UserConversion, locator); // FIXME: Use separate score kind?
  if (worseThanBestSolution()) {
    return SolutionKind::Error;
  }

  // Local function to count the optional injections that will be performed
  // after the bridging conversion.
  auto countOptionalInjections = [&] {
    if (numToOptionals > numFromOptionals)
      increaseScore(SK_ValueToOptional, locator,
                    numToOptionals - numFromOptionals);
  };

  // Anything can be explicitly converted to AnyObject using the universal
  // bridging conversion. This allows both extraneous optionals in the source
  // (because optionals themselves can be boxed for AnyObject) and in the
  // destination (we'll perform the extra injections at the end).
  if (unwrappedToType->isAnyObject()) {
    countOptionalInjections();
    return SolutionKind::Solved;
  }

  // In a previous version of Swift, we could accidentally drop the coercion
  // constraint in certain cases. In most cases this led to either miscompiles
  // or crashes later down the pipeline, but for coercions between collections
  // we generated somewhat reasonable code that performed a force cast. To
  // maintain compatibility with that behavior, allow the coercion between
  // two collections, but add a warning fix telling the user to use as! or as?
  // instead. In Swift 6 mode, this becomes an error.
  //
  // We only need to perform this compatibility logic if this is a coercion of
  // something that isn't a collection expr (as collection exprs would have
  // crashed in codegen due to CSApply peepholing them). Additionally, the LHS
  // type must be a (potentially optional) type variable, as only such a
  // constraint could have been previously been left unsolved.
  auto canUseCompatFix = [&]() {
    if (Context.isLanguageModeAtLeast(LanguageMode::v6))
      return false;

    if (!rawType1->lookThroughAllOptionalTypes()->isTypeVariableOrMember())
      return false;

    SmallVector<LocatorPathElt, 4> elts;
    auto anchor = locator.getLocatorParts(elts);
    if (elts.empty() || !elts.back().is<LocatorPathElt::CoercionOperand>())
      return false;

    auto *coercion = getAsExpr<CoerceExpr>(anchor);
    if (!coercion)
      return false;

    auto *subject = coercion->getSubExpr();
    while (auto *paren = dyn_cast<ParenExpr>(subject))
      subject = paren->getSubExpr();

    return !isa<CollectionExpr>(subject);
  }();

  // Unless we're allowing the collection compatibility fix, the source cannot
  // be more optional than the destination.
  if (!canUseCompatFix && numFromOptionals > numToOptionals)
    return SolutionKind::Error;

  auto makeCollectionResult = [&](SolutionKind result) -> SolutionKind {
    // If we encountered an error and can use the compatibility fix, do so.
    if (canUseCompatFix) {
      if (numFromOptionals > numToOptionals || result == SolutionKind::Error) {
        auto *loc = getConstraintLocator(locator);
        auto *fix = AllowCoercionToForceCast::create(*this, type1, type2, loc);
        return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
      }
    }
    return result;
  };

  // Bridging the elements of an array.
  if (auto fromElement = unwrappedFromType->getArrayElementType()) {
    if (auto toElement = unwrappedToType->getArrayElementType()) {
      countOptionalInjections();
      auto result = simplifyBridgingConstraint(
          fromElement, toElement, subflags,
          locator.withPathElement(LocatorPathElt::GenericArgument(0)));
      return makeCollectionResult(result);
    }
  }

  // Bridging the keys/values of a dictionary.
  if (auto fromKeyValue = isDictionaryType(unwrappedFromType)) {
    if (auto toKeyValue = isDictionaryType(unwrappedToType)) {
      ConstraintFix *compatFix = nullptr;
      if (canUseCompatFix) {
        compatFix = AllowCoercionToForceCast::create(
            *this, type1, type2, getConstraintLocator(locator));
      }
      addExplicitConversionConstraint(fromKeyValue->first, toKeyValue->first,
                                      ForgetChoice,
                                      locator.withPathElement(
                                        LocatorPathElt::GenericArgument(0)),
                                      compatFix);
      addExplicitConversionConstraint(fromKeyValue->second, toKeyValue->second,
                                      ForgetChoice,
                                      locator.withPathElement(
                                        LocatorPathElt::GenericArgument(1)),
                                      compatFix);
      countOptionalInjections();
      return makeCollectionResult(SolutionKind::Solved);
    }
  }

  // Bridging the elements of a set.
  if (auto fromElement = isSetType(unwrappedFromType)) {
    if (auto toElement = isSetType(unwrappedToType)) {
      countOptionalInjections();
      auto result = simplifyBridgingConstraint(
          *fromElement, *toElement, subflags,
          locator.withPathElement(LocatorPathElt::GenericArgument(0)));
      return makeCollectionResult(result);
    }
  }

  // The source cannot be more optional than the destination, because bridging
  // conversions don't allow us to implicitly check for a value in the optional.
  if (numFromOptionals > numToOptionals) {
    return SolutionKind::Error;
  }

  // Explicit bridging from a value type to an Objective-C class type.
  auto &ctx = getASTContext();
  if (unwrappedFromType->isPotentiallyBridgedValueType() &&
      (unwrappedToType->isBridgeableObjectType() ||
       (unwrappedToType->isExistentialType() &&
        !unwrappedToType->isAny()))) {
    countOptionalInjections();
    if (Type classType = ctx.getBridgedToObjC(DC, unwrappedFromType)) {
      return matchTypes(classType, unwrappedToType, ConstraintKind::Conversion,
                        subflags, locator);
    }
  }

  // Bridging from an Objective-C class type to a value type.
  // Note that specifically require a class or class-constrained archetype
  // here, because archetypes cannot be bridged.
  if (unwrappedFromType->mayHaveSuperclass() &&
      unwrappedToType->isPotentiallyBridgedValueType()) {
    Type bridgedValueType;
    if (auto objcClass = ctx.getBridgedToObjC(DC, unwrappedToType,
                                              &bridgedValueType)) {
      // Bridging NSNumber to NSValue is one-way, since there are multiple Swift
      // value types that bridge to those object types. It requires a checked
      // cast to get back.
      if (ctx.isObjCClassWithMultipleSwiftBridgedTypes(objcClass))
        return SolutionKind::Error;

      // If the bridged value type is generic, the generic arguments
      // must either match or be bridged.
      // FIXME: This should be an associated type of the protocol.
      auto &ctx = getASTContext();
      if (auto fromBGT = unwrappedToType->getAs<BoundGenericType>()) {
        if (fromBGT->isArray()) {
          // [AnyObject]
          addConstraint(ConstraintKind::Bind, fromBGT->getGenericArgs()[0],
                        ctx.getAnyObjectType(),
                        getConstraintLocator(locator.withPathElement(
                            LocatorPathElt::GenericArgument(0))));
        } else if (fromBGT->isDictionary()) {
          // [NSObject : AnyObject]
          auto nsObjectType = ctx.getNSObjectType();
          if (!nsObjectType) {
            // Not a bridging case. Should we detect this earlier?
            return SolutionKind::Error;
          }

          addConstraint(ConstraintKind::Bind, fromBGT->getGenericArgs()[0],
                        nsObjectType,
                        getConstraintLocator(
                          locator.withPathElement(
                            LocatorPathElt::GenericArgument(0))));

          addConstraint(ConstraintKind::Bind, fromBGT->getGenericArgs()[1],
                        ctx.getAnyObjectType(),
                        getConstraintLocator(
                          locator.withPathElement(
                            LocatorPathElt::GenericArgument(1))));
        } else if (fromBGT->isSet()) {
          auto nsObjectType = ctx.getNSObjectType();
          if (!nsObjectType) {
            // Not a bridging case. Should we detect this earlier?
            return SolutionKind::Error;
          }
          addConstraint(ConstraintKind::Bind, fromBGT->getGenericArgs()[0],
                        nsObjectType,
                        getConstraintLocator(
                          locator.withPathElement(
                            LocatorPathElt::GenericArgument(0))));
        } else {
          // Nothing special to do; matchTypes will match generic arguments.
        }
      }

      // Make sure we have the bridged value type.
      if (matchTypes(unwrappedToType, bridgedValueType, ConstraintKind::Bind,
                     subflags, locator) == SolutionKind::Error)
        return SolutionKind::Error;

      countOptionalInjections();
      return matchTypes(unwrappedFromType, objcClass, ConstraintKind::Subtype,
                        subflags, locator);
    }
  }

  return SolutionKind::Error;
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyEscapableFunctionOfConstraint(
                                        Type type1, Type type2,
                                        TypeMatchOptions flags,
                                        ConstraintLocatorBuilder locator) {
  TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);

  // Local function to form an unsolved result.
  auto formUnsolved = [&] {
    if (flags.contains(TMF_GenerateConstraints)) {
      addUnsolvedConstraint(
        Constraint::create(*this, ConstraintKind::EscapableFunctionOf,
                           type1, type2, getConstraintLocator(locator)));
      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  };

  type2 = getFixedTypeRecursive(type2, flags, /*wantRValue=*/true);
  if (auto fn2 = type2->getAs<FunctionType>()) {
    // Solve forward by binding the other type variable to the escapable
    // variation of this type.
    auto fn1 = fn2->withExtInfo(fn2->getExtInfo().withNoEscape(false));
    return matchTypes(type1, fn1, ConstraintKind::Bind, subflags, locator);
  }
  if (!type2->isTypeVariableOrMember())
    // We definitely don't have a function, so bail.
    return SolutionKind::Error;
  
  type1 = getFixedTypeRecursive(type1, flags, /*wantRValue=*/true);
  if (auto fn1 = type1->getAs<FunctionType>()) {
    // We should have the escaping end of the relation.
    if (fn1->getExtInfo().isNoEscape())
      return SolutionKind::Error;
    
    // Solve backward by binding the other type variable to the noescape
    // variation of this type.
    auto fn2 = fn1->withExtInfo(fn1->getExtInfo().withNoEscape(true));
    return matchTypes(type2, fn2, ConstraintKind::Bind, subflags, locator);
  }
  if (!type1->isTypeVariableOrMember())
    // We definitely don't have a function, so bail.
    return SolutionKind::Error;

  return formUnsolved();
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyOpenedExistentialOfConstraint(
                                        Type type1, Type type2,
                                        TypeMatchOptions flags,
                                        ConstraintLocatorBuilder locator) {
  TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);
  type2 = getFixedTypeRecursive(type2, flags, /*wantRValue=*/true);
  if (type2->isAnyExistentialType()) {
    // We have the existential side. Produce an opened archetype and bind
    // type1 to it.
    Type openedTy =
        openAnyExistentialType(type2, getConstraintLocator(locator)).first;
    return matchTypes(type1, openedTy, ConstraintKind::Bind, subflags, locator);
  }
  if (!type2->isTypeVariableOrMember())
    // We definitely don't have an existential, so bail.
    return SolutionKind::Error;
  
  // If type1 is constrained to anything concrete, the constraint fails.
  // It can only be bound to a type we opened for it.
  type1 = getFixedTypeRecursive(type1, flags, /*wantRValue=*/true);
  if (!type1->isTypeVariableOrMember())
    return SolutionKind::Error;
  
  if (flags.contains(TMF_GenerateConstraints)) {
    addUnsolvedConstraint(
      Constraint::create(*this, ConstraintKind::OpenedExistentialOf,
                         type1, type2, getConstraintLocator(locator)));
    return SolutionKind::Solved;
  }
  return SolutionKind::Unsolved;
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyKeyPathConstraint(
    Type keyPathTy,
    Type rootTy,
    Type valueTy,
    ArrayRef<TypeVariableType *> componentTypeVars,
    TypeMatchOptions flags,
    ConstraintLocatorBuilder locator) {
  auto subflags = getDefaultDecompositionOptions(flags);
  keyPathTy = getFixedTypeRecursive(keyPathTy, /*want rvalue*/ true);

  auto formUnsolved = [&]() -> SolutionKind {
    if (flags.contains(TMF_GenerateConstraints)) {
      addUnsolvedConstraint(Constraint::create(
          *this, ConstraintKind::KeyPath, keyPathTy, rootTy, valueTy,
          getConstraintLocator(locator), componentTypeVars));
      return SolutionKind::Solved;
    }
    return SolutionKind::Unsolved;
  };

  if (keyPathTy->isTypeVariableOrMember())
    return formUnsolved();

  auto tryMatchRootAndValueFromContextualType = [&](Type contextualTy) -> bool {
    Type contextualRootTy = Type(), contextualValueTy = Type();

    // Placeholders are only allowed in the diagnostic mode so it's
    // okay to simply return `true` here.
    if (contextualTy->isPlaceholder())
      return true;

    if (contextualTy->is<ArchetypeType>()) {
      contextualTy = contextualTy->getSuperclass();
      if (!contextualTy)
        return true;
    }

    // Situations like `any KeyPath<...> & Sendable`.
    if (contextualTy->isExistentialType()) {
      contextualTy = contextualTy->getSuperclass();
      if (!contextualTy)
        return true;
    }

    if (auto bgt = contextualTy->getAs<BoundGenericType>()) {
      // We can get root and value from a concrete key path type.
      if (!(bgt->isKeyPath() || bgt->isWritableKeyPath() ||
            bgt->isReferenceWritableKeyPath())) {
        return true;
      }

      contextualRootTy = bgt->getGenericArgs()[0];
      contextualValueTy = bgt->getGenericArgs()[1];
    }

    if (auto fnTy = contextualTy->getAs<FunctionType>()) {
      assert(fnTy->getParams().size() == 1);
      // Key paths may be converted to a function of compatible type. We will
      // later form from this key path an implicit closure of the form
      // `{ root in root[keyPath: kp] }` so any conversions that are valid with
      // a source type of `(Root) -> Value` should be valid here too.
      auto rootParam = AnyFunctionType::Param(rootTy);
      auto kpFnTy = FunctionType::get(rootParam, valueTy, fnTy->getExtInfo());

      // Note: because the keypath is applied to `root` as a parameter internal
      // to the closure, we use the function parameter's "parameter type" rather
      // than the raw type. This enables things like:
      // ```
      // let countKeyPath: (String...) -> Int = \.count
      // ```
      auto paramTy = fnTy->getParams()[0].getParameterType();
      auto paramParam = AnyFunctionType::Param(paramTy);
      auto paramFnTy = FunctionType::get(paramParam, fnTy->getResult(),
                                         fnTy->getExtInfo());

      // Form a key path type as well to make sure that root and value
      // types satisfy all of its requirements.
      // Note that `KeyPath` types used to have no requirements but now
      // they do require `Root` and `Value` to be `Copyable` and `Escapable`.
      {
        auto keyPathTy =
            openUnboundGenericType(
                getASTContext().getKeyPathDecl(),
                /*parent=*/Type(),
                locator.withPathElement(LocatorPathElt::KeyPathType()),
                /*isTypeResolution=*/false)
                ->castTo<BoundGenericType>();
        addConstraint(ConstraintKind::Bind, keyPathTy->getGenericArgs()[0],
                      rootTy,
                      locator.withPathElement(LocatorPathElt::KeyPathRoot()));
        addConstraint(ConstraintKind::Bind, keyPathTy->getGenericArgs()[1],
                      valueTy,
                      locator.withPathElement(LocatorPathElt::KeyPathValue()));
      }

      return matchTypes(kpFnTy, paramFnTy, ConstraintKind::Conversion,
                         subflags, locator)
                  != SolutionKind::Error;
    }

    assert(contextualRootTy && contextualValueTy);

    if (matchTypes(rootTy, contextualRootTy, ConstraintKind::Bind, subflags,
                   locator.withPathElement(ConstraintLocator::KeyPathRoot))
            == SolutionKind::Error)
      return false;

    if (matchTypes(valueTy, contextualValueTy, ConstraintKind::Bind, subflags,
                   locator.withPathElement(ConstraintLocator::KeyPathValue))
            == SolutionKind::Error)
      return false;

    return true;
  };

  // If key path has to be converted to a function, let's check that
  // the contextual type has precisely one parameter.
  if (auto *fnTy = keyPathTy->getAs<FunctionType>()) {
    increaseScore(SK_FunctionConversion, locator);

    // Key paths never throw, so if the function has a thrown error type
    // that is a type variable, infer it to be Never.
    if (auto thrownError = fnTy->getThrownError()) {
      if (thrownError->isTypeVariableOrMember()) {
        (void)matchTypes(thrownError, getASTContext().getNeverType(),
                         ConstraintKind::Equal, TMF_GenerateConstraints,
                         locator);
      }
    }

    if (fnTy->getParams().size() != 1) {
      if (!shouldAttemptFixes())
        return SolutionKind::Error;

      recordAnyTypeVarAsPotentialHole(rootTy);
      recordAnyTypeVarAsPotentialHole(valueTy);

      auto *fix = AllowMultiArgFuncKeyPathMismatch::create(
          *this, fnTy, getConstraintLocator(locator));
      // Pretend the keypath type got resolved and move on.
      return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
    }
  }

  // If we have a hole somewhere in the key path, the solver won't be able to
  // infer the key path type. So let's just assume this is solved.
  if (shouldAttemptFixes()) {
    auto keyPath = castToExpr<KeyPathExpr>(locator.getAnchor());

    if (hasFixFor(getConstraintLocator(keyPath),
                  FixKind::AllowKeyPathWithoutComponents))
      return SolutionKind::Solved;

    // If the root type has been bound to a hole, we cannot infer it.
    if (getFixedTypeRecursive(rootTy, /*wantRValue*/ true)->isPlaceholder())
      return SolutionKind::Solved;
  }

  return tryMatchRootAndValueFromContextualType(keyPathTy)
             ? SolutionKind::Solved
             : SolutionKind::Error;
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyKeyPathApplicationConstraint(
                                        Type keyPathTy,
                                        Type rootTy,
                                        Type valueTy,
                                        TypeMatchOptions flags,
                                        ConstraintLocatorBuilder locator) {
  TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);
  keyPathTy = getFixedTypeRecursive(keyPathTy, flags, /*wantRValue=*/true);
  valueTy = getFixedTypeRecursive(valueTy, flags, /*wantRValue=*/false);

  auto unsolved = [&]() -> SolutionKind {
    if (flags.contains(TMF_GenerateConstraints)) {
      addUnsolvedConstraint(Constraint::create(*this,
                    ConstraintKind::KeyPathApplication,
                    keyPathTy, rootTy, valueTy, getConstraintLocator(locator)));
      return SolutionKind::Solved;
    }
    return SolutionKind::Unsolved;
  };

  // When locator points to a KeyPathDynamicMemberLookup, reject the
  // key path application.
  if (locator.endsWith<LocatorPathElt::KeyPathDynamicMember>())
    return SolutionKind::Error;

  if (keyPathTy->is<ArchetypeType>()) {
    if (auto superclassTy = keyPathTy->getSuperclass())
      keyPathTy = superclassTy;
  }

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

  if (keyPathTy->isAnyKeyPath()) {
    // Read-only keypath, whose projected value is upcast to `Any?`.
    // The root type can be anything.
    Type resultTy = getASTContext().getAnyExistentialType();
    resultTy = OptionalType::get(resultTy);
    return matchTypes(resultTy, valueTy, ConstraintKind::Bind,
                      subflags, locator);
  }

  if (keyPathTy->isPlaceholder()) {
    if (rootTy->hasTypeVariable()) {
      recordAnyTypeVarAsPotentialHole(rootTy);
    }
    if (valueTy->hasTypeVariable()) {
      recordAnyTypeVarAsPotentialHole(valueTy);
    }
    return SolutionKind::Solved;
  }

  if (auto bgt = keyPathTy->getAs<BoundGenericType>()) {
    // We have the key path type. Match it to the other ends of the constraint.
    auto kpRootTy = bgt->getGenericArgs()[0];
    
    // Try to match the root type.
    rootTy = getFixedTypeRecursive(rootTy, flags, /*wantRValue=*/false);

    auto matchRoot = [&](ConstraintKind kind) -> bool {
      auto rootMatches =
          matchTypes(rootTy, kpRootTy, kind, subflags,
                     locator.withPathElement(LocatorPathElt::KeyPathRoot()));
      switch (rootMatches) {
      case SolutionKind::Error:
        return false;
      case SolutionKind::Solved:
        return true;
      case SolutionKind::Unsolved:
        llvm_unreachable("should have generated constraints");
      }
      llvm_unreachable("unhandled match");
    };

    if (bgt->isPartialKeyPath()) {
      // Read-only keypath, whose projected value is upcast to `Any`.
      auto resultTy = getASTContext().getAnyExistentialType();

      if (!matchRoot(ConstraintKind::Conversion))
        return SolutionKind::Error;

      return matchTypes(resultTy, valueTy,
                        ConstraintKind::Bind, subflags, locator);
    }

    if (bgt->getGenericArgs().size() < 2)
      return SolutionKind::Error;
    auto kpValueTy = bgt->getGenericArgs()[1];

    bool isKnownRValue = !valueTy->isTypeVariableOrMember() &&
                         !valueTy->is<LValueType>();

    /// Solve for an rvalue base.
    auto solveRValue = [&]() -> ConstraintSystem::SolutionKind {
      // An rvalue base can be converted to a supertype.
      return matchTypes(kpValueTy, valueTy,
                        ConstraintKind::Bind, subflags, locator);
    };
    /// Solve for a base whose lvalueness is to be determined.
    auto solveUnknown = [&]() -> ConstraintSystem::SolutionKind {
      if (matchTypes(kpValueTy, valueTy, ConstraintKind::Equal, subflags,
                     locator) == SolutionKind::Error)
        return SolutionKind::Error;
      return unsolved();
    };
    /// Solve for an lvalue base.
    auto solveLValue = [&]() -> ConstraintSystem::SolutionKind {
      return matchTypes(LValueType::get(kpValueTy), valueTy,
                        ConstraintKind::Bind, subflags, locator);
    };

    if (bgt->isKeyPath()) {
      // Read-only keypath.
      if (!matchRoot(ConstraintKind::Conversion))
        return SolutionKind::Error;

      return solveRValue();
    }
    if (bgt->isWritableKeyPath()) {
      kpRootTy = getFixedTypeRecursive(kpRootTy, flags, /*wantRValueType=*/true);

      // We might not know if the value is ultimately going to be used as an
      // lvalue or rvalue yet, but this determines whether we can convert the
      // base. To avoid introducing a disjunction, just guess if the keypath
      // root type is already bound, and conservatively assume we will not
      // convert the base if the keypath root type is not bound.
      if (!kpRootTy->isTypeVariableOrMember()) {
        auto result = isLikelyExactMatch(rootTy->getRValueType(), kpRootTy);
        if (result && !*result) {
          // Proceed as in the read-only case.
          if (!matchRoot(ConstraintKind::Conversion))
            return SolutionKind::Error;

          return solveRValue();
        }
      }

      // Writable keypath. The result can be an lvalue if the root was.
      // We can't convert the base without giving up lvalue-ness, though.
      if (!matchRoot(ConstraintKind::Equal))
        return SolutionKind::Error;

      if (isKnownRValue)
        return solveRValue();
      if (rootTy->is<LValueType>())
        return solveLValue();
      if (rootTy->isTypeVariableOrMember())
        return solveUnknown();

      return solveRValue();
    }
    if (bgt->isReferenceWritableKeyPath()) {
      if (!matchRoot(ConstraintKind::Conversion))
        return SolutionKind::Error;

      if (isKnownRValue)
        return solveRValue();

      return solveLValue();
    }
    // Otherwise, we don't have a key path type at all.
    return SolutionKind::Error;
  }

  if (!keyPathTy->isTypeVariableOrMember()) {
    if (shouldAttemptFixes()) {
      auto *fix = IgnoreKeyPathSubscriptIndexMismatch::create(
          *this, keyPathTy, getConstraintLocator(locator));
      recordAnyTypeVarAsPotentialHole(valueTy);
      return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
    }

    return SolutionKind::Error;
  }

  return unsolved();
}

/// Create an implicit dot-member reference expression to be used
/// as a root for injected `.callAsFunction` call.
static UnresolvedDotExpr *createImplicitRootForCallAsFunction(
    ConstraintSystem &cs, Type refType, ArgumentList *baseArgs,
    ArgumentList *trailingArgs, ConstraintLocator *calleeLocator) {
  auto &ctx = cs.getASTContext();
  auto *baseExpr = castToExpr(calleeLocator->getAnchor());

  SmallVector<Identifier, 2> closureLabelsScratch;
  // Create implicit `.callAsFunction` expression to use as an anchor
  // for new argument list that only has trailing closures in it.
  auto *implicitRef = UnresolvedDotExpr::createImplicit(
      ctx, baseExpr, {ctx.Id_callAsFunction},
      trailingArgs->getArgumentLabels(closureLabelsScratch));

  {
    // Record a type of the new reference in the constraint system.
    cs.setType(implicitRef, refType);
    // Record new `.callAsFunction` in the constraint system.
    cs.recordImplicitCallAsFunction(calleeLocator, implicitRef, baseArgs);

    auto *implicitRefLocator = cs.getConstraintLocator(
        implicitRef, ConstraintLocator::ApplyArgument);
    cs.associateArgumentList(implicitRefLocator, trailingArgs);
  }

  return implicitRef;
}

ConstraintSystem::SolutionKind ConstraintSystem::simplifyApplicableFnConstraint(
    FunctionType *func1, Type type2,
    std::optional<TrailingClosureMatching> trailingClosureMatching,
    DeclContext *useDC,
    TypeMatchOptions flags, ConstraintLocatorBuilder locator) {
  auto &ctx = getASTContext();

  // Before stripping lvalue-ness and optional types, save the original second
  // type for handling `func callAsFunction` and `@dynamicCallable`
  // applications. This supports the following cases:
  // - Generating constraints for `mutating func callAsFunction`. The nominal
  //   type (`type2`) should be an lvalue type.
  // - Extending `Optional` itself with `func callAsFunction` or
  //   `@dynamicCallable` functionality. Optional types are stripped below if
  //   `shouldAttemptFixes()` is true.
  auto origLValueType2 =
      getFixedTypeRecursive(type2, flags, /*wantRValue=*/false);
  // Drill down to the concrete type on the right hand side.
  type2 = getFixedTypeRecursive(type2, flags, /*wantRValue=*/true);
  auto desugar2 = type2->getDesugaredType();

  // If a type variable representing "function type" is a hole
  // or it could be bound to some concrete type with a help of
  // a fix, let's propagate holes to the "input" type. Doing so
  // provides more information to upcoming argument and result matching.
  if (shouldAttemptFixes()) {
    if (auto *typeVar = type2->getAs<TypeVariableType>()) {
      auto *locator = typeVar->getImpl().getLocator();
      if (hasFixFor(locator)) {
        recordAnyTypeVarAsPotentialHole(func1);
      }
    }
    Type underlyingType = desugar2;
    while (auto *MT = underlyingType->getAs<AnyMetatypeType>()) {
      underlyingType = MT->getInstanceType();
    }
    underlyingType =
        getFixedTypeRecursive(underlyingType, flags, /*wantRValue=*/true);
    if (underlyingType->isPlaceholder()) {
      if (isForCodeCompletion()) {
        // In code completion mode it's important to attempt to infer as much
        // type information as possible from the context, so let's delay placeholder
        // propagation to give parameters a chance to be inferred from
        // arguments and the result type a chance to be inferred from context.
        recordAnyTypeVarAsPotentialHole(func1);
      } else {
        // In diagnostic mode, propagate function value placeholder to
        // arguments and result. This helps to avoid recording extraneous
        // fixes because generic parameters and leading-dot syntax arguments
        // cannot be inferred in this case.
        //
        // Note that `recordTypeVariablesAsHoles` is not used here because
        // an existing hole from a function value is propagated to
        // arguments/result instead of creating a direct one per type variable
        // like that method would do, this is an important distinction later on,
        // when solver decides whether to add a fix or not when a hole is
        // subsequently propagated to other locations like leading-dot syntax
        // base or literals.
        Type(func1).visit([&](Type componentTy) {
          if (auto *typeVar = componentTy->getAs<TypeVariableType>()) {
            if (!typeVar->getImpl().hasRepresentativeOrFixed())
              assignFixedType(typeVar, underlyingType);
          }
        });
      }
      return SolutionKind::Solved;
    }

    if (!isForCodeCompletion()) {
      auto argsTy = simplifyType(func1)->castTo<FunctionType>();
      // Short-circuit calls where all arguments are invalid, otherwise the
      // solver would end up producing a solution for every overload which
      // it cannot disambiguate which quickly turns into "too complex" situation.
      if (argsTy->getNumParams() > 0 &&
          llvm::all_of(argsTy->getParams(),
                       [](const FunctionType::Param &param) {
                         return param.getPlainType()->isPlaceholder();
                       })) {
        recordTypeVariablesAsHoles(func1);
        recordTypeVariablesAsHoles(type2);
        return SolutionKind::Solved;
      }
    }
  }

  TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);

  SmallVector<LocatorPathElt, 2> parts;
  auto anchor = locator.getLocatorParts(parts);
  bool isOperator =
      (isExpr<PrefixUnaryExpr>(anchor) || isExpr<PostfixUnaryExpr>(anchor) ||
       isExpr<BinaryExpr>(anchor));

  auto hasInOut = [&]() {
    for (auto param : func1->getParams())
      if (param.isInOut())
        return true;
    return false;
  };

  // Local function to form an unsolved result.
  auto formUnsolved = [&](bool activate = false) {
    if (flags.contains(TMF_GenerateConstraints)) {
      auto *application = Constraint::createApplicableFunction(
          *this, func1, type2, trailingClosureMatching, useDC,
          getConstraintLocator(locator));

      addUnsolvedConstraint(application);
      if (activate)
        activateConstraint(application);

      return SolutionKind::Solved;
    }
    
    return SolutionKind::Unsolved;
  };

  // If right-hand side is a type variable, the constraint is unsolved.
  if (desugar2->isTypeVariableOrMember()) {
    return formUnsolved();
  }

  // Strip the 'ApplyFunction' off the locator.
  // FIXME: Perhaps ApplyFunction can go away entirely?
  assert(!parts.empty() && "Nonsensical applicable-function locator");
  assert(parts.back().getKind() == ConstraintLocator::ApplyFunction);
  assert(parts.back().getNewSummaryFlags() == 0);
  parts.pop_back();
  ConstraintLocatorBuilder outerLocator =
    getConstraintLocator(anchor, parts, locator.getSummaryFlags());

  // If the types are obviously equivalent, we're done. This optimization
  // is not valid for operators though, where an inout parameter does not
  // have an explicit inout argument.
  if (func1 == desugar2) {
    // Note that this could throw.
    recordPotentialThrowSite(
        PotentialThrowSite::Application, Type(desugar2), outerLocator);

    if (!isOperator || !hasInOut()) {
      recordMatchCallArgumentResult(
          getConstraintLocator(
              outerLocator.withPathElement(ConstraintLocator::ApplyArgument)),
          MatchCallArgumentResult::forArity(func1->getNumParams()));
      return SolutionKind::Solved;
    }
  }

  // Handle applications of types with `callAsFunction` methods.
  // Do this before stripping optional types below, when `shouldAttemptFixes()`
  // is true.
  if (desugar2->isCallAsFunctionType(DC)) {
    auto memberLoc = getConstraintLocator(
        locator.withPathElement(ConstraintLocator::ImplicitCallAsFunction));
    // Add a `callAsFunction` member constraint, binding the member type to a
    // type variable.
    auto memberTy = createTypeVariable(memberLoc, /*options=*/0);
    // TODO: Revisit this if `static func callAsFunction` is to be supported.
    // Static member constraint requires `FunctionRefInfo::DoubleApply`.
    addValueMemberConstraint(origLValueType2,
                             DeclNameRef(ctx.Id_callAsFunction), memberTy, DC,
                             FunctionRefInfo::singleBaseNameApply(),
                             /*outerAlternatives*/ {}, memberLoc);
    // Add new applicable function constraint based on the member type
    // variable.
    addApplicationConstraint(func1, memberTy, trailingClosureMatching, useDC,
                             locator);
    return SolutionKind::Solved;
  }

  // Record the second type before unwrapping optionals.
  auto origType2 = desugar2;
  unsigned unwrapCount = 0;
  if (shouldAttemptFixes()) {
    // If we have an optional type, try forcing it to see if that
    // helps. Note that we only deal with function and metatype types
    // below, so there is no reason not to attempt to strip these off
    // immediately.
    while (auto objectType2 = desugar2->getOptionalObjectType()) {
      type2 = objectType2;
      desugar2 = type2->getDesugaredType();

      // Track how many times we do this so that we can record a fix for each.
      ++unwrapCount;
    }
  }

  // For a function, bind the output and convert the argument to the input.
  if (auto func2 = dyn_cast<FunctionType>(desugar2)) {
    // Note that this could throw.
    recordPotentialThrowSite(
        PotentialThrowSite::Application, Type(desugar2), outerLocator);

    ConstraintKind subKind = (isOperator
                              ? ConstraintKind::OperatorArgumentConversion
                              : ConstraintKind::ArgumentConversion);

    auto *argumentsLoc = getConstraintLocator(
        outerLocator.withPathElement(ConstraintLocator::ApplyArgument));

    auto *argumentList = getArgumentList(argumentsLoc);
    // The argument type must be convertible to the input type.
    SmallVector<std::pair<TypeVariableType *, ExistentialArchetypeType *>, 2>
        openedExistentials;
    auto matchCallResult = ::matchCallArguments(
        *this, func2, argumentList, func1->getParams(), func2->getParams(),
        subKind, argumentsLoc, trailingClosureMatching, openedExistentials);

    switch (matchCallResult) {
    case SolutionKind::Error: {
      auto resultTy = func2->getResult();

      // If this is a call that constructs a callable type with
      // trailing closure(s), closure(s) might not belong to
      // the constructor but rather to implicit `callAsFunction`,
      // there is no way to determine that without trying.
      if (resultTy->isCallAsFunctionType(DC) &&
          argumentList->hasAnyTrailingClosures()) {
        auto *calleeLoc = getCalleeLocator(argumentsLoc);

        bool isInit = false;
        if (auto overload = findSelectedOverloadFor(calleeLoc)) {
          isInit = bool(dyn_cast_or_null<ConstructorDecl>(
              overload->choice.getDeclOrNull()));
        }

        if (!isInit)
          return SolutionKind::Error;

        auto &ctx = getASTContext();
        auto numTrailing = argumentList->getNumTrailingClosures();

        SmallVector<Argument, 4> newArguments(
            argumentList->getNonTrailingArgs());
        SmallVector<Argument, 4> trailingClosures(
            argumentList->getTrailingClosures());

        // Original argument list with all the trailing closures removed.
        auto *newArgumentList = ArgumentList::createParsed(
            ctx, argumentList->getLParenLoc(), newArguments,
            argumentList->getRParenLoc(),
            /*firstTrailingClosureIndex=*/std::nullopt);

        auto trailingClosureTypes = func1->getParams().take_back(numTrailing);
        // The original result type is going to become a result of
        // implicit `.callAsFunction` instead since `.callAsFunction`
        // is inserted between `.init` and trailing closures.
        auto callAsFunctionResultTy = func1->getResult();

        // The implicit replacement for original result type which
        // represents a callable type produced by `.init` call.
        auto callableType =
            createTypeVariable(getConstraintLocator({}), /*flags=*/0);

        // The original application type with all the trailing closures
        // dropped from it and result replaced to the implicit variable.
        func1 = FunctionType::get(func1->getParams().drop_back(numTrailing),
                                  callableType, func1->getExtInfo());

        auto matchCallResult = ::matchCallArguments(
            *this, func2, newArgumentList, func1->getParams(),
            func2->getParams(), subKind, argumentsLoc, trailingClosureMatching,
            openedExistentials);

        if (matchCallResult != SolutionKind::Solved)
          return SolutionKind::Error;

        auto *implicitCallArgumentList =
            ArgumentList::createImplicit(ctx, trailingClosures,
                                         /*firstTrailingClosureIndex=*/0);

        auto *implicitRef = createImplicitRootForCallAsFunction(
            *this, callAsFunctionResultTy, newArgumentList,
            implicitCallArgumentList, calleeLoc);

        auto callAsFunctionArguments =
            FunctionType::get(trailingClosureTypes, callAsFunctionResultTy,
                              FunctionType::ExtInfo());

        // Form an unsolved constraint to apply trailing closures to a
        // callable type produced by `.init`. This constraint would become
        // active when `callableType` is bound.
        addUnsolvedConstraint(Constraint::createApplicableFunction(
            *this, callAsFunctionArguments, callableType,
            trailingClosureMatching, useDC,
            getConstraintLocator(implicitRef,
                                 ConstraintLocator::ApplyFunction)));
        break;
      }

      return SolutionKind::Error;
    }

    case SolutionKind::Unsolved: {
      // Only occurs when there is an ambiguity between forward scanning and
      // backward scanning for the unlabeled trailing closure. Create a
      // disjunction so that we explore both paths, and can diagnose
      // ambiguities later.
      assert(!trailingClosureMatching.has_value());

      auto applyLocator = getConstraintLocator(locator);
      auto forwardConstraint = Constraint::createApplicableFunction(
          *this, func1, type2, TrailingClosureMatching::Forward, useDC,
          applyLocator);
      auto backwardConstraint = Constraint::createApplicableFunction(
          *this, func1, type2, TrailingClosureMatching::Backward, useDC,
          applyLocator);
      addDisjunctionConstraint({forwardConstraint, backwardConstraint},
                               applyLocator);
      break;
    }

    case SolutionKind::Solved:
      // Keep going.
      break;
    }

    // Erase all of the opened existentials.
    Type result2 = func2->getResult();
    if (result2->hasTypeVariable() && !openedExistentials.empty()) {
      for (const auto &opened : openedExistentials) {
        auto originalTy = result2;
        if (auto *lvalueTy = dyn_cast<LValueType>(originalTy.getPointer())) {
          originalTy = lvalueTy->getObjectType();
        }

        const auto erasedTy = typeEraseOpenedExistentialReference(
            originalTy, opened.second->getExistentialType(), opened.first,
            TypePosition::Covariant);

        if (originalTy.getPointer() != erasedTy.getPointer()) {
          // We currently cannot keep lvalueness if the object type changed.
          result2 = erasedTy;
        }
      }
    }

    // The result types are equivalent.
    if (matchFunctionResultTypes(
            func1->getResult(), result2, subflags,
            locator.withPathElement(ConstraintLocator::FunctionResult))
            == SolutionKind::Error)
      return SolutionKind::Error;

    if (unwrapCount == 0)
      return SolutionKind::Solved;

    // Record any fixes we attempted to get to the correct solution.
    auto *fix = ForceOptional::create(*this, origType2, func1,
                                      getConstraintLocator(locator));
    if (recordFix(fix, FixImpact::Mismatch * unwrapCount))
      return SolutionKind::Error;

    return SolutionKind::Solved;
  }

  // For a metatype, perform a construction.
  if (auto meta2 = dyn_cast<AnyMetatypeType>(desugar2)) {
    auto instance2 = getFixedTypeRecursive(meta2->getInstanceType(), true);
    if (instance2->isTypeVariableOrMember())
      return formUnsolved();

    // Construct the instance from the input arguments.
    auto simplified = simplifyConstructionConstraint(
        instance2, func1, subflags,
        useDC, FunctionRefInfo::singleBaseNameApply(),
        getConstraintLocator(outerLocator));

    // Record any fixes we attempted to get to the correct solution.
    if (simplified == SolutionKind::Solved) {
      if (unwrapCount == 0)
        return SolutionKind::Solved;

      auto *fix = ForceOptional::create(*this, origType2, func1,
                                        getConstraintLocator(locator));
      if (recordFix(fix, FixImpact::Mismatch * unwrapCount))
        return SolutionKind::Error;
    }

    return simplified;
  }

  // Handle applications of @dynamicCallable types.
  auto result = simplifyDynamicCallableApplicableFnConstraint(
      func1, origType2, subflags, locator);

  if (shouldAttemptFixes() && result == SolutionKind::Error) {
    // Skip this fix if the type is not yet resolved or
    // it's a function type/metatype which points to argument mismatches.
    if (desugar2->is<TypeVariableType>() || desugar2->is<FunctionType>() ||
        desugar2->is<AnyMetatypeType>())
      return SolutionKind::Error;

    // If there are any type variables associated with arguments/result
    // they have to be marked as "holes".
    recordAnyTypeVarAsPotentialHole(func1);

    if (desugar2->isPlaceholder())
      return SolutionKind::Solved;

    auto *fix = RemoveInvalidCall::create(*this, getConstraintLocator(locator));
    // Let's make this fix a high impact so if there is a function or member
    // overload with e.g. argument-to-parameter type mismatches or missing/extra
    // arguments it would take a higher priority.
    return recordFix(fix, FixImpact::InvalidApplication) ? SolutionKind::Error
                                                         : SolutionKind::Solved;
  }

  return result;
}

/// Looks up and returns the @dynamicCallable required methods (if they exist)
/// implemented by a type.
static llvm::DenseSet<FuncDecl *>
lookupDynamicCallableMethods(NominalTypeDecl *decl, ConstraintSystem &CS,
                             const ConstraintLocatorBuilder &locator,
                             Identifier argumentName, bool hasKeywordArgs) {
  auto &ctx = CS.getASTContext();

  // The generic arguments don't matter because we only want the member decls,
  // not concrete overload choices (we form those later when adding the overload
  // set). We map into context here to avoid an OverloadChoice assertion for an
  // interface type base.
  // TODO: We really ought to separate out the actual lookup part of
  // `performMemberLookup` from the choice construction. That would allow us to
  // requestify the lookup of dynamicCallable members on a per-decl basis, and
  // map them onto viable and unviable choices onto a given base type.
  auto type = decl->getDeclaredTypeInContext();

  DeclNameRef methodName({ ctx, ctx.Id_dynamicallyCall, { argumentName } });
  auto matches = CS.performMemberLookup(
      ConstraintKind::ValueMember, methodName, type,
      FunctionRefInfo::singleBaseNameApply(), CS.getConstraintLocator(locator),
      /*includeInaccessibleMembers*/ false);
  // Filter valid candidates.
  auto candidates = matches.ViableCandidates;
  auto filter = [&](OverloadChoice choice) {
    auto cand = cast<FuncDecl>(choice.getDecl());
    return !isValidDynamicCallableMethod(cand, hasKeywordArgs);
  };
  candidates.erase(
      std::remove_if(candidates.begin(), candidates.end(), filter),
      candidates.end());

  llvm::DenseSet<FuncDecl *> methods;
  for (auto candidate : candidates)
    methods.insert(cast<FuncDecl>(candidate.getDecl()));
  return methods;
}

/// Looks up and returns the @dynamicCallable required methods (if they exist)
/// implemented by a given nominal type decl.
static DynamicCallableMethods
lookupDynamicCallableMethods(NominalTypeDecl *decl, ConstraintSystem &CS,
                             const ConstraintLocatorBuilder &locator) {
  auto it = CS.DynamicCallableCache.find(decl);
  if (it != CS.DynamicCallableCache.end())
    return it->second;

  // The decl must have @dynamicCallable.
  auto &ctx = CS.getASTContext();
  HasDynamicCallableAttributeRequest req(decl);
  if (!evaluateOrDefault(ctx.evaluator, req, false))
    return DynamicCallableMethods();

  DynamicCallableMethods methods;
  methods.argumentsMethods =
    lookupDynamicCallableMethods(decl, CS, locator, ctx.Id_withArguments,
                                 /*hasKeywordArgs*/ false);
  methods.keywordArgumentsMethods =
    lookupDynamicCallableMethods(decl, CS, locator,
                                 ctx.Id_withKeywordArguments,
                                 /*hasKeywordArgs*/ true);
  CS.DynamicCallableCache[decl] = methods;
  return methods;
}

/// Returns the @dynamicCallable required methods (if they exist) implemented
/// by a type.
static DynamicCallableMethods
getDynamicCallableMethods(Type type, ConstraintSystem &CS,
                          const ConstraintLocatorBuilder &locator) {
  SmallVector<NominalTypeDecl *, 4> decls;
  namelookup::tryExtractDirectlyReferencedNominalTypes(type, decls);

  DynamicCallableMethods result;
  for (auto *decl : decls)
    result.addMethods(lookupDynamicCallableMethods(decl, CS, locator));

  return result;
}

// TODO: Refactor/simplify this function.
// - It should perform less duplicate work with its caller
//   `ConstraintSystem::simplifyApplicableFnConstraint`.
// - It should generate a member constraint instead of manually forming an
//   overload set for `func dynamicallyCall` candidates.
// - It should support `mutating func dynamicallyCall`. This should fall out of
//   using member constraints with an lvalue base type.
ConstraintSystem::SolutionKind
ConstraintSystem::simplifyDynamicCallableApplicableFnConstraint(
                                            Type type1,
                                            Type type2,
                                            TypeMatchOptions flags,
                                            ConstraintLocatorBuilder locator) {
  auto &ctx = getASTContext();

  // By construction, the left hand side is a function type: $T1 -> $T2.
  assert(type1->is<FunctionType>());

  // Drill down to the concrete type on the right hand side.
  type2 = getFixedTypeRecursive(type2, flags, /*wantRValue=*/true);
  auto desugar2 = type2->getDesugaredType();

  TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);

  // If the types are obviously equivalent, we're done.
  if (type1.getPointer() == desugar2)
    return SolutionKind::Solved;

  // Local function to form an unsolved result.
  auto formUnsolved = [&] {
    if (flags.contains(TMF_GenerateConstraints)) {
      addUnsolvedConstraint(
        Constraint::create(*this,
          ConstraintKind::DynamicCallableApplicableFunction, type1, type2,
          getConstraintLocator(locator)));
      return SolutionKind::Solved;
    }
    return SolutionKind::Unsolved;
  };

  // If right-hand side is a type variable, the constraint is unsolved.
  if (desugar2->isTypeVariableOrMember())
    return formUnsolved();

  // If right-hand side is a function type, it must be a valid
  // `dynamicallyCall` method type. Bind the output and convert the argument
  // to the input.
  auto func1 = type1->castTo<FunctionType>();
  if (auto func2 = dyn_cast<FunctionType>(desugar2)) {
    // The argument type must be convertible to the input type.
    assert(func1->getParams().size() == 1 && func2->getParams().size() == 1 &&
           "Expected `dynamicallyCall` method with one parameter");
    assert((func2->getParams()[0].getLabel() == ctx.Id_withArguments ||
            func2->getParams()[0].getLabel() == ctx.Id_withKeywordArguments) &&
           "Expected 'dynamicallyCall' method argument label 'withArguments' "
           "or 'withKeywordArguments'");
    if (matchTypes(func1->getParams()[0].getPlainType(),
                   func2->getParams()[0].getPlainType(),
                   ConstraintKind::ArgumentConversion,
                   subflags,
                   locator.withPathElement(
                     ConstraintLocator::ApplyArgument)) == SolutionKind::Error)
      return SolutionKind::Error;

    // The result types are equivalent.
    if (matchFunctionResultTypes(
            func1->getResult(), func2->getResult(), subflags,
            locator.withPathElement(ConstraintLocator::FunctionResult))
            == SolutionKind::Error)
      return SolutionKind::Error;

    return SolutionKind::Solved;
  }

  // If the right-hand side is not a function type, it must be a valid
  // @dynamicCallable type. Attempt to get valid `dynamicallyCall` methods.
  auto methods = getDynamicCallableMethods(desugar2, *this, locator);
  if (!methods.isValid()) return SolutionKind::Error;

  // Determine whether to call a `withArguments` method or a
  // `withKeywordArguments` method.
  bool useKwargsMethod = methods.argumentsMethods.empty();
  useKwargsMethod |= llvm::any_of(
    func1->getParams(), [](AnyFunctionType::Param p) { return p.hasLabel(); });

  auto candidates = useKwargsMethod ?
    methods.keywordArgumentsMethods :
    methods.argumentsMethods;

  // Create a type variable for the `dynamicallyCall` method.
  auto loc = getConstraintLocator(locator);
  auto tv = createTypeVariable(loc,
                               TVO_CanBindToLValue |
                               TVO_CanBindToNoEscape);

  // Record the 'dynamicallyCall` method overload set.
  SmallVector<OverloadChoice, 4> choices;
  for (auto candidate : candidates) {
    if (candidate->isInvalid()) continue;
    choices.push_back(OverloadChoice::getDecl(type2, candidate,
                                              FunctionRefInfo::singleBaseNameApply()));
  }

  if (choices.empty()) {
    if (!shouldAttemptFixes())
      return SolutionKind::Error;

    // TODO(diagnostics): This is not going to be necessary once
    // `@dynamicCallable` uses existing `member` machinery.

    auto argLabel = useKwargsMethod ? ctx.Id_withKeywordArguments
                                    : ctx.Id_withArguments;
    DeclNameRef memberName({ ctx, ctx.Id_dynamicallyCall, {argLabel} });

    auto *fix = DefineMemberBasedOnUse::create(
        *this, desugar2, memberName, /*alreadyDiagnosed=*/false,
        getConstraintLocator(loc, ConstraintLocator::DynamicCallable));

    if (recordFix(fix, FixImpact::InvalidReference))
      return SolutionKind::Error;

    recordPotentialHole(tv);
    recordAnyTypeVarAsPotentialHole(func1);

    return SolutionKind::Solved;
  }

  addOverloadSet(tv, choices, DC, loc);

  // Create a type variable for the argument to the `dynamicallyCall` method.
  auto tvParam = createTypeVariable(loc, TVO_CanBindToNoEscape);
  AnyFunctionType *funcType =
    FunctionType::get({ AnyFunctionType::Param(tvParam) }, func1->getResult());
  addConstraint(ConstraintKind::DynamicCallableApplicableFunction,
                funcType, tv, locator);

  // Get argument type for the `dynamicallyCall` method.
  Type argumentType;
  if (!useKwargsMethod) {
    auto arrayLitProto =
      ctx.getProtocol(KnownProtocolKind::ExpressibleByArrayLiteral);
    addConstraint(ConstraintKind::ConformsTo, tvParam,
                  arrayLitProto->getDeclaredInterfaceType(), locator);
    auto elementAssocType = arrayLitProto->getAssociatedType(
        ctx.Id_ArrayLiteralElement);
    argumentType = DependentMemberType::get(tvParam, elementAssocType);
  } else {
    auto dictLitProto =
      ctx.getProtocol(KnownProtocolKind::ExpressibleByDictionaryLiteral);
    addConstraint(ConstraintKind::ConformsTo, tvParam,
                  dictLitProto->getDeclaredInterfaceType(), locator);
    auto valueAssocType = dictLitProto->getAssociatedType(ctx.Id_Value);
    argumentType = DependentMemberType::get(tvParam, valueAssocType);
  }

  // Argument type can default to `Any`.
  addConstraint(ConstraintKind::Defaultable, argumentType,
                ctx.getAnyExistentialType(), locator);

  auto *baseArgLoc = getConstraintLocator(
      loc->getAnchor(),
      {ConstraintLocator::DynamicCallable, ConstraintLocator::ApplyArgument},
      /*summaryFlags=*/0);

  // All dynamic call parameter types must be convertible to the argument type.
  for (auto i : indices(func1->getParams())) {
    auto param = func1->getParams()[i];
    auto paramType = param.getPlainType();

    addConstraint(
        ConstraintKind::ArgumentConversion, paramType, argumentType,
        getConstraintLocator(baseArgLoc, LocatorPathElt::ApplyArgToParam(
                                             i, 0, param.getParameterFlags())));
  }

  return SolutionKind::Solved;
}

static bool hasUnresolvedPackVars(Type type) {
  // We can't compute a reduced shape if the input type still
  // contains type variables that might bind to pack archetypes
  // or pack expansions.
  SmallPtrSet<TypeVariableType *, 2> typeVars;
  type->getTypeVariables(typeVars);
  return llvm::any_of(typeVars, [](const TypeVariableType *typeVar) {
    return typeVar->getImpl().canBindToPack() ||
           typeVar->getImpl().isPackExpansion();
  });
}

ConstraintSystem::SolutionKind ConstraintSystem::simplifyShapeOfConstraint(
    Type shapeTy, Type packTy, TypeMatchOptions flags,
    ConstraintLocatorBuilder locator) {
  // Recursively replace all type variables with fixed bindings if
  // possible.
  packTy = simplifyType(packTy, flags);

  auto formUnsolved = [&]() {
    // If we're supposed to generate constraints, do so.
    if (flags.contains(TMF_GenerateConstraints)) {
      auto *shapeOf = Constraint::create(
          *this, ConstraintKind::ShapeOf, shapeTy, packTy,
          getConstraintLocator(locator));

      addUnsolvedConstraint(shapeOf);
      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  };

  // Don't try computing the shape of a type variable.
  if (packTy->isTypeVariableOrMember())
    return formUnsolved();

  // We can't compute a reduced shape if the input type still
  // contains type variables that might bind to pack archetypes
  // or pack expansions.
  SmallPtrSet<TypeVariableType *, 2> typeVars;
  packTy->getTypeVariables(typeVars);
  for (auto *typeVar : typeVars) {
    if (typeVar->getImpl().canBindToPack() ||
        typeVar->getImpl().isPackExpansion())
      return formUnsolved();
  }

  if (packTy->hasPlaceholder()) {
    if (!shouldAttemptFixes())
      return SolutionKind::Error;

    recordTypeVariablesAsHoles(shapeTy);
    return SolutionKind::Solved;
  }

  if (isSingleUnlabeledPackExpansionTuple(packTy)) {
    auto *packVar = addMaterializePackExpansionConstraint(packTy, locator);
    addConstraint(ConstraintKind::ShapeOf, shapeTy, packVar, locator);
    return SolutionKind::Solved;
  }

  // Map element archetypes to the pack context to check for equality.
  if (packTy->hasElementArchetype()) {
    auto *packEnv = DC->getGenericEnvironmentOfContext();
    packTy = packEnv->mapElementTypeIntoPackContext(packTy);
  }

  auto shape = packTy->getReducedShape();
  addConstraint(ConstraintKind::Bind, shapeTy, shape, locator);
  return SolutionKind::Solved;
}

ConstraintSystem::SolutionKind ConstraintSystem::simplifySameShapeConstraint(
    Type type1, Type type2, TypeMatchOptions flags,
    ConstraintLocatorBuilder locator) {
  type1 = simplifyType(type1);
  type2 = simplifyType(type2);

  auto formUnsolved = [&]() {
    // If we're supposed to generate constraints, do so.
    if (flags.contains(TMF_GenerateConstraints)) {
      auto *sameShape =
          Constraint::create(*this, ConstraintKind::SameShape, type1, type2,
                             getConstraintLocator(locator));

      addUnsolvedConstraint(sameShape);
      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  };

  if (hasUnresolvedPackVars(type1) || hasUnresolvedPackVars(type2))
    return formUnsolved();

  auto shape1 = type1->getReducedShape();
  auto shape2 = type2->getReducedShape();

  if (shape1->isEqual(shape2))
    return SolutionKind::Solved;

  if (shouldAttemptFixes()) {
    // If there are placeholders involved shape mismatches are most
    // likely just a symptom of some other issue i.e. type mismatch.
    if (type1->hasPlaceholder() || type2->hasPlaceholder())
      return SolutionKind::Solved;

    auto recordShapeFix = [&](ConstraintFix *fix,
                              FixImpact impact) -> SolutionKind {
      return recordFix(fix, impact) ? SolutionKind::Error
                                    : SolutionKind::Solved;
    };

    auto recordShapeMismatchFix = [&]() -> SolutionKind {
      auto impact = FixImpact::Mismatch;
      if (locator.endsWith<LocatorPathElt::AnyRequirement>())
        impact = assessRequirementFailureImpact(*this, shape1, locator);

      return recordShapeFix(
          SkipSameShapeRequirement::create(*this, type1, type2,
                                           getConstraintLocator(locator)),
          impact);
    };

    // Let's check whether we can produce a tailored fix for argument/parameter
    // mismatches.
    if (locator.endsWith<LocatorPathElt::PackShape>()) {
      SmallVector<LocatorPathElt> path;
      auto anchor = locator.getLocatorParts(path);

      // Drop `PackShape`
      path.pop_back();

      // Tailed diagnostics for argument/parameter mismatches - there
      // are either missing or extra arguments.
      if (path.size() > 0 &&
          path[path.size() - 1].is<LocatorPathElt::ApplyArgToParam>()) {
        auto &ctx = getASTContext();

        auto *loc = getConstraintLocator(anchor, path);
        auto argLoc =
            loc->castLastElementTo<LocatorPathElt::ApplyArgToParam>();

        if (type1->is<PackArchetypeType>() &&
            type2->is<PackArchetypeType>())
          return recordShapeMismatchFix();

        auto numArgs = (shape1->is<PackType>()
                        ? shape1->castTo<PackType>()->getNumElements()
                        : 1);
        auto numParams = (shape2->is<PackType>()
                          ? shape2->castTo<PackType>()->getNumElements()
                          : 1);

        // Tailed diagnostic to explode tuples.
        // FIXME: This is very similar to
        // 'cannot_convert_single_tuple_into_multiple_arguments'; can we emit
        // both of these in the same place?
        if (numArgs == 1) {
          if (type1->is<TupleType>() &&
              numParams >= 1) {
            return recordShapeFix(
                DestructureTupleToMatchPackExpansionParameter::create(
                    *this,
                    (type2->is<PackType>()
                         ? type2->castTo<PackType>()
                         : PackType::getSingletonPackExpansion(type2)),
                    loc),
                FixImpact::TypeMismatch * numParams);
          }
        }

        // Drops `ApplyArgToParam` and left with `ApplyArgument`.
        path.pop_back();

        auto *argListLoc = getConstraintLocator(anchor, path);

        // Missing arguments.
        if (numParams > numArgs) {
          SmallVector<SynthesizedArg> synthesizedArgs;
          for (unsigned i = 0, n = numParams - numArgs; i != n; ++i) {
            auto eltTy = shape2->castTo<PackType>()->getElementType(i);
            synthesizedArgs.push_back(SynthesizedArg{
                argLoc.getParamIdx(), AnyFunctionType::Param(eltTy)});
          }

          return recordShapeFix(
              AddMissingArguments::create(*this, synthesizedArgs, argListLoc),
              FixImpact::TypeMismatch * synthesizedArgs.size());
        } else {
          auto argIdx = argLoc.getArgIdx() + numParams;
          SmallVector<std::pair<unsigned, AnyFunctionType::Param>, 4>
              extraneousArgs;

          for (unsigned i = 0, n = numArgs - numParams; i != n; ++i) {
            extraneousArgs.push_back(
                {argIdx + i, AnyFunctionType::Param(ctx.TheEmptyTupleType)});
          }

          auto overload = findSelectedOverloadFor(getCalleeLocator(argListLoc));
          if (!overload)
            return SolutionKind::Error;

          return recordShapeFix(
              RemoveExtraneousArguments::create(
                  *this, overload->openedType->castTo<FunctionType>(),
                  extraneousArgs, argListLoc),
              FixImpact::TypeMismatch * extraneousArgs.size());
        }
      }
    }

    return recordShapeMismatchFix();
  }

  return SolutionKind::Error;
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyMaterializePackExpansionConstraint(
    Type type1, Type type2, TypeMatchOptions flags,
    ConstraintLocatorBuilder locator) {
  auto formUnsolved = [&]() {
    // If we're supposed to generate constraints, do so.
    if (flags.contains(TMF_GenerateConstraints)) {
      auto *explictGenericArgs =
          Constraint::create(*this, ConstraintKind::MaterializePackExpansion,
                             type1, type2, getConstraintLocator(locator));

      addUnsolvedConstraint(explictGenericArgs);
      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  };

  type1 = simplifyType(type1);
  if (type1->hasTypeVariable()) {
    return formUnsolved();
  }
  if (auto patternType =
          getPatternTypeOfSingleUnlabeledPackExpansionTuple(type1)) {
    addConstraint(ConstraintKind::Equal, patternType, type2, locator);
    return SolutionKind::Solved;
  }

  return SolutionKind::Error;
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyExplicitGenericArgumentsConstraint(
    Type type1, Type type2, TypeMatchOptions flags,
    ConstraintLocatorBuilder locator) {
  auto formUnsolved = [&]() {
    // If we're supposed to generate constraints, do so.
    if (flags.contains(TMF_GenerateConstraints)) {
      auto *explictGenericArgs =
          Constraint::create(*this, ConstraintKind::ExplicitGenericArguments,
                             type1, type2, getConstraintLocator(locator));

      addUnsolvedConstraint(explictGenericArgs);
      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  };

  // Bail out if we haven't selected an overload yet.
  auto simplifiedBoundType = simplifyType(type1, flags);
  if (simplifiedBoundType->isPlaceholder())
    return SolutionKind::Solved;
  if (simplifiedBoundType->isTypeVariableOrMember())
    return formUnsolved();

  std::function<GenericParamList *(ValueDecl *)> getGenericParams =
      [&](ValueDecl *decl) -> GenericParamList * {
    auto genericContext = decl->getAsGenericContext();
    if (!genericContext)
      return nullptr;

    auto genericParams = genericContext->getGenericParams();
    if (!genericParams) {
      // If declaration is a non-generic typealias, let's point
      // to the underlying generic declaration.
      if (auto *TA = dyn_cast<TypeAliasDecl>(decl)) {
        if (auto *UGT = TA->getUnderlyingType()->getAs<AnyGenericType>())
          return getGenericParams(UGT->getDecl());
      }
    }

    return genericParams;
  };

  auto fixInvalidSpecialization = [&](ValueDecl *decl) -> SolutionKind {
    if (isa<AbstractFunctionDecl>(decl)) {
      return recordFix(AllowFunctionSpecialization::create(
                 *this, decl, getConstraintLocator(locator)))
                 ? SolutionKind::Error
                 : SolutionKind::Solved;
    }

    // Allow concrete macros to have specializations with just a warning.
    return recordFix(AllowConcreteTypeSpecialization::create(
               *this, type1, decl, getConstraintLocator(locator),
               isa<MacroDecl>(decl) ? FixBehavior::DowngradeToWarning
                                    : FixBehavior::Error))
               ? SolutionKind::Error
               : SolutionKind::Solved;
  };

  ValueDecl *decl;
  SmallVector<OpenedType, 2> openedTypes;
  if (auto *bound = dyn_cast<TypeAliasType>(type1.getPointer())) {
    decl = bound->getDecl();
    for (auto argType : bound->getDirectGenericArgs()) {
      auto *typeVar = argType->getAs<TypeVariableType>();
      if (!typeVar)
        return SolutionKind::Error;
      auto *genericParam = typeVar->getImpl().getGenericParameter();
      openedTypes.push_back({genericParam, typeVar});
    }
  } else if (locator.directlyAt<TypeExpr>()) {
    auto *BGT = type1->getAs<BoundGenericType>();
    if (!BGT)
      return SolutionKind::Error;

    decl = BGT->getDecl();

    auto genericParams = BGT->getDecl()->getInnermostGenericParamTypes();
    if (genericParams.size() != BGT->getGenericArgs().size())
      return SolutionKind::Error;

    for (unsigned i = 0, n = genericParams.size(); i != n; ++i) {
      auto argType = BGT->getGenericArgs()[i];
      if (auto *typeVar = argType->getAs<TypeVariableType>()) {
        openedTypes.push_back({genericParams[i], typeVar});
      } else {
        // If we have a concrete substitution then we need to create
        // a new type variable to be able to add it to the list as-if
        // it is opened generic parameter type.
        auto *GP = genericParams[i];

        unsigned options = TVO_CanBindToNoEscape;
        if (GP->isParameterPack())
          options |= TVO_CanBindToPack;

        auto *argVar = createTypeVariable(
            getConstraintLocator(locator, LocatorPathElt::GenericArgument(i)),
            options);
        addConstraint(ConstraintKind::Bind, argVar, argType, locator);
        openedTypes.push_back({GP, argVar});
      }
    }
  } else {
    // If the overload hasn't been resolved, we can't simplify this constraint.
    auto overloadLocator = getCalleeLocator(getConstraintLocator(locator));

    // If there was a problem resolving specialization expression
    // it would be diagnosted as invalid AST node.
    if (overloadLocator->directlyAt<ErrorExpr>()) {
      return shouldAttemptFixes() ? SolutionKind::Error : SolutionKind::Solved;
    }

    auto selectedOverload = findSelectedOverloadFor(overloadLocator);
    if (!selectedOverload)
      return formUnsolved();

    auto overloadChoice = selectedOverload->choice;
    if (!overloadChoice.isDecl()) {
      return SolutionKind::Error;
    }

    decl = overloadChoice.getDecl();

    auto openedOverloadTypes = getOpenedTypes(overloadLocator);
    // Attempting to specialize a non-generic declaration.
    if (openedOverloadTypes.empty()) {
      // Note that this is unconditional because the fix is
      // downgraded to a warning in swift language modes < 6.
      return fixInvalidSpecialization(decl);
    }

    auto genericParams = getGenericParams(decl);
    if (genericParams) {
      for (auto gp : *genericParams) {
        auto found = find_if(openedOverloadTypes, [&](auto entry) {
          return entry.first->getDepth() == gp->getDepth() &&
              entry.first->getIndex() == gp->getIndex();
        });
        assert(found != openedOverloadTypes.end());
        openedTypes.push_back(*found);
      }
    }
  }

  auto genericParams = getGenericParams(decl);
  if (!decl->getAsGenericContext() || !genericParams)
    return fixInvalidSpecialization(decl);

  // Map the generic parameters we have over to their opened types.
  bool hasParameterPack = false;
  SmallVector<Type, 2> openedGenericParams;
  auto genericParamDepth = genericParams->getParams()[0]->getDepth();
  for (const auto &openedType : openedTypes) {
    if (openedType.first->getDepth() == genericParamDepth) {
      // A generic argument list containing pack references expects
      // those packs to be wrapped in pack expansion types. If this
      // opened type represents the generic argument for a parameter
      // pack, wrap generate the appropriate shape constraints and
      // add a pack expansion to the argument list.
      if (openedType.first->isParameterPack()) {
        auto patternType = openedType.second;
        auto *shapeLoc = getConstraintLocator(
            locator.withPathElement(ConstraintLocator::PackShape));
        auto *shapeType = createTypeVariable(shapeLoc,
                                            TVO_CanBindToPack |
                                            TVO_CanBindToHole);
        addConstraint(ConstraintKind::ShapeOf,
                      shapeType, patternType, shapeLoc);

        auto *expansion = PackExpansionType::get(patternType, shapeType);
        openedGenericParams.push_back(expansion);
        hasParameterPack = true;
      } else {
        openedGenericParams.push_back(Type(openedType.second));
      }
    }
  }

  // FIXME: We could support explicit function specialization.
  if (openedGenericParams.empty() ||
      (isa<AbstractFunctionDecl>(decl) && !hasParameterPack)) {
    return recordFix(AllowFunctionSpecialization::create(
               *this, decl, getConstraintLocator(locator)))
               ? SolutionKind::Error
               : SolutionKind::Solved;
  }

  assert(openedGenericParams.size() == genericParams->size());

  // Match the opened generic parameters to the specialized arguments.
  auto specializedArgs = type2->castTo<PackType>()->getElementTypes();
  PackMatcher matcher(openedGenericParams, specializedArgs, getASTContext(),
                      isPackExpansionType);
  if (matcher.match()) {
    if (!shouldAttemptFixes())
      return SolutionKind::Error;

    auto *fix = IgnoreGenericSpecializationArityMismatch::create(
        *this, decl, openedGenericParams.size(), specializedArgs.size(),
        hasParameterPack, getConstraintLocator(locator));
    return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
  }

  // Bind the opened generic parameters to the specialization arguments.
  for (const auto &pair : matcher.pairs) {
    addConstraint(
        ConstraintKind::Bind, pair.lhs, pair.rhs,
        getConstraintLocator(
            locator, LocatorPathElt::GenericArgument(pair.lhsIdx)));
  }

  return SolutionKind::Solved;
}

ConstraintSystem::SolutionKind 
ConstraintSystem::simplifyLValueObjectConstraint(
    Type type1, Type type2, TypeMatchOptions flags,
    ConstraintLocatorBuilder locator) {
  auto lvalueTy = simplifyType(type1);

  auto formUnsolved = [&]() {
    // If we're supposed to generate constraints, do so.
    if (flags.contains(TMF_GenerateConstraints)) {
      auto *lvalueObject =
          Constraint::create(*this, ConstraintKind::LValueObject,
                             type1, type2, getConstraintLocator(locator));

      addUnsolvedConstraint(lvalueObject);
      return SolutionKind::Solved;
    }

    return SolutionKind::Unsolved;
  };

  auto isOrCanBeLValueType = [](Type type) {
    if (auto *typeVar = type->getAs<TypeVariableType>()) {
      return typeVar->getImpl().canBindToLValue();
    }
    return type->is<LValueType>();
  };

  if (lvalueTy->isPlaceholder()) {
    if (!shouldAttemptFixes())
      return SolutionKind::Error;

    recordAnyTypeVarAsPotentialHole(type2);
    return SolutionKind::Solved;
  }

  if (!isOrCanBeLValueType(lvalueTy)) {
    if (!shouldAttemptFixes())
      return SolutionKind::Error;

    auto *fixLoc = getConstraintLocator(locator);
    if (recordFix(TreatRValueAsLValue::create(*this, fixLoc),
                  TreatRValueAsLValue::assessImpact(*this, fixLoc)))
      return SolutionKind::Error;

    lvalueTy = LValueType::get(lvalueTy);
  }

  if (lvalueTy->isTypeVariableOrMember())
    return formUnsolved();

  // TODO: This operation deserves its own locator just like OptionalObject.
  addConstraint(ConstraintKind::Equal,
                lvalueTy->castTo<LValueType>()->getObjectType(), type2,
                getConstraintLocator(locator));
  return SolutionKind::Solved;
}

static llvm::PointerIntPair<Type, 3, unsigned>
getBaseTypeForPointer(TypeBase *type) {
  unsigned unwrapCount = 0;
  while (auto objectTy = type->getOptionalObjectType()) {
    type = objectTy.getPointer();
    ++unwrapCount;
  }

  auto pointeeTy = type->getAnyPointerElementType();
  assert(pointeeTy);
  return {pointeeTy, unwrapCount};
}

void ConstraintSystem::addRestrictedConstraint(
                             ConstraintKind kind,
                             ConversionRestrictionKind restriction,
                             Type first, Type second,
                             ConstraintLocatorBuilder locator) {
  (void)simplifyRestrictedConstraint(restriction, first, second, kind,
                                     TMF_GenerateConstraints, locator);
}

/// Given that we have a conversion constraint between two types, and
/// that the given constraint-reduction rule applies between them at
/// the top level, apply it and generate any necessary recursive
/// constraints.
ConstraintSystem::SolutionKind
ConstraintSystem::simplifyRestrictedConstraintImpl(
                                         ConversionRestrictionKind restriction,
                                         Type type1, Type type2,
                                         ConstraintKind matchKind,
                                         TypeMatchOptions flags,
                                         ConstraintLocatorBuilder locator) {
  assert(!type1->isTypeVariableOrMember() && !type2->isTypeVariableOrMember());

  // Add to the score based on context.
  auto addContextualScore = [&] {
    // Okay, we need to perform one or more conversions.  If this
    // conversion will cause a function conversion, score it as worse.
    // This induces conversions to occur within closures instead of
    // outside of them wherever possible.
    if (locator.isFunctionConversion()) {
      // This conversion exists only to check adjustments in the member
      // type, so the fact that adjustments also cause a function conversion
      // is unrelated.
      if (locator.isForExistentialMemberAccessConversion())
        return;

      increaseScore(SK_FunctionConversion, locator);
    }
  };

  TypeMatchOptions subflags = getDefaultDecompositionOptions(flags);

  auto matchPointerBaseTypes =
      [&](llvm::PointerIntPair<Type, 3, unsigned> baseType1,
          llvm::PointerIntPair<Type, 3, unsigned> baseType2) -> SolutionKind {
    if (restriction != ConversionRestrictionKind::PointerToPointer)
      increaseScore(ScoreKind::SK_ValueToPointerConversion, locator);

    // Since this match doesn't add any custom path elements, let's drop
    // `OptionalInjection` to avoid confusing generic argument matching.
    // This is important, for example, in situations like
    // `inout T` -> `UnsafePointer<U>?` in argument positions,
    // `inout T` would be "injected" into optional by unwrapping `UnsafePointer`
    // so the only match that matters here is pointers and their
    // `Pointee` generic parameters.
    if (shouldAttemptFixes()) {
      if (locator.endsWith<LocatorPathElt::OptionalInjection>()) {
        SmallVector<LocatorPathElt, 2> path;
        auto anchor = locator.getLocatorParts(path);

        while (!path.empty() &&
               path.back().is<LocatorPathElt::OptionalInjection>()) {
          path.pop_back();
        }

        locator = getConstraintLocator(anchor, path);
      }
    }

    auto result =
        matchTypes(baseType1.getPointer(), baseType2.getPointer(),
                   ConstraintKind::BindToPointerType, subflags, locator);

    if (!(result == SolutionKind::Error && shouldAttemptFixes()))
      return result;

    BoundGenericType *ptr1 = nullptr;
    BoundGenericType *ptr2 = nullptr;

    switch (restriction) {
    case ConversionRestrictionKind::ArrayToPointer:
    case ConversionRestrictionKind::InoutToPointer: {
      ptr2 = type2->lookThroughAllOptionalTypes()->castTo<BoundGenericType>();
      ptr1 = BoundGenericType::get(ptr2->getDecl(), ptr2->getParent(),
                                   {baseType1.getPointer()});
      break;
    }

    case ConversionRestrictionKind::PointerToPointer:
      // Original types could be wrapped into a different number of optional.
      ptr1 = type1->lookThroughAllOptionalTypes()->castTo<BoundGenericType>();
      ptr2 = type2->lookThroughAllOptionalTypes()->castTo<BoundGenericType>();
      break;

    default:
      return SolutionKind::Error;
    }

    auto *fix = GenericArgumentsMismatch::create(*this, ptr1, ptr2, {0},
                                                 getConstraintLocator(locator));

    // Treat this as a contextual type mismatch.
    auto baseImpact = FixImpact::TypeMismatch;
    // It's possible to implicitly promote pointer into an optional
    // before matching base types if other side is an optional, so
    // score needs to account for number of such promotions.
    int optionalWraps = baseType2.getInt() - baseType1.getInt();
    return recordFix(fix, baseImpact + std::abs(optionalWraps))
               ? SolutionKind::Error
               : SolutionKind::Solved;
  };

  auto fixContextualFailure = [&](Type fromType, Type toType,
                                  ConstraintLocatorBuilder locator) -> bool {
    auto *loc = getConstraintLocator(locator);
    // Since this is a contextual type mismatch, let's start from higher
    // impact than regular fix to avoid ambiguities.
    auto impact = FixImpact::TypeMismatch;
    if (loc->isForAssignment() || loc->isForCoercion() ||
        loc->isForContextualType() ||
        loc->isLastElement<LocatorPathElt::ApplyArgToParam>() ||
        loc->isLastElement<LocatorPathElt::UnresolvedMemberChainResult>() ||
        loc->isForOptionalTry()) {
      if (restriction == ConversionRestrictionKind::Superclass) {
        if (auto *fix = CoerceToCheckedCast::attempt(
                *this, fromType, toType, /*useConditionalCast*/ false, loc))
          return !recordFix(fix, impact);
      }
      
      // We already have a fix for this locator indicating a
      // tuple mismatch.
      if (hasFixFor(loc, FixKind::AllowTupleTypeMismatch))
        return true;

      if (restriction == ConversionRestrictionKind::ValueToOptional) {
        // If this is an optional injection we can drop optional from
        // "to" type since it's not significant for the diagnostic.
        toType = toType->getOptionalObjectType();
      }

      ConstraintFix *fix = nullptr;
      if (loc->isLastElement<LocatorPathElt::ApplyArgToParam>()) {
        fix = AllowArgumentMismatch::create(*this, fromType, toType, loc);
      } else if (loc->isForAssignment()) {
        fix = IgnoreAssignmentDestinationType::create(*this, fromType, toType,
                                                      loc);
      } else {
        fix = ContextualMismatch::create(*this, fromType, toType, loc);
      }

      assert(fix);

      if (recordFix(fix, impact))
        return false;

      // If we are trying to check whether "from" key path is a subclass of
      // of a "to" key path, the problem here is either mutablity or erasure
      // and we should still try to match their root and value types top help
      // inference.
      if (restriction == ConversionRestrictionKind::Superclass) {
        if (fromType->isKnownKeyPathType() && toType->isKnownKeyPathType() &&
            !fromType->isAnyKeyPath()) {
          auto fromKeyPath = fromType->castTo<BoundGenericType>();
          auto toKeyPath = toType->castTo<BoundGenericType>();

          auto flags = subflags;
          flags |= TMF_ApplyingFix;
          flags |= TMF_MatchingGenericArguments;

          (void)matchDeepTypeArguments(*this, flags,
                                       fromKeyPath->getGenericArgs(),
                                       toKeyPath->getGenericArgs(), locator);
        }
      }

      return true;
    }

    return false;
  };

  switch (restriction) {
  // for $< in { <, <c, <oc }:
  //   T_i $< U_i ===> (T_i...) $< (U_i...)
  case ConversionRestrictionKind::DeepEquality:
    return matchDeepEqualityTypes(type1, type2, locator);

  case ConversionRestrictionKind::Superclass: {
    addContextualScore();

    auto result = matchSuperclassTypes(type1, type2, subflags, locator);

    if (!(shouldAttemptFixes() && result == SolutionKind::Error))
      return result;

    return fixContextualFailure(type1, type2, locator)
               ? SolutionKind::Solved
               : SolutionKind::Error;
  }

  // for $< in { <, <c, <oc }:
  //   T $< U, U : P_i ===> T $< protocol<P_i...>
  case ConversionRestrictionKind::Existential:
    addContextualScore();
    return matchExistentialTypes(type1, type2,
                                 ConstraintKind::Subtype,
                                 subflags, locator);

  // for $< in { <, <c, <oc }:
  //   for P protocol, Q protocol,
  //     P : Q ===> T.Protocol $< Q.Type
  //   for P protocol, Q protocol,
  //     P $< Q ===> P.Type $< Q.Type
  case ConversionRestrictionKind::MetatypeToExistentialMetatype: {
    addContextualScore();

    auto instanceTy1 = type1->getMetatypeInstanceType();
    auto instanceTy2 = type2->getMetatypeInstanceType();

    auto result = matchExistentialTypes(
        instanceTy1, instanceTy2, ConstraintKind::ConformsTo, subflags,
        locator.withPathElement(ConstraintLocator::InstanceType));

    if (!(shouldAttemptFixes() && result == SolutionKind::Error))
      return result;

    return fixContextualFailure(type1, type2, locator)
               ? SolutionKind::Solved
               : SolutionKind::Error;
  }

  // for $< in { <, <c, <oc }:
  //   for P protocol, C class, D class,
  //     (P & C) : D ===> (P & C).Type $< D.Type
  case ConversionRestrictionKind::ExistentialMetatypeToMetatype: {
    addContextualScore();

    auto instance1 = type1->castTo<ExistentialMetatypeType>()->getInstanceType();
    auto instance2 = type2->castTo<MetatypeType>()->getInstanceType();
    auto superclass1 = instance1->getSuperclass();

    if (!superclass1)
      return SolutionKind::Error;

    auto result =
        matchTypes(superclass1, instance2, ConstraintKind::Subtype, subflags,
                   locator.withPathElement(ConstraintLocator::InstanceType));

    if (!(shouldAttemptFixes() && result == SolutionKind::Error))
      return result;

    return fixContextualFailure(type1, type2, locator)
               ? SolutionKind::Solved
               : SolutionKind::Error;
  }
  // for $< in { <, <c, <oc }:
  //   T $< U ===> T $< U?
  case ConversionRestrictionKind::ValueToOptional: {
    addContextualScore();
    increaseScore(SK_ValueToOptional, locator);

    assert(matchKind >= ConstraintKind::Subtype);
    if (auto generic2 = type2->getAs<BoundGenericType>()) {
      if (generic2->getDecl()->isOptionalDecl()) {
        auto result = matchTypes(
            type1, generic2->getGenericArgs()[0], matchKind, subflags,
            locator.withPathElement(ConstraintLocator::OptionalInjection));

        if (!(shouldAttemptFixes() && result == SolutionKind::Error))
          return result;
      }
    }

    return shouldAttemptFixes() && fixContextualFailure(type1, type2, locator)
               ? SolutionKind::Solved
               : SolutionKind::Error;
  }

  // for $< in { <, <c, <oc }:
  //   T $< U ===> T? $< U?
  //   T $< U ===> T! $< U!
  //   T $< U ===> T! $< U?
  // also:
  //   T <c U ===> T? <c U!
  case ConversionRestrictionKind::OptionalToOptional: {
    addContextualScore();

    assert(matchKind >= ConstraintKind::Subtype);
    if (auto generic1 = type1->getAs<BoundGenericType>()) {
      if (auto generic2 = type2->getAs<BoundGenericType>()) {
        if (generic1->getDecl()->isOptionalDecl() &&
            generic2->getDecl()->isOptionalDecl()) {
          auto result = matchTypes(
              generic1->getGenericArgs()[0], generic2->getGenericArgs()[0],
              matchKind, subflags,
              locator.withPathElement(LocatorPathElt::GenericArgument(0)));

          if (!(shouldAttemptFixes() && result == SolutionKind::Error))
            return result;
        }
      }
    }

    return shouldAttemptFixes() && fixContextualFailure(type1, type2, locator)
               ? SolutionKind::Solved
               : SolutionKind::Error;
  }

  case ConversionRestrictionKind::ClassMetatypeToAnyObject:
  case ConversionRestrictionKind::ExistentialMetatypeToAnyObject:
  case ConversionRestrictionKind::ProtocolMetatypeToProtocolClass: {
    // Nothing more to solve.
    addContextualScore();
    return SolutionKind::Solved;
  }
  
  // T <p U ===> T[] <a UnsafeMutablePointer<U>
  case ConversionRestrictionKind::ArrayToPointer: {
    addContextualScore();
    // Unwrap an inout type.
    auto obj1 = type1->getInOutObjectType();

    obj1 = getFixedTypeRecursive(obj1, false);
    
    auto t2 = type2->getDesugaredType();

    auto baseType1 = getFixedTypeRecursive(obj1->getArrayElementType(), false);
    auto ptr2 = getBaseTypeForPointer(t2);

    increaseScore(SK_ValueToOptional, locator, ptr2.getInt());

    return matchPointerBaseTypes({baseType1, 0}, ptr2);
  }

  // String ===> UnsafePointer<[U]Int8>
  case ConversionRestrictionKind::StringToPointer: {
    addContextualScore();

    auto ptr2 = getBaseTypeForPointer(type2->getDesugaredType());

    increaseScore(SK_ValueToOptional, locator, ptr2.getInt());

    // The pointer element type must be void or a byte-sized type.
    // TODO: Handle different encodings based on pointer element type, such as
    // UTF16 for [U]Int16 or UTF32 for [U]Int32. For now we only interop with
    // Int8 pointers using UTF8 encoding.
    auto baseType2 = getFixedTypeRecursive(ptr2.getPointer(), false);
    // If we haven't resolved the element type, generate constraints.
    if (baseType2->isTypeVariableOrMember()) {
      if (flags.contains(TMF_GenerateConstraints)) {
        increaseScore(ScoreKind::SK_ValueToPointerConversion, locator);

        auto &ctx = getASTContext();
        auto int8Con = Constraint::create(*this, ConstraintKind::Bind,
                                          baseType2,
                                          ctx.getInt8Type(),
                                          getConstraintLocator(locator));
        auto uint8Con = Constraint::create(*this, ConstraintKind::Bind,
                                           baseType2,
                                           ctx.getUInt8Type(),
                                           getConstraintLocator(locator));
        auto voidCon = Constraint::create(*this, ConstraintKind::Bind,
                                          baseType2, ctx.TheEmptyTupleType,
                                          getConstraintLocator(locator));
        
        Constraint *disjunctionChoices[] = {int8Con, uint8Con, voidCon};
        addDisjunctionConstraint(disjunctionChoices, locator);
        return SolutionKind::Solved;
      }

      return SolutionKind::Unsolved;
    }
    
    if (!isStringCompatiblePointerBaseType(getASTContext(), baseType2)) {
      return SolutionKind::Error;
    }

    increaseScore(ScoreKind::SK_ValueToPointerConversion, locator);
    return SolutionKind::Solved;
  }
      
  // T <p U ===> inout T <a UnsafeMutablePointer<U>
  case ConversionRestrictionKind::InoutToPointer: {
    addContextualScore();

    auto t2 = type2->getDesugaredType();

    auto baseType1 = type1->getInOutObjectType();
    auto ptr2 = getBaseTypeForPointer(t2);

    increaseScore(SK_ValueToOptional, locator, ptr2.getInt());

    return matchPointerBaseTypes({baseType1, 0}, ptr2);
  }
      
  // T <p U ===> UnsafeMutablePointer<T> <a UnsafeMutablePointer<U>
  case ConversionRestrictionKind::PointerToPointer: {
    auto t1 = type1->getDesugaredType();
    auto t2 = type2->getDesugaredType();

    auto ptr1 = getBaseTypeForPointer(t1);
    auto ptr2 = getBaseTypeForPointer(t2);

    return matchPointerBaseTypes(ptr1, ptr2);
  }

  case ConversionRestrictionKind::PointerToCPointer:
    return simplifyPointerToCPointerRestriction(type1, type2, flags, locator);

  case ConversionRestrictionKind::ArrayToCPointer: {
    auto ptr2 = type2->getDesugaredType()->lookThroughAllOptionalTypes();

    PointerTypeKind pointerKind;
    auto cPtr = ptr2->getAnyPointerElementType(pointerKind);

    // If the parameter is a raw pointer or its element type is not a
    // supported (un-)signed integer it implies a regular ArrayToPointer
    // conversion.
    if (isRawPointerKind(pointerKind) ||
        !(cPtr->isInt()   || cPtr->isUInt()   ||
          cPtr->isInt8()  || cPtr->isUInt8()  ||
          cPtr->isInt16() || cPtr->isUInt16() ||
          cPtr->isInt32() || cPtr->isUInt32() ||
          cPtr->isInt64() || cPtr->isUInt64())) {
      return SolutionKind::Error;
    }

    increaseScore(SK_ValueToPointerConversion, locator);

    type1 = getFixedTypeRecursive(type1->getInOutObjectType()->getArrayElementType(),
                                  /*wantRValue=*/false);
    LLVM_FALLTHROUGH;
  }

  case ConversionRestrictionKind::InoutToCPointer: {
    SmallVector<Type, 2> optionals;

    auto ptr2 =
        type2->getDesugaredType()->lookThroughAllOptionalTypes(optionals);

    increaseScore(SK_ValueToOptional, locator, optionals.size());

    PointerTypeKind pointerKind;
    (void)ptr2->getAnyPointerElementType(pointerKind);

    auto baseType1 = type1->getInOutObjectType();

    Type ptr1;
    // The right-hand size is a raw pointer, so let's use `UnsafeMutablePointer`
    // for the `inout` type.
    if (pointerKind == PTK_UnsafeRawPointer ||
        pointerKind == PTK_UnsafeMutableRawPointer) {
      ptr1 = BoundGenericType::get(Context.getUnsafeMutablePointerDecl(),
                                   /*parent=*/nullptr, {baseType1});
    } else {
      ptr1 = baseType1->wrapInPointer(pointerKind);
    }

    assert(ptr1);

    return simplifyPointerToCPointerRestriction(ptr1, ptr2, flags, locator);
  }

  // T < U or T is bridged to V where V < U ===> Array<T> <c Array<U>
  case ConversionRestrictionKind::ArrayUpcast: {
    Type baseType1 = type1->getArrayElementType();
    Type baseType2 = type2->getArrayElementType();

    increaseScore(SK_CollectionUpcastConversion, locator);
    return matchTypes(baseType1,
                      baseType2,
                      matchKind,
                      subflags,
                      locator.withPathElement(
                          LocatorPathElt::GenericArgument(0)));
  }

  // K1 < K2 && V1 < V2 || K1 bridges to K2 && V1 bridges to V2 ===> 
  //   Dictionary<K1, V1> <c Dictionary<K2, V2>
  case ConversionRestrictionKind::DictionaryUpcast: {
    auto t1 = type1->getDesugaredType();    
    Type key1, value1;
    std::tie(key1, value1) = *isDictionaryType(t1);

    auto t2 = type2->getDesugaredType();
    Type key2, value2;
    std::tie(key2, value2) = *isDictionaryType(t2);

    auto subMatchKind = matchKind; // TODO: Restrict this?
    increaseScore(SK_CollectionUpcastConversion, locator);
    // The source key and value types must be subtypes of the destination
    // key and value types, respectively.
    auto result =
        matchTypes(key1, key2, subMatchKind, subflags,
                   locator.withPathElement(LocatorPathElt::GenericArgument(0)));
    if (result == SolutionKind::Error)
      return result;

    switch (matchTypes(
        value1, value2, subMatchKind, subflags,
        locator.withPathElement(LocatorPathElt::GenericArgument(1)))) {
    case SolutionKind::Solved:
      return result;

    case SolutionKind::Unsolved:
      return SolutionKind::Unsolved;

    case SolutionKind::Error:
      return SolutionKind::Error;
    }
  }

  // T1 < T2 || T1 bridges to T2 ===> Set<T1> <c Set<T2>
  case ConversionRestrictionKind::SetUpcast: {
    Type baseType1 = *isSetType(type1);
    Type baseType2 = *isSetType(type2);

    increaseScore(SK_CollectionUpcastConversion, locator);
    return matchTypes(baseType1,
                      baseType2,
                      matchKind,
                      subflags,
                      locator.withPathElement(LocatorPathElt::GenericArgument(0)));
  }

  // T1 <c T2 && T2 : Hashable ===> T1 <c AnyHashable
  case ConversionRestrictionKind::HashableToAnyHashable: {
    // We never want to do this if the LHS is already AnyHashable.
    type1 = simplifyType(type1);
    if (type1->getRValueType()->lookThroughAllOptionalTypes()->isAnyHashable()) {
      return SolutionKind::Error;
    }

    addContextualScore();
    increaseScore(SK_UserConversion,
                  locator); // FIXME: Use separate score kind?
    if (worseThanBestSolution()) {
      return SolutionKind::Error;
    }

    auto hashableProtocol =
      getASTContext().getProtocol(KnownProtocolKind::Hashable);
    if (!hashableProtocol)
      return SolutionKind::Error;

    auto constraintLocator = getConstraintLocator(locator);
    auto tv = createTypeVariable(constraintLocator,
                                 TVO_PrefersSubtypeBinding |
                                 TVO_CanBindToNoEscape);
    
    addConstraint(ConstraintKind::ConformsTo, tv,
                  hashableProtocol->getDeclaredInterfaceType(),
                  constraintLocator);

    return matchTypes(type1, tv, ConstraintKind::Conversion, subflags,
                      locator);
  }

  // T' < U and T a toll-free-bridged to T' ===> T' <c U
  case ConversionRestrictionKind::CFTollFreeBridgeToObjC: {
    increaseScore(SK_UserConversion,
                  locator); // FIXME: Use separate score kind?
    if (worseThanBestSolution()) {
      return SolutionKind::Error;
    }

    auto nativeClass = type1->getClassOrBoundGenericClass();
    auto bridgedObjCClass
      = nativeClass->getAttrs().getAttribute<ObjCBridgedAttr>()->getObjCClass();

    return matchTypes(bridgedObjCClass->getDeclaredInterfaceType(),
                      type2, ConstraintKind::Subtype, subflags, locator);
  }

  // T < U' and U a toll-free-bridged to U' ===> T <c U
  case ConversionRestrictionKind::ObjCTollFreeBridgeToCF: {
    increaseScore(SK_UserConversion,
                  locator); // FIXME: Use separate score kind?
    if (worseThanBestSolution()) {
      return SolutionKind::Error;
    }

    auto nativeClass = type2->getClassOrBoundGenericClass();
    auto bridgedObjCClass
      = nativeClass->getAttrs().getAttribute<ObjCBridgedAttr>()->getObjCClass();

    return matchTypes(type1,
                      bridgedObjCClass->getDeclaredInterfaceType(),
                      ConstraintKind::Subtype, subflags, locator);
  }

  case ConversionRestrictionKind::DoubleToCGFloat:
  case ConversionRestrictionKind::CGFloatToDouble: {
    // Prefer CGFloat -> Double over other way araund.
    auto impact =
        restriction == ConversionRestrictionKind::CGFloatToDouble ? 2 : 10;

    // Slightly disfavor such conversions inside optionals and collections.
    SmallVector<LocatorPathElt> originalPath;
    auto anchor = locator.getLocatorParts(originalPath);

    SourceRange range;
    ArrayRef<LocatorPathElt> path(originalPath);
    simplifyLocator(anchor, path, range);

    if (!path.empty() && path.back().is<LocatorPathElt::GenericArgument>()) {
      impact += 11;
    }

    if (restriction == ConversionRestrictionKind::DoubleToCGFloat) {
      if (path.empty() || llvm::all_of(path, [](const LocatorPathElt &elt) {
            return elt.is<LocatorPathElt::OptionalInjection>();
          })) {
        if (auto *expr = getAsExpr(anchor))
          if (auto depth = getExprDepth(expr))
            impact = (*depth + 1) * impact;
      }
    } else if (locator.directlyAt<AssignExpr>() ||
               locator.endsWith<LocatorPathElt::ContextualType>()) {
      // Situations like:
      //
      //  let _: Double = <<CGFloat>>
      //  <var/property of type Double> = <<CGFloat>>
      //
      // Used to be supported due to an incorrect fix added in
      // diagnostic mode. Lower impact here means that right-hand
      // side of the assignment is allowed to maintain CGFloat
      // until the very end which minimizes the number of conversions
      // used and keeps literals as Double when possible.
      impact = 1;
    }

    increaseScore(SK_ImplicitValueConversion, locator, impact);

    if (worseThanBestSolution())
      return SolutionKind::Error;

    return SolutionKind::Solved;
  }
  }
  
  llvm_unreachable("bad conversion restriction");
}

static void increaseScoreForGenericParamPointerConversion(
    ConstraintSystem &cs, ConversionRestrictionKind kind,
    ConstraintLocatorBuilder locator) {
  switch (kind) {
  case ConversionRestrictionKind::InoutToPointer:
  case ConversionRestrictionKind::ArrayToPointer:
  case ConversionRestrictionKind::StringToPointer:
  case ConversionRestrictionKind::PointerToPointer:
    break;
  default:
    return;
  }

  auto *loc = cs.getConstraintLocator(locator);
  auto argInfo = loc->findLast<LocatorPathElt::ApplyArgToParam>();
  if (!argInfo)
    return;

  auto overload = cs.findSelectedOverloadFor(cs.getCalleeLocator(loc));
  if (!overload)
    return;

  auto *D = overload->choice.getDeclOrNull();
  if (!D)
    return;

  auto *param = getParameterAt(D, argInfo->getParamIdx());
  if (!param)
    return;

  // Check to see if the parameter is a generic parameter, or dependent member.
  // Look though optionals and pack expansions.
  auto paramTy = param->getInterfaceType();
  while (true) {
    paramTy = paramTy->lookThroughAllOptionalTypes();
    if (auto packExpansion = paramTy->getAs<PackExpansionType>()) {
      paramTy = packExpansion->getPatternType();
      continue;
    }
    // Also look through "vanishing" tuples.
    if (auto *tupleTy = paramTy->getAs<TupleType>()) {
      if (tupleTy->getNumElements() == 1 && !tupleTy->getElement(0).hasName()) {
        paramTy = tupleTy->getElement(0).getType();
        continue;
      }
    }
    break;
  }
  if (!paramTy->isTypeParameter())
    return;

  // Don't increase the score if the type parameter is rooted on the protocol
  // 'Self' type, e.g extensions on `_Pointer` shouldn't be penalized.
  if (auto *PD = D->getDeclContext()->getSelfProtocolDecl()) {
    if (PD->getSelfInterfaceType()->isEqual(paramTy->getRootGenericParam()))
      return;
  }
  cs.increaseScore(SK_GenericParamPointerConversion, locator);
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyRestrictedConstraint(
                                       ConversionRestrictionKind restriction,
                                       Type type1, Type type2,
                                       ConstraintKind matchKind,
                                       TypeMatchOptions flags,
                                       ConstraintLocatorBuilder locator) {
  switch (simplifyRestrictedConstraintImpl(restriction, type1, type2,
                                           matchKind, flags, locator)) {
  case SolutionKind::Solved: {
    // If we have an application of a non-ephemeral parameter, then record a
    // fix if we have to treat an ephemeral conversion as non-ephemeral. It's
    // important that this is solved as an independent constraint, as the
    // solving of this restriction may be required in order to evaluate it. For
    // example, when solving `foo(&.x)`, we need to first match types for the
    // inout-to-pointer conversion, which then allows us to resolve the overload
    // of `x`, which may or may not produce an ephemeral pointer.
    if (locator.isNonEphemeralParameterApplication()) {
      bool downgradeToWarning =
          !getASTContext().LangOpts.DiagnoseInvalidEphemeralnessAsError;

      auto *fix = TreatEphemeralAsNonEphemeral::create(
          *this, getConstraintLocator(locator), type1, type2, restriction,
          downgradeToWarning);
      addFixConstraint(fix, matchKind, type1, type2, locator);
    }

    // Increase the score if needed for a pointer conversion to a generic
    // parameter type.
    // FIXME: We ought to consider outright banning pointer conversions to
    // generic parameter types, in which case this logic could be adjusted to
    // record a fix instead.
    increaseScoreForGenericParamPointerConversion(*this, restriction, locator);

    addConversionRestriction(type1, type2, restriction);
    return SolutionKind::Solved;
  }
  case SolutionKind::Unsolved:
    return SolutionKind::Unsolved;

  case SolutionKind::Error:
    return SolutionKind::Error;
  }

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

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyPointerToCPointerRestriction(
    Type type1, Type type2, TypeMatchOptions flags,
    ConstraintLocatorBuilder locator) {
  bool inCorrectPosition = isArgumentOfImportedDecl(locator);
  if (!inCorrectPosition) {
    // If this is not an imported function, let's not proceed with
    // the conversion, unless in diagnostic mode.
    if (!shouldAttemptFixes())
      return SolutionKind::Error;

    // Let's attempt to convert the types and record a tailored
    // fix if that succeeds.
  }

  auto &ctx = getASTContext();

  PointerTypeKind swiftPtrKind, cPtrKind;

  auto swiftPtr = type1->getAnyPointerElementType(swiftPtrKind);
  auto cPtr = type2->getAnyPointerElementType(cPtrKind);

  assert(swiftPtr);
  assert(cPtr);

  auto markSupported = [&]() -> SolutionKind {
    // Make sure that solutions with implicit pointer conversions
    // are always worse than the ones without them.
    increaseScore(SK_ImplicitValueConversion, locator);

    if (inCorrectPosition)
      return SolutionKind::Solved;

    // If conversion cannot be allowed on account of declaration,
    // let's add a tailored fix.
    auto *fix = AllowSwiftToCPointerConversion::create(
        *this, getConstraintLocator(locator));

    return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
  };

  auto elementLoc = locator.withPathElement(LocatorPathElt::GenericArgument(0));

  if (swiftPtr->isTypeVariableOrMember()) {
    // Inference between the equivalent pointer kinds is
    // handled by regular pointer conversions.
    if (swiftPtrKind == cPtrKind)
      return SolutionKind::Error;

    addConstraint(ConstraintKind::BindToPointerType, swiftPtr, cPtr,
                  elementLoc);
    return markSupported();
  }

  // If pointers have the same element type there is nothing to do.
  if (swiftPtr->isEqual(cPtr))
    return markSupported();

  // Unsafe[Mutable]RawPointer -> Unsafe[Mutable]Pointer<[U]Int8>
  if (swiftPtrKind == PTK_UnsafeRawPointer ||
      swiftPtrKind == PTK_UnsafeMutableRawPointer) {
    // Since it's a C pointer on parameter side it would always
    // be fully resolved.
    if (cPtr->isInt8() || cPtr->isUInt8())
      return markSupported();
  } else {
    // Unsafe[Mutable]Pointer<T> -> Unsafe[Mutable]Pointer<[U]Int8>
    if (cPtr->isInt8() || cPtr->isUInt8()) {
      // <T> can default to the type of C pointer.
      addConstraint(ConstraintKind::Defaultable, swiftPtr, cPtr, elementLoc);
      return markSupported();
    }

    // Unsafe[Mutable]Pointer<Int{8, 16, ...}> <->
    // Unsafe[Mutable]Pointer<UInt{8, 16, ...}>
    if (swiftPtr->isInt() || swiftPtr->isUInt()) {
      addConstraint(ConstraintKind::Equal, cPtr,
                    swiftPtr->isUInt() ? ctx.getIntType() : ctx.getUIntType(),
                    elementLoc);
      return markSupported();
    }

    if (swiftPtr->isInt8() || swiftPtr->isUInt8()) {
      addConstraint(ConstraintKind::Equal, cPtr,
                    swiftPtr->isUInt8() ? ctx.getInt8Type()
                                        : ctx.getUInt8Type(),
                    elementLoc);
      return markSupported();
    }

    if (swiftPtr->isInt16() || swiftPtr->isUInt16()) {
      addConstraint(ConstraintKind::Equal, cPtr,
                    swiftPtr->isUInt16() ? ctx.getInt16Type()
                                         : ctx.getUInt16Type(),
                    elementLoc);
      return markSupported();
    }

    if (swiftPtr->isInt32() || swiftPtr->isUInt32()) {
      addConstraint(ConstraintKind::Equal, cPtr,
                    swiftPtr->isUInt32() ? ctx.getInt32Type()
                                         : ctx.getUInt32Type(),
                    elementLoc);
      return markSupported();
    }

    if (swiftPtr->isInt64() || swiftPtr->isUInt64()) {
      addConstraint(ConstraintKind::Equal, cPtr,
                    swiftPtr->isUInt64() ? ctx.getInt64Type()
                                         : ctx.getUInt64Type(),
                    elementLoc);
      return markSupported();
    }
  }

  // If the conversion is unsupported, let's record a generic argument mismatch.
  if (shouldAttemptFixes() && !inCorrectPosition) {
    auto *fix = AllowArgumentMismatch::create(*this, type1, type2,
                                              getConstraintLocator(locator));
    return recordFix(fix, /*impact=*/FixImpact::TypeMismatch)
               ? SolutionKind::Error
               : SolutionKind::Solved;
  }

  return SolutionKind::Error;
}

static bool isAugmentingFix(ConstraintFix *fix) {
  switch (fix->getKind()) {
    case FixKind::TreatRValueAsLValue:
      return false;
    default:
      return true;
  }
}

bool ConstraintSystem::recordFix(ConstraintFix *fix, FixImpact impact,
                                 PreparedOverloadBuilder *preparedOverload) {
  if (preparedOverload) {
    ASSERT(PreparingOverload);
    preparedOverload->addedFix(fix, impact);
    return true;
  }

  ASSERT(!PreparingOverload);

  if (isDebugMode()) {
    auto &log = llvm::errs();
    log.indent(solverState ? solverState->getCurrentIndent() : 0)
      << "(attempting fix ";
    fix->print(log);
    log << ")\n";
  }

  if (hasArgumentsIgnoredForCodeCompletion()) {
    // Avoid simplifying the locator if the constraint system didn't ignore any
    // arguments.
    auto argExpr = simplifyLocatorToAnchor(fix->getLocator());
    if (isArgumentIgnoredForCodeCompletion(getAsExpr<Expr>(argExpr))) {
      // The argument was ignored. Don't record any fixes for it.
      return false;
    }
  }

  // Record the fix.

  // If this should affect the solution score, do so.
  if (auto impactScoreKind = fix->impact())
    increaseScore(*impactScoreKind, fix->getLocator(),
                  static_cast<unsigned>(impact));

  // If we've made the current solution worse than the best solution we've seen
  // already, stop now.
  if (worseThanBestSolution())
    return true;

  if (isAugmentingFix(fix)) {
    addFix(fix);
    return false;
  }

  auto anchor = fix->getAnchor();
  assert(bool(anchor) && "non-augmenting fix without an anchor?");

  // Only useful to record if no pre-existing fix is associated with
  // current anchor or, in case of anchor being an expression, any of
  // its sub-expressions.
  llvm::SmallDenseSet<ASTNode> anchors;
  for (const auto *fix : Fixes) {
    // Fixes that don't affect the score shouldn't be considered because even
    // if such a fix is recorded at that anchor this should not
    // have any affect in the recording of any other fix.
    if (!fix->impact())
      continue;

    anchors.insert(fix->getAnchor());
  }
  
  bool found = false;
  if (auto *expr = getAsExpr(anchor)) {
    forEachExpr(expr, [&](Expr *subExpr) -> Expr * {
      found |= anchors.count(subExpr);
      return subExpr;
    });
  } else {
    found = anchors.count(anchor);
  }

  if (!found)
    addFix(fix);

  return false;
}

void ConstraintSystem::recordPotentialHole(TypeVariableType *typeVar) {
  typeVar->getImpl().enableCanBindToHole(getTrail());
}

void ConstraintSystem::recordAnyTypeVarAsPotentialHole(Type type) {
  if (!type->hasTypeVariable())
    return;

  type.visit([&](Type type) {
    if (auto *typeVar = type->getAs<TypeVariableType>()) {
      typeVar->getImpl().enableCanBindToHole(getTrail());
    }
  });
}

void ConstraintSystem::recordTypeVariablesAsHoles(Type type) {
  type.visit([&](Type componentTy) {
    if (auto *typeVar = componentTy->getAs<TypeVariableType>()) {
      // Ignore bound type variables. This can happen if a type variable
      // occurs in multiple positions and/or  if type hasn't been fully
      // simplified before this call.
      if (typeVar->getImpl().hasRepresentativeOrFixed())
        return;

      assignFixedType(typeVar,
                      PlaceholderType::get(getASTContext(), typeVar));
    }
  });
}

void ConstraintSystem::recordMatchCallArgumentResult(
    ConstraintLocator *locator, MatchCallArgumentResult result) {
  assert(locator->isLastElement<LocatorPathElt::ApplyArgument>());
  bool inserted = argumentMatchingChoices.insert({locator, result}).second;
  ASSERT(inserted);

  if (solverState)
    recordChange(SolverTrail::Change::RecordedMatchCallArgumentResult(locator));
}

void ConstraintSystem::recordImplicitCallAsFunction(ConstraintLocator *locator,
                                                    UnresolvedDotExpr *root,
                                                    ArgumentList *baseArgs) {
  ImplicitCallAsFunctionInfo info{root, baseArgs};
  bool inserted = ImplicitCallAsFunctions.insert({locator, info}).second;
  ASSERT(inserted);

  if (solverState)
    recordChange(SolverTrail::Change::RecordedImplicitCallAsFunction(locator));
}

void ConstraintSystem::recordKeyPath(const KeyPathExpr *keypath,
                                     TypeVariableType *root,
                                     TypeVariableType *value, DeclContext *dc) {
  bool inserted = KeyPaths.insert(
    std::make_pair(keypath, std::make_tuple(root, value, dc))).second;
  ASSERT(inserted);

  if (solverState) {
    recordChange(SolverTrail::Change::RecordedKeyPath(
      const_cast<KeyPathExpr *>(keypath)));
  }
}

void ConstraintSystem::removeKeyPath(const KeyPathExpr *keypath) {
  bool erased = KeyPaths.erase(keypath);
  ASSERT(erased);
}

ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
    ConstraintFix *fix, Type type1, Type type2, ConstraintKind matchKind,
    TypeMatchOptions flags, ConstraintLocatorBuilder locator) {

  // Try with the fix.
  TypeMatchOptions subflags =
    getDefaultDecompositionOptions(flags) | TMF_ApplyingFix;
  switch (fix->getKind()) {
  case FixKind::ForceOptional: {
    SmallVector<Type, 4> unwraps1;
    type1->lookThroughAllOptionalTypes(unwraps1);

    SmallVector<Type, 4> unwraps2;
    type2->lookThroughAllOptionalTypes(unwraps2);

    auto impact = FixImpact::None;
    if (unwraps1.size() > unwraps2.size())
      impact += (unwraps1.size() - unwraps2.size());
    else if (unwraps2.size() > unwraps1.size())
      impact += (unwraps2.size() - unwraps1.size());
    else
      impact = FixImpact::Mismatch;

    return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved;
  }

  case FixKind::UnwrapOptionalBase:
  case FixKind::UnwrapOptionalBaseWithOptionalResult: {
    if (recordFix(fix))
      return SolutionKind::Error;

    type1 = simplifyType(type1);
    type2 = simplifyType(type2);

    // Explicitly preserve l-valueness of an unwrapped member type.
    if (!type1->is<LValueType>() && type2->is<LValueType>())
      type1 = LValueType::get(type1);

    // First type already appropriately set.
    return matchTypes(type1, type2, matchKind, subflags, locator);
  }

  case FixKind::ForceDowncast:
    // These work whenever they are suggested.
    if (recordFix(fix))
      return SolutionKind::Error;

    return SolutionKind::Solved;

  case FixKind::AddressOf: {
    // Assume that '&' was applied to the first type, turning an lvalue into
    // an inout.
    auto result = matchTypes(InOutType::get(type1->getRValueType()), type2,
                             matchKind, subflags, locator);
    if (result == SolutionKind::Solved)
      if (recordFix(fix))
        return SolutionKind::Error;

    return result;
  }

  case FixKind::AllowTupleTypeMismatch: {
    if (fix->getAs<AllowTupleTypeMismatch>()->isElementMismatch()) {
      auto *locator = fix->getLocator();
      if (recordFix(fix, FixImpact::Mismatch *
                             (locator->isForContextualType() ? 5 : 1)))
        return SolutionKind::Error;
      return SolutionKind::Solved;
    }
    auto lhs = type1->castTo<TupleType>();
    auto rhs = type2->castTo<TupleType>();
    // Create a new tuple type the size of the smaller tuple with elements
    // from the larger tuple whenever either side contains a type variable.
    // For example (A, $0, B, $2) and (X, Y, $1) produces: (X, $0, B).
    // This allows us to guarantee that the types will match, and all
    // type variables will get bound to something as long as we default
    // excess types in the larger tuple to Any. In the prior example,
    // when the tuples (X, Y, $1) and (X, $0, B) get matched, $0 is equated
    // to Y, $1 is equated to B, and $2 is defaulted to Any.
    auto lhsLarger = lhs->getNumElements() >= rhs->getNumElements();
    auto isLabelingFailure = lhs->getNumElements() == rhs->getNumElements();
    auto larger = lhsLarger ? lhs : rhs;
    auto smaller = lhsLarger ? rhs : lhs;
    llvm::SmallVector<TupleTypeElt, 4> newTupleTypes;

    // FIXME: For now, if either side contains pack expansion types, consider
    // the fix constraint solved without trying to figure out which tuple
    // elements were part of the pack.
    {
      if (containsPackExpansionType(lhs) ||
          containsPackExpansionType(rhs)) {
        if (recordFix(fix))
          return SolutionKind::Error;
        return SolutionKind::Solved;
      }
    }

    for (unsigned i = 0; i < larger->getNumElements(); ++i) {
      auto largerElt = larger->getElement(i);
      if (i < smaller->getNumElements()) {
        auto smallerElt = smaller->getElement(i);
        if (isLabelingFailure)
          newTupleTypes.push_back(TupleTypeElt(largerElt.getType()));
        else if (largerElt.getType()->isTypeVariableOrMember() ||
            smallerElt.getType()->isTypeVariableOrMember())
          newTupleTypes.push_back(largerElt);
        else
          newTupleTypes.push_back(smallerElt);
      } else {
        if (largerElt.getType()->isTypeVariableOrMember())
          recordAnyTypeVarAsPotentialHole(largerElt.getType());
      }
    }
    auto matchingType =
        TupleType::get(newTupleTypes, getASTContext());
    if (recordFix(fix))
      return SolutionKind::Error;
    return matchTupleTypes(matchingType, smaller, matchKind, subflags, locator);
  }

  case FixKind::AllowFunctionTypeMismatch: {
    if (recordFix(fix, FixImpact::FunctionTypeMismatch))
      return SolutionKind::Error;
    return SolutionKind::Solved;
  }

  case FixKind::TreatEphemeralAsNonEphemeral: {
    auto *theFix = static_cast<TreatEphemeralAsNonEphemeral *>(fix);
    // If we have a non-ephemeral locator for an ephemeral conversion, make a
    // note of the fix.
    auto conversion = theFix->getConversionKind();
    switch (isConversionEphemeral(conversion, locator)) {
    case ConversionEphemeralness::Ephemeral:
      // Record the fix with an impact of zero. This ensures that non-ephemeral
      // diagnostics don't impact solver behavior.
      if (recordFix(fix, FixImpact::None))
        return SolutionKind::Error;

      return SolutionKind::Solved;
    case ConversionEphemeralness::Unresolved:
    case ConversionEphemeralness::NonEphemeral:
      // FIXME: The unresolved case should form an unsolved constraint rather
      // than being treated as fully solved. This will require a way to connect
      // the unsolved constraint to the type variable for the unresolved
      // overload such that the fix gets re-activated when the overload is
      // bound.
      return SolutionKind::Solved;
    }
  }

  case FixKind::AllowSendingMismatch:
  case FixKind::InsertCall:
  case FixKind::RemoveReturn:
  case FixKind::RemoveAddressOf:
  case FixKind::AddMissingArguments:
  case FixKind::MoveOutOfOrderArgument:
  case FixKind::SkipUnhandledConstructInResultBuilder:
  case FixKind::UsePropertyWrapper:
  case FixKind::UseWrappedValue:
  case FixKind::AllowInvalidPropertyWrapperType:
  case FixKind::RemoveProjectedValueArgument:
  case FixKind::ExpandArrayIntoVarargs:
  case FixKind::UseRawValue:
  case FixKind::SpecifyBaseTypeForContextualMember:
  case FixKind::CoerceToCheckedCast:
  case FixKind::SpecifyObjectLiteralTypeImport:
  case FixKind::AllowKeyPathRootTypeMismatch:
  case FixKind::UnwrapOptionalBaseKeyPathApplication:
  case FixKind::AllowCoercionToForceCast:
  case FixKind::SpecifyKeyPathRootType:
  case FixKind::SpecifyLabelToAssociateTrailingClosure:
  case FixKind::AllowKeyPathWithoutComponents:
  case FixKind::IgnoreInvalidResultBuilderBody:
  case FixKind::IgnoreResultBuilderWithReturnStmts:
  case FixKind::SpecifyContextualTypeForNil:
  case FixKind::AllowRefToInvalidDecl:
  case FixKind::SpecifyBaseTypeForOptionalUnresolvedMember:
  case FixKind::SpecifyPackElementType:
  case FixKind::AllowCheckedCastCoercibleOptionalType:
  case FixKind::AllowNoopCheckedCast:
  case FixKind::AllowNoopExistentialToCFTypeCheckedCast:
  case FixKind::AllowUnsupportedRuntimeCheckedCast:
  case FixKind::AllowCheckedCastToUnrelated:
  case FixKind::AllowInvalidStaticMemberRefOnProtocolMetatype:
  case FixKind::AllowWrappedValueMismatch:
  case FixKind::RemoveExtraneousArguments:
  case FixKind::SpecifyTypeForPlaceholder:
  case FixKind::AllowAutoClosurePointerConversion:
  case FixKind::NotCompileTimeLiteral:
  case FixKind::RenameConflictingPatternVariables:
  case FixKind::AllowInvalidPackElement:
  case FixKind::AllowInvalidPackReference:
  case FixKind::AllowInvalidPackExpansion:
  case FixKind::IgnoreWhereClauseInPackIteration:
  case FixKind::MacroMissingPound:
  case FixKind::AllowGlobalActorMismatch:
  case FixKind::AllowAssociatedValueMismatch:
  case FixKind::AllowConcreteTypeSpecialization:
  case FixKind::AllowFunctionSpecialization:
  case FixKind::IgnoreGenericSpecializationArityMismatch:
  case FixKind::IgnoreKeyPathSubscriptIndexMismatch:
  case FixKind::AllowMemberRefOnExistential:
  case FixKind::AllowNonClassTypeToConvertToAnyObject: {
    return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
  }

  case FixKind::GenericArgumentsMismatch: {
    auto impact = FixImpact::Mismatch;
    if (type1->isMarkerExistential() || type2->isMarkerExistential())
      impact += 1;

    // If generic arguments mismatch ends up being recorded on the result
    // of the chain or a try expression it means that there is a contextual 
    // conversion mismatch.
    //
    // For optional conversions the solver currently generates a disjunction
    // with two choices - bind and optional-to-optional conversion which is
    // anchored on the contextual expression. If we can get a fix recorded
    // there that would result in a better diagnostic. It's only possible
    // for optional-to-optional choice because it doesn't bind the
    // variable immediately, so we need to downgrade direct fixes to prevent
    // `bind` choice from considered better.
    if (fix->directlyAt<OptionalEvaluationExpr>() ||
        fix->directlyAt<AnyTryExpr>())
      impact += FixImpact::TypeMismatch;

    return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved;
  }

  case FixKind::IgnoreThrownErrorMismatch: {
    return recordFix(fix, FixImpact::TypeMismatch) ? SolutionKind::Error
                                                   : SolutionKind::Solved;
  }
  case FixKind::IgnoreInvalidASTNode: {
    return recordFix(fix, FixImpact::InvalidAST) ? SolutionKind::Error
                                                 : SolutionKind::Solved;
  }
  case FixKind::IgnoreUnresolvedPatternVar: {
    return recordFix(fix, FixImpact::InvalidAST * 10) ? SolutionKind::Error
                                                      : SolutionKind::Solved;
  }

  case FixKind::AllowInvalidMemberReferenceInInitAccessor: {
    return recordFix(fix, FixImpact::InvalidReference) ? SolutionKind::Error
                                                       : SolutionKind::Solved;
  }

  case FixKind::ExplicitlyConstructRawRepresentable: {
    // Let's increase impact of this fix for binary operators because
    // it's possible to get both `.rawValue` and construction fixes for
    // different overloads of a binary operator and `.rawValue` is a
    // better fix because raw representable has a failable constructor.
    return recordFix(fix,
                     /*impact=*/isExpr<BinaryExpr>(locator.getAnchor())
                         ? FixImpact::TypeMismatch
                         : FixImpact::Mismatch)
               ? SolutionKind::Error
               : SolutionKind::Solved;
  }

  case FixKind::TreatRValueAsLValue: {
    auto impact = TreatRValueAsLValue::assessImpact(*this, fix->getLocator());
    return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved;
  }

  case FixKind::AddConformance:
  case FixKind::SkipSameTypeRequirement:
  case FixKind::SkipSameShapeRequirement:
  case FixKind::SkipSuperclassRequirement: {
    return recordFix(fix, assessRequirementFailureImpact(*this, type1,
                                                         fix->getLocator()))
               ? SolutionKind::Error
               : SolutionKind::Solved;
  }

  case FixKind::AllowArgumentTypeMismatch: {
    auto *loc = fix->getLocator();
    // If this is a "destination" argument to a mutating operator
    // like `+=`, let's give it a higher impact to make sure that
    // if a different overload choice has a mismatch at the "source"
    // it would always be preferred instead of causing an ambiguity
    // since `inout` doesn't support conversions and only the "source"
    // can be fixed.
    if (auto argLoc = loc->findLast<LocatorPathElt::ApplyArgToParam>()) {
      if (argLoc->getArgIdx() == 0 && isOperatorArgument(loc) &&
          loc->isLastElement<LocatorPathElt::LValueConversion>()) {
        return recordFix(fix, FixImpact::TypeMismatch + 1)
                   ? SolutionKind::Error
                   : SolutionKind::Solved;
      }
    }

    LLVM_FALLTHROUGH;
  }

  case FixKind::IgnoreDefaultExprTypeMismatch: {
    auto impact = FixImpact::TypeMismatch;
    // If there are any other argument mismatches already detected for this
    // call, we increase the score even higher so more argument fixes means
    // less viable is the overload.
    if (llvm::any_of(getFixes(), [&](const ConstraintFix *fix) {
          auto *fixLocator = fix->getLocator();
          return fixLocator->findLast<LocatorPathElt::ApplyArgToParam>()
                     ? fixLocator->getAnchor() == locator.getAnchor()
                     : false;
        }))
      impact += FixImpact::TypeMismatch + 1;

    // Passing a closure to a parameter that doesn't expect one should
    // be scored lower because there might be an overload that expects
    // a closure but has other issues e.g. wrong number of parameters.
    if (!type2->lookThroughAllOptionalTypes()->is<FunctionType>()) {
      auto argument = simplifyLocatorToAnchor(fix->getLocator());
      if (isExpr<ClosureExpr>(argument)) {
        impact += FixImpact::TypeMismatch;
      }
    }

    // De-prioritize `Builtin.RawPointer` and `OpaquePointer` parameters
    // because they usually clash with generic parameter mismatches e.g.
    //
    // let ptr: UnsafePointer<String> = ...
    // _ = UnsafePointer<Int>(ups)
    //
    // Here initializer overloads have both `Builtin.RawPointer` and
    // `OpaquePointer` variants, but the actual issue is that generic argument
    // `String` doesn't match `Int`.
    {
      if (type2->is<BuiltinRawPointerType>())
        impact += FixImpact::Mismatch;

      if (type2->getAnyNominal() == getASTContext().getOpaquePointerDecl())
        impact += FixImpact::Mismatch;
    }

    return recordFix(fix, impact) ? SolutionKind::Error : SolutionKind::Solved;
  }

  case FixKind::TreatArrayLiteralAsDictionary: {
    ArrayExpr *AE = getAsExpr<ArrayExpr>(fix->getAnchor());
    assert(AE);

    // If the array was empty, there's nothing to do.
    if (AE->getNumElements() == 0)
      return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;

    // For arrays with a single element, match the element type to the
    // dictionary's key type.
    SmallVector<Type, 2> optionals;
    auto dictTy = type2->lookThroughAllOptionalTypes(optionals);

    // If the fix is worse than the best solution, there's no point continuing.
    if (recordFix(fix, FixImpact::Mismatch + optionals.size()))
      return SolutionKind::Error;

    // Extract the dictionary key type.
    ProtocolDecl *dictionaryProto =
        Context.getProtocol(KnownProtocolKind::ExpressibleByDictionaryLiteral);
    auto keyAssocTy = dictionaryProto->getAssociatedType(Context.Id_Key);
    auto valueBaseTy = createTypeVariable(getConstraintLocator(locator),
                                      TVO_CanBindToLValue |
                                        TVO_CanBindToNoEscape |
                                        TVO_CanBindToHole);
    assignFixedType(valueBaseTy, dictTy);
    auto dictionaryKeyTy = DependentMemberType::get(valueBaseTy, keyAssocTy);

    // Extract the array element type.
    auto elemTy = type1->getArrayElementType();

    ConstraintLocator *elemLoc = getConstraintLocator(AE->getElement(0));
    ConstraintKind kind = isDictionaryType(dictTy)
        ? ConstraintKind::Conversion
        : ConstraintKind::Equal;
    return matchTypes(elemTy, dictionaryKeyTy, kind, subflags, elemLoc);
  }

  case FixKind::ContextualMismatch:
  case FixKind::IgnoreContextualType:
  case FixKind::IgnoreAssignmentDestinationType:
  case FixKind::AllowConversionThroughInOut:
  case FixKind::IgnoreCollectionElementContextualMismatch: {
    auto impact = FixImpact::Mismatch;

    auto locator = fix->getLocator();
    if (auto branchElt =
            locator->getLastElementAs<LocatorPathElt::TernaryBranch>()) {
      // If this is `else` branch of a ternary operator, let's
      // increase its impact to eliminate the chance of ambiguity.
      //
      // Branches are connected through two `subtype` constraints
      // to a common type variable with represents their join, which
      // means that result would attempt a type from each side if
      // one is available and that would result in two fixes - one for
      // each mismatched branch.
      if (branchElt->forElse()) {
        impact = FixImpact::TypeMismatch * 5;
      } else {
        // Also increase impact for `then` branch lower than `else` to still
        // eliminate ambiguity, but slightly worst than the average fix to avoid
        // so the solution which record this fix wouldn't be picked over one
        // that has contextual mismatch fix on the result of ternary expression.
        impact += FixImpact::TypeMismatch * 2;
      }
    }
    using SingleValueStmtResult = LocatorPathElt::SingleValueStmtResult;
    if (auto branchElt = locator->getLastElementAs<SingleValueStmtResult>()) {
      // Similar to a ternary, except we have N branches. Let's prefer the fix
      // on the first branch, and discount subsequent branches by index.
      if (branchElt->getIndex() > 0)
        impact = FixImpact::TypeMismatch * 5 + (branchElt->getIndex() - 1);
    }
    // Increase impact of invalid conversions to `Any` and `AnyHashable`
    // associated with collection elements (i.e. for-in sequence element)
    // because it means that other side is structurally incompatible.
    if (fix->getKind() == FixKind::IgnoreCollectionElementContextualMismatch) {
      if (type2->isAny() || type2->isAnyHashable())
        impact += FixImpact::Mismatch;
    }

    if (fix->getKind() == FixKind::IgnoreCollectionElementContextualMismatch &&
        locator->isForCollectionElement()) {
      auto *collection = castToExpr<CollectionExpr>(locator->getAnchor());
      // If the literal is passed to a call or subscript or used in a nested
      // position, let's attempt to prefer a fix for a contextual mismatch.
      // For example, `test([1])` if none of the overloads match it's better
      // to prefer an argument type  mismatch over a collection element type
      // mismatch because that points to an ambiguity with the `test` instead
      // of the collection literal.
      if (getSemanticsProvidingParentExpr(collection))
        impact = FixImpact::TypeMismatch * 2;
    }

    if (recordFix(fix, impact))
      return SolutionKind::Error;

    if (auto *fnType1 = type1->getAs<FunctionType>()) {
      // If this is a contextual mismatch between two
      // function types which we couldn't find a more
      // specific fix for. Let's assume that such types
      // are completely disjoint and adjust impact of
      // the fix accordingly.
      if (type2->is<FunctionType>()) {
        increaseScore(
            SK_Fix, locator,
            static_cast<unsigned>(FixImpact::FunctionTypeMismatch * 2));
      } else {
        // If type produced by expression is a function type
        // with result type matching contextual, it should have
        // been diagnosed as "missing explicit call", let's
        // increase the score to make sure that we don't impede that.
        auto result = matchTypes(fnType1->getResult(), type2, matchKind,
                                 TMF_ApplyingFix, locator);
        if (result == SolutionKind::Solved)
          increaseScore(SK_Fix, locator);
      }
    }

    return SolutionKind::Solved;
  }

  case FixKind::AllowNonOptionalWeak: {
    if (recordFix(fix))
      return SolutionKind::Error;

    // NOTE: The order here is important! Pattern matching equality is
    // not symmetric (we need to fix that either by using a different
    // constraint, or actually making it symmetric).
    (void)matchTypes(OptionalType::get(type1), type2, ConstraintKind::Equal,
                     TypeMatchFlags::TMF_ApplyingFix, locator);

    return SolutionKind::Solved;
  }

  case FixKind::AllowInlineArrayLiteralCountMismatch:
    return recordFix(fix, FixImpact::TypeMismatch) ? SolutionKind::Error
                                                   : SolutionKind::Solved;

  case FixKind::UseSubscriptOperator:
  case FixKind::ExplicitlyEscaping:
  case FixKind::MarkGlobalActorFunction:
  case FixKind::RelabelArguments:
  case FixKind::RemoveCall:
  case FixKind::RemoveUnwrap:
  case FixKind::DefineMemberBasedOnUse:
  case FixKind::AllowTypeOrInstanceMember:
  case FixKind::AllowMetatypeExtensionMemberOnConformingType:
  case FixKind::AllowInvalidPartialApplication:
  case FixKind::AllowInvalidInitRef:
  case FixKind::AllowClosureParameterDestructuring:
  case FixKind::AllowInaccessibleMember:
  case FixKind::AllowMemberFromWrongModule:
  case FixKind::AllowAnyObjectKeyPathRoot:
  case FixKind::AllowMultiArgFuncKeyPathMismatch:
  case FixKind::TreatKeyPathSubscriptIndexAsHashable:
  case FixKind::AllowInvalidRefInKeyPath:
  case FixKind::DefaultGenericArgument:
  case FixKind::AllowMutatingMemberOnRValueBase:
  case FixKind::AllowTupleSplatForSingleParameter:
  case FixKind::SpecifyClosureParameterType:
  case FixKind::SpecifyClosureReturnType:
  case FixKind::AddQualifierToAccessTopLevelName:
  case FixKind::AddSendableAttribute:
  case FixKind::DropThrowsAttribute:
  case FixKind::DropAsyncAttribute:
  case FixKind::AllowSwiftToCPointerConversion:
  case FixKind::AllowTupleLabelMismatch:
  case FixKind::AddExplicitExistentialCoercion:
  case FixKind::DestructureTupleToMatchPackExpansionParameter:
  case FixKind::AllowValueExpansionWithoutPackReferences:
  case FixKind::IgnoreInvalidPatternInExpr:
  case FixKind::IgnoreInvalidPlaceholder:
  case FixKind::IgnoreOutOfPlaceThenStmt:
  case FixKind::IgnoreMissingEachKeyword:
  case FixKind::TooManyDynamicMemberLookups:
  case FixKind::IgnoreNonMetatypeDynamicType:
  case FixKind::IgnoreIsolatedConformance:
  case FixKind::IgnoreClassRequirementForDynamicMemberLookup:
  case FixKind::ExecutionSemanticsMismatch:
    llvm_unreachable("handled elsewhere");
  }

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

ConstraintSystem::SolutionKind
ConstraintSystem::addConstraintImpl(ConstraintKind kind, Type first,
                                    Type second,
                                    ConstraintLocatorBuilder locator,
                                    bool isFavored) {
  ASSERT(!PreparingOverload);

  assert(first && "Missing first type");
  assert(second && "Missing second type");

  TypeMatchOptions subflags = TMF_GenerateConstraints;
  switch (kind) {
  case ConstraintKind::Equal:
  case ConstraintKind::Bind:
  case ConstraintKind::BindParam:
  case ConstraintKind::BindToPointerType:
  case ConstraintKind::Subtype:
  case ConstraintKind::Conversion:
    return matchTypes(first, second, kind, subflags, locator);

  case ConstraintKind::ArgumentConversion:
  case ConstraintKind::OperatorArgumentConversion:
    return addArgumentConversionConstraintImpl(kind, first, second, locator);

  case ConstraintKind::BridgingConversion:
    return simplifyBridgingConstraint(first, second, subflags, locator);

  case ConstraintKind::DynamicCallableApplicableFunction:
    return simplifyDynamicCallableApplicableFnConstraint(first, second,
                                                         subflags, locator);

  case ConstraintKind::DynamicTypeOf:
    return simplifyDynamicTypeOfConstraint(first, second, subflags, locator);

  case ConstraintKind::EscapableFunctionOf:
    return simplifyEscapableFunctionOfConstraint(first, second,
                                                 subflags, locator);

  case ConstraintKind::OpenedExistentialOf:
    return simplifyOpenedExistentialOfConstraint(first, second,
                                                 subflags, locator);

  case ConstraintKind::SubclassOf:
    return simplifySubclassOfConstraint(first, second, locator, subflags);

  case ConstraintKind::NonisolatedConformsTo:
  case ConstraintKind::ConformsTo:
  case ConstraintKind::LiteralConformsTo:
    return simplifyConformsToConstraint(first, second, kind, locator,
                                        subflags);

  case ConstraintKind::TransitivelyConformsTo:
    return simplifyTransitivelyConformsTo(first, second, locator,
                                          subflags);

  case ConstraintKind::CheckedCast:
    return simplifyCheckedCastConstraint(first, second, subflags, locator);

  case ConstraintKind::ForEachElement:
    return simplifyForEachElementConstraint(first, second, subflags, locator);

  case ConstraintKind::OptionalObject:
    return simplifyOptionalObjectConstraint(first, second, subflags, locator);

  case ConstraintKind::Defaultable:
    return simplifyDefaultableConstraint(first, second, subflags, locator);

  case ConstraintKind::PropertyWrapper:
    return simplifyPropertyWrapperConstraint(first, second, subflags, locator);

  case ConstraintKind::OneWayEqual:
    return simplifyOneWayConstraint(kind, first, second, subflags, locator);

  case ConstraintKind::UnresolvedMemberChainBase:
    return simplifyUnresolvedMemberChainBaseConstraint(first, second, subflags,
                                                       locator);

  case ConstraintKind::BindTupleOfFunctionParams:
    return simplifyBindTupleOfFunctionParamsConstraint(first, second, subflags,
                                                       locator);

  case ConstraintKind::PackElementOf:
    return simplifyPackElementOfConstraint(first, second, subflags, locator);

  case ConstraintKind::ShapeOf:
    return simplifyShapeOfConstraint(first, second, subflags, locator);

  case ConstraintKind::SameShape:
    return simplifySameShapeConstraint(first, second, subflags, locator);

  case ConstraintKind::ExplicitGenericArguments:
    return simplifyExplicitGenericArgumentsConstraint(
        first, second, subflags, locator);

  case ConstraintKind::MaterializePackExpansion:
    return simplifyMaterializePackExpansionConstraint(first, second, subflags,
                                                      locator);

  case ConstraintKind::LValueObject:
    return simplifyLValueObjectConstraint(first, second, subflags, locator);

  case ConstraintKind::ValueMember:
  case ConstraintKind::UnresolvedValueMember:
  case ConstraintKind::BindOverload:
  case ConstraintKind::Disjunction:
  case ConstraintKind::Conjunction:
  case ConstraintKind::KeyPath:
  case ConstraintKind::KeyPathApplication:
  case ConstraintKind::FallbackType:
  case ConstraintKind::SyntacticElement:
  case ConstraintKind::ApplicableFunction:
    llvm_unreachable("Use the correct addConstraint()");
  }

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

ConstraintSystem::SolutionKind
ConstraintSystem::addArgumentConversionConstraintImpl(
    ConstraintKind kind, Type first, Type second,
    ConstraintLocatorBuilder locator) {
  assert(kind == ConstraintKind::ArgumentConversion ||
         kind == ConstraintKind::OperatorArgumentConversion);

  // If we have an unresolved closure argument, form an unsolved argument
  // conversion constraint, making sure to reference the type variables for
  // a result builder if applicable. This ensures we properly connect the
  // closure type variable with any type variables in the result builder, as
  // such type variables will be accessible within the body of the closure when
  // we open it.
  first = getFixedTypeRecursive(first, /*rvalue*/ false);
  if (auto *argTypeVar = first->getAs<TypeVariableType>()) {
    if (argTypeVar->getImpl().isClosureType()) {
      // Extract any type variables present in the parameter's result builder.
      SmallPtrSet<TypeVariableType *, 4> typeVars;
      if (auto builderTy = getOpenedResultBuilderTypeFor(*this, locator))
        builderTy->getTypeVariables(typeVars);

      SmallVector<TypeVariableType *, 4> referencedVars{typeVars.begin(),
                                                        typeVars.end()};

      auto *loc = getConstraintLocator(locator);
      addUnsolvedConstraint(
          Constraint::create(*this, kind, first, second, loc, referencedVars));
      return SolutionKind::Solved;
    }
  }
  return matchTypes(first, second, kind, TMF_GenerateConstraints, locator);
}

void
ConstraintSystem::addKeyPathApplicationRootConstraint(Type root, ConstraintLocatorBuilder locator) {
  // If this is a subscript with a KeyPath expression, add a constraint that
  // connects the subscript's root type to the root type of the KeyPath.
  SmallVector<LocatorPathElt, 4> path;
  auto anchor = locator.getLocatorParts(path);

  auto subscript = getAsExpr<SubscriptExpr>(anchor);
  if (!subscript)
    return;

  assert((path.size() == 1 &&
          path[0].getKind() == ConstraintLocator::SubscriptMember) ||
         (path.size() == 2 &&
          path[1].getKind() == ConstraintLocator::KeyPathDynamicMember));

  // If a keypath subscript is used without the expected `keyPath:` label,
  // continue with type-checking when attempting fixes so that it gets caught
  // by the argument label checking.
  auto *argList = subscript->getArgs();
  auto *unaryArg = argList->getUnaryExpr();
  assert(unaryArg && "Expected KeyPathExpr apply to have single argument");

  auto *keyPathExpr = dyn_cast<KeyPathExpr>(unaryArg);
  if (!keyPathExpr)
    return;

  auto typeVar = getType(keyPathExpr)->getAs<TypeVariableType>();
  if (!typeVar)
    return;

  auto constraints = CG.gatherNearbyConstraints(
      typeVar,
      [&keyPathExpr](Constraint *constraint) -> bool {
        if (constraint->getKind() != ConstraintKind::KeyPath)
          return false;

        auto *locator = constraint->getLocator();
        if (auto KPE = getAsExpr<KeyPathExpr>(locator->getAnchor()))
          return KPE == keyPathExpr;
        return false;
      });

  for (auto constraint : constraints) {
    auto keyPathRootTy = constraint->getSecondType();
    addConstraint(ConstraintKind::Subtype, root->getWithoutSpecifierType(),
                  keyPathRootTy, locator);
  }
}

void
ConstraintSystem::addKeyPathApplicationConstraint(Type keypath,
                                              Type root, Type value,
                                              ConstraintLocatorBuilder locator,
                                              bool isFavored) {
  addKeyPathApplicationRootConstraint(root, locator);
  
  switch (simplifyKeyPathApplicationConstraint(keypath, root, value,
                                               TMF_GenerateConstraints,
                                               locator)) {
  case SolutionKind::Error:
    if (shouldRecordFailedConstraint()) {
      auto c = Constraint::create(*this, ConstraintKind::KeyPathApplication,
                                  keypath, root, value,
                                  getConstraintLocator(locator));
      if (isFavored) c->setFavored();
      recordFailedConstraint(c);
    }
    return;
  
  case SolutionKind::Solved:
    return;
    
  case SolutionKind::Unsolved:
    llvm_unreachable("should have generated constraints");
  }
}

void
ConstraintSystem::addKeyPathConstraint(
    Type keypath,
    Type root, Type value,
    ArrayRef<TypeVariableType *> componentTypeVars,
    ConstraintLocatorBuilder locator,
    bool isFavored) {
  switch (simplifyKeyPathConstraint(keypath, root, value,
                                    componentTypeVars,
                                    TMF_GenerateConstraints,
                                    locator)) {
  case SolutionKind::Error:
    if (shouldRecordFailedConstraint()) {
      auto c = Constraint::create(*this, ConstraintKind::KeyPath,
                                  keypath, root, value,
                                  getConstraintLocator(locator),
                                  componentTypeVars);
      if (isFavored) c->setFavored();
      recordFailedConstraint(c);
    }
    return;
  
  case SolutionKind::Solved:
    return;
    
  case SolutionKind::Unsolved:
    llvm_unreachable("should have generated constraints");
  }
}

void ConstraintSystem::addConstraint(Requirement req,
                                     ConstraintLocatorBuilder locator,
                                     bool isFavored,
                                     bool prohibitNonisolatedConformance,
                                     PreparedOverloadBuilder *preparedOverload) {
  bool conformsToAnyObject = false;
  std::optional<ConstraintKind> kind;
  switch (req.getKind()) {
  case RequirementKind::SameShape: {
    auto type1 = req.getFirstType();
    auto type2 = req.getSecondType();

    addConstraint(ConstraintKind::SameShape, type1, type2, locator,
                  /*isFavored=*/false, preparedOverload);
    return;
  }

  case RequirementKind::Conformance:
    kind = prohibitNonisolatedConformance
        ? ConstraintKind::NonisolatedConformsTo
        : ConstraintKind::ConformsTo;
    break;
  case RequirementKind::Superclass: {
    // FIXME: Should always use ConstraintKind::SubclassOf, but that breaks
    // a couple of diagnostics
    if (auto *typeVar = req.getFirstType()->getAs<TypeVariableType>()) {
      if (typeVar->getImpl().canBindToPack()) {
        kind = ConstraintKind::SubclassOf;
        break;
      }
    }

    conformsToAnyObject = true;
    kind = ConstraintKind::Subtype;
    break;
  }
  case RequirementKind::SameType:
    kind = ConstraintKind::Bind;
    break;
  case RequirementKind::Layout:
    // Only a class constraint can be modeled as a constraint, and only that can
    // appear outside of a @_specialize at the moment anyway.
    if (req.getLayoutConstraint()->isClass()) {
      conformsToAnyObject = true;
      break;
    } else {
      llvm_unreachable("unexpected LayoutConstraint kind");
    }
    return;
  }

  auto firstType = req.getFirstType();
  if (kind) {
    addConstraint(*kind, req.getFirstType(), req.getSecondType(), locator,
                  isFavored, preparedOverload);
  }

  if (conformsToAnyObject) {
    auto anyObject = getASTContext().getAnyObjectConstraint();
    addConstraint(ConstraintKind::ConformsTo, firstType, anyObject, locator,
                  /*isFavored=*/false, preparedOverload);
  }
}

void ConstraintSystem::addConstraint(ConstraintKind kind, Type first,
                                     Type second,
                                     ConstraintLocatorBuilder locator,
                                     bool isFavored,
                                     PreparedOverloadBuilder *preparedOverload) {
  if (preparedOverload) {
    ASSERT(PreparingOverload);

    auto *locatorPtr = getConstraintLocator(locator);

    // Fast path to save on memory usage.
    if (kind == ConstraintKind::Bind &&
        locatorPtr == preparedOverload->Locator) {
      ASSERT(!isFavored);
      preparedOverload->addedBindConstraint(first, second);
      return;
    }

    auto c = Constraint::create(*this, kind, first, second, locatorPtr);

    if (isFavored)
      c->setFavored();

    preparedOverload->addedConstraint(c);
    return;
  }

  ASSERT(!PreparingOverload);

  switch (addConstraintImpl(kind, first, second, locator, isFavored)) {
  case SolutionKind::Error:
    // Add a failing constraint, if needed.
    if (shouldRecordFailedConstraint()) {
      auto c = Constraint::create(*this, kind, first, second,
                                  getConstraintLocator(locator));
      if (isFavored) c->setFavored();
      recordFailedConstraint(c);
    }
    return;

  case SolutionKind::Unsolved:
    llvm_unreachable("should have generated constraints");

  case SolutionKind::Solved:
    return;
  }
}

void ConstraintSystem::addApplicationConstraint(
    FunctionType *appliedFn, Type calleeType,
    std::optional<TrailingClosureMatching> trailingClosureMatching,
    DeclContext *useDC,
    ConstraintLocatorBuilder locator) {
  auto recordFailure = [&]() {
    if (shouldRecordFailedConstraint()) {
      auto *c = Constraint::createApplicableFunction(
          *this, appliedFn, calleeType, trailingClosureMatching, useDC,
          getConstraintLocator(locator));
      recordFailedConstraint(c);
    }
  };

  // First try to simplify the overload set for the function being applied.
  if (simplifyAppliedOverloads(calleeType, appliedFn, locator)) {
    recordFailure();
    return;
  }

  switch (simplifyApplicableFnConstraint(appliedFn, calleeType,
                                         trailingClosureMatching, useDC,
                                         TMF_GenerateConstraints, locator)) {
  case SolutionKind::Error:
    recordFailure();
    break;

  case SolutionKind::Unsolved:
    llvm_unreachable("should have generated constraints");

  case SolutionKind::Solved:
    return;
  }
}

void ConstraintSystem::addContextualConversionConstraint(
    Expr *expr, Type conversionType, ContextualTypePurpose purpose,
    ConstraintLocator *locator, bool shouldOpenOpaqueType) {
  if (conversionType.isNull())
    return;

  // Determine the type of the constraint.
  auto constraintKind = ConstraintKind::Conversion;
  switch (purpose) {
  case CTP_ReturnStmt:
  case CTP_Initialization: {
    if (conversionType->is<OpaqueTypeArchetypeType>())
      constraintKind = ConstraintKind::Equal;
    // Alternatively, we might have a nested opaque archetype, e.g. `(some P)?`.
    // In that case, we want `ConstraintKind::Conversion`.
    break;
  }
  case CTP_CallArgument:
    constraintKind = ConstraintKind::ArgumentConversion;
    break;

  case CTP_YieldByReference:
    // In a by-reference yield, we expect the contextual type to be an
    // l-value type, so the result must be bound to that.
    constraintKind = ConstraintKind::Bind;
    break;

  case CTP_DiscardStmt:
    // For the 'discard X', we always expect the contextual type to be
    // equal to the type of 'self'.
    constraintKind = ConstraintKind::Equal;
    break;

  case CTP_ForEachSequence:
    // Sequence expression associated with `for-in` loop has to conform
    // to `Sequence` or `AsyncSequence` protocol depending on the context.
    constraintKind = ConstraintKind::ConformsTo;
    break;

  case CTP_ArrayElement:
  case CTP_AssignSource:
  case CTP_Condition:
  case CTP_Unused:
  case CTP_YieldByValue:
  case CTP_CaseStmt:
  case CTP_ThrowStmt:
  case CTP_EnumCaseRawValue:
  case CTP_IntGenericParam:
  case CTP_DefaultParameter:
  case CTP_AutoclosureDefaultParameter:
  case CTP_ClosureResult:
  case CTP_DictionaryKey:
  case CTP_DictionaryValue:
  case CTP_CoerceOperand:
  case CTP_SubscriptAssignSource:
  case CTP_WrappedProperty:
  case CTP_ExprPattern:
  case CTP_SingleValueStmtBranch:
    break;
  }

  // Add the constraint.
  // FIXME: This is the wrong place to be opening the opaque type.
  if (shouldOpenOpaqueType) {
    conversionType = openOpaqueType(conversionType, purpose, locator,
                                    /*ownerDecl=*/nullptr);
  }
  addConstraint(constraintKind, getType(expr), conversionType, locator,
                /*isFavored*/ true);
}

void ConstraintSystem::addFixConstraint(ConstraintFix *fix, ConstraintKind kind,
                                        Type first, Type second,
                                        ConstraintLocatorBuilder locator,
                                        bool isFavored) {
  TypeMatchOptions subflags = TMF_GenerateConstraints;
  switch (simplifyFixConstraint(fix, first, second, kind, subflags, locator)) {
  case SolutionKind::Error:
    // Add a failing constraint, if needed.
    if (shouldRecordFailedConstraint()) {
      auto c = Constraint::createFixed(*this, kind, fix, first, second,
                                       getConstraintLocator(locator));
      if (isFavored) c->setFavored();
      recordFailedConstraint(c);
    }
    return;

  case SolutionKind::Unsolved:
    llvm_unreachable("should have generated constraints");

  case SolutionKind::Solved:
    return;
  }
}

void ConstraintSystem::addExplicitConversionConstraint(
    Type fromType, Type toType, RememberChoice_t rememberChoice,
    ConstraintLocatorBuilder locator, ConstraintFix *compatFix) {
  SmallVector<Constraint *, 3> constraints;

  auto locatorPtr = getConstraintLocator(locator);

  // Coercion (the common case).
  Constraint *coerceConstraint =
    Constraint::create(*this, ConstraintKind::Conversion,
                       fromType, toType, locatorPtr);
  coerceConstraint->setFavored();
  constraints.push_back(coerceConstraint);

  // The source type can be explicitly converted to the destination type.
  Constraint *bridgingConstraint =
  Constraint::create(*this, ConstraintKind::BridgingConversion,
                     fromType, toType, locatorPtr);
  constraints.push_back(bridgingConstraint);

  // If we're allowed to use a compatibility fix that emits a warning on
  // failure, add it to the disjunction so that it's recorded on failure.
  if (compatFix) {
    constraints.push_back(
        Constraint::createFixed(*this, ConstraintKind::BridgingConversion,
                                compatFix, fromType, toType, locatorPtr));
  }

  addDisjunctionConstraint(constraints, locator, rememberChoice);
}

TypeVariableType *ConstraintSystem::addMaterializePackExpansionConstraint(
    Type tupleType, ConstraintLocatorBuilder locator) {
  assert(isSingleUnlabeledPackExpansionTuple(tupleType));
  TypeVariableType *packVar =
      createTypeVariable(getConstraintLocator(locator), TVO_CanBindToPack);
  addConstraint(ConstraintKind::MaterializePackExpansion, tupleType, packVar,
                getConstraintLocator(locator, {ConstraintLocator::Member}));
  return packVar;
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifyConstraint(const Constraint &constraint) {
  auto matchKind = constraint.getKind();
  switch (matchKind) {
  case ConstraintKind::Bind:
  case ConstraintKind::Equal:
  case ConstraintKind::BindParam:
  case ConstraintKind::BindToPointerType:
  case ConstraintKind::Subtype:
  case ConstraintKind::Conversion:
  case ConstraintKind::ArgumentConversion:
  case ConstraintKind::OperatorArgumentConversion: {
    // Relational constraints.

    // If there is a fix associated with this constraint, apply it.
    if (auto fix = constraint.getFix()) {
      return simplifyFixConstraint(fix, constraint.getFirstType(),
                                   constraint.getSecondType(), matchKind,
                                   std::nullopt, constraint.getLocator());
    }

    // If there is a restriction on this constraint, apply it directly rather
    // than going through the general \c matchTypes() machinery.
    if (auto restriction = constraint.getRestriction()) {
      return simplifyRestrictedConstraint(
          *restriction, constraint.getFirstType(), constraint.getSecondType(),
          matchKind, std::nullopt, constraint.getLocator());
    }

    return matchTypes(constraint.getFirstType(), constraint.getSecondType(),
                      matchKind, std::nullopt, constraint.getLocator());
  }

  case ConstraintKind::BridgingConversion:
    // If there is a fix associated with this constraint, apply it.
    if (auto fix = constraint.getFix()) {
      return simplifyFixConstraint(fix, constraint.getFirstType(),
                                   constraint.getSecondType(), matchKind,
                                   std::nullopt, constraint.getLocator());
    }

    return simplifyBridgingConstraint(constraint.getFirstType(),
                                      constraint.getSecondType(), std::nullopt,
                                      constraint.getLocator());

  case ConstraintKind::ApplicableFunction:
    return simplifyApplicableFnConstraint(
        constraint.getAppliedFunctionType(), constraint.getCalleeType(),
        constraint.getTrailingClosureMatching(),
        constraint.getDeclContext(), /*flags=*/std::nullopt,
        constraint.getLocator());

  case ConstraintKind::DynamicCallableApplicableFunction:
    return simplifyDynamicCallableApplicableFnConstraint(
        constraint.getFirstType(), constraint.getSecondType(), std::nullopt,
        constraint.getLocator());

  case ConstraintKind::DynamicTypeOf:
    return simplifyDynamicTypeOfConstraint(
        constraint.getFirstType(), constraint.getSecondType(), std::nullopt,
        constraint.getLocator());

  case ConstraintKind::EscapableFunctionOf:
    return simplifyEscapableFunctionOfConstraint(
        constraint.getFirstType(), constraint.getSecondType(), std::nullopt,
        constraint.getLocator());

  case ConstraintKind::OpenedExistentialOf:
    return simplifyOpenedExistentialOfConstraint(
        constraint.getFirstType(), constraint.getSecondType(), std::nullopt,
        constraint.getLocator());

  case ConstraintKind::KeyPath:
    return simplifyKeyPathConstraint(
        constraint.getFirstType(), constraint.getSecondType(),
        constraint.getThirdType(), constraint.getTypeVariables(), std::nullopt,
        constraint.getLocator());

  case ConstraintKind::KeyPathApplication:
    return simplifyKeyPathApplicationConstraint(
        constraint.getFirstType(), constraint.getSecondType(),
        constraint.getThirdType(), std::nullopt, constraint.getLocator());

  case ConstraintKind::BindOverload: {
    if (auto *fix = constraint.getFix()) {
      // TODO(diagnostics): Impact should be associated with a fix unless
      // it's a contextual problem, then only solver can decide what the impact
      // would be in each particular situation.
      auto impact = fix->getKind() == FixKind::AddQualifierToAccessTopLevelName
                        ? FixImpact::InvalidAST
                        : FixImpact::Mismatch;
      if (recordFix(fix, impact))
        return SolutionKind::Error;
    }

    // FIXME: Transitional hack.
    bool enablePreparedOverloads = getASTContext().TypeCheckerOpts.SolverEnablePreparedOverloads;
    bool forDiagnostics = inSalvageMode();

    // Don't reuse prepared overloads from normal type checking in salvage(),
    // since they will contain fixes and such.
    auto *preparedOverload = constraint.getPreparedOverload();
    if (preparedOverload &&
        preparedOverload->wasForDiagnostics() != forDiagnostics) {
      preparedOverload = nullptr;
    }

    if (!preparedOverload &&
        enablePreparedOverloads &&
        constraint.getOverloadChoice().canBePrepared()) {
      preparedOverload = prepareOverload(constraint.getOverloadChoice(),
                                         constraint.getDeclContext(),
                                         constraint.getLocator(),
                                         forDiagnostics);
      const_cast<Constraint &>(constraint).setPreparedOverload(preparedOverload);
    }

    resolveOverload(constraint.getOverloadChoice(),
                    constraint.getDeclContext(),
                    constraint.getLocator(),
                    constraint.getFirstType(),
                    preparedOverload);
    return SolutionKind::Solved;
  }

  case ConstraintKind::SubclassOf:
    return simplifySubclassOfConstraint(constraint.getFirstType(),
                                        constraint.getSecondType(),
                                        constraint.getLocator(),
                                        /*flags*/ std::nullopt);

  case ConstraintKind::NonisolatedConformsTo:
  case ConstraintKind::ConformsTo:
  case ConstraintKind::LiteralConformsTo:
    return simplifyConformsToConstraint(
        constraint.getFirstType(), constraint.getSecondType(),
        constraint.getKind(), constraint.getLocator(), std::nullopt);

  case ConstraintKind::TransitivelyConformsTo:
    return simplifyTransitivelyConformsTo(
        constraint.getFirstType(), constraint.getSecondType(),
        constraint.getLocator(), std::nullopt);

  case ConstraintKind::CheckedCast: {
    auto result = simplifyCheckedCastConstraint(
        constraint.getFirstType(), constraint.getSecondType(),
        /*flags*/ std::nullopt, constraint.getLocator());
    // NOTE: simplifyCheckedCastConstraint() may return Unsolved, e.g. if the
    // subexpression's type is unresolved. Don't record the fix until we
    // successfully simplify the constraint.
    if (result == SolutionKind::Solved) {
      if (auto *fix = constraint.getFix()) {
        if (recordFix(fix)) {
          return SolutionKind::Error;
        }
      }
    }
    return result;
  }

  case ConstraintKind::ForEachElement:
    return simplifyForEachElementConstraint(
        constraint.getFirstType(), constraint.getSecondType(),
        /*flags*/ std::nullopt, constraint.getLocator());

  case ConstraintKind::OptionalObject:
    return simplifyOptionalObjectConstraint(
        constraint.getFirstType(), constraint.getSecondType(),
        /*flags*/ std::nullopt, constraint.getLocator());

  case ConstraintKind::ValueMember:
  case ConstraintKind::UnresolvedValueMember:
    return simplifyMemberConstraint(
        constraint.getKind(), constraint.getFirstType(), constraint.getMember(),
        constraint.getSecondType(), constraint.getDeclContext(),
        constraint.getFunctionRefInfo(),
        /*outerAlternatives=*/{},
        /*flags*/ std::nullopt, constraint.getLocator());

  case ConstraintKind::Defaultable:
    return simplifyDefaultableConstraint(
        constraint.getFirstType(), constraint.getSecondType(),
        /*flags*/ std::nullopt, constraint.getLocator());

  case ConstraintKind::FallbackType:
    return simplifyFallbackTypeConstraint(
        constraint.getFirstType(), constraint.getSecondType(),
        constraint.getTypeVariables(),
        /*flags*/ std::nullopt, constraint.getLocator());

  case ConstraintKind::PropertyWrapper:
    return simplifyPropertyWrapperConstraint(
        constraint.getFirstType(), constraint.getSecondType(),
        /*flags*/ std::nullopt, constraint.getLocator());

  case ConstraintKind::Disjunction: {
    if (shouldAttemptFixes()) {
      auto *choice = constraint.getNestedConstraints()[0];
      if (choice->getKind() == ConstraintKind::BindOverload) {
        if (auto *choiceTy = choice->getFirstType()->getAs<TypeVariableType>()) {
          auto resolvedTy = getFixedType(choiceTy);
          if (resolvedTy && resolvedTy->isPlaceholder())
            return SolutionKind::Solved;
        }
      }
    }
    LLVM_FALLTHROUGH;
  }
  case ConstraintKind::Conjunction:
    // See {Dis, Con}junctionStep class in CSStep.cpp for solving
    return SolutionKind::Unsolved;

  case ConstraintKind::OneWayEqual:
    return simplifyOneWayConstraint(
        constraint.getKind(), constraint.getFirstType(),
        constraint.getSecondType(),
        /*flags*/ std::nullopt, constraint.getLocator());

  case ConstraintKind::UnresolvedMemberChainBase:
    return simplifyUnresolvedMemberChainBaseConstraint(
        constraint.getFirstType(), constraint.getSecondType(),
        /*flags=*/std::nullopt, constraint.getLocator());

  case ConstraintKind::SyntacticElement:
    return simplifySyntacticElementConstraint(
        constraint.getSyntacticElement(), constraint.getElementContext(),
        constraint.isDiscardedElement(),
        /*flags=*/std::nullopt, constraint.getLocator());

  case ConstraintKind::BindTupleOfFunctionParams:
    return simplifyBindTupleOfFunctionParamsConstraint(
        constraint.getFirstType(), constraint.getSecondType(),
        /*flags*/ std::nullopt, constraint.getLocator());

  case ConstraintKind::PackElementOf:
    return simplifyPackElementOfConstraint(
        constraint.getFirstType(), constraint.getSecondType(),
        /*flags*/ std::nullopt, constraint.getLocator());

  case ConstraintKind::ShapeOf:
    return simplifyShapeOfConstraint(
        constraint.getFirstType(), constraint.getSecondType(),
        /*flags*/ std::nullopt, constraint.getLocator());

  case ConstraintKind::SameShape:
    return simplifySameShapeConstraint(
        constraint.getFirstType(), constraint.getSecondType(),
        /*flags*/ std::nullopt, constraint.getLocator());

  case ConstraintKind::ExplicitGenericArguments:
    return simplifyExplicitGenericArgumentsConstraint(
        constraint.getFirstType(), constraint.getSecondType(),
        /*flags*/ std::nullopt, constraint.getLocator());

  case ConstraintKind::MaterializePackExpansion:
    return simplifyMaterializePackExpansionConstraint(
        constraint.getFirstType(), constraint.getSecondType(),
        /*flags*/ std::nullopt, constraint.getLocator());

  case ConstraintKind::LValueObject:
    return simplifyLValueObjectConstraint(
        constraint.getFirstType(), constraint.getSecondType(),
        /*flags*/ std::nullopt, constraint.getLocator());
  }

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

void ConstraintSystem::simplifyDisjunctionChoice(Constraint *choice) {
  // Simplify this term in the disjunction.
  switch (simplifyConstraint(*choice)) {
  case ConstraintSystem::SolutionKind::Error:
    recordFailedConstraint(choice);
    break;

  case ConstraintSystem::SolutionKind::Solved:
    break;

  case ConstraintSystem::SolutionKind::Unsolved:
    addUnsolvedConstraint(choice);
    break;
  }
}
