//===--- CSSyntacticElement.cpp - Syntactic Element Constraints -----------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 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 SyntacticElement constraint generation and solution
// application, which is used to type-check the bodies of closures. It provides
// part of the implementation of the ConstraintSystem class.
//
//===----------------------------------------------------------------------===//

#include "MiscDiagnostics.h"
#include "TypeChecker.h"
#include "TypeCheckAvailability.h"
#include "swift/Basic/Assertions.h"
#include "swift/Sema/ConstraintSystem.h"
#include "swift/Sema/IDETypeChecking.h"

using namespace swift;
using namespace swift::constraints;

void ConstraintSystem::setTargetFor(SyntacticElementTargetKey key,
                                    SyntacticElementTarget target) {
  bool inserted = targets.insert({key, target}).second;
  ASSERT(inserted);

  if (solverState)
    recordChange(SolverTrail::Change::RecordedTarget(key));
}

void ConstraintSystem::removeTargetFor(SyntacticElementTargetKey key) {
  bool erased = targets.erase(key);
  ASSERT(erased);
}

std::optional<SyntacticElementTarget>
ConstraintSystem::getTargetFor(SyntacticElementTargetKey key) const {
  auto known = targets.find(key);
  if (known == targets.end())
    return std::nullopt;
  return known->second;
}

std::optional<SyntacticElementTarget>
Solution::getTargetFor(SyntacticElementTargetKey key) const {
  auto known = targets.find(key);
  if (known == targets.end())
    return std::nullopt;
  return known->second;
}

// MARK: Constraint generation

/// Check whether it makes sense to convert this element into a constraint.
static bool isViableElement(ASTNode element,
                            bool isForSingleValueStmtCompletion,
                            ConstraintSystem &cs) {
  if (auto *decl = element.dyn_cast<Decl *>()) {
    // - Ignore variable declarations, they are handled by pattern bindings;
    if (isa<VarDecl>(decl))
      return false;
  }

  if (auto *stmt = element.dyn_cast<Stmt *>()) {
    if (auto *braceStmt = dyn_cast<BraceStmt>(stmt)) {
      // Empty brace statements are not viable because they do not require
      // inference.
      if (braceStmt->empty())
        return false;

      // Skip if we're doing completion for a SingleValueStmtExpr, and have a
      // brace that doesn't involve a single expression, and doesn't have a
      // code completion token, as it won't contribute to the type of the
      // SingleValueStmtExpr. We also need to skip if the body has a ReturnStmt,
      // which isn't something that's currently allowed, but is necessary to
      // correctly infer the contextual type without leaving it unbound.
      if (isForSingleValueStmtCompletion &&
          !SingleValueStmtExpr::hasResult(braceStmt) &&
          !cs.containsIDEInspectionTarget(braceStmt) &&
          !braceStmt->hasExplicitReturnStmt(cs.getASTContext())) {
        return false;
      }
    }
  }

  return true;
}

using ElementInfo = std::tuple<ASTNode, ContextualTypeInfo,
                               /*isDiscarded=*/bool, ConstraintLocator *>;

static void createConjunction(ConstraintSystem &cs, DeclContext *dc,
                              ArrayRef<ElementInfo> elements,
                              ConstraintLocator *locator, bool isIsolated,
                              ArrayRef<TypeVariableType *> extraTypeVars) {
  SmallVector<Constraint *, 4> constraints;
  SmallVector<TypeVariableType *, 2> referencedVars;
  referencedVars.append(extraTypeVars.begin(), extraTypeVars.end());

  if (locator->directlyAt<ClosureExpr>()) {
    auto *closure = castToExpr<ClosureExpr>(locator->getAnchor());
    // Conjunction associated with the body of the closure has to
    // reference a type variable representing closure type,
    // otherwise it would get disconnected from its contextual type.
    referencedVars.push_back(cs.getType(closure)->castTo<TypeVariableType>());

    // Result builder could be generic but attribute allows its use
    // in "unbound" form (i.e. `@Builder` where `Builder` is defined
    // as `struct Builder<T>`). Generic parameters of such a result
    // builder type are inferable from context, namely from `build*`
    // calls injected by the transform, and are not always resolved at
    // the time conjunction is created.
    //
    // Conjunction needs to reference all the type variables associated
    // with result builder just like parameters and result type of
    // the closure in order to stay connected to its context.
    if (auto builder = cs.getAppliedResultBuilderTransform(closure)) {
      SmallPtrSet<TypeVariableType *, 4> builderVars;
      builder->builderType->getTypeVariables(builderVars);
      referencedVars.append(builderVars.begin(), builderVars.end());
    }

    // Body of the closure is always isolated from its context, only
    // its individual elements are allowed access to type information
    // from the outside e.g. parameters/result type.
    isIsolated = true;
  }

  if (locator->isForSingleValueStmtConjunction()) {
    auto *SVE = castToExpr<SingleValueStmtExpr>(locator->getAnchor());
    referencedVars.push_back(cs.getType(SVE)->castTo<TypeVariableType>());

    // Single value statement conjunctions are always isolated, as we want to
    // solve the branches independently of the rest of the system.
    isIsolated = true;
  }

  if (locator->directlyAt<TapExpr>()) {
    // Body of the interpolation is always isolated from its context, only
    // its individual elements are allowed access to type information
    // from the outside e.g. external declaration references.
    isIsolated = true;
  }

  TypeVarRefCollector paramCollector(cs, dc, locator);

  // Whether we're doing completion, and the conjunction is for a
  // SingleValueStmtExpr, or one of its braces.
  const auto isForSingleValueStmtCompletion =
      cs.isForCodeCompletion() &&
      locator->isForSingleValueStmtConjunctionOrBrace();

  for (const auto &entry : elements) {
    ASTNode element = std::get<0>(entry);
    ContextualTypeInfo context = std::get<1>(entry);
    bool isDiscarded = std::get<2>(entry);
    ConstraintLocator *elementLoc = std::get<3>(entry);

    if (!isViableElement(element, isForSingleValueStmtCompletion, cs))
      continue;

    // If this conjunction going to represent a body of a closure,
    // let's collect references to not yet resolved outer
    // closure parameters.
    if (isIsolated)
      element.walk(paramCollector);

    constraints.push_back(Constraint::createSyntacticElement(
        cs, element, context, elementLoc, isDiscarded));
  }

  // It's possible that there are no viable elements in the body,
  // because e.g. whole body is an `#if` statement or it only has
  // declarations that are checked during solution application.
  // In such cases, let's avoid creating a conjunction.
  if (constraints.empty())
    return;

  for (auto *externalVar : paramCollector.getTypeVars())
    referencedVars.push_back(externalVar);

  cs.addUnsolvedConstraint(Constraint::createConjunction(
      cs, constraints, isIsolated, locator, referencedVars));
}

ElementInfo makeElement(ASTNode node, ConstraintLocator *locator,
                        ContextualTypeInfo context = ContextualTypeInfo(),
                        bool isDiscarded = false) {
  return std::make_tuple(node, context, isDiscarded, locator);
}

ElementInfo makeJoinElement(ConstraintSystem &cs, TypeJoinExpr *join,
                            ConstraintLocator *locator) {

  return makeElement(
      join, cs.getConstraintLocator(locator,
                                    {LocatorPathElt::SyntacticElement(join)}));
}

using SyntacticElementContextBase =
    llvm::PointerUnion<AbstractFunctionDecl *, AbstractClosureExpr *,
                       SingleValueStmtExpr *, ExprPattern *, TapExpr *,
                       CaptureListExpr *>;

struct SyntacticElementContext : public SyntacticElementContextBase {
  using Base = SyntacticElementContextBase;

  // Inherit the constructors from PointerUnion.
  using PointerUnion::PointerUnion;

  /// A join that should be applied to the elements of a SingleValueStmtExpr.
  NullablePtr<TypeJoinExpr> ElementJoin;

  static SyntacticElementContext forTapExpr(TapExpr *tap) { return {tap}; }

  static SyntacticElementContext forFunctionRef(AnyFunctionRef ref) {
    if (auto *decl = ref.getAbstractFunctionDecl()) {
      return {decl};
    }

    return {ref.getAbstractClosureExpr()};
  }

  static SyntacticElementContext forClosure(ClosureExpr *closure) {
    return {closure};
  }

  static SyntacticElementContext forFunction(AbstractFunctionDecl *func) {
    return {func};
  }

  static SyntacticElementContext forCaptureList(CaptureListExpr *CLE) {
    return {CLE};
  }

  static SyntacticElementContext
  forSingleValueStmtExpr(SingleValueStmtExpr *SVE,
                         TypeJoinExpr *Join = nullptr) {
    auto context = SyntacticElementContext{SVE};
    context.ElementJoin = Join;
    return context;
  }

  static SyntacticElementContext forExprPattern(ExprPattern *EP) {
    return SyntacticElementContext{EP};
  }

  DeclContext *getAsDeclContext() const {
    if (auto *fn = this->dyn_cast<AbstractFunctionDecl *>()) {
      return fn;
    } else if (auto *closure = this->dyn_cast<AbstractClosureExpr *>()) {
      return closure;
    } else if (auto *SVE = dyn_cast<SingleValueStmtExpr *>()) {
      return SVE->getDeclContext();
    } else if (auto *EP = dyn_cast<ExprPattern *>()) {
      return EP->getDeclContext();
    } else if (auto *tap = this->dyn_cast<TapExpr *>()) {
      return tap->getVar()->getDeclContext();
    } else if (auto *CLE = this->dyn_cast<CaptureListExpr *>()) {
      // The capture list is part of the closure's parent context.
      return CLE->getClosureBody()->getParent();
    } else {
      llvm_unreachable("unsupported kind");
    }
  }

  NullablePtr<ClosureExpr> getAsClosureExpr() const {
    return dyn_cast_or_null<ClosureExpr>(
        this->dyn_cast<AbstractClosureExpr *>());
  }

  NullablePtr<AbstractClosureExpr> getAsAbstractClosureExpr() const {
    return this->dyn_cast<AbstractClosureExpr *>();
  }

  NullablePtr<AbstractFunctionDecl> getAsAbstractFunctionDecl() const {
    return this->dyn_cast<AbstractFunctionDecl *>();
  }

  NullablePtr<SingleValueStmtExpr> getAsSingleValueStmtExpr() const {
    return this->dyn_cast<SingleValueStmtExpr *>();
  }

  std::optional<AnyFunctionRef> getAsAnyFunctionRef() const {
    if (auto *fn = this->dyn_cast<AbstractFunctionDecl *>()) {
      return {fn};
    } else if (auto *closure = this->dyn_cast<AbstractClosureExpr *>()) {
      return {closure};
    } else {
      return std::nullopt;
    }
  }

  Stmt *getStmt() const {
    if (auto *fn = this->dyn_cast<AbstractFunctionDecl *>()) {
      return fn->getBody();
    } else if (auto *closure = this->dyn_cast<AbstractClosureExpr *>()) {
      return closure->getBody();
    } else if (auto *SVE = dyn_cast<SingleValueStmtExpr *>()) {
      return SVE->getStmt();
    } else if (auto *tap = this->dyn_cast<TapExpr *>()) {
      return tap->getBody();
    } else {
      llvm_unreachable("unsupported kind");
    }
  }

  bool isSingleExpressionClosure(ConstraintSystem &cs) const {
    if (auto ref = getAsAnyFunctionRef()) {
      if (cs.getAppliedResultBuilderTransform(*ref))
        return false;

      if (auto *closure = ref->getAbstractClosureExpr())
        return closure->hasSingleExpressionBody();
    }

    return false;
  }
};

// Produce an implicit empty tuple expression.
static Expr *getVoidExpr(ASTContext &ctx, SourceLoc contextLoc = SourceLoc()) {
  auto *voidExpr = TupleExpr::createEmpty(ctx,
                                          /*LParenLoc=*/contextLoc,
                                          /*RParenLoc=*/contextLoc,
                                          /*Implicit=*/true);
  voidExpr->setType(ctx.TheEmptyTupleType);
  return voidExpr;
}

namespace {

/// Statement visitor that generates constraints for a given closure body.
class SyntacticElementConstraintGenerator
    : public StmtVisitor<SyntacticElementConstraintGenerator, void> {
  friend StmtVisitor<SyntacticElementConstraintGenerator, void>;

  ConstraintSystem &cs;
  SyntacticElementContext context;
  ConstraintLocator *locator;

  std::optional<llvm::SaveAndRestore<DeclContext *>> DCScope;

  /// Whether a conjunction was generated.
  bool generatedConjunction = false;

public:
  /// Whether an error was encountered while generating constraints.
  bool hadError = false;

  SyntacticElementConstraintGenerator(ConstraintSystem &cs,
                                      SyntacticElementContext context,
                                      ConstraintLocator *locator)
      : cs(cs), context(context), locator(locator) {
    // Capture list bindings in multi-statement closures get solved as part of
    // the closure's conjunction, which has the DeclContext set to the closure.
    // This is wrong for captures though, which are semantically bound outside
    // of the closure body. So we need to re-adjust their DeclContext here for
    // constraint generation. The constraint system's DeclContext will be wrong
    // for solving, but CSGen should ensure that constraints carry the correct
    // DeclContext.
    if (isa<CaptureListExpr *>(context))
      DCScope.emplace(cs.DC, context.getAsDeclContext());
  }

  void createConjunction(ArrayRef<ElementInfo> elements,
                         ConstraintLocator *locator, bool isIsolated = false,
                         ArrayRef<TypeVariableType *> extraTypeVars = {}) {
    assert(!generatedConjunction && "Already generated conjunction");
    generatedConjunction = true;

    // Inject a join if we have one.
    SmallVector<ElementInfo, 4> scratch;
    if (auto *join = context.ElementJoin.getPtrOrNull()) {
      scratch.append(elements.begin(), elements.end());
      scratch.push_back(makeJoinElement(cs, join, locator));
      elements = scratch;
    }
    ::createConjunction(cs, context.getAsDeclContext(), elements, locator,
                        isIsolated, extraTypeVars);
  }

  void visitExprPattern(ExprPattern *EP) {
    auto target = SyntacticElementTarget::forExprPattern(EP);

    if (cs.preCheckTarget(target)) {
      hadError = true;
      return;
    }
    cs.setType(EP->getMatchVar(), cs.getType(EP));

    if (cs.generateConstraints(target)) {
      hadError = true;
      return;
    }
    cs.setTargetFor(EP, target);
    cs.setExprPatternFor(EP->getSubExpr(), EP);
  }

  void visitPattern(Pattern *pattern, ContextualTypeInfo contextInfo) {
    if (isa<ExprPattern *>(context)) {
      // This is for an ExprPattern conjunction, go ahead and generate
      // constraints for the match expression.
      visitExprPattern(cast<ExprPattern>(pattern));
      return;
    }

    auto parentElement =
        locator->getLastElementAs<LocatorPathElt::SyntacticElement>();

    if (!parentElement) {
      hadError = true;
      return;
    }

    if (auto *stmt = parentElement->getElement().dyn_cast<Stmt *>()) {
      if (isa<ForEachStmt>(stmt)) {
        visitForEachPattern(pattern, cast<ForEachStmt>(stmt));
        return;
      }

      if (isa<CaseStmt>(stmt)) {
        visitCaseItemPattern(pattern, contextInfo);
        return;
      }
    }

    llvm_unreachable("Unsupported pattern");
  }

  void visitCaseItem(CaseLabelItem *caseItem, ContextualTypeInfo contextInfo) {
    assert(contextInfo.purpose == CTP_CaseStmt);

    auto *DC = context.getAsDeclContext();
    auto &ctx = DC->getASTContext();

    // Resolve the pattern.
    auto *pattern = caseItem->getPattern();
    if (!caseItem->isPatternResolved()) {
      pattern = TypeChecker::resolvePattern(pattern, context.getAsDeclContext(),
                                            /*isStmtCondition=*/false);
      if (!pattern) {
        hadError = true;
        return;
      }

      caseItem->setPattern(pattern, /*resolved=*/true);
    }

    // Let's generate constraints for pattern + where clause.
    // The assumption is that this shouldn't be too complex
    // to handle, but if it turns out to be false, this could
    // always be converted into a conjunction.

    // Generate constraints for pattern.
    visitPattern(pattern, contextInfo);

    auto *guardExpr = caseItem->getGuardExpr();

    // Generate constraints for `where` clause (if any).
    if (guardExpr) {
      SyntacticElementTarget guardTarget(
          guardExpr, DC, CTP_Condition, ctx.getBoolType(), /*discarded*/ false);

      if (cs.generateConstraints(guardTarget)) {
        hadError = true;
        return;
      }
      guardExpr = guardTarget.getAsExpr();
      cs.setTargetFor(guardExpr, guardTarget);
    }

    // Save information about case item so it could be referenced during
    // solution application.
    cs.setCaseLabelItemInfo(caseItem, {pattern, guardExpr});
  }

private:
  /// This method handles both pattern and the sequence expression
  /// associated with `for-in` loop because types in this situation
  /// flow in both directions:
  ///
  /// - From pattern to sequence, informing its element type e.g.
  ///   `for i: Int8 in 0 ..< 8`
  ///
  /// - From sequence to pattern, when pattern has no type information.
  void visitForEachPattern(Pattern *pattern, ForEachStmt *forEachStmt) {
    auto target = SyntacticElementTarget::forForEachPreamble(
        forEachStmt, context.getAsDeclContext());

    if (cs.generateConstraints(target)) {
      hadError = true;
      return;
    }

    // After successful constraint generation, let's record
    // syntactic element target with all relevant information.
    cs.setTargetFor(forEachStmt, target);
  }

  void visitCaseItemPattern(Pattern *pattern, ContextualTypeInfo context) {
    Type patternType = cs.generateConstraints(
        pattern, locator, /*bindPatternVarsOneWay=*/false,
        /*patternBinding=*/nullptr, /*patternIndex=*/0);

    if (!patternType) {
      hadError = true;
      return;
    }

    // Convert the contextual type to the pattern, which establishes the
    // bindings.
    auto *loc = cs.getConstraintLocator(
        locator, {LocatorPathElt::PatternMatch(pattern),
                  LocatorPathElt::ContextualType(context.purpose)});
    cs.addConstraint(ConstraintKind::Equal, context.getType(), patternType,
                     loc);
  }

  void visitPatternBinding(PatternBindingDecl *patternBinding,
                           SmallVectorImpl<ElementInfo> &patterns) {
    auto *baseLoc = cs.getConstraintLocator(
        locator, LocatorPathElt::SyntacticElement(patternBinding));

    for (unsigned index : range(patternBinding->getNumPatternEntries())) {
      if (patternBinding->isInitializerChecked(index))
        continue;

      auto *pattern = TypeChecker::resolvePattern(
          patternBinding->getPattern(index), patternBinding->getDeclContext(),
          /*isStmtCondition=*/true);

      if (!pattern) {
        hadError = true;
        return;
      }

      // Reset binding to point to the resolved pattern. This is required
      // before calling `forPatternBindingDecl`.
      patternBinding->setPattern(index, pattern);

      patterns.push_back(makeElement(
          patternBinding,
          cs.getConstraintLocator(
              baseLoc, LocatorPathElt::PatternBindingElement(index))));
    }
  }

  std::optional<SyntacticElementTarget>
  getTargetForPattern(PatternBindingDecl *patternBinding, unsigned index,
                      Type patternType) {
    auto hasPropertyWrapper = [&](Pattern *pattern) -> bool {
      if (auto *singleVar = pattern->getSingleVar())
        return singleVar->hasAttachedPropertyWrapper();
      return false;
    };

    auto *pattern = patternBinding->getPattern(index);
    auto *init = patternBinding->getInit(index);

    if (!init && patternBinding->isDefaultInitializable(index) &&
        pattern->hasStorage()) {
      init = TypeChecker::buildDefaultInitializer(patternType);
    }

    // A property wrapper initializer (either user-defined
    // or a synthesized one) has to be pre-checked before use.
    //
    // This is not a problem in top-level code because pattern
    // bindings go through `typeCheckExpression` which does
    // pre-check automatically and result builders do not allow
    // declaring local wrapped variables (yet).
    if (hasPropertyWrapper(pattern)) {
      auto target = SyntacticElementTarget::forInitialization(
          init, patternType, patternBinding, index,
          /*bindPatternVarsOneWay=*/false);

      if (ConstraintSystem::preCheckTarget(target))
        return std::nullopt;

      return target;
    }

    if (init) {
      return SyntacticElementTarget::forInitialization(
          init, patternType, patternBinding, index,
          /*bindPatternVarsOneWay=*/false);
    }

    return SyntacticElementTarget::forUninitializedVar(patternBinding, index,
                                                       patternType);
  }

  void setPropertyWrapperAuxiliaryTypes(const SyntacticElementTarget &target) {
    if (!target.isForInitialization())
      return;

    auto *wrappedVar = target.getInitializationWrappedVar();
    if (!wrappedVar)
      return;

    auto *outermostAttr = wrappedVar->getOutermostAttachedPropertyWrapper();
    auto *wrapperTypeExpr = outermostAttr->getTypeExpr();

    auto wrapperTy = cs.simplifyType(cs.getType(wrapperTypeExpr));
    if (auto *projectedVal = wrappedVar->getPropertyWrapperProjectionVar()) {
      auto projectedTy = computeProjectedValueType(wrappedVar, wrapperTy);
      // The projected type may have an error, make sure we turn it into a hole
      // if necessary.
      ASSERT(!projectedTy->hasUnboundGenericType());
      projectedTy = cs.replaceInferableTypesWithTypeVars(projectedTy, locator);
      cs.setType(projectedVal, projectedTy);
    }
    if (auto *backing = wrappedVar->getPropertyWrapperBackingProperty())
      cs.setType(backing, wrapperTy);
  }

  void visitPatternBindingElement(PatternBindingDecl *patternBinding) {
    assert(locator->isLastElement<LocatorPathElt::PatternBindingElement>());

    auto index =
        locator->castLastElementTo<LocatorPathElt::PatternBindingElement>()
            .getIndex();

    if (patternBinding->isInitializerChecked(index))
      return;

    auto contextualPattern =
        ContextualPattern::forPatternBindingDecl(patternBinding, index);
    Type patternType = TypeChecker::typeCheckPattern(contextualPattern);

    if (patternType->hasError()) {
      // Fail immediately if this is an invalid pattern and the solver is not
      // in the diagnostic mode.
      if (!cs.shouldAttemptFixes()) {
        hadError = true;
        return;
      }

      // In diagnostic mode, let's let the type through. It would get pattern
      // type turned into a hole during the constraint generation for the
      // pattern.
    }

    auto target = getTargetForPattern(patternBinding, index, patternType);
    if (!target) {
      hadError = true;
      return;
    }

    // Keep track of this binding entry.
    cs.setTargetFor({patternBinding, index}, *target);

    if (isPlaceholderVar(patternBinding))
      return;

    if (cs.generateConstraints(*target)) {
      hadError = true;
      return;
    }

    // Set the types of any auxiliary property wrapper vars.
    setPropertyWrapperAuxiliaryTypes(*target);
  }

  void visitDecl(Decl *decl) {
    if (!context.isSingleExpressionClosure(cs)) {
      if (auto patternBinding = dyn_cast<PatternBindingDecl>(decl)) {
        if (locator->isLastElement<LocatorPathElt::PatternBindingElement>())
          visitPatternBindingElement(patternBinding);
        else
          llvm_unreachable("cannot visit pattern binding directly");
        return;
      }
    }

    // Ignore variable declarations, because they're always handled within
    // their enclosing pattern bindings.
    if (isa<VarDecl>(decl))
      return;

    // Other declarations will be handled at application time.
  }

  // These statements don't require any type-checking.
  void visitOpaqueStmt(OpaqueStmt *opaqueStmt) {}
  void visitBreakStmt(BreakStmt *breakStmt) {}
  void visitContinueStmt(ContinueStmt *continueStmt) {}
  void visitDeferStmt(DeferStmt *deferStmt) {}
  void visitFallthroughStmt(FallthroughStmt *fallthroughStmt) {}
  void visitFailStmt(FailStmt *fail) {}

  void visitStmtCondition(LabeledConditionalStmt *S,
                          SmallVectorImpl<ElementInfo> &elements,
                          ConstraintLocator *locator) {
    auto *condLocator =
        cs.getConstraintLocator(locator, ConstraintLocator::Condition);
    for (auto &condition : S->getCond())
      elements.push_back(makeElement(&condition, condLocator));
  }

  void visitIfStmt(IfStmt *ifStmt) {
    SmallVector<ElementInfo, 4> elements;

    // Condition
    visitStmtCondition(ifStmt, elements, locator);

    // Then Branch
    {
      auto *thenLoc = cs.getConstraintLocator(
        locator, LocatorPathElt::TernaryBranch(/*then=*/true));
      elements.push_back(makeElement(ifStmt->getThenStmt(), thenLoc));
    }

    // Else Branch (if any).
    if (auto *elseStmt = ifStmt->getElseStmt()) {
      auto *elseLoc = cs.getConstraintLocator(
          locator, LocatorPathElt::TernaryBranch(/*then=*/false));
      elements.push_back(makeElement(elseStmt, elseLoc));
    }

    createConjunction(elements, locator);
  }

  void visitGuardStmt(GuardStmt *guardStmt) {
    SmallVector<ElementInfo, 4> elements;

    visitStmtCondition(guardStmt, elements, locator);
    elements.push_back(makeElement(guardStmt->getBody(), locator));

    createConjunction(elements, locator);
  }

  void visitWhileStmt(WhileStmt *whileStmt) {
    SmallVector<ElementInfo, 4> elements;

    visitStmtCondition(whileStmt, elements, locator);
    elements.push_back(makeElement(whileStmt->getBody(), locator));

    createConjunction(elements, locator);
  }

  void visitDoStmt(DoStmt *doStmt) {
    visitBraceStmt(doStmt->getBody());
  }

  void visitRepeatWhileStmt(RepeatWhileStmt *repeatWhileStmt) {
    createConjunction({makeElement(repeatWhileStmt->getCond(),
                                   cs.getConstraintLocator(
                                       locator, ConstraintLocator::Condition),
                                   getContextForCondition()),
                       makeElement(repeatWhileStmt->getBody(), locator)},
                      locator);
  }

  void visitPoundAssertStmt(PoundAssertStmt *poundAssertStmt) {
    createConjunction({makeElement(poundAssertStmt->getCondition(),
                                   cs.getConstraintLocator(
                                       locator, ConstraintLocator::Condition),
                                   getContextForCondition())},
                      locator);
  }

  void visitThrowStmt(ThrowStmt *throwStmt) {
    // Look up the catch node for this "throw" to determine the error type.
    auto dc = context.getAsDeclContext();
    auto module = dc->getParentModule();
    auto throwLoc = throwStmt->getThrowLoc();
    Type errorType;
    if (auto catchNode = ASTScope::lookupCatchNode(module, throwLoc)) {
      errorType = cs.getExplicitCaughtErrorType(catchNode);

      if (!errorType) {
        if (auto *closure = catchNode.dyn_cast<ClosureExpr *>()) {
          auto *closureTy =
              cs.simplifyType(cs.getType(closure))->castTo<FunctionType>();
          errorType = closureTy->getEffectiveThrownErrorTypeOrNever();
        }
      }
    }

    if (!errorType) {
      if (!cs.getASTContext().getErrorDecl()) {
        hadError = true;
        return;
      }

      errorType = cs.getASTContext().getErrorExistentialType();
    }

    auto *errorExpr = throwStmt->getSubExpr();

    createConjunction(
        {makeElement(errorExpr,
                     cs.getConstraintLocator(
                         locator, LocatorPathElt::SyntacticElement(errorExpr)),
                     {errorType, CTP_ThrowStmt})},
        locator);
  }

  void visitDiscardStmt(DiscardStmt *discardStmt) {
    auto *fn = discardStmt->getInnermostMethodContext();
    if (!fn) {
      hadError = true;
      return;
    }

    auto nominal = fn->getDeclContext()->getSelfNominalTypeDecl();
    if (!nominal) {
      hadError = true;
      return;
    }
    auto nominalType = nominal->getDeclaredType();
    auto *selfExpr = discardStmt->getSubExpr();

    createConjunction(
        {makeElement(selfExpr,
                     cs.getConstraintLocator(
                         locator, LocatorPathElt::SyntacticElement(selfExpr)),
                     {nominalType, CTP_DiscardStmt})},
        locator);
  }

  void visitForEachStmt(ForEachStmt *forEachStmt) {
    auto *stmtLoc = cs.getConstraintLocator(locator);

    SmallVector<ElementInfo, 4> elements;

    // For-each pattern.
    //
    // Note that we don't record a sequence here, it would be handled
    // together with pattern because pattern can inform a type of sequence
    // element e.g. `for i: Int8 in 0 ..< 8`
    elements.push_back(makeElement(forEachStmt->getPattern(), stmtLoc));

    // Where clause if any.
    if (auto *where = forEachStmt->getWhere()) {
      Type boolType = cs.getASTContext().getBoolType();
      if (!boolType) {
        hadError = true;
        return;
      }

      ContextualTypeInfo context(boolType, CTP_Condition);
      elements.push_back(
          makeElement(where, stmtLoc, context, /*isDiscarded=*/false));
    }

    // Body of the `for-in` loop.
    elements.push_back(makeElement(forEachStmt->getBody(), stmtLoc));

    createConjunction(elements, locator);
  }

  void visitSwitchStmt(SwitchStmt *switchStmt) {
    SmallVector<ElementInfo, 4> elements;
    {
      auto *subjectExpr = switchStmt->getSubjectExpr();
      {
        elements.push_back(makeElement(subjectExpr, locator));

        SyntacticElementTarget target(subjectExpr, context.getAsDeclContext(),
                                      CTP_Unused, Type(),
                                      /*isDiscarded=*/false);

        cs.setTargetFor(switchStmt, target);
      }

      for (auto &CS : switchStmt->getCases())
        elements.push_back(makeElement(CS, locator));
    }

    createConjunction(elements, locator);
  }

  void visitDoCatchStmt(DoCatchStmt *doStmt) {
    SmallVector<ElementInfo, 4> elements;

    // First, let's record a body of `do` statement. Note we need to add a
    // SyntaticElement locator path element here to avoid treating the inner
    // brace conjunction as being isolated if 'doLoc' is for an isolated
    // conjunction (as is the case with 'do' expressions).
    auto *doBodyLoc = cs.getConstraintLocator(
        locator, LocatorPathElt::SyntacticElement(doStmt->getBody()));
    elements.push_back(makeElement(doStmt->getBody(), doBodyLoc));

    // After that has been type-checked, let's switch to
    // individual `catch` statements.
    for (auto *catchStmt : doStmt->getCatches())
      elements.push_back(makeElement(catchStmt, locator));

    createConjunction(elements, locator);
  }

  void visitCaseStmt(CaseStmt *caseStmt) {
    Type contextualTy;

    {
      auto parent =
          locator->castLastElementTo<LocatorPathElt::SyntacticElement>()
              .getElement();

      if (parent.isStmt(StmtKind::Switch)) {
        auto *switchStmt = cast<SwitchStmt>(cast<Stmt *>(parent));
        contextualTy = cs.getType(switchStmt->getSubjectExpr());
      } else if (auto doCatch =
                     dyn_cast_or_null<DoCatchStmt>(parent.dyn_cast<Stmt *>())) {
        contextualTy = cs.getCaughtErrorType(doCatch);

        // A non-exhaustive do..catch statement is a potential throw site.
        if (caseStmt == doCatch->getCatches().back() &&
            !doCatch->isSyntacticallyExhaustive()) {
          cs.recordPotentialThrowSite(
              PotentialThrowSite::NonExhaustiveDoCatch, contextualTy,
              cs.getConstraintLocator(doCatch));
        }
      } else {
        hadError = true;
        return;
      }
    }

    auto *caseLoc = cs.getConstraintLocator(
        locator, LocatorPathElt::SyntacticElement(caseStmt));

    SmallVector<ElementInfo, 4> elements;
    for (auto &caseLabelItem : caseStmt->getMutableCaseLabelItems()) {
      elements.push_back(
          makeElement(&caseLabelItem, caseLoc, {contextualTy, CTP_CaseStmt}));
    }

    elements.push_back(makeElement(caseStmt->getBody(), caseLoc));

    createConjunction(elements, caseLoc);
  }

  void visitBraceStmt(BraceStmt *braceStmt) {
    auto &ctx = cs.getASTContext();

    CaptureListExpr *captureList = nullptr;
    {
      if (locator->directlyAt<ClosureExpr>()) {
        auto *closure = castToExpr<ClosureExpr>(locator->getAnchor());
        captureList = getAsExpr<CaptureListExpr>(cs.getParentExpr(closure));
      }
    }

    if (context.isSingleExpressionClosure(cs)) {
      // Generate constraints for the capture list first.
      //
      // TODO: This should be a conjunction connected to
      // the closure body to make sure that each capture
      // is solved in isolation.
      if (captureList) {
        for (const auto &capture : captureList->getCaptureList()) {
          SyntacticElementTarget target(capture.PBD);
          if (cs.generateConstraints(target)) {
            hadError = true;
            return;
          }
        }
      }

      for (auto node : braceStmt->getElements()) {
        if (auto expr = node.dyn_cast<Expr *>()) {
          auto generatedExpr =
              cs.generateConstraints(expr, context.getAsDeclContext());
          if (!generatedExpr) {
            hadError = true;
          }
        } else if (auto stmt = node.dyn_cast<Stmt *>()) {
          visit(stmt);
        } else {
          visitDecl(cast<Decl *>(node));
        }
      }
      return;
    }

    SmallVector<ElementInfo, 4> elements;

    // If this brace statement represents a body of an empty or
    // multi-statement closure.
    if (locator->directlyAt<ClosureExpr>()) {
      auto *closure = context.getAsClosureExpr().get();
      // If this closure has an empty body or no `return` statements with
      // results let's bind result type to `Void` since that's the only type
      // empty body can produce.
      //
      // Note that result builder bodies always have a `return` statement
      // at the end, so they don't need to be defaulted.
      if (!cs.getAppliedResultBuilderTransform({closure}) &&
          !hasResultExpr(closure)) {
        auto constraintKind =
            (closure->hasEmptyBody() && !closure->hasExplicitResultType())
                ? ConstraintKind::Bind
                : ConstraintKind::Defaultable;

        cs.addConstraint(
            constraintKind, cs.getClosureType(closure)->getResult(),
            ctx.TheEmptyTupleType,
            cs.getConstraintLocator(closure, ConstraintLocator::ClosureResult));
      }

      // If this multi-statement closure has captures, let's solve
      // them first.
      if (captureList) {
        for (const auto &capture : captureList->getCaptureList())
          visitPatternBinding(capture.PBD, elements);
      }
    }

    if (isChildOf(StmtKind::Case)) {
      auto *caseStmt = cast<CaseStmt>(
          locator->castLastElementTo<LocatorPathElt::SyntacticElement>()
              .asStmt());

      if (recordInferredSwitchCasePatternVars(caseStmt)) {
        hadError = true;
      }
    }

    for (auto element : braceStmt->getElements()) {
      if (cs.isForCodeCompletion() &&
          !cs.containsIDEInspectionTarget(element)) {
        // To improve performance, skip type checking elements that can't
        // influence the code completion token.
        if (isa<Stmt *>(element) && !element.isStmt(StmtKind::Guard) &&
            !element.isStmt(StmtKind::Return) &&
            !element.isStmt(StmtKind::Then)) {
          // Statements can't influence the expresion that contains the code
          // completion token.
          // Guard statements might define variables that are used in the code
          // completion expression. Don't skip them.
          // Return statements influence the type of the closure itself. Don't
          // skip them either.
          continue;
        }
        if (element.isExpr(ExprKind::Assign)) {
          // Assignments are also similar to statements and can't influence the
          // code completion token.
          continue;
        }
        if (element.isExpr(ExprKind::Error)) {
          // ErrorExpr can't influcence the expresssion that contains the code
          // completion token. Since they are causing type checking to abort
          // early, just skip them.
          continue;
        }
      }

      if (auto *decl = element.dyn_cast<Decl *>()) {
        if (auto *PDB = dyn_cast<PatternBindingDecl>(decl)) {
          visitPatternBinding(PDB, elements);
          continue;
        }
      }

      bool isDiscarded = false;
      auto contextInfo = cs.getContextualTypeInfo(element);

      if (isa<Expr *>(element) && !ctx.LangOpts.Playground &&
          !ctx.LangOpts.DebuggerSupport) {
        isDiscarded = !contextInfo || contextInfo->purpose == CTP_Unused;
      }

      elements.push_back(makeElement(
          element,
          cs.getConstraintLocator(locator,
                                  LocatorPathElt::SyntacticElement(element)),
          contextInfo.value_or(ContextualTypeInfo()), isDiscarded));
    }

    createConjunction(elements, locator);
  }

  void visitReturnStmt(ReturnStmt *returnStmt) {
    // Record an implied result if we have one.
    if (returnStmt->isImplied()) {
      auto kind = context.getAsClosureExpr() ? ImpliedResultKind::ForClosure
                                             : ImpliedResultKind::Regular;
      auto *result = returnStmt->getResult();
      cs.recordImpliedResult(result, kind);
    }

    Expr *resultExpr;
    if (returnStmt->hasResult()) {
      resultExpr = returnStmt->getResult();
      assert(resultExpr && "non-empty result without expression?");
    } else {
      // If this is simplify `return`, let's create an empty tuple
      // which is also useful if contextual turns out to be e.g. `Void?`.
      // Also, attach return stmt source location so if there is a contextual
      // mismatch we can produce a diagnostic in a valid source location.
      resultExpr = getVoidExpr(cs.getASTContext(), returnStmt->getEndLoc());
    }

    auto contextualResultInfo = getContextualResultInfoFor(returnStmt);

    SyntacticElementTarget target(resultExpr, context.getAsDeclContext(),
                                  contextualResultInfo, /*isDiscarded=*/false);

    if (cs.generateConstraints(target)) {
      hadError = true;
      return;
    }

    cs.setContextualInfo(target.getAsExpr(), contextualResultInfo);
    cs.setTargetFor(returnStmt, target);
  }

  void visitThenStmt(ThenStmt *thenStmt) {
    auto *resultExpr = thenStmt->getResult();
    auto contextInfo = cs.getContextualTypeInfo(resultExpr);

    // First check to make sure the ThenStmt is in a valid position.
    SmallVector<ThenStmt *, 4> validThenStmts;
    if (auto SVE = context.getAsSingleValueStmtExpr()) {
      (void)SVE.get()->getThenStmts(validThenStmts);
      if (SVE.get()->getStmtKind() == SingleValueStmtExpr::Kind::For) {
        contextInfo = std::nullopt;
      }
    }

    if (!llvm::is_contained(validThenStmts, thenStmt)) {
      auto *thenLoc = cs.getConstraintLocator(thenStmt);
      (void)cs.recordFix(IgnoreOutOfPlaceThenStmt::create(cs, thenLoc));
    }

    // For an if/switch expression, if the contextual type for the branch is
    // still a type variable, we can drop it. This avoids needlessly
    // propagating the type of the branch to subsequent branches, instead
    // we'll let the join handle the conversion.
    if (contextInfo) {
      auto contextualFixedTy =
          cs.getFixedTypeRecursive(contextInfo->getType(), /*wantRValue*/ true);
      if (contextualFixedTy->isTypeVariableOrMember())
        contextInfo = std::nullopt;
    }

    // We form a single element conjunction here to ensure the context type var
    // gets taken out of the active type vars (assuming we dropped it) before we
    // produce a solution.
    auto resultElt = makeElement(resultExpr, locator,
                                 contextInfo.value_or(ContextualTypeInfo()),
                                 /*isDiscarded=*/false);
    createConjunction({resultElt}, locator);
  }

  ContextualTypeInfo getContextualResultInfoFor(ReturnStmt *returnStmt) const {
    auto funcRef = AnyFunctionRef::fromDeclContext(context.getAsDeclContext());
    if (!funcRef)
      return {Type(), CTP_Unused};

    if (auto transform = cs.getAppliedResultBuilderTransform(*funcRef))
      return {transform->bodyResultType, CTP_ReturnStmt};

    if (auto *closure =
            getAsExpr<ClosureExpr>(funcRef->getAbstractClosureExpr())) {
      // Single-expression closures need their contextual type locator anchored
      // on the closure itself. Otherwise we use the default contextual type
      // locator, which will be created for us.
      ConstraintLocator *loc = nullptr;
      if (context.isSingleExpressionClosure(cs) && returnStmt->hasResult())
        loc = cs.getConstraintLocator(closure, {LocatorPathElt::ClosureBody()});

      return {cs.getClosureType(closure)->getResult(), CTP_ClosureResult, loc};
    }

    return {funcRef->getBodyResultType(), CTP_ReturnStmt};
  }

#define UNSUPPORTED_STMT(STMT) void visit##STMT##Stmt(STMT##Stmt *) { \
      llvm_unreachable("Unsupported statement kind " #STMT);          \
  }
  UNSUPPORTED_STMT(Yield)
#undef UNSUPPORTED_STMT

private:
  ContextualTypeInfo getContextForCondition() const {
    auto boolDecl = cs.getASTContext().getBoolDecl();
    assert(boolDecl && "Bool is missing");
    return {boolDecl->getDeclaredInterfaceType(), CTP_Condition};
  }

  bool isChildOf(StmtKind kind) {
    if (locator->getPath().empty())
      return false;

    auto parentElt =
        locator->getLastElementAs<LocatorPathElt::SyntacticElement>();
    return parentElt ? parentElt->getElement().isStmt(kind) : false;
  }

  bool recordInferredSwitchCasePatternVars(CaseStmt *caseStmt) {
    llvm::SmallDenseMap<Identifier, SmallVector<VarDecl *, 2>, 4> patternVars;

    auto recordVar = [&](VarDecl *var) {
      if (!var->hasName())
        return;
      patternVars[var->getName()].push_back(var);
    };

    for (auto &caseItem : caseStmt->getMutableCaseLabelItems()) {
      assert(caseItem.isPatternResolved());

      auto *pattern = caseItem.getPattern();
      pattern->forEachVariable([&](VarDecl *var) { recordVar(var); });
    }

    for (auto bodyVar : caseStmt->getCaseBodyVariables()) {
      if (!bodyVar->hasName())
        continue;

      const auto &variants = patternVars[bodyVar->getName()];

      auto getType = [&](VarDecl *var) {
        auto type = cs.simplifyType(cs.getType(var));
        assert(!type->hasTypeVariable());
        return type;
      };

      switch (variants.size()) {
      case 0:
        break;

      case 1:
        // If there is only one choice here, let's use it directly.
        cs.setType(bodyVar, getType(variants.front()));
        break;

      default: {
        // If there are multiple choices it could only mean multiple
        // patterns e.g. `.a(let x), .b(let x), ...:`. Let's join them.
        Type joinType = getType(variants.front());

        SmallVector<VarDecl *, 2> conflicts;
        for (auto *var : llvm::drop_begin(variants)) {
          auto varType = getType(var);
          // Type mismatch between different patterns.
          if (!joinType->isEqual(varType))
            conflicts.push_back(var);
        }

        if (!conflicts.empty()) {
          if (!cs.shouldAttemptFixes())
            return true;

          // dfdf
          auto *locator = cs.getConstraintLocator(bodyVar);
          if (cs.recordFix(RenameConflictingPatternVariables::create(
                  cs, joinType, conflicts, locator)))
            return true;
        }

        cs.setType(bodyVar, joinType);
      }
      }
    }

    return false;
  }
};

}

namespace llvm {

using ::SyntacticElementContext;

/// `isa`, `dyn_cast`, `cast` for `SyntacticElementContext`.
template <typename To>
struct CastInfo<To, SyntacticElementContext>
    : public CastInfo<To, SyntacticElementContext::Base> {};
template <typename To>
struct CastInfo<To, const SyntacticElementContext>
    : public CastInfo<To, const SyntacticElementContext::Base> {};

} // end namespace llvm

bool ConstraintSystem::generateConstraints(TapExpr *tap) {
  SyntacticElementConstraintGenerator generator(
      *this, SyntacticElementContext::forTapExpr(tap),
      getConstraintLocator(tap));

  auto *body = tap->getBody();

  if (!body) {
    assert(tap->getSubExpr());
    return false;
  }

  generator.visit(tap->getBody());
  return generator.hadError;
}

bool ConstraintSystem::generateConstraints(AnyFunctionRef fn, BraceStmt *body) {
  NullablePtr<ConstraintLocator> locator;

  if (auto *func = fn.getAbstractFunctionDecl()) {
    locator = getConstraintLocator(func);
  } else {
    locator = getConstraintLocator(fn.getAbstractClosureExpr());
  }

  SyntacticElementConstraintGenerator generator(
      *this, SyntacticElementContext::forFunctionRef(fn), locator.get());

  generator.visit(body);

  return generator.hadError;
}

bool ConstraintSystem::generateConstraints(SingleValueStmtExpr *E) {
  auto *S = E->getStmt();
  auto &ctx = getASTContext();

  auto *loc = getConstraintLocator(E);
  Type resultType = createTypeVariable(loc, /*options*/ 0);
  setType(E, resultType);

  if (E->getStmtKind() == SingleValueStmtExpr::Kind::For) {
    auto *rrcProtocol =
        ctx.getProtocol(KnownProtocolKind::RangeReplaceableCollection);
    auto *sequenceProtocol = ctx.getProtocol(KnownProtocolKind::Sequence);

    addConstraint(ConstraintKind::ConformsTo, resultType,
                  rrcProtocol->getDeclaredInterfaceType(), loc);
    Type elementTypeVar = createTypeVariable(loc, /*options*/ 0);
    Type elementType = DependentMemberType::get(
        resultType, sequenceProtocol->getAssociatedType(ctx.Id_Element));

    addConstraint(ConstraintKind::Bind, elementTypeVar, elementType, loc);
    addConstraint(ConstraintKind::Defaultable, resultType,
                  ArraySliceType::get(elementTypeVar), loc);

    auto *binding = E->getForExpressionPreamble()->ForAccumulatorBinding;

    auto *initializer = binding->getInit(0);
    auto target = SyntacticElementTarget::forInitialization(initializer, Type(),
                                                            binding, 0, false);
    setTargetFor({binding, 0}, target);

    if (generateConstraints(target)) {
      return true;
    }

    addConstraint(ConstraintKind::Bind, getType(initializer), resultType, loc);
  }

  // Propagate the implied result kind from the if/switch expression itself
  // into the branches.
  auto impliedResultKind =
      isImpliedResult(E).value_or(ImpliedResultKind::Regular);

  // Assign contextual types for each of the result exprs.
  SmallVector<ThenStmt *, 4> scratch;
  auto branches = E->getThenStmts(scratch);
  for (auto idx : indices(branches)) {
    auto *thenStmt = branches[idx];
    auto *result = thenStmt->getResult();

    // If we have an implicit 'then' statement, record it as an implied result.
    // TODO: Should we track 'implied' as a separate bit on ThenStmt? Currently
    // it's the same as being implicit, but may not always be.
    if (thenStmt->isImplicit())
      recordImpliedResult(result, impliedResultKind);

    auto ctpElt = LocatorPathElt::ContextualType(CTP_SingleValueStmtBranch);
    auto *loc = getConstraintLocator(
        E, {LocatorPathElt::SingleValueStmtResult(idx), ctpElt});

    ContextualTypeInfo info(resultType, CTP_SingleValueStmtBranch, loc);
    setContextualInfo(result, info);
  }

  TypeJoinExpr *join = nullptr;

  if (E->getStmtKind() != SingleValueStmtExpr::Kind::For) {
    if (branches.empty()) {
      // If we only have statement branches, the expression is typed as Void.
      // This should only be the case for 'if' and 'switch' statements that must
      // be expressions that have branches that all end in a throw, and we'll
      // warn that we've inferred Void.
      addConstraint(ConstraintKind::Bind, resultType, ctx.getVoidType(), loc);
    } else {
      // Otherwise, we join the result types for each of the branches.
      join = TypeJoinExpr::forBranchesOfSingleValueStmtExpr(
          ctx, resultType, E, AllocationArena::ConstraintSolver);
    }
  }

  // If this is an implied return in a closure, we need to account for the fact
  // that the result type may be bound to Void. This is necessary to correctly
  // handle the following case:
  //
  // func foo<T>(_ fn: () -> T) {}
  // foo {
  //   if .random() { 0 } else { "" }
  // }
  //
  // Before if/switch expressions, this was treated as a regular statement,
  // with the branches being discarded (and we'd warn). We need to ensure we
  // maintain compatibility by continuing to infer T as Void in the case where
  // the branches mismatch. This example is contrived, but can occur in the real
  // world with e.g branches that insert and remove elements from a set, in both
  // cases the methods have mismatching discardable returns.
  //
  // To maintain this behavior, form a disjunction that will attempt to either
  // bind the expression type to the closure result type, or bind it to Void.
  // Only if we fail to solve with the closure result type will we attempt with
  // Void. We can't rely on the usual defaulting of the closure result type,
  // as we need to solve the conjunction before trying defaults.
  //
  // This only needs to happen for cases where the return is implicit, we don't
  // need to do this with 'return if'. We also don't need to do it for function
  // decls, as we proactively avoid transforming the if/switch into an
  // expression if the result is known to be Void.
  if (impliedResultKind == ImpliedResultKind::ForClosure) {
    auto *CE = cast<ClosureExpr>(E->getDeclContext());
    assert(!getAppliedResultBuilderTransform(CE) &&
           "Should have applied the builder with statement semantics");
    if (getParentExpr(E) == CE) {
      // We may not have a closure type if we're solving a sub-expression
      // independently for e.g code completion.
      // TODO: This won't be necessary once we stop doing the fallback
      // type-check.
      if (auto *closureTy = getClosureTypeIfAvailable(CE)) {
        auto closureResultTy = closureTy->getResult();
        auto *bindToClosure = Constraint::create(
            *this, ConstraintKind::Bind, resultType, closureResultTy, loc);
        bindToClosure->setFavored();

        auto *bindToVoid = Constraint::create(
            *this, ConstraintKind::Bind, resultType, ctx.getVoidType(), loc);

        addDisjunctionConstraint({bindToClosure, bindToVoid}, loc);
      }
    }
  }

  // Generate the conjunction for the branches.
  auto context = SyntacticElementContext::forSingleValueStmtExpr(E, join);
  auto *stmtLoc =
      getConstraintLocator(loc, LocatorPathElt::SyntacticElement(S));
  SyntacticElementConstraintGenerator generator(*this, context, stmtLoc);
  generator.visit(S);
  return generator.hadError;
}

void ConstraintSystem::generateConstraints(ArrayRef<ExprPattern *> exprPatterns,
                                           ConstraintLocatorBuilder locator) {
  assert(!exprPatterns.empty());
  auto *DC = exprPatterns.front()->getDeclContext();

  // Form a conjunction of ExprPattern elements, isolated from the rest of the
  // pattern.
  SmallVector<ElementInfo> elements;
  SmallVector<TypeVariableType *, 2> referencedTypeVars;
  for (auto *EP : exprPatterns) {
    auto ty = getType(EP)->castTo<TypeVariableType>();
    referencedTypeVars.push_back(ty);

    ContextualTypeInfo context(ty, CTP_ExprPattern);
    elements.push_back(makeElement(EP, getConstraintLocator(EP), context));
  }
  auto *loc = getConstraintLocator(locator);
  createConjunction(*this, DC, elements, loc, /*isIsolated*/ true,
                    referencedTypeVars);
}

bool isConditionOfStmt(ConstraintLocatorBuilder locator) {
  if (!locator.endsWith<LocatorPathElt::Condition>())
    return false;

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

  path.pop_back();

  if (path.empty())
    return false;

  if (auto closureElt = path.back().getAs<LocatorPathElt::SyntacticElement>())
    return closureElt->getElement().dyn_cast<Stmt *>();

  return false;
}

static std::optional<SyntacticElementContext>
getSyntacticElementContext(ASTNode element, ConstraintLocatorBuilder locator) {
  /// Capture list bindings are part of the capture list, which is semantically
  /// outside the closure it's part of. As such, it needs its own context.
  if (auto *PBD = getAsDecl<PatternBindingDecl>(element)) {
    if (auto *VD = PBD->getSingleVar()) {
      if (auto *CLE = VD->getParentCaptureList())
        return SyntacticElementContext::forCaptureList(CLE);
    }
  }

  auto anchor = locator.getAnchor();
  if (auto *closure = getAsExpr<ClosureExpr>(anchor))
    return SyntacticElementContext::forClosure(closure);
  if (auto *fn = getAsDecl<AbstractFunctionDecl>(anchor))
    return SyntacticElementContext::forFunction(fn);
  if (auto *SVE = getAsExpr<SingleValueStmtExpr>(anchor))
    return SyntacticElementContext::forSingleValueStmtExpr(SVE);
  if (auto *EP = getAsPattern<ExprPattern>(anchor))
    return SyntacticElementContext::forExprPattern(EP);
  if (auto *tap = getAsExpr<TapExpr>(anchor))
    return SyntacticElementContext::forTapExpr(tap);

  return std::nullopt;
}

ConstraintSystem::SolutionKind
ConstraintSystem::simplifySyntacticElementConstraint(
    ASTNode element, ContextualTypeInfo contextInfo, bool isDiscarded,
    TypeMatchOptions flags, ConstraintLocatorBuilder locator) {

  auto context = getSyntacticElementContext(element, locator);
  if (!context)
    return SolutionKind::Error;

  SyntacticElementConstraintGenerator generator(*this, *context,
                                                getConstraintLocator(locator));

  if (auto *expr = element.dyn_cast<Expr *>()) {
    SyntacticElementTarget target(expr, context->getAsDeclContext(),
                                  contextInfo, isDiscarded);

    if (generateConstraints(target))
      return SolutionKind::Error;

    // If this expression is the operand of a `throw` statement, record it as
    // a potential throw site.
    if (contextInfo.purpose == CTP_ThrowStmt) {
      recordPotentialThrowSite(PotentialThrowSite::ExplicitThrow,
                               getType(expr), getConstraintLocator(expr));
    }

    setTargetFor(expr, target);
    return SolutionKind::Solved;
  } else if (auto *stmt = element.dyn_cast<Stmt *>()) {
    generator.visit(stmt);
  } else if (auto *cond = element.dyn_cast<StmtConditionElement *>()) {
    if (generateConstraints({*cond}, context->getAsDeclContext()))
      return SolutionKind::Error;
  } else if (auto *pattern = element.dyn_cast<Pattern *>()) {
    generator.visitPattern(pattern, contextInfo);
  } else if (auto *caseItem = element.dyn_cast<CaseLabelItem *>()) {
    generator.visitCaseItem(caseItem, contextInfo);
  } else {
    generator.visit(cast<Decl *>(element));
  }

  return generator.hadError ? SolutionKind::Error : SolutionKind::Solved;
}

// MARK: Solution application

namespace {

/// Statement visitor that applies constraints for a given closure body.
class SyntacticElementSolutionApplication
    : public StmtVisitor<SyntacticElementSolutionApplication, ASTNode> {
  friend StmtVisitor<SyntacticElementSolutionApplication, ASTNode>;
  friend class ResultBuilderRewriter;

protected:
  Solution &solution;
  SyntacticElementContext context;
  SyntacticElementTargetRewriter &rewriter;

public:
  /// Whether an error was encountered while generating constraints.
  bool hadError = false;

  SyntacticElementSolutionApplication(Solution &solution,
                                      SyntacticElementContext context,
                                      SyntacticElementTargetRewriter &rewriter)
      : solution(solution), context(context), rewriter(rewriter) {}

  virtual ~SyntacticElementSolutionApplication() {}

private:
  Type getContextualResultType() const {
    // Taps do not have a contextual result type.
    if (isa<TapExpr *>(context)) {
      return Type();
    }

    auto fn = context.getAsAnyFunctionRef();

    if (isa<SingleValueStmtExpr *>(context)) {
      // if/switch expressions can have `return` inside.
      fn = AnyFunctionRef::fromDeclContext(context.getAsDeclContext());
    }

    if (fn) {
      if (auto transform = solution.getAppliedBuilderTransform(*fn)) {
        return solution.simplifyType(transform->bodyResultType);
      } else if (auto *closure =
                     getAsExpr<ClosureExpr>(fn->getAbstractClosureExpr())) {
        return solution.getResolvedType(closure)
            ->castTo<FunctionType>()
            ->getResult();
      } else {
        return fn->getBodyResultType();
      }
    }

    return Type();
  }

  bool visitPatternBindingDecl(PatternBindingDecl *PBD) {
    // If this is a placeholder variable with an initializer, we just need to
    // set the inferred type.
    if (isPlaceholderVar(PBD) && PBD->getInit(0)) {
      auto *pattern = PBD->getPattern(0);
      pattern->setType(solution.getResolvedType(PBD->getSingleVar()));
      return false;
    }

    SyntacticElementTarget target(PBD);
    return !rewriter.rewriteTarget(target).has_value();
  }

  void visitDecl(Decl *decl) {
    if (auto *PBD = dyn_cast<PatternBindingDecl>(decl)) {
      if (visitPatternBindingDecl(PBD)) {
        hadError = true;
        return;
      }
      // Fall through to allow `typeCheckDecl` to be called after solution is
      // applied to a pattern binding. That will materialize required
      // information e.g. accessors and do access/availability checks.
    }

    // Delay the type-checking of local decls to ensure that parent closures
    // have solutions applied, which is needed by MiscDiagnostics passes such as
    // `diagnoseImplicitSelfUseInClosure`
    rewriter.addLocalDeclToTypeCheck(decl);
  }

  ASTNode visitOpaqueStmt(OpaqueStmt *opaqueStmt) { return opaqueStmt; }

  ASTNode visitBreakStmt(BreakStmt *breakStmt) {
    // Force the target to be computed in case it produces diagnostics.
    (void)breakStmt->getTarget();
    return breakStmt;
  }

  ASTNode visitContinueStmt(ContinueStmt *continueStmt) {
    // Force the target to be computed in case it produces diagnostics.
    (void)continueStmt->getTarget();
    return continueStmt;
  }

  ASTNode visitFallthroughStmt(FallthroughStmt *fallthroughStmt) {
    if (checkFallthroughStmt(fallthroughStmt))
      hadError = true;
    return fallthroughStmt;
  }

  ASTNode visitFailStmt(FailStmt *failStmt) {
    return failStmt;
  }

  ASTNode visitDeferStmt(DeferStmt *deferStmt) {
    rewriter.addLocalDeclToTypeCheck(deferStmt->getTempDecl());

    Expr *theCall = deferStmt->getCallExpr();
    TypeChecker::typeCheckExpression(theCall, context.getAsDeclContext());
    deferStmt->setCallExpr(theCall);

    return deferStmt;
  }

  ASTNode visitIfStmt(IfStmt *ifStmt) {
    // Rewrite the condition.
    if (auto condition = rewriter.rewriteTarget(SyntacticElementTarget(
            ifStmt->getCond(), context.getAsDeclContext())))
      ifStmt->setCond(*condition->getAsStmtCondition());
    else
      hadError = true;

    ifStmt->setThenStmt(castToStmt<BraceStmt>(visit(ifStmt->getThenStmt())));

    if (auto elseStmt = ifStmt->getElseStmt()) {
      ifStmt->setElseStmt(cast<Stmt *>(visit(elseStmt)));
    }

    return ifStmt;
  }

  ASTNode visitGuardStmt(GuardStmt *guardStmt) {
    if (auto condition = rewriter.rewriteTarget(SyntacticElementTarget(
            guardStmt->getCond(), context.getAsDeclContext())))
      guardStmt->setCond(*condition->getAsStmtCondition());
    else
      hadError = true;

    auto *body = cast<Stmt *>(visit(guardStmt->getBody()));
    guardStmt->setBody(cast<BraceStmt>(body));
    return guardStmt;
  }

  ASTNode visitWhileStmt(WhileStmt *whileStmt) {
    if (auto condition = rewriter.rewriteTarget(SyntacticElementTarget(
            whileStmt->getCond(), context.getAsDeclContext())))
      whileStmt->setCond(*condition->getAsStmtCondition());
    else
      hadError = true;

    auto *body = cast<Stmt *>(visit(whileStmt->getBody()));
    whileStmt->setBody(cast<BraceStmt>(body));
    return whileStmt;
  }

  virtual ASTNode visitDoStmt(DoStmt *doStmt) {
    auto *body = cast<Stmt *>(visit(doStmt->getBody()));
    doStmt->setBody(cast<BraceStmt>(body));
    return doStmt;
  }

  ASTNode visitRepeatWhileStmt(RepeatWhileStmt *repeatWhileStmt) {
    auto *body = cast<Stmt *>(visit(repeatWhileStmt->getBody()));
    repeatWhileStmt->setBody(cast<BraceStmt>(body));

    // Rewrite the condition.
    auto &cs = solution.getConstraintSystem();
    auto target = *cs.getTargetFor(repeatWhileStmt->getCond());
    if (auto condition = rewriter.rewriteTarget(target))
      repeatWhileStmt->setCond(condition->getAsExpr());
    else
      hadError = true;

    return repeatWhileStmt;
  }

  ASTNode visitPoundAssertStmt(PoundAssertStmt *poundAssertStmt) {
    // FIXME: This should be done through \c solution instead of
    //        constraint system.
    auto &cs = solution.getConstraintSystem();
    // Rewrite the condition.
    auto target = *cs.getTargetFor(poundAssertStmt->getCondition());

    if (auto result = rewriter.rewriteTarget(target))
      poundAssertStmt->setCondition(result->getAsExpr());
    else
      hadError = true;

    return poundAssertStmt;
  }

  ASTNode visitThrowStmt(ThrowStmt *throwStmt) {
    auto &cs = solution.getConstraintSystem();

    // Rewrite the error.
    auto target = *cs.getTargetFor(throwStmt->getSubExpr());
    if (auto result = rewriter.rewriteTarget(target))
      throwStmt->setSubExpr(result->getAsExpr());
    else
      hadError = true;

    return throwStmt;
  }

  ASTNode visitDiscardStmt(DiscardStmt *discardStmt) {
    auto &cs = solution.getConstraintSystem();

    // Rewrite the `discard` expression.
    auto target = *cs.getTargetFor(discardStmt->getSubExpr());
    if (auto result = rewriter.rewriteTarget(target))
      discardStmt->setSubExpr(result->getAsExpr());
    else
      hadError = true;

    return discardStmt;
  }

  ASTNode visitForEachStmt(ForEachStmt *forEachStmt) {
    ConstraintSystem &cs = solution.getConstraintSystem();

    // Apply solution to the preamble first.
    if (!rewriter.rewriteTarget(*cs.getTargetFor(forEachStmt))) {
      hadError = true;
    }

    // Then apply the solution to the filtering condition, if there is one.
    if (auto *whereExpr = forEachStmt->getWhere()) {
      auto whereTarget = *cs.getTargetFor(whereExpr);
      if (auto rewrittenWhereTarget = rewriter.rewriteTarget(whereTarget)) {
        forEachStmt->setWhere(rewrittenWhereTarget->getAsExpr());
      } else {
        hadError = true;
      }
    }

    auto *body = cast<Stmt *>(visit(forEachStmt->getBody()));
    forEachStmt->setBody(cast<BraceStmt>(body));

    return forEachStmt;
  }

  ASTNode visitSwitchStmt(SwitchStmt *switchStmt) {
    ConstraintSystem &cs = solution.getConstraintSystem();

    // Rewrite the switch subject.
    auto subjectTarget = rewriter.rewriteTarget(*cs.getTargetFor(switchStmt));
    if (subjectTarget) {
      switchStmt->setSubjectExpr(subjectTarget->getAsExpr());
    } else {
      hadError = true;
    }

    // Visit the raw cases.
    bool limitExhaustivityChecks = false;
    for (auto *CS : switchStmt->getCases()) {
      // Body of the `case` statement can contain a `fallthrough`
      // statement that requires both source and destination
      // `case` preambles to be type-checked, so bodies of `case`
      // statements should be visited after preambles.
      visitCaseStmtPreamble(CS);
    }

    for (auto *caseStmt : switchStmt->getCases()) {
      visitCaseStmtBody(caseStmt);

      // Check restrictions on '@unknown'.
      if (caseStmt->hasUnknownAttr()) {
        checkUnknownAttrRestrictions(cs.getASTContext(), caseStmt,
                                     limitExhaustivityChecks);
      }
    }

    // Note we perform a limited exhaustiveness check if we weren't able to
    // apply the solution, as the subject and patterns may not be well-formed.
    TypeChecker::checkSwitchExhaustiveness(
        switchStmt, context.getAsDeclContext(),
        /*limited*/ limitExhaustivityChecks || hadError);

    return switchStmt;
  }

  ASTNode visitDoCatchStmt(DoCatchStmt *doStmt) {
    // Translate the body.
    auto newBody = visit(doStmt->getBody());
    doStmt->setBody(cast<Stmt *>(newBody));

    // Visit the catch blocks.
    for (auto catchStmt : doStmt->getCatches())
      visitCaseStmt(catchStmt);

    return doStmt;
  }

  void visitCaseStmtPreamble(CaseStmt *caseStmt) {
    // Translate the patterns and guard expressions for each case label item.
    for (auto &caseItem : caseStmt->getMutableCaseLabelItems()) {
      SyntacticElementTarget caseTarget(&caseItem, context.getAsDeclContext());
      if (!rewriter.rewriteTarget(caseTarget)) {
        hadError = true;
      }
    }

    diagnoseCaseVarMutabilityMismatch(context.getAsDeclContext(), caseStmt);

    for (auto *expected : caseStmt->getCaseBodyVariables()) {
      assert(expected->hasName());
      auto prev = expected->getParentVarDecl();
      auto type = solution.getResolvedType(prev)->mapTypeOutOfEnvironment();
      expected->setInterfaceType(type);
    }
  }

  void visitCaseStmtBody(CaseStmt *caseStmt) {
    auto *newBody = cast<Stmt *>(visit(caseStmt->getBody()));
    caseStmt->setBody(cast<BraceStmt>(newBody));
  }

  ASTNode visitCaseStmt(CaseStmt *caseStmt) {
    visitCaseStmtPreamble(caseStmt);
    visitCaseStmtBody(caseStmt);
    return caseStmt;
  }

  virtual ASTNode visitBraceElement(ASTNode node) {
    auto &cs = solution.getConstraintSystem();
    if (auto *expr = node.dyn_cast<Expr *>()) {
      // Rewrite the expression.
      auto target = *cs.getTargetFor(expr);
      if (auto rewrittenTarget = rewriter.rewriteTarget(target)) {
        node = rewrittenTarget->getAsExpr();

        if (target.isDiscardedExpr())
          TypeChecker::checkIgnoredExpr(castToExpr(node));
      } else {
        hadError = true;
      }
    } else if (auto stmt = node.dyn_cast<Stmt *>()) {
      node = visit(stmt);
    } else {
      visitDecl(cast<Decl *>(node));
    }
    return node;
  }

  ASTNode visitBraceStmt(BraceStmt *braceStmt) {
    auto &cs = solution.getConstraintSystem();

    // Diagnose defer statement being last one in block.
    if (!braceStmt->empty()) {
      if (auto stmt = braceStmt->getLastElement().dyn_cast<Stmt *>()) {
        if (auto deferStmt = dyn_cast<DeferStmt>(stmt)) {
          auto &diags = cs.getASTContext().Diags;
          diags
              .diagnose(deferStmt->getStartLoc(), diag::defer_stmt_at_block_end)
              .fixItReplace(deferStmt->getStartLoc(), "do");
        }
      }
    }

    for (auto &node : braceStmt->getElements())
      node = visitBraceElement(node);

    // Source compatibility workaround.
    //
    // func test<T>(_: () -> T?) {
    //   ...
    // }
    //
    // A multi-statement closure passed to `test` that has an optional
    // `Void` result type inferred from the body allows:
    //   - empty `return`(s);
    //   - to skip `return nil` or `return ()` at the end.
    //
    // Implicit `return ()` has to be inserted as the last element
    // of the body if there is none. This wasn't needed before SE-0326
    // because result type was (incorrectly) inferred as `Void` due to
    // the body being skipped.
    auto closure = context.getAsAbstractClosureExpr();
    if (closure && !closure.get()->hasSingleExpressionBody() &&
        closure.get()->getBody() == braceStmt) {
      auto resultType = getContextualResultType();
      if (resultType->getOptionalObjectType() &&
          resultType->lookThroughAllOptionalTypes()->isVoid() &&
          !braceStmt->getLastElement().isStmt(StmtKind::Return)) {
        return addImplicitVoidReturn(braceStmt, resultType);
      }
    }

    return braceStmt;
  }

  ASTNode addImplicitVoidReturn(BraceStmt *braceStmt, Type contextualResultTy) {
    auto &cs = solution.getConstraintSystem();
    auto &ctx = cs.getASTContext();

    auto *resultExpr = getVoidExpr(ctx);
    cs.cacheExprTypes(resultExpr);

    auto *returnStmt = ReturnStmt::createImplicit(ctx, resultExpr);

    // For a target for newly created result and apply a solution
    // to it, to make sure that optional injection happens required
    // number of times.
    {
      SyntacticElementTarget target(resultExpr, context.getAsDeclContext(),
                                    CTP_ReturnStmt, contextualResultTy,
                                    /*isDiscarded=*/false);
      cs.setTargetFor(returnStmt, target);

      visitReturnStmt(returnStmt);
    }

    // Re-create brace statement with an additional `return` at the end.

    SmallVector<ASTNode, 4> elements;
    elements.append(braceStmt->getElements().begin(),
                    braceStmt->getElements().end());
    elements.push_back(returnStmt);

    return BraceStmt::create(ctx, braceStmt->getLBraceLoc(), elements,
                             braceStmt->getRBraceLoc());
  }

  ASTNode visitReturnStmt(ReturnStmt *returnStmt) {
    auto &cs = solution.getConstraintSystem();

    auto resultType = getContextualResultType();

    if (!returnStmt->hasResult()) {
      // If contextual is not optional, there is nothing to do here.
      if (resultType->isVoid())
        return returnStmt;

      // Constraint generation injects an implicit `()` expresion
      // into return statements without one. This helps to match
      // cases like `Void` and `Any` that could be wrapped into a
      // number of optional types.

      auto target = *cs.getTargetFor(returnStmt);
      returnStmt->setResult(target.getAsExpr());
    }

    auto *resultExpr = returnStmt->getResult();

    enum {
      convertToResult,
      coerceToVoid
    } mode;

    auto resultExprType =
        solution.simplifyType(solution.getType(resultExpr))->getRValueType();
    // A closure with a non-void return expression can coerce to a closure
    // that returns Void.
    // TODO: We probably ought to introduce an implicit conversion expr to Void
    // and eliminate this case.
    if (resultType->isVoid() && !resultExprType->isVoid()) {
      mode = coerceToVoid;

      // Normal rule is to coerce to the return expression to the closure type.
    } else {
      mode = convertToResult;
    }

    auto target = *cs.getTargetFor(returnStmt);

    // If we're not converting to a result, unset the contextual type.
    if (mode != convertToResult) {
      target.setExprConversionType(Type());
      target.setExprContextualTypePurpose(CTP_Unused);
    }

    if (auto newResultTarget = rewriter.rewriteTarget(target)) {
      resultExpr = newResultTarget->getAsExpr();
    } else {
      hadError = true;
    }

    switch (mode) {
    case convertToResult:
      // Record the coerced expression.
      returnStmt->setResult(resultExpr);
      return returnStmt;

    case coerceToVoid: {
      // Evaluate the expression, then produce a return statement that
      // returns nothing.
      TypeChecker::checkIgnoredExpr(resultExpr);

      // For a single expression closure, we can just preserve the result expr,
      // and leave the return as implied. This avoids neededing to jump through
      // nested brace statements to dig out the single expression in
      // ClosureExpr::getSingleExpressionBody.
      if (context.isSingleExpressionClosure(cs))
        return resultExpr;

      auto &ctx = solution.getConstraintSystem().getASTContext();
      auto *newReturnStmt = ReturnStmt::createImplicit(
          ctx, returnStmt->getStartLoc(), /*result*/ nullptr);
      ASTNode elements[2] = { resultExpr, newReturnStmt };
      return BraceStmt::create(ctx, returnStmt->getStartLoc(),
                               elements, returnStmt->getEndLoc(),
                               /*implicit*/ true);
    }
    }

    return returnStmt;
  }

  ASTNode visitThenStmt(ThenStmt *thenStmt) {
    auto SVE = context.getAsSingleValueStmtExpr();
    assert(SVE && "Should have diagnosed an out-of-place ThenStmt");
    auto ty = solution.getResolvedType(SVE.get());

    // We need to fixup the conversion type to the full result type,
    // not the branch result type. This is necessary as there may be
    // an additional conversion required for the branch.
    auto target = solution.getTargetFor(thenStmt->getResult());
    if (SVE.get()->getStmtKind() != SingleValueStmtExpr::Kind::For) {
      target->setExprConversionType(ty);
    }

    auto *resultExpr = thenStmt->getResult();
    if (auto newResultTarget = rewriter.rewriteTarget(*target)) {
      resultExpr = newResultTarget->getAsExpr();
    } else {
      hadError = true;
    }

    thenStmt->setResult(resultExpr);

    // If the expression was typed as Void, its branches are effectively
    // discarded, so treat them as ignored expressions.
    if (ty->lookThroughAllOptionalTypes()->isVoid()) {
      TypeChecker::checkIgnoredExpr(resultExpr);
    }
    return thenStmt;
  }

#define UNSUPPORTED_STMT(STMT) ASTNode visit##STMT##Stmt(STMT##Stmt *) { \
      llvm_unreachable("Unsupported statement kind " #STMT);          \
  }
  UNSUPPORTED_STMT(Yield)
#undef UNSUPPORTED_STMT

public:
  /// Apply the solution to the context and return updated statement.
  Stmt *apply() {
    auto body = visit(context.getStmt());
    return body ? cast<Stmt *>(body) : nullptr;
  }
};

class ResultBuilderRewriter : public SyntacticElementSolutionApplication {
  const AppliedBuilderTransform &Transform;

public:
  ResultBuilderRewriter(AnyFunctionRef context,
                        const AppliedBuilderTransform &transform,
                        SyntacticElementTargetRewriter &rewriter)
      : SyntacticElementSolutionApplication(
            rewriter.getSolution(),
            SyntacticElementContext::forFunctionRef(context), rewriter),
        Transform(transform) {}

  bool apply() {
    auto body = visit(context.getStmt());

    if (!body || hadError)
      return true;

    auto funcRef = context.getAsAnyFunctionRef();
    assert(funcRef);

    funcRef->setTypecheckedBody(castToStmt<BraceStmt>(body));
    return false;
  }

private:
  ASTNode visitDoStmt(DoStmt *doStmt) override {
    if (auto transformed = transformDo(doStmt)) {
      return visit(transformed.get());
    }

    auto newBody = visit(doStmt->getBody());
    if (!newBody)
      return nullptr;

    doStmt->setBody(castToStmt<BraceStmt>(newBody));
    return doStmt;
  }

  ASTNode visitBraceElement(ASTNode node) override {
    if (auto *SVE = getAsExpr<SingleValueStmtExpr>(node)) {
      // This should never be treated as an expression in a result builder,
      // it should have statement semantics.
      return visitBraceElement(SVE->getStmt());
    }
    return SyntacticElementSolutionApplication::visitBraceElement(node);
  }

  NullablePtr<Stmt> transformDo(DoStmt *doStmt) {
    if (!doStmt->isImplicit())
      return nullptr;

    // Implicit `do` wraps a statement and it's `type_join` expression.
    auto *body = doStmt->getBody();

    // If there are more than two elements, this `do` doesn't need to
    // get be transformed.
    if (body->getNumElements() != 2)
      return nullptr;

    auto *stmt = castToStmt(body->getFirstElement());
    auto *join = castToExpr<TypeJoinExpr>(body->getLastElement());

    switch (stmt->getKind()) {
    case StmtKind::If:
      return transformIf(castToStmt<IfStmt>(stmt), join, /*index=*/0);

    case StmtKind::Switch:
      return transformSwitch(castToStmt<SwitchStmt>(stmt), join);

    default:
      llvm_unreachable("only 'if' and 'switch' statements are transformed");
    }
  }

  NullablePtr<Stmt> transformSwitch(SwitchStmt *switchStmt,
                                    TypeJoinExpr *join) {
    unsigned caseIndex = 0;
    for (auto *caseStmt : switchStmt->getCases()) {
      auto newBody = transformBody(caseStmt->getBody(), join, caseIndex++);
      if (!newBody)
        return nullptr;

      caseStmt->setBody(newBody.get());
    }

    return switchStmt;
  }

  NullablePtr<Stmt> transformIf(IfStmt *ifStmt, TypeJoinExpr *join,
                                unsigned index) {
    // FIXME: Turn this into a condition once warning is an error.
    (void)diagnoseMissingBuildWithAvailability(ifStmt, join);

    auto *joinVar = join->getVar();

    // First, let's add assignment to the end of `then` branch
    {
      auto *thenBody = castToStmt<BraceStmt>(ifStmt->getThenStmt());
      auto newBody = transformBody(thenBody, join, index);
      if (!newBody)
        return nullptr;

      ifStmt->setThenStmt(newBody.get());
    }

    if (auto *elseStmt = ifStmt->getElseStmt()) {
      if (auto *innerIfStmt = getAsStmt<IfStmt>(elseStmt)) {
        auto transformedIf = transformIf(innerIfStmt, join, index + 1);
        if (!transformedIf)
          return nullptr;

        ifStmt->setElseStmt(transformedIf.get());
      } else {
        auto newBody =
            transformBody(castToStmt<BraceStmt>(elseStmt), join, index + 1);
        if (!newBody)
          return nullptr;

        ifStmt->setElseStmt(newBody.get());
      }
    } else {
      auto &ctx = getASTContext();
      SmallVector<ASTNode, 2> elseBranch;

      elseBranch.push_back(
          createAssignment(joinVar, join->getElement(index + 1)));

      ifStmt->setElseStmt(BraceStmt::create(ctx, ifStmt->getEndLoc(),
                                            elseBranch, ifStmt->getEndLoc(),
                                            /*implicit=*/true));
    }

    return ifStmt;
  }

  NullablePtr<BraceStmt> transformBody(BraceStmt *body, TypeJoinExpr *join,
                                       unsigned index) {
    for (auto &element : body->getElements()) {
      if (auto *doStmt = getAsStmt<DoStmt>(element)) {
        if (auto transformed = transformDo(doStmt))
          element = transformed.get();
      }
    }

    return addBuilderAssignment(body, join->getVar(), join->getElement(index));
  }

  // Add `$__bulderN = build{Optional, Either}(...)` at the end of a block body.
  BraceStmt *addBuilderAssignment(BraceStmt *body, DeclRefExpr *joinVar,
                                  Expr *builderCall) {
    SmallVector<ASTNode, 4> newBody;
    llvm::copy(body->getElements(), std::back_inserter(newBody));

    newBody.push_back(createAssignment(joinVar, builderCall));

    return BraceStmt::create(getASTContext(), body->getLBraceLoc(), newBody,
                             body->getRBraceLoc(), body->isImplicit());
  }

  AssignExpr *createAssignment(DeclRefExpr *destRef, Expr *source) {
    auto &ctx = getASTContext();
    auto &CS = solution.getConstraintSystem();

    auto *assignment = new (ctx) AssignExpr(destRef, /*EqualLoc=*/SourceLoc(),
                                            source, /*Implicit=*/true);

    {
      // Assignment expression is always `Void`.
      CS.setType(assignment, ctx.TheEmptyTupleType);

      CS.setTargetFor({assignment},
                      {assignment, context.getAsDeclContext(), CTP_Unused,
                       /*contextualType=*/Type(), /*isDiscarded=*/false});
    }

    return assignment;
  }

  ASTContext &getASTContext() const {
    return context.getAsDeclContext()->getASTContext();
  }

private:
  /// Look for a #available condition. If there is one, we need to check
  /// that the resulting type of the "then" doesn't refer to any types that
  /// are unavailable in the enclosing context.
  ///
  /// Note that this is for staging in support for buildLimitedAvailability();
  /// the diagnostic is currently a warning, so that existing code that
  /// compiles today will continue to compile. Once result builder types
  /// have had the chance to adopt buildLimitedAvailability(), we'll upgrade
  /// this warning to an error.
  [[nodiscard]]
  bool diagnoseMissingBuildWithAvailability(IfStmt *ifStmt,
                                            TypeJoinExpr *join) {
    auto findAvailabilityCondition =
        [](StmtCondition stmtCond) -> const StmtConditionElement * {
      for (const auto &cond : stmtCond) {
        switch (cond.getKind()) {
        case StmtConditionElement::CK_Boolean:
        case StmtConditionElement::CK_PatternBinding:
        case StmtConditionElement::CK_HasSymbol:
          continue;

        case StmtConditionElement::CK_Availability:
          return &cond;
          break;
        }
      }

      return nullptr;
    };

    auto availabilityCond = findAvailabilityCondition(ifStmt->getCond());
    if (!availabilityCond)
      return false;

    SourceLoc loc = availabilityCond->getStartLoc();
    auto builderType = solution.simplifyType(Transform.builderType);
    // Since all of the branches of `if` statement have to join into the same
    // type we can just use the type of the join variable here.
    Type bodyType = solution.getResolvedType(join->getVar());

    return bodyType.findIf([&](Type type) {
      auto nominal = type->getAnyNominal();
      if (!nominal)
        return false;

      auto restriction = getUnsatisfiedAvailabilityRestriction(
          nominal, context.getAsDeclContext(), loc);
      if (restriction && !restriction->isUnavailable()) {
        auto &ctx = getASTContext();
        ctx.Diags.diagnose(loc,
                           diag::result_builder_missing_limited_availability,
                           builderType);

        // Add a note to the result builder with a stub for
        // buildLimitedAvailability().
        if (auto builder = builderType->getAnyNominal()) {
          SourceLoc buildInsertionLoc;
          std::string stubIndent;
          Type componentType;
          std::tie(buildInsertionLoc, stubIndent, componentType) =
              determineResultBuilderBuildFixItInfo(builder);
          if (buildInsertionLoc.isValid()) {
            std::string fixItString;
            {
              llvm::raw_string_ostream out(fixItString);
              printResultBuilderBuildFunction(
                  builder, componentType,
                  ResultBuilderBuildFunction::BuildLimitedAvailability,
                  stubIndent, out);

              builder
                  ->diagnose(
                      diag::result_builder_missing_build_limited_availability,
                      builderType)
                  .fixItInsert(buildInsertionLoc, fixItString);
            }
          }
        }

        return true;
      }

      return false;
    });
  }
};
} // namespace

static void applySolutionToClosurePropertyWrappers(ClosureExpr *closure,
                                                   const Solution &solution) {
  for (auto *param : *closure->getParameters()) {
    if (!param->hasAttachedPropertyWrapper())
      continue;

    // Set the interface type of each property wrapper synthesized var
    auto *backingVar = param->getPropertyWrapperBackingProperty();
    auto backingType = solution.simplifyType(solution.getType(backingVar))
                           ->mapTypeOutOfEnvironment();
    backingVar->setInterfaceType(backingType);

    if (auto *projectionVar = param->getPropertyWrapperProjectionVar()) {
      projectionVar->setInterfaceType(
          solution.simplifyType(solution.getType(projectionVar))
              ->mapTypeOutOfEnvironment());
    }

    auto *wrappedValueVar = param->getPropertyWrapperWrappedValueVar();
    auto wrappedValueType =
        solution.simplifyType(solution.getType(wrappedValueVar))
            ->mapTypeOutOfEnvironment();
    wrappedValueVar->setInterfaceType(
        wrappedValueType->getWithoutSpecifierType());

    if (param->hasImplicitPropertyWrapper()) {
      if (wrappedValueType->is<LValueType>())
        wrappedValueVar->setImplInfo(StorageImplInfo::getMutableComputed());

      // Add an explicit property wrapper attribute, which is needed for
      // synthesizing the accessors.
      auto &context = wrappedValueVar->getASTContext();
      auto *typeExpr = TypeExpr::createImplicit(backingType, context);
      auto *attr =
          CustomAttr::create(context, SourceLoc(), typeExpr,
                             /*owner*/ wrappedValueVar, /*implicit=*/true);
      wrappedValueVar->addAttribute(attr);
    }
  }
}

bool ConstraintSystem::applySolution(AnyFunctionRef fn,
                                     SyntacticElementTargetRewriter &rewriter) {
  auto &solution = rewriter.getSolution();
  auto &cs = solution.getConstraintSystem();
  auto *closure = getAsExpr<ClosureExpr>(fn.getAbstractClosureExpr());
  FunctionType *closureFnType = nullptr;
  if (closure) {
    // Update the closure's type.
    auto closureType = solution.simplifyType(cs.getType(closure));
    cs.setType(closure, closureType);

    // Coerce the parameter types.
    closureFnType = closureType->castTo<FunctionType>();
    auto *params = closure->getParameters();
    TypeChecker::coerceParameterListToType(params, closureFnType);

    // Find any isolated parameters in this closure and mark them as isolated.
    for (auto param : solution.isolatedParams) {
      if (param->getDeclContext() == closure)
        param->setIsolated(true);
    }

    if (solution.preconcurrencyClosures.count(closure))
      closure->setIsolatedByPreconcurrency();

    // Coerce the result type, if it was written explicitly.
    if (closure->hasExplicitResultType()) {
      closure->setExplicitResultType(closureFnType->getResult());
    }

    applySolutionToClosurePropertyWrappers(closure, solution);

    TypeChecker::checkClosureAttributes(closure);
    TypeChecker::checkParameterList(closure->getParameters(), closure);
  }

  // Enter the context of the function before performing any additional
  // transformations.
  llvm::SaveAndRestore<DeclContext *> savedDC(rewriter.getCurrentDC(),
                                              fn.getAsDeclContext());

  // Apply the result builder transform, if there is one.
  if (auto transform = solution.getAppliedBuilderTransform(fn)) {
    NullablePtr<BraceStmt> newBody;

    fn.setParsedBody(transform->transformedBody);

    ResultBuilderRewriter builderRewriter(fn, *transform, rewriter);
    return builderRewriter.apply();
  }
  assert(closure && "Can only get here with a closure at the moment");
  return applySolutionToBody(closure, rewriter);
}

bool ConstraintSystem::applySolutionToBody(
    AnyFunctionRef fn, SyntacticElementTargetRewriter &rewriter) {
  // Enter the context of the function before performing any additional
  // transformations.
  llvm::SaveAndRestore<DeclContext *> savedDC(rewriter.getCurrentDC(),
                                              fn.getAsDeclContext());

  auto &solution = rewriter.getSolution();
  SyntacticElementSolutionApplication application(
      solution, SyntacticElementContext::forFunctionRef(fn), rewriter);

  auto *body = application.apply();

  if (!body || application.hadError)
    return true;

  fn.setTypecheckedBody(cast<BraceStmt>(body));
  return false;
}

bool ConstraintSystem::applySolutionToBody(
    TapExpr *tapExpr, SyntacticElementTargetRewriter &rewriter) {
  auto &solution = rewriter.getSolution();
  SyntacticElementSolutionApplication application(
      solution, SyntacticElementContext::forTapExpr(tapExpr), rewriter);

  auto body = application.apply();

  if (!body || application.hadError)
    return true;

  tapExpr->setBody(castToStmt<BraceStmt>(body));
  return false;
}

bool ConstraintSystem::applySolutionToSingleValueStmt(
    SingleValueStmtExpr *SVE, SyntacticElementTargetRewriter &rewriter) {
  auto &solution = rewriter.getSolution();
  setType(SVE, solution.getResolvedType(SVE));

  auto context = SyntacticElementContext::forSingleValueStmtExpr(SVE);
  SyntacticElementSolutionApplication application(solution, context, rewriter);
  auto *stmt = application.apply();
  if (!stmt || application.hadError)
    return true;

  if (SVE->getStmtKind() == SingleValueStmtExpr::Kind::For) {
    auto *binding = SVE->getForExpressionPreamble()->ForAccumulatorBinding;
    auto target = getTargetFor({binding, 0}).value();

    auto newTarget = rewriter.rewriteTarget(target);
    if (!newTarget) {
      return true;
    }

    binding->setInit(0, newTarget->getAsExpr());
  }

  SVE->setStmt(stmt);
  return false;
}

Type constraints::isPlaceholderVar(PatternBindingDecl *PB) {
  auto *var = PB->getSingleVar();
  if (!var)
    return Type();

  if (!var->getName().hasDollarPrefix())
    return Type();

  auto *pattern = PB->getPattern(0);
  auto *typedPattern = dyn_cast<TypedPattern>(pattern);
  if (!typedPattern || !typedPattern->hasType())
    return Type();

  auto type = typedPattern->getType();
  if (!type->hasPlaceholder())
    return Type();

  return type;
}
