text: //===--- BuilderTransform.cpp - Result-builder transformation -----------===//
  //
  // 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 routines associated with the result-builder
  // transformation.
  //
  //===----------------------------------------------------------------------===//
  
  #include "MiscDiagnostics.h"
  #include "TypeChecker.h"
  #include "TypeCheckAvailability.h"
  #include "swift/Sema/IDETypeChecking.h"
  #include "swift/AST/ASTPrinter.h"
  #include "swift/AST/ASTVisitor.h"
  #include "swift/AST/ASTWalker.h"
  #include "swift/AST/NameLookup.h"
  #include "swift/AST/NameLookupRequests.h"
  #include "swift/AST/ParameterList.h"
  #include "swift/AST/TypeCheckRequests.h"
  #include "swift/Sema/ConstraintSystem.h"
  #include "swift/Sema/SolutionResult.h"
  #include "llvm/ADT/DenseMap.h"
  #include "llvm/ADT/SmallVector.h"
  #include <iterator>
  #include <map>
  #include <memory>
  #include <utility>
  #include <tuple>
  
  using namespace swift;
  using namespace constraints;
  
  namespace {
  
  /// Find the first #available condition within the statement condition,
  /// or return NULL if there isn't one.
  const StmtConditionElement *findAvailabilityCondition(StmtCondition stmtCond) {
    for (const auto &cond : stmtCond) {
      switch (cond.getKind()) {
      case StmtConditionElement::CK_Boolean:
      case StmtConditionElement::CK_PatternBinding:
        continue;
  
      case StmtConditionElement::CK_Availability:
        return &cond;
        break;
      }
    }
  
    return nullptr;
  }
  
  /// Visitor to classify the contents of the given closure.
  class BuilderClosureVisitor
      : private StmtVisitor<BuilderClosureVisitor, VarDecl *> {
  
    friend StmtVisitor<BuilderClosureVisitor, VarDecl *>;
  
    ConstraintSystem *cs;
    DeclContext *dc;
    ASTContext &ctx;
  
    ResultBuilder builder;
  
    SkipUnhandledConstructInResultBuilder::UnhandledNode unhandledNode;
  
    /// Whether an error occurred during application of the builder closure,
    /// e.g., during constraint generation.
    bool hadError = false;
  
    /// The record of what happened when we applied the builder transform.
    AppliedBuilderTransform applied;
  
    /// Produce a builder call to the given named function with the given
    /// arguments.
    Expr *buildCallIfWanted(SourceLoc loc, Identifier fnName,
                            ArrayRef<Expr *> argExprs,
                            ArrayRef<Identifier> argLabels) {
      if (!cs)
        return nullptr;
  
      return builder.buildCall(loc, fnName, argExprs, argLabels);
    }
  
    /// Capture the given expression into an implicitly-generated variable.
    VarDecl *captureExpr(Expr *expr, bool oneWay,
                         llvm::PointerUnion<Stmt *, Expr *> forEntity = nullptr) {
      if (!cs)
        return nullptr;
  
      Expr *origExpr = expr;
  
      if (oneWay) {
        // Form a one-way constraint to prevent backward propagation.
        expr = new (ctx) OneWayExpr(expr);
      }
  
      // Generate constraints for this expression.
      expr = cs->generateConstraints(expr, dc);
      if (!expr) {
        hadError = true;
        return nullptr;
      }
  
      // Create the implicit variable.
      auto var = builder.buildVar(expr->getStartLoc());
  
      // Record the new variable and its corresponding expression & statement.
      if (auto forStmt = forEntity.dyn_cast<Stmt *>()) {
        applied.capturedStmts.insert({forStmt, { var, { expr } }});
      } else {
        if (auto forExpr = forEntity.dyn_cast<Expr *>())
          origExpr = forExpr;
  
        applied.capturedExprs.insert({origExpr, {var, expr}});
      }
  
      cs->setType(var, cs->getType(expr));
      return var;
    }
  
  public:
    BuilderClosureVisitor(ASTContext &ctx, ConstraintSystem *cs, DeclContext *dc,
                          Type builderType, Type bodyResultType)
        : cs(cs), dc(dc), ctx(ctx), builder(cs, dc, builderType) {
      applied.builderType = builder.getType();
      applied.bodyResultType = bodyResultType;
    }
  
    /// Apply the builder transform to the given statement.
    Optional<AppliedBuilderTransform> apply(Stmt *stmt) {
      VarDecl *bodyVar = visit(stmt);
      if (!bodyVar)
        return None;
  
      applied.returnExpr = builder.buildVarRef(bodyVar, stmt->getEndLoc());
  
      // If there is a buildFinalResult(_:), call it.
      ASTContext &ctx = cs->getASTContext();
      if (builder.supports(ctx.Id_buildFinalResult, {Identifier()})) {
        applied.returnExpr = buildCallIfWanted(
            applied.returnExpr->getLoc(), ctx.Id_buildFinalResult,
            { applied.returnExpr }, { Identifier() });
      }
  
      applied.returnExpr = cs->buildTypeErasedExpr(
          applied.returnExpr, dc, applied.bodyResultType, CTP_ReturnStmt);
  
      applied.returnExpr = cs->generateConstraints(applied.returnExpr, dc);
      if (!applied.returnExpr) {
        hadError = true;
        return None;
      }
  
      return std::move(applied);
    }
  
    /// Check whether the result builder can be applied to this statement.
    /// \returns the node that cannot be handled by this builder on failure.
    SkipUnhandledConstructInResultBuilder::UnhandledNode check(Stmt *stmt) {
      (void)visit(stmt);
      return unhandledNode;
    }
  
  protected:
  #define CONTROL_FLOW_STMT(StmtClass)                       \
    VarDecl *visit##StmtClass##Stmt(StmtClass##Stmt *stmt) { \
      if (!unhandledNode)                                    \
        unhandledNode = stmt;                                \
                                                             \
      return nullptr;                                        \
    }
  
    void visitPatternBindingDecl(PatternBindingDecl *patternBinding) {
      // Enforce some restrictions on local variables inside a result builder.
      for (unsigned i : range(patternBinding->getNumPatternEntries())) {
        // The pattern binding must have an initial value expression.
        if (!patternBinding->isExplicitlyInitialized(i)) {
          if (!unhandledNode)
            unhandledNode = patternBinding;
          return;
        }
  
        // Each variable bound by the pattern must be stored, and cannot
        // have observers.
        SmallVector<VarDecl *, 8> variables;
        patternBinding->getPattern(i)->collectVariables(variables);
  
        for (auto *var : variables) {
          if (!var->getImplInfo().isSimpleStored()) {
            if (!unhandledNode)
              unhandledNode = patternBinding;
            return;
          }
  
          // Also check for invalid attributes.
          TypeChecker::checkDeclAttributes(var);
        }
      }
  
      // If there is a constraint system, generate constraints for the pattern
      // binding.
      if (cs) {
        SolutionApplicationTarget target(patternBinding);
        if (cs->generateConstraints(target, FreeTypeVariableBinding::Disallow))
          hadError = true;
      }
    }
  
    VarDecl *visitBraceStmt(BraceStmt *braceStmt) {
      SmallVector<Expr *, 4> expressions;
      auto addChild = [&](VarDecl *childVar) {
        if (!childVar)
          return;
  
        expressions.push_back(builder.buildVarRef(childVar, childVar->getLoc()));
      };
  
      for (auto node : braceStmt->getElements()) {
        // Implicit returns in single-expression function bodies are treated
        // as the expression.
        if (auto returnStmt =
                dyn_cast_or_null<ReturnStmt>(node.dyn_cast<Stmt *>())) {
          assert(returnStmt->isImplicit());
          node = returnStmt->getResult();
        }
  
        if (auto stmt = node.dyn_cast<Stmt *>()) {
          addChild(visit(stmt));
          continue;
        }
  
        if (auto decl = node.dyn_cast<Decl *>()) {
          // Just ignore #if; the chosen children should appear in the
          // surrounding context.  This isn't good for source tools but it
          // at least works.
          if (isa<IfConfigDecl>(decl))
            continue;
  
          // Skip #warning/#error; we'll handle them when applying the builder.
          if (isa<PoundDiagnosticDecl>(decl)) {
            continue;
          }
  
          // Pattern bindings are okay so long as all of the entries are
          // initialized.
          if (auto patternBinding = dyn_cast<PatternBindingDecl>(decl)) {
            visitPatternBindingDecl(patternBinding);
            continue;
          }
  
          // Ignore variable declarations, because they're always handled within
          // their enclosing pattern bindings.
          if (isa<VarDecl>(decl))
            continue;
  
          if (!unhandledNode)
            unhandledNode = decl;
  
          continue;
        }
  
        auto expr = node.get<Expr *>();
        if (cs && builder.supports(ctx.Id_buildExpression)) {
          expr = buildCallIfWanted(expr->getLoc(), ctx.Id_buildExpression,
                                   { expr }, { Identifier() });
        }
  
        addChild(captureExpr(expr, /*oneWay=*/true, node.get<Expr *>()));
      }
  
      if (!cs || hadError)
        return nullptr;
  
      Expr *call = nullptr;
      // If the builder supports `buildPartialBlock(first:)` and
      // `buildPartialBlock(accumulated:next:)`, use this to combine
      // subexpressions pairwise.
      if (!expressions.empty() &&
          builder.supports(ctx.Id_buildPartialBlock, {ctx.Id_first},
                           /*checkAvailability*/ true) &&
          builder.supports(ctx.Id_buildPartialBlock,
                           {ctx.Id_accumulated, ctx.Id_next},
                           /*checkAvailability*/ true)) {
        // NOTE: The current implementation uses one-way constraints in between
        // subexpressions. It's functionally equivalent to the following:
        //   let v0 = Builder.buildPartialBlock(first: arg_0)
        //   let v1 = Builder.buildPartialBlock(accumulated: arg_1, next: v0)
        //   ...
        //   return Builder.buildPartialBlock(accumulated: arg_n, next: ...)
        call = buildCallIfWanted(braceStmt->getStartLoc(),
                                 ctx.Id_buildPartialBlock,
                                 {expressions.front()},
                                 /*argLabels=*/{ctx.Id_first});
        for (auto *expr : llvm::drop_begin(expressions)) {
          call = buildCallIfWanted(braceStmt->getStartLoc(),
                                   ctx.Id_buildPartialBlock,
                                   {new (ctx) OneWayExpr(call), expr},
                                   {ctx.Id_accumulated, ctx.Id_next});
        }
      }
      // If `buildBlock` does not exist at this point, it could be the case that
      // `buildPartialBlock` did not have the sufficient availability for this
      // call site.  Diagnose it.
      else if (!builder.supports(ctx.Id_buildBlock)) {
        ctx.Diags.diagnose(
            braceStmt->getStartLoc(),
            diag::result_builder_missing_available_buildpartialblock,
            builder.getType());
        return nullptr;
      }
      // Otherwise, call `buildBlock` on all subexpressions.
      else {
        // Call Builder.buildBlock(... args ...)
        call = buildCallIfWanted(braceStmt->getStartLoc(),
                                 ctx.Id_buildBlock, expressions,
                                 /*argLabels=*/{ });
      }
  
      if (!call)
        return nullptr;
  
      return captureExpr(call, /*oneWay=*/true, braceStmt);
    }
  
    VarDecl *visitDoStmt(DoStmt *doStmt) {
      auto childVar = visitBraceStmt(doStmt->getBody());
      if (!childVar)
        return nullptr;
  
      auto childRef = builder.buildVarRef(childVar, doStmt->getEndLoc());
  
      return captureExpr(childRef, /*oneWay=*/true, doStmt);
    }
  
    CONTROL_FLOW_STMT(Yield)
    CONTROL_FLOW_STMT(Defer)
  
    static bool isBuildableIfChainRecursive(IfStmt *ifStmt,
                                            unsigned &numPayloads,
                                            bool &isOptional) {
      // The 'then' clause contributes a payload.
      ++numPayloads;
  
      // If there's an 'else' clause, it contributes payloads:
      if (auto elseStmt = ifStmt->getElseStmt()) {
        // If it's 'else if', it contributes payloads recursively.
        if (auto elseIfStmt = dyn_cast<IfStmt>(elseStmt)) {
          return isBuildableIfChainRecursive(elseIfStmt, numPayloads,
                                             isOptional);
        // Otherwise it's just the one.
        } else {
          ++numPayloads;
        }
  
      // If not, the chain result is at least optional.
      } else {
        isOptional = true;
      }
  
      return true;
    }
  
    bool isBuildableIfChain(IfStmt *ifStmt, unsigned &numPayloads,
                            bool &isOptional) {
      if (!isBuildableIfChainRecursive(ifStmt, numPayloads, isOptional))
        return false;
  
      // If there's a missing 'else', we need 'buildOptional' to exist.
      if (isOptional && !builder.supportsOptional())
        return false;
  
      // If there are multiple clauses, we need 'buildEither(first:)' and
      // 'buildEither(second:)' to both exist.
      if (numPayloads > 1) {
        if (!builder.supports(ctx.Id_buildEither, {ctx.Id_first}) ||
            !builder.supports(ctx.Id_buildEither, {ctx.Id_second}))
          return false;
      }
  
      return true;
    }
  
    VarDecl *visitIfStmt(IfStmt *ifStmt) {
      // Check whether the chain is buildable and whether it terminates
      // without an `else`.
      bool isOptional = false;
      unsigned numPayloads = 0;
      if (!isBuildableIfChain(ifStmt, numPayloads, isOptional)) {
        if (!unhandledNode)
          unhandledNode = ifStmt;
        return nullptr;
      }
  
      // Attempt to build the chain, propagating short-circuits, which
      // might arise either do to error or not wanting an expression.
      return buildIfChainRecursive(ifStmt, 0, numPayloads, isOptional,
                                   /*isTopLevel=*/true);
    }
  
    /// Recursively build an if-chain: build an expression which will have
    /// a value of the chain result type before any call to `buildIf`.
    /// The expression will perform any necessary calls to `buildEither`,
    /// and the result will have optional type if `isOptional` is true.
    VarDecl *buildIfChainRecursive(IfStmt *ifStmt, unsigned payloadIndex,
                                   unsigned numPayloads, bool isOptional,
                                   bool isTopLevel = false) {
      assert(payloadIndex < numPayloads);
  
      // First generate constraints for the conditions. This can introduce
      // variable bindings that will be used within the "then" branch.
      if (cs && cs->generateConstraints(ifStmt->getCond(), dc)) {
        hadError = true;
        return nullptr;
      }
  
      // Make sure we recursively visit both sides even if we're not
      // building expressions.
  
      // Build the then clause.  This will have the corresponding payload
      // type (i.e. not wrapped in any way).
      VarDecl *thenVar = visit(ifStmt->getThenStmt());
  
      // Build the else clause, if present.  If this is from an else-if,
      // this will be fully wrapped; otherwise it will have the corresponding
      // payload type (at index `payloadIndex + 1`).
      assert(ifStmt->getElseStmt() || isOptional);
      bool isElseIf = false;
      Optional<VarDecl *> elseChainVar;
      if (auto elseStmt = ifStmt->getElseStmt()) {
        if (auto elseIfStmt = dyn_cast<IfStmt>(elseStmt)) {
          isElseIf = true;
          elseChainVar = buildIfChainRecursive(elseIfStmt, payloadIndex + 1,
                                               numPayloads, isOptional);
        } else {
          elseChainVar = visit(elseStmt);
        }
      }
  
      // Short-circuit if appropriate.
      if (!cs || !thenVar || (elseChainVar && !*elseChainVar))
        return nullptr;
  
      Expr *thenVarRefExpr =
          builder.buildVarRef(thenVar, ifStmt->getThenStmt()->getEndLoc());
  
      // If there is a #available in the condition, wrap the 'then' in a call to
      // buildLimitedAvailability(_:).
      auto availabilityCond = findAvailabilityCondition(ifStmt->getCond());
      bool supportsAvailability =
          availabilityCond && builder.supports(ctx.Id_buildLimitedAvailability);
      if (supportsAvailability &&
          !availabilityCond->getAvailability()->isUnavailability()) {
        thenVarRefExpr = buildCallIfWanted(ifStmt->getThenStmt()->getEndLoc(),
                                           ctx.Id_buildLimitedAvailability,
                                           {thenVarRefExpr}, {Identifier()});
      }
  
      // Prepare the `then` operand by wrapping it to produce a chain result.
      Expr *thenExpr = buildWrappedChainPayload(
          thenVarRefExpr, payloadIndex, numPayloads, isOptional);
  
      // Prepare the `else operand:
      Expr *elseExpr;
      SourceLoc elseLoc;
  
      // - If there's no `else` clause, use `Optional.none`.
      if (!elseChainVar) {
        assert(isOptional);
        elseLoc = ifStmt->getEndLoc();
        elseExpr = buildNoneExpr(elseLoc);
  
      // - If there's an `else if`, the chain expression from that
      //   should already be producing a chain result.
      } else if (isElseIf) {
        elseExpr = builder.buildVarRef(*elseChainVar, ifStmt->getEndLoc());
        elseLoc = ifStmt->getElseLoc();
  
        // - Otherwise, wrap it to produce a chain result.
      } else {
        Expr *elseVarRefExpr =
            builder.buildVarRef(*elseChainVar, ifStmt->getEndLoc());
  
        // If there is a #unavailable in the condition, wrap the 'else' in a call
        // to buildLimitedAvailability(_:).
        if (supportsAvailability &&
            availabilityCond->getAvailability()->isUnavailability()) {
          elseVarRefExpr = buildCallIfWanted(ifStmt->getEndLoc(),
                                             ctx.Id_buildLimitedAvailability,
                                             {elseVarRefExpr}, {Identifier()});
        }
  
        elseExpr = buildWrappedChainPayload(elseVarRefExpr, payloadIndex + 1,
                                            numPayloads, isOptional);
        elseLoc = ifStmt->getElseLoc();
      }
  
      // The operand should have optional type if we had optional results,
      // so we just need to call `buildIf` now, since we're at the top level.
      if (isOptional && isTopLevel) {
        thenExpr =
            buildCallIfWanted(ifStmt->getEndLoc(), builder.getBuildOptionalId(),
                              thenExpr, /*argLabels=*/{});
        elseExpr =
            buildCallIfWanted(ifStmt->getEndLoc(), builder.getBuildOptionalId(),
                              elseExpr, /*argLabels=*/{});
      }
  
      thenExpr = cs->generateConstraints(thenExpr, dc);
      if (!thenExpr) {
        hadError = true;
        return nullptr;
      }
  
      elseExpr = cs->generateConstraints(elseExpr, dc);
      if (!elseExpr) {
        hadError = true;
        return nullptr;
      }
  
      Type resultType = cs->addJoinConstraint(cs->getConstraintLocator(ifStmt),
          {
            { cs->getType(thenExpr), cs->getConstraintLocator(thenExpr) },
            { cs->getType(elseExpr), cs->getConstraintLocator(elseExpr) }
          });
      if (!resultType) {
        hadError = true;
        return nullptr;
      }
  
      // Create a variable to capture the result of this expression.
      auto ifVar = builder.buildVar(ifStmt->getStartLoc());
      cs->setType(ifVar, resultType);
      applied.capturedStmts.insert({ifStmt, { ifVar, { thenExpr, elseExpr }}});
      return ifVar;
    }
  
    /// Wrap a payload value in an expression which will produce a chain
    /// result (without `buildIf`).
    Expr *buildWrappedChainPayload(Expr *operand, unsigned payloadIndex,
                                   unsigned numPayloads, bool isOptional) {
      assert(payloadIndex < numPayloads);
  
      // Inject into the appropriate chain position.
      //
      // We produce a (left-biased) balanced binary tree of Eithers in order
      // to prevent requiring a linear number of injections in the worst case.
      // That is, if we have 13 clauses, we want to produce:
      //
      //                      /------------------Either------------\
      //           /-------Either-------\                     /--Either--\
      //     /--Either--\          /--Either--\          /--Either--\     \
      //   /-E-\      /-E-\      /-E-\      /-E-\      /-E-\      /-E-\    \
      // 0000 0001  0010 0011  0100 0101  0110 0111  1000 1001  1010 1011 1100
      //
      // Note that a prefix of length D of the payload index acts as a path
      // through the tree to the node at depth D.  On the rightmost path
      // through the tree (when this prefix is equal to the corresponding
      // prefix of the maximum payload index), the bits of the index mark
      // where Eithers are required.
      //
      // Since we naturally want to build from the innermost Either out, and
      // therefore work with progressively shorter prefixes, we can do it all
      // with right-shifts.
      for (auto path = payloadIndex, maxPath = numPayloads - 1;
           maxPath != 0; path >>= 1, maxPath >>= 1) {
        // Skip making Eithers on the rightmost path where they aren't required.
        // This isn't just an optimization: adding spurious Eithers could
        // leave us with unresolvable type variables if `buildEither` has
        // a signature like:
        //    static func buildEither<T,U>(first value: T) -> Either<T,U>
        // which relies on unification to work.
        if (path == maxPath && !(maxPath & 1)) continue;
  
        bool isSecond = (path & 1);
        operand = buildCallIfWanted(operand->getStartLoc(),
                                    ctx.Id_buildEither, operand,
                                    {isSecond ? ctx.Id_second : ctx.Id_first});
      }
  
      // Inject into Optional if required.  We'll be adding the call to
      // `buildIf` after all the recursive calls are complete.
      if (isOptional) {
        operand = buildSomeExpr(operand);
      }
  
      return operand;
    }
  
    Expr *buildSomeExpr(Expr *arg) {
      auto optionalDecl = ctx.getOptionalDecl();
      auto optionalType = optionalDecl->getDeclaredType();
  
      auto loc = arg->getStartLoc();
      auto optionalTypeExpr =
        TypeExpr::createImplicitHack(loc, optionalType, ctx);
      auto someRef = new (ctx) UnresolvedDotExpr(
          optionalTypeExpr, loc, DeclNameRef(ctx.getIdentifier("some")),
          DeclNameLoc(loc), /*implicit=*/true);
      auto *argList = ArgumentList::forImplicitUnlabeled(ctx, {arg});
      return CallExpr::createImplicit(ctx, someRef, argList);
    }
  
    Expr *buildNoneExpr(SourceLoc endLoc) {
      auto optionalDecl = ctx.getOptionalDecl();
      auto optionalType = optionalDecl->getDeclaredType();
  
      auto optionalTypeExpr =
        TypeExpr::createImplicitHack(endLoc, optionalType, ctx);
      return new (ctx) UnresolvedDotExpr(
          optionalTypeExpr, endLoc, DeclNameRef(ctx.getIdentifier("none")),
          DeclNameLoc(endLoc), /*implicit=*/true);
    }
  
    VarDecl *visitSwitchStmt(SwitchStmt *switchStmt) {
      // Generate constraints for the subject expression, and capture its
      // type for use in matching the various patterns.
      Expr *subjectExpr = switchStmt->getSubjectExpr();
      if (cs) {
        // Form a one-way constraint to prevent backward propagation.
        subjectExpr = new (ctx) OneWayExpr(subjectExpr);
  
        // FIXME: Add contextual type purpose for switch subjects?
        SolutionApplicationTarget target(subjectExpr, dc, CTP_Unused, Type(),
                                         /*isDiscarded=*/false);
        if (cs->generateConstraints(target, FreeTypeVariableBinding::Disallow)) {
          hadError = true;
          return nullptr;
        }
  
        cs->setSolutionApplicationTarget(switchStmt, target);
        subjectExpr = target.getAsExpr();
        assert(subjectExpr && "Must have a subject expression here");
      }
  
      // Generate constraints and capture variables for all of the cases.
      SmallVector<std::pair<CaseStmt *, VarDecl *>, 4> capturedCaseVars;
      for (auto *caseStmt : switchStmt->getCases()) {
        if (auto capturedCaseVar = visitCaseStmt(caseStmt, subjectExpr)) {
          capturedCaseVars.push_back({caseStmt, capturedCaseVar});
        }
      }
  
      if (!cs)
        return nullptr;
  
      // If there are no 'case' statements in the body let's try
      // to diagnose this situation via limited exhaustiveness check
      // before failing a builder transform, otherwise type-checker
      // might end up without any diagnostics which leads to crashes
      // in SILGen.
      if (capturedCaseVars.empty()) {
        TypeChecker::checkSwitchExhaustiveness(switchStmt, dc,
                                               /*limitChecking=*/true);
        hadError = true;
        return nullptr;
      }
  
      // Form the expressions that inject the result of each case into the
      // appropriate
      llvm::TinyPtrVector<Expr *> injectedCaseExprs;
      SmallVector<std::pair<Type, ConstraintLocator *>, 4> injectedCaseTerms;
      for (unsigned idx : indices(capturedCaseVars)) {
        auto caseStmt = capturedCaseVars[idx].first;
        auto caseVar = capturedCaseVars[idx].second;
  
        // Build the expression that injects the case variable into appropriate
        // buildEither(first:)/buildEither(second:) chain.
        Expr *caseVarRef = builder.buildVarRef(caseVar, caseStmt->getEndLoc());
        Expr *injectedCaseExpr = buildWrappedChainPayload(
            caseVarRef, idx, capturedCaseVars.size(), /*isOptional=*/false);
  
        // Generate constraints for this injected case result.
        injectedCaseExpr = cs->generateConstraints(injectedCaseExpr, dc);
        if (!injectedCaseExpr) {
          hadError = true;
          return nullptr;
        }
  
        // Record this injected case expression.
        injectedCaseExprs.push_back(injectedCaseExpr);
  
        // Record the type and locator for this injected case expression, to be
        // used in the "join" constraint later.
        injectedCaseTerms.push_back(
          { cs->getType(injectedCaseExpr)->getRValueType(),
            cs->getConstraintLocator(injectedCaseExpr) });
      }
  
      // Form the type of the switch itself.
      Type resultType = cs->addJoinConstraint(
          cs->getConstraintLocator(switchStmt), injectedCaseTerms);
      if (!resultType) {
        hadError = true;
        return nullptr;
      }
  
      // Create a variable to capture the result of evaluating the switch.
      auto switchVar = builder.buildVar(switchStmt->getStartLoc());
      cs->setType(switchVar, resultType);
      applied.capturedStmts.insert(
          {switchStmt, { switchVar, std::move(injectedCaseExprs) } });
      return switchVar;
    }
  
    VarDecl *visitCaseStmt(CaseStmt *caseStmt, Expr *subjectExpr) {
      auto *body = caseStmt->getBody();
  
      // Explicitly disallow `case` statements with empty bodies
      // since that helps to diagnose other issues with switch
      // statements by excluding invalid cases.
      if (auto *BS = dyn_cast<BraceStmt>(body)) {
        if (BS->getNumElements() == 0) {
          // HACK: still allow empty bodies if typechecking for code
          // completion. Code completion ignores diagnostics
          // and won't get any types if we fail.
          if (!ctx.SourceMgr.hasCodeCompletionBuffer()) {
            hadError = true;
            return nullptr;
          }
        }
      }
  
      // If needed, generate constraints for everything in the case statement.
      if (cs) {
        auto locator = cs->getConstraintLocator(
            subjectExpr, LocatorPathElt::ContextualType(CTP_Initialization));
        Type subjectType = cs->getType(subjectExpr);
  
        if (cs->generateConstraints(caseStmt, dc, subjectType, locator)) {
          hadError = true;
          return nullptr;
        }
      }
  
      // Translate the body.
      return visit(caseStmt->getBody());
    }
  
    VarDecl *visitForEachStmt(ForEachStmt *forEachStmt) {
      // for...in statements are handled via buildArray(_:); bail out if the
      // builder does not support it.
      if (!builder.supports(ctx.Id_buildArray)) {
        if (!unhandledNode)
          unhandledNode = forEachStmt;
        return nullptr;
      }
  
      // Generate constraints for the loop header. This also wires up the
      // types for the patterns.
      auto target = SolutionApplicationTarget::forForEachStmt(
          forEachStmt, dc, /*bindPatternVarsOneWay=*/true);
      if (cs) {
        if (cs->generateConstraints(target, FreeTypeVariableBinding::Disallow)) {
          hadError = true;
          return nullptr;
        }
  
        cs->setSolutionApplicationTarget(forEachStmt, target);
      }
  
      // Visit the loop body itself.
      VarDecl *bodyVar = visit(forEachStmt->getBody());
      if (!bodyVar)
        return nullptr;
  
      // If there's no constraint system, there is nothing left to visit.
      if (!cs)
        return nullptr;
  
      // Form a variable of array type that will capture the result of each
      // iteration of the loop. We need a fresh type variable to remove the
      // lvalue-ness of the array variable.
      SourceLoc loc = forEachStmt->getForLoc();
      VarDecl *arrayVar = builder.buildVar(loc);
      Type arrayElementType = cs->createTypeVariable(
          cs->getConstraintLocator(forEachStmt), 0);
      cs->addConstraint(ConstraintKind::Equal, cs->getType(bodyVar),
                        arrayElementType,
                        cs->getConstraintLocator(
                            forEachStmt, ConstraintLocator::SequenceElementType));
      Type arrayType = ArraySliceType::get(arrayElementType);
      cs->setType(arrayVar, arrayType);
  
      // Form an initialization of the array to an empty array literal.
      Expr *arrayInitExpr = ArrayExpr::create(ctx, loc, { }, { }, loc);
      cs->setContextualType(
          arrayInitExpr, TypeLoc::withoutLoc(arrayType), CTP_CannotFail);
      arrayInitExpr = cs->generateConstraints(arrayInitExpr, dc);
      if (!arrayInitExpr) {
        hadError = true;
        return nullptr;
      }
      cs->addConstraint(
          ConstraintKind::Equal, cs->getType(arrayInitExpr), arrayType,
          cs->getConstraintLocator(
              arrayInitExpr, LocatorPathElt::ContextualType(CTP_Initialization)));
  
      // Form a call to Array.append(_:) to add the result of executing each
      // iteration of the loop body to the array formed above.
      SourceLoc endLoc = forEachStmt->getEndLoc();
      auto arrayVarRef = builder.buildVarRef(arrayVar, endLoc);
      auto arrayAppendRef = new (ctx) UnresolvedDotExpr(
          arrayVarRef, endLoc, DeclNameRef(ctx.getIdentifier("append")),
          DeclNameLoc(endLoc), /*implicit=*/true);
      arrayAppendRef->setFunctionRefKind(FunctionRefKind::SingleApply);
  
      auto bodyVarRef = builder.buildVarRef(bodyVar, endLoc);
      auto *argList = ArgumentList::createImplicit(
          ctx, endLoc, {Argument::unlabeled(bodyVarRef)}, endLoc);
      Expr *arrayAppendCall =
          CallExpr::createImplicit(ctx, arrayAppendRef, argList);
      arrayAppendCall = cs->generateConstraints(arrayAppendCall, dc);
      if (!arrayAppendCall) {
        hadError = true;
        return nullptr;
      }
  
      // Form the final call to buildArray(arrayVar) to allow the function
      // builder to reshape the array into whatever it wants as the result of
      // the for-each loop.
      auto finalArrayVarRef = builder.buildVarRef(arrayVar, endLoc);
      auto buildArrayCall = buildCallIfWanted(
          endLoc, ctx.Id_buildArray, { finalArrayVarRef }, { Identifier() });
      assert(buildArrayCall);
      buildArrayCall = cs->generateConstraints(buildArrayCall, dc);
      if (!buildArrayCall) {
        hadError = true;
        return nullptr;
      }
  
      // Form a final variable for the for-each expression itself, which will
      // be initialized with the call to the result builder's buildArray(_:).
      auto finalForEachVar = builder.buildVar(loc);
      cs->setType(finalForEachVar, cs->getType(buildArrayCall));
      applied.capturedStmts.insert(
        {forEachStmt, {
            finalForEachVar,
            { arrayVarRef, arrayInitExpr, arrayAppendCall, buildArrayCall }}});
  
      return finalForEachVar;
    }
  
    /// Visit a throw statement, which never produces a result.
    VarDecl *visitThrowStmt(ThrowStmt *throwStmt) {
      if (!ctx.getErrorDecl()) {
        hadError = true;
      }
  
      if (cs) {
       SolutionApplicationTarget target(
           throwStmt->getSubExpr(), dc, CTP_ThrowStmt,
           ctx.getErrorExistentialType(),
           /*isDiscarded=*/false);
       if (cs->generateConstraints(target, FreeTypeVariableBinding::Disallow))
         hadError = true;
  
       cs->setSolutionApplicationTarget(throwStmt, target);
     }
  
      return nullptr;
    }
  
    CONTROL_FLOW_STMT(Guard)
    CONTROL_FLOW_STMT(While)
    CONTROL_FLOW_STMT(DoCatch)
    CONTROL_FLOW_STMT(RepeatWhile)
    CONTROL_FLOW_STMT(Case)
    CONTROL_FLOW_STMT(Break)
    CONTROL_FLOW_STMT(Continue)
    CONTROL_FLOW_STMT(Fallthrough)
    CONTROL_FLOW_STMT(Fail)
    CONTROL_FLOW_STMT(PoundAssert)
    CONTROL_FLOW_STMT(Return)
  
  #undef CONTROL_FLOW_STMT
  };
  
  /// Describes the target into which the result of a particular statement in
  /// a closure involving a result builder should be written.
  struct ResultBuilderTarget {
    enum Kind {
      /// The resulting value is returned from the closure.
      ReturnValue,
      /// The temporary variable into which the result should be assigned.
      TemporaryVar,
      /// An expression to evaluate at the end of the block, allowing the update
      /// of some state from an outer scope.
      Expression,
    } kind;
  
    /// Captured variable information.
    std::pair<VarDecl *, llvm::TinyPtrVector<Expr *>> captured;
  
    static ResultBuilderTarget forReturn(Expr *expr) {
      return ResultBuilderTarget{ReturnValue, {nullptr, {expr}}};
    }
  
    static ResultBuilderTarget forAssign(VarDecl *temporaryVar,
                                           llvm::TinyPtrVector<Expr *> exprs) {
      return ResultBuilderTarget{TemporaryVar, {temporaryVar, exprs}};
    }
  
    static ResultBuilderTarget forExpression(Expr *expr) {
      return ResultBuilderTarget{Expression, { nullptr, { expr }}};
    }
  };
  
  /// Handles the rewrite of the body of a closure to which a result builder
  /// has been applied.
  class BuilderClosureRewriter
      : public StmtVisitor<BuilderClosureRewriter, NullablePtr<Stmt>,
                           ResultBuilderTarget> {
    ASTContext &ctx;
    const Solution &solution;
    DeclContext *dc;
    AppliedBuilderTransform builderTransform;
    std::function<
        Optional<SolutionApplicationTarget> (SolutionApplicationTarget)>
          rewriteTarget;
  
    /// Retrieve the temporary variable that will be used to capture the
    /// value of the given expression.
    AppliedBuilderTransform::RecordedExpr takeCapturedExpr(Expr *expr) {
      auto found = builderTransform.capturedExprs.find(expr);
      assert(found != builderTransform.capturedExprs.end());
  
      // Set the type of the temporary variable.
      auto recorded = found->second;
      if (auto temporaryVar = recorded.temporaryVar) {
        Type type = solution.simplifyType(solution.getType(temporaryVar));
        temporaryVar->setInterfaceType(type->mapTypeOutOfContext());
      }
  
      // Erase the captured expression, so we're sure we never do this twice.
      builderTransform.capturedExprs.erase(found);
      return recorded;
    }
  
    /// Rewrite an expression without any particularly special context.
    Expr *rewriteExpr(Expr *expr) {
      auto result = rewriteTarget(
        SolutionApplicationTarget(expr, dc, CTP_Unused, Type(),
                                  /*isDiscarded=*/false));
      if (result)
        return result->getAsExpr();
  
      return nullptr;
    }
  
  public:
    /// Retrieve information about a captured statement.
    std::pair<VarDecl *, llvm::TinyPtrVector<Expr *>>
    takeCapturedStmt(Stmt *stmt) {
      auto found = builderTransform.capturedStmts.find(stmt);
      assert(found != builderTransform.capturedStmts.end());
  
      // Set the type of the temporary variable.
      auto temporaryVar = found->second.first;
      Type type = solution.simplifyType(solution.getType(temporaryVar));
      temporaryVar->setInterfaceType(type->mapTypeOutOfContext());
  
      // Take the expressions.
      auto exprs = std::move(found->second.second);
  
      // Erase the statement, so we're sure we never do this twice.
      builderTransform.capturedStmts.erase(found);
      return std::make_pair(temporaryVar, std::move(exprs));
    }
  
  private:
    /// Build the statement or expression to initialize the target.
    ASTNode initializeTarget(ResultBuilderTarget target) {
      assert(target.captured.second.size() == 1);
      auto capturedExpr = target.captured.second.front();
      SourceLoc implicitLoc = capturedExpr->getEndLoc();
      switch (target.kind) {
      case ResultBuilderTarget::ReturnValue: {
        // Return the expression.
        Type bodyResultInterfaceType =
            solution.simplifyType(builderTransform.bodyResultType);
  
        SolutionApplicationTarget returnTarget(capturedExpr, dc, CTP_ReturnStmt,
                                               bodyResultInterfaceType,
                                               /*isDiscarded=*/false);
        Expr *resultExpr = nullptr;
        if (auto resultTarget = rewriteTarget(returnTarget))
          resultExpr = resultTarget->getAsExpr();
  
        return new (ctx) ReturnStmt(implicitLoc, resultExpr);
      }
  
      case ResultBuilderTarget::TemporaryVar: {
        // Assign the expression into a variable.
        auto temporaryVar = target.captured.first;
        auto declRef = new (ctx) DeclRefExpr(
            temporaryVar, DeclNameLoc(implicitLoc), /*implicit=*/true);
        declRef->setType(LValueType::get(temporaryVar->getType()));
  
        // Load the right-hand side if needed.
        auto finalCapturedExpr = rewriteExpr(capturedExpr);
        if (finalCapturedExpr->getType()->hasLValueType()) {
          finalCapturedExpr =
              TypeChecker::addImplicitLoadExpr(ctx, finalCapturedExpr);
        }
  
        auto assign = new (ctx) AssignExpr(
            declRef, implicitLoc, finalCapturedExpr, /*implicit=*/true);
        assign->setType(TupleType::getEmpty(ctx));
        return assign;
      }
  
      case ResultBuilderTarget::Expression:
        // Execute the expression.
        return rewriteExpr(capturedExpr);
      }
      llvm_unreachable("invalid result builder target");
    }
  
    /// Declare the given temporary variable, adding the appropriate
    /// entries to the elements of a brace stmt.
    void declareTemporaryVariable(VarDecl *temporaryVar,
                                  std::vector<ASTNode> &elements,
                                  Expr *initExpr = nullptr) {
      if (!temporaryVar)
        return;
  
      // Form a new pattern binding to bind the temporary variable to the
      // transformed expression.
      auto pattern = NamedPattern::createImplicit(ctx, temporaryVar);
      pattern->setType(temporaryVar->getType());
  
      auto pbd = PatternBindingDecl::create(
          ctx, SourceLoc(), StaticSpellingKind::None, temporaryVar->getLoc(),
          pattern, SourceLoc(), initExpr, dc);
      if (temporaryVar->isImplicit())
        pbd->setImplicit();
      elements.push_back(temporaryVar);
      elements.push_back(pbd);
    }
  
  public:
    BuilderClosureRewriter(
        const Solution &solution,
        DeclContext *dc,
        const AppliedBuilderTransform &builderTransform,
        std::function<
            Optional<SolutionApplicationTarget> (SolutionApplicationTarget)>
              rewriteTarget
      ) : ctx(solution.getConstraintSystem().getASTContext()),
          solution(solution), dc(dc), builderTransform(builderTransform),
          rewriteTarget(rewriteTarget) { }
  
    NullablePtr<Stmt>
    visitBraceStmt(BraceStmt *braceStmt, ResultBuilderTarget target,
                   Optional<ResultBuilderTarget> innerTarget = None) {
      std::vector<ASTNode> newElements;
  
      // If there is an "inner" target corresponding to this brace, declare
      // it's temporary variable if needed.
      if (innerTarget) {
        declareTemporaryVariable(innerTarget->captured.first, newElements);
      }
  
      for (auto node : braceStmt->getElements()) {
        // Implicit returns in single-expression function bodies are treated
        // as the expression.
        if (auto returnStmt =
                dyn_cast_or_null<ReturnStmt>(node.dyn_cast<Stmt *>())) {
          assert(returnStmt->isImplicit());
          node = returnStmt->getResult();
        }
  
        if (auto expr = node.dyn_cast<Expr *>()) {
          // Skip error expressions.
          if (isa<ErrorExpr>(expr))
            continue;
  
          // Each expression turns into a 'let' that captures the value of
          // the expression.
          auto recorded = takeCapturedExpr(expr);
  
          // Rewrite the expression
          Expr *finalExpr = rewriteExpr(recorded.generatedExpr);
  
          // Form a new pattern binding to bind the temporary variable to the
          // transformed expression.
          declareTemporaryVariable(recorded.temporaryVar, newElements, finalExpr);
          continue;
        }
  
        if (auto stmt = node.dyn_cast<Stmt *>()) {
          // "throw" statements produce no value. Transform them directly.
          if (auto throwStmt = dyn_cast<ThrowStmt>(stmt)) {
            if (auto newStmt = visitThrowStmt(throwStmt)) {
              newElements.push_back(newStmt.get());
            }
            continue;
          }
  
          // Each statement turns into a (potential) temporary variable
          // binding followed by the statement itself.
          auto captured = takeCapturedStmt(stmt);
  
          declareTemporaryVariable(captured.first, newElements);
  
          auto finalStmt = visit(
              stmt,
              ResultBuilderTarget{ResultBuilderTarget::TemporaryVar,
                                    std::move(captured)});
  
          // Re-write of statements that envolve type-checking
          // could fail, such a failure terminates the walk.
          if (!finalStmt)
            return nullptr;
  
          newElements.push_back(finalStmt.get());
          continue;
        }
  
        auto decl = node.get<Decl *>();
  
        // Skip #if declarations.
        if (isa<IfConfigDecl>(decl)) {
          newElements.push_back(decl);
          continue;
        }
  
        // Diagnose #warning / #error during application.
        if (auto poundDiag = dyn_cast<PoundDiagnosticDecl>(decl)) {
          TypeChecker::typeCheckDecl(poundDiag);
          newElements.push_back(decl);
          continue;
        }
  
        // Skip variable declarations; they're always part of a pattern
        // binding.
        if (isa<VarDecl>(decl)) {
          TypeChecker::typeCheckDecl(decl);
          newElements.push_back(decl);
          continue;
        }
  
        // Handle pattern bindings.
        if (auto patternBinding = dyn_cast<PatternBindingDecl>(decl)) {
          auto resultTarget = rewriteTarget(SolutionApplicationTarget{patternBinding});
          assert(resultTarget.hasValue()
                 && "Could not rewrite pattern binding entries!");
          TypeChecker::typeCheckDecl(resultTarget->getAsPatternBinding());
          newElements.push_back(resultTarget->getAsPatternBinding());
          continue;
        }
  
        llvm_unreachable("Cannot yet handle declarations");
      }
  
      // If there is an "inner" target corresponding to this brace, initialize
      // it.
      if (innerTarget) {
        newElements.push_back(initializeTarget(*innerTarget));
      }
  
      // Capture the result of the buildBlock() call in the manner requested
      // by the caller.
      newElements.push_back(initializeTarget(target));
  
      return BraceStmt::create(ctx, braceStmt->getLBraceLoc(), newElements,
                               braceStmt->getRBraceLoc());
    }
  
    NullablePtr<Stmt> visitIfStmt(IfStmt *ifStmt, ResultBuilderTarget target) {
      // Rewrite the condition.
      if (auto condition = rewriteTarget(
              SolutionApplicationTarget(ifStmt->getCond(), dc)))
        ifStmt->setCond(*condition->getAsStmtCondition());
  
      assert(target.kind == ResultBuilderTarget::TemporaryVar);
      auto temporaryVar = target.captured.first;
  
      // Translate the "then" branch.
      auto capturedThen = takeCapturedStmt(ifStmt->getThenStmt());
      auto newThen = visitBraceStmt(cast<BraceStmt>(ifStmt->getThenStmt()),
            ResultBuilderTarget::forAssign(
              temporaryVar, {target.captured.second[0]}),
            ResultBuilderTarget::forAssign(
              capturedThen.first, {capturedThen.second.front()}));
      if (!newThen)
        return nullptr;
  
      ifStmt->setThenStmt(newThen.get());
  
      // 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.
      if (auto availabilityCond = findAvailabilityCondition(ifStmt->getCond())) {
        SourceLoc loc = availabilityCond->getStartLoc();
        Type bodyType;
        if (availabilityCond->getAvailability()->isUnavailability()) {
          // For #unavailable, we need to check the "else".
          Type elseBodyType = solution.simplifyType(
            solution.getType(target.captured.second[1]));
          bodyType = elseBodyType;
        } else {
          Type thenBodyType = solution.simplifyType(
            solution.getType(target.captured.second[0]));
          bodyType = thenBodyType;
        }
        bodyType.findIf([&](Type type) {
          auto nominal = type->getAnyNominal();
          if (!nominal)
            return false;
  
          ExportContext where = ExportContext::forFunctionBody(dc, loc);
          if (auto reason = TypeChecker::checkDeclarationAvailability(
                                nominal, where)) {
            ctx.Diags.diagnose(
                loc, diag::result_builder_missing_limited_availability,
                builderTransform.builderType);
  
            // Add a note to the result builder with a stub for
            // buildLimitedAvailability().
            if (auto builder = builderTransform.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,
                      builderTransform.builderType)
                    .fixItInsert(buildInsertionLoc, fixItString);
                }
              }
            }
  
            return true;
          }
  
          return false;
        });
      }
  
      if (auto elseBraceStmt =
              dyn_cast_or_null<BraceStmt>(ifStmt->getElseStmt())) {
        // Translate the "else" branch when it's a stmt-brace.
        auto capturedElse = takeCapturedStmt(elseBraceStmt);
        auto newElse = visitBraceStmt(
            elseBraceStmt,
            ResultBuilderTarget::forAssign(
              temporaryVar, {target.captured.second[1]}),
            ResultBuilderTarget::forAssign(
              capturedElse.first, {capturedElse.second.front()}));
        if (!newElse)
          return nullptr;
  
        ifStmt->setElseStmt(newElse.get());
      } else if (auto elseIfStmt = cast_or_null<IfStmt>(ifStmt->getElseStmt())){
        // Translate the "else" branch when it's an else-if.
        auto capturedElse = takeCapturedStmt(elseIfStmt);
        std::vector<ASTNode> newElseElements;
        declareTemporaryVariable(capturedElse.first, newElseElements);
        auto newElseElt =
            visitIfStmt(elseIfStmt, ResultBuilderTarget::forAssign(
                                        capturedElse.first, capturedElse.second));
        if (!newElseElt)
          return nullptr;
  
        newElseElements.push_back(newElseElt.get());
        newElseElements.push_back(
            initializeTarget(
              ResultBuilderTarget::forAssign(
                temporaryVar, {target.captured.second[1]})));
  
        Stmt *newElse = BraceStmt::create(
            ctx, elseIfStmt->getStartLoc(), newElseElements,
            elseIfStmt->getEndLoc());
        ifStmt->setElseStmt(newElse);
      } else {
        // Form an "else" brace containing an assignment to the temporary
        // variable.
        auto init = initializeTarget(
            ResultBuilderTarget::forAssign(
              temporaryVar, {target.captured.second[1]}));
        auto newElse = BraceStmt::create(
            ctx, ifStmt->getEndLoc(), { init }, ifStmt->getEndLoc());
        ifStmt->setElseStmt(newElse);
      }
  
      return ifStmt;
    }
  
    NullablePtr<Stmt> visitDoStmt(DoStmt *doStmt, ResultBuilderTarget target) {
      // Each statement turns into a (potential) temporary variable
      // binding followed by the statement itself.
      auto body = cast<BraceStmt>(doStmt->getBody());
      auto captured = takeCapturedStmt(body);
  
      auto newInnerBody =
          visitBraceStmt(body, target,
                         ResultBuilderTarget::forAssign(
                             captured.first, {captured.second.front()}));
      if (!newInnerBody)
        return nullptr;
  
      doStmt->setBody(cast<BraceStmt>(newInnerBody.get()));
      return doStmt;
    }
  
    NullablePtr<Stmt> visitSwitchStmt(SwitchStmt *switchStmt,
                                      ResultBuilderTarget target) {
      // Translate the subject expression.
      ConstraintSystem &cs = solution.getConstraintSystem();
      auto subjectTarget =
          rewriteTarget(*cs.getSolutionApplicationTarget(switchStmt));
      if (!subjectTarget)
        return nullptr;
  
      switchStmt->setSubjectExpr(subjectTarget->getAsExpr());
  
      // Handle any declaration nodes within the case list first; we'll
      // handle the cases in a second pass.
      for (auto child : switchStmt->getRawCases()) {
        if (auto decl = child.dyn_cast<Decl *>()) {
          TypeChecker::typeCheckDecl(decl);
        }
      }
  
      // Translate all of the cases.
      bool limitExhaustivityChecks = false;
      assert(target.kind == ResultBuilderTarget::TemporaryVar);
      auto temporaryVar = target.captured.first;
      unsigned caseIndex = 0;
      for (auto caseStmt : switchStmt->getCases()) {
        if (!visitCaseStmt(
              caseStmt,
              ResultBuilderTarget::forAssign(
                temporaryVar, {target.captured.second[caseIndex]})))
          return nullptr;
  
        // Check restrictions on '@unknown'.
        if (caseStmt->hasUnknownAttr()) {
          checkUnknownAttrRestrictions(
              cs.getASTContext(), caseStmt, limitExhaustivityChecks);
        }
  
        ++caseIndex;
      }
  
      TypeChecker::checkSwitchExhaustiveness(
          switchStmt, dc, limitExhaustivityChecks);
  
      return switchStmt;
    }
  
    NullablePtr<Stmt> visitCaseStmt(CaseStmt *caseStmt,
                                    ResultBuilderTarget target) {
      // Translate the patterns and guard expressions for each case label item.
      for (auto &caseLabelItem : caseStmt->getMutableCaseLabelItems()) {
        SolutionApplicationTarget caseLabelTarget(&caseLabelItem, dc);
        if (!rewriteTarget(caseLabelTarget))
          return nullptr;
      }
  
      // Setup the types of our case body var decls.
      for (auto *expected : caseStmt->getCaseBodyVariablesOrEmptyArray()) {
        assert(expected->hasName());
        auto prev = expected->getParentVarDecl();
        auto type = solution.resolveInterfaceType(
            solution.getType(prev)->mapTypeOutOfContext());
        expected->setInterfaceType(type);
      }
  
      // Transform the body of the case.
      auto body = cast<BraceStmt>(caseStmt->getBody());
      auto captured = takeCapturedStmt(body);
      auto newInnerBody =
          visitBraceStmt(body, target,
                         ResultBuilderTarget::forAssign(
                             captured.first, {captured.second.front()}));
      if (!newInnerBody)
        return nullptr;
  
      caseStmt->setBody(cast<BraceStmt>(newInnerBody.get()));
      return caseStmt;
    }
  
    NullablePtr<Stmt> visitForEachStmt(ForEachStmt *forEachStmt,
                                       ResultBuilderTarget target) {
      // Translate the for-each loop header.
      ConstraintSystem &cs = solution.getConstraintSystem();
      auto forEachTarget =
          rewriteTarget(*cs.getSolutionApplicationTarget(forEachStmt));
      if (!forEachTarget)
        return nullptr;
  
      const auto &captured = target.captured;
      auto finalForEachVar = captured.first;
      auto arrayVarRef = captured.second[0];
      auto arrayVar = cast<VarDecl>(cast<DeclRefExpr>(arrayVarRef)->getDecl());
      auto arrayInitExpr = captured.second[1];
      auto arrayAppendCall = captured.second[2];
      auto buildArrayCall = captured.second[3];
  
      // Collect the three steps to initialize the array variable to an
      // empty array, execute the loop to collect the results of each iteration,
      // then form the buildArray() call to the write the result.
      std::vector<ASTNode> outerBodySteps;
  
      // Step 1: Declare and initialize the array variable.
      arrayVar->setInterfaceType(solution.simplifyType(cs.getType(arrayVar)));
      arrayInitExpr = rewriteExpr(arrayInitExpr);
      declareTemporaryVariable(arrayVar, outerBodySteps, arrayInitExpr);
  
      // Step 2. Transform the body of the for-each statement. Each iteration
      // will append the result of executing the loop body to the array.
      auto body = forEachStmt->getBody();
      auto capturedBody = takeCapturedStmt(body);
      auto newBody = visitBraceStmt(
          body, ResultBuilderTarget::forExpression(arrayAppendCall),
          ResultBuilderTarget::forAssign(capturedBody.first,
                                         {capturedBody.second.front()}));
      if (!newBody)
        return nullptr;
  
      forEachStmt->setBody(cast<BraceStmt>(newBody.get()));
      outerBodySteps.push_back(forEachStmt);
  
      // Step 3. Perform the buildArray() call to turn the array of results
      // collected from the iterations into a single value under the control of
      // the result builder.
      outerBodySteps.push_back(
          initializeTarget(
            ResultBuilderTarget::forAssign(finalForEachVar, {buildArrayCall})));
  
      // Form a brace statement to put together the three main steps for the
      // for-each loop translation outlined above.
      return BraceStmt::create(ctx, forEachStmt->getStartLoc(), outerBodySteps,
                               newBody.get()->getEndLoc());
    }
  
    NullablePtr<Stmt> visitThrowStmt(ThrowStmt *throwStmt) {
      // Rewrite the error.
      auto target = *solution.getConstraintSystem()
          .getSolutionApplicationTarget(throwStmt);
      if (auto result = rewriteTarget(target))
        throwStmt->setSubExpr(result->getAsExpr());
      else
        return nullptr;
  
      return throwStmt;
    }
  
    NullablePtr<Stmt> visitThrowStmt(ThrowStmt *throwStmt,
                                     ResultBuilderTarget target) {
      llvm_unreachable("Throw statements produce no value");
    }
  
  #define UNHANDLED_RESULT_BUILDER_STMT(STMT) \
    NullablePtr<Stmt> \
    visit##STMT##Stmt(STMT##Stmt *stmt, ResultBuilderTarget target) { \
      llvm_unreachable("Function builders do not allow statement of kind " \
                       #STMT); \
    }
  
    UNHANDLED_RESULT_BUILDER_STMT(Return)
    UNHANDLED_RESULT_BUILDER_STMT(Yield)
    UNHANDLED_RESULT_BUILDER_STMT(Guard)
    UNHANDLED_RESULT_BUILDER_STMT(While)
    UNHANDLED_RESULT_BUILDER_STMT(Defer)
    UNHANDLED_RESULT_BUILDER_STMT(DoCatch)
    UNHANDLED_RESULT_BUILDER_STMT(RepeatWhile)
    UNHANDLED_RESULT_BUILDER_STMT(Break)
    UNHANDLED_RESULT_BUILDER_STMT(Continue)
    UNHANDLED_RESULT_BUILDER_STMT(Fallthrough)
    UNHANDLED_RESULT_BUILDER_STMT(Fail)
    UNHANDLED_RESULT_BUILDER_STMT(PoundAssert)
  #undef UNHANDLED_RESULT_BUILDER_STMT
  };
  
  } // end anonymous namespace
  
  BraceStmt *swift::applyResultBuilderTransform(
      const Solution &solution,
      AppliedBuilderTransform applied,
      BraceStmt *body,
      DeclContext *dc,
      std::function<
          Optional<SolutionApplicationTarget> (SolutionApplicationTarget)>
            rewriteTarget) {
    BuilderClosureRewriter rewriter(solution, dc, applied, rewriteTarget);
    auto captured = rewriter.takeCapturedStmt(body);
    auto result = rewriter.visitBraceStmt(
        body, ResultBuilderTarget::forReturn(applied.returnExpr),
        ResultBuilderTarget::forAssign(captured.first, captured.second));
    if (!result)
      return nullptr;
    return cast<BraceStmt>(result.get());
  }
  
  Optional<BraceStmt *> TypeChecker::applyResultBuilderBodyTransform(
      FuncDecl *func, Type builderType) {
    // Pre-check the body: pre-check any expressions in it and look
    // for return statements.
    //
    // If we encountered an error or there was an explicit result type,
    // bail out and report that to the caller.
    auto &ctx = func->getASTContext();
    auto request =
        PreCheckResultBuilderRequest{{AnyFunctionRef(func),
                                        /*SuppressDiagnostics=*/false}};
    switch (evaluateOrDefault(ctx.evaluator, request,
                              ResultBuilderBodyPreCheck::Error)) {
    case ResultBuilderBodyPreCheck::Okay:
      // If the pre-check was okay, apply the result-builder transform.
      break;
  
    case ResultBuilderBodyPreCheck::Error:
      return nullptr;
  
    case ResultBuilderBodyPreCheck::HasReturnStmt: {
      // One or more explicit 'return' statements were encountered, which
      // disables the result builder transform. Warn when we do this.
      auto returnStmts = findReturnStatements(func);
      assert(!returnStmts.empty());
  
      ctx.Diags.diagnose(
          returnStmts.front()->getReturnLoc(),
          diag::result_builder_disabled_by_return_warn, builderType);
  
      // Note that one can remove the result builder attribute.
      auto attr = func->getAttachedResultBuilder();
      if (!attr) {
        if (auto accessor = dyn_cast<AccessorDecl>(func)) {
          attr = accessor->getStorage()->getAttachedResultBuilder();
        }
      }
  
      if (attr) {
        diagnoseAndRemoveAttr(func, attr, diag::result_builder_remove_attr);
      }
  
      // Note that one can remove all of the return statements.
      {
        auto diag = ctx.Diags.diagnose(
            returnStmts.front()->getReturnLoc(),
            diag::result_builder_remove_returns);
        for (auto returnStmt : returnStmts) {
          diag.fixItRemove(returnStmt->getReturnLoc());
        }
      }
  
      return None;
    }
    }
  
    ConstraintSystemOptions options = ConstraintSystemFlags::AllowFixes;
    auto resultInterfaceTy = func->getResultInterfaceType();
    auto resultContextType = func->mapTypeIntoContext(resultInterfaceTy);
  
    // Determine whether we're inferring the underlying type for the opaque
    // result type of this function.
    ConstraintKind resultConstraintKind = ConstraintKind::Conversion;
    if (auto opaque = resultContextType->getAs<OpaqueTypeArchetypeType>()) {
      if (opaque->getDecl()->isOpaqueReturnTypeOfFunction(func)) {
        resultConstraintKind = ConstraintKind::Equal;
      }
    }
  
    // Build a constraint system in which we can check the body of the function.
    ConstraintSystem cs(func, options);
  
    if (cs.isDebugMode()) {
      auto &log = llvm::errs();
  
      log << "--- Applying result builder to function ---\n";
      func->dump(log);
      log << '\n';
    }
  
    if (auto result = cs.matchResultBuilder(
            func, builderType, resultContextType, resultConstraintKind,
            cs.getConstraintLocator(func->getBody()))) {
      if (result->isFailure())
        return nullptr;
    }
  
    // Solve the constraint system.
    if (cs.getASTContext().CompletionCallback) {
      SmallVector<Solution, 4> solutions;
      cs.solveForCodeCompletion(solutions);
  
      CompletionContextFinder analyzer(func, func->getDeclContext());
      filterSolutionsForCodeCompletion(solutions, analyzer);
      for (const auto &solution : solutions) {
        cs.getASTContext().CompletionCallback->sawSolution(solution);
      }
      return nullptr;
    }
  
    SmallVector<Solution, 4> solutions;
    bool solvingFailed = cs.solve(solutions);
  
    if (solvingFailed || solutions.size() != 1) {
      // Try to fix the system or provide a decent diagnostic.
      auto salvagedResult = cs.salvage();
      switch (salvagedResult.getKind()) {
      case SolutionResult::Kind::Success:
        solutions.clear();
        solutions.push_back(std::move(salvagedResult).takeSolution());
        break;
  
      case SolutionResult::Kind::Error:
      case SolutionResult::Kind::Ambiguous:
        return nullptr;
  
      case SolutionResult::Kind::UndiagnosedError:
        cs.diagnoseFailureFor(SolutionApplicationTarget(func));
        salvagedResult.markAsDiagnosed();
        return nullptr;
  
      case SolutionResult::Kind::TooComplex:
        func->diagnose(diag::expression_too_complex)
          .highlight(func->getBodySourceRange());
        salvagedResult.markAsDiagnosed();
        return nullptr;
      }
  
      // The system was salvaged; continue on as if nothing happened.
    }
  
    if (cs.isDebugMode()) {
      auto &log = llvm::errs();
      log << "--- Applying Solution ---\n";
      solutions.front().dump(log);
      log << '\n';
    }
  
    // FIXME: Shouldn't need to do this.
    cs.applySolution(solutions.front());
  
    // Apply the solution to the function body.
    if (auto result = cs.applySolution(
            solutions.front(),
            SolutionApplicationTarget(func))) {
      performSyntacticDiagnosticsForTarget(*result, /*isExprStmt*/ false);
      auto *body = result->getFunctionBody();
  
      if (cs.isDebugMode()) {
        auto &log = llvm::errs();
        log << "--- Type-checked function body ---\n";
        body->dump(log);
        log << '\n';
      }
  
      return body;
    }
  
    return nullptr;
  }
  
  Optional<ConstraintSystem::TypeMatchResult>
  ConstraintSystem::matchResultBuilder(AnyFunctionRef fn, Type builderType,
                                       Type bodyResultType,
                                       ConstraintKind bodyResultConstraintKind,
                                       ConstraintLocatorBuilder locator) {
    auto builder = builderType->getAnyNominal();
    assert(builder && "Bad result builder type");
    assert(builder->getAttrs().hasAttribute<ResultBuilderAttr>());
  
    if (InvalidResultBuilderBodies.count(fn)) {
      (void)recordFix(IgnoreInvalidResultBuilderBody::create(
          *this, getConstraintLocator(fn.getAbstractClosureExpr())));
      return getTypeMatchSuccess();
    }
  
    // Pre-check the body: pre-check any expressions in it and look
    // for return statements.
    auto request =
        PreCheckResultBuilderRequest{{fn, /*SuppressDiagnostics=*/false}};
    switch (evaluateOrDefault(getASTContext().evaluator, request,
                              ResultBuilderBodyPreCheck::Error)) {
    case ResultBuilderBodyPreCheck::Okay:
      // If the pre-check was okay, apply the result-builder transform.
      break;
  
    case ResultBuilderBodyPreCheck::Error: {
      InvalidResultBuilderBodies.insert(fn);
  
      if (!shouldAttemptFixes())
        return getTypeMatchFailure(locator);
  
      if (recordFix(IgnoreInvalidResultBuilderBody::create(
              *this, getConstraintLocator(fn.getAbstractClosureExpr()))))
        return getTypeMatchFailure(locator);
  
      return getTypeMatchSuccess();
    }
  
    case ResultBuilderBodyPreCheck::HasReturnStmt:
      // Diagnostic mode means that solver couldn't reach any viable
      // solution, so let's diagnose presence of a `return` statement
      // in the closure body.
      if (shouldAttemptFixes()) {
        if (recordFix(IgnoreResultBuilderWithReturnStmts::create(
                *this, builderType,
                getConstraintLocator(fn.getAbstractClosureExpr()))))
          return getTypeMatchFailure(locator);
  
        return getTypeMatchSuccess();
      }
  
      // If the body has a return statement, suppress the transform but
      // continue solving the constraint system.
      return None;
    }
  
    // Check the form of this body to see if we can apply the
    // result-builder translation at all.
    auto dc = fn.getAsDeclContext();
    {
      // Check whether we can apply this specific result builder.
      BuilderClosureVisitor visitor(getASTContext(), nullptr, dc, builderType,
                                    bodyResultType);
  
      // If we saw a control-flow statement or declaration that the builder
      // cannot handle, we don't have a well-formed result builder application.
      if (auto unhandledNode = visitor.check(fn.getBody())) {
        // If we aren't supposed to attempt fixes, fail.
        if (!shouldAttemptFixes()) {
          return getTypeMatchFailure(locator);
        }
  
        // If we're solving for code completion and the body contains the code
        // completion location, skipping it won't get us to a useful solution so
        // just bail.
        if (isForCodeCompletion() && containsCodeCompletionLoc(fn.getBody())) {
          return getTypeMatchFailure(locator);
        }
  
        // Record the first unhandled construct as a fix.
        if (recordFix(
                SkipUnhandledConstructInResultBuilder::create(
                  *this, unhandledNode, builder,
                  getConstraintLocator(locator)))) {
          return getTypeMatchFailure(locator);
        }
      }
    }
  
    BuilderClosureVisitor visitor(getASTContext(), this, dc, builderType,
                                  bodyResultType);
  
    Optional<AppliedBuilderTransform> applied = None;
    {
      DiagnosticTransaction transaction(dc->getASTContext().Diags);
  
      applied = visitor.apply(fn.getBody());
      if (!applied)
        return getTypeMatchFailure(locator);
  
      if (transaction.hasErrors()) {
        InvalidResultBuilderBodies.insert(fn);
  
        if (recordFix(IgnoreInvalidResultBuilderBody::create(
                *this, getConstraintLocator(fn.getAbstractClosureExpr()))))
          return getTypeMatchFailure(locator);
  
        return getTypeMatchSuccess();
      }
    }
  
    Type transformedType = getType(applied->returnExpr);
    assert(transformedType && "Missing type");
  
    // Record the transformation.
    assert(std::find_if(
        resultBuilderTransformed.begin(),
        resultBuilderTransformed.end(),
        [&](const std::pair<AnyFunctionRef, AppliedBuilderTransform> &elt) {
          return elt.first == fn;
        }) == resultBuilderTransformed.end() &&
           "already transformed this body along this path!?!");
    resultBuilderTransformed.insert(std::make_pair(fn, std::move(*applied)));
  
    // If builder is applied to the closure expression then
    // `closure body` to `closure result` matching should
    // use special locator.
    if (auto *closure = fn.getAbstractClosureExpr()) {
      locator = getConstraintLocator(closure, ConstraintLocator::ClosureResult);
    } else {
      locator = getConstraintLocator(fn.getAbstractFunctionDecl(),
                                     ConstraintLocator::ResultBuilderBodyResult);
    }
  
    // Bind the body result type to the type of the transformed expression.
    addConstraint(bodyResultConstraintKind, transformedType,
                  openOpaqueType(bodyResultType, CTP_ReturnStmt, locator),
                  locator);
    return getTypeMatchSuccess();
  }
  
  namespace {
  
  /// Pre-check all the expressions in the body.
  class PreCheckResultBuilderApplication : public ASTWalker {
    AnyFunctionRef Fn;
    bool SkipPrecheck = false;
    bool SuppressDiagnostics = false;
    std::vector<ReturnStmt *> ReturnStmts;
    bool HasError = false;
  
    bool hasReturnStmt() const { return !ReturnStmts.empty(); }
  
  public:
    PreCheckResultBuilderApplication(AnyFunctionRef fn, bool skipPrecheck,
                                       bool suppressDiagnostics)
        : Fn(fn), SkipPrecheck(skipPrecheck),
          SuppressDiagnostics(suppressDiagnostics) {}
  
    const std::vector<ReturnStmt *> getReturnStmts() const { return ReturnStmts; }
  
    ResultBuilderBodyPreCheck run() {
      Stmt *oldBody = Fn.getBody();
  
      Stmt *newBody = oldBody->walk(*this);
  
      // If the walk was aborted, it was because we had a problem of some kind.
      assert((newBody == nullptr) == HasError &&
             "unexpected short-circuit while walking body");
      if (HasError)
        return ResultBuilderBodyPreCheck::Error;
  
      assert(oldBody == newBody && "pre-check walk wasn't in-place?");
  
      if (hasReturnStmt())
        return ResultBuilderBodyPreCheck::HasReturnStmt;
  
      return ResultBuilderBodyPreCheck::Okay;
    }
  
    std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
      if (SkipPrecheck)
        return std::make_pair(false, E);
  
      // Pre-check the expression.  If this fails, abort the walk immediately.
      // Otherwise, replace the expression with the result of pre-checking.
      // In either case, don't recurse into the expression.
      {
        auto *DC = Fn.getAsDeclContext();
        auto &diagEngine = DC->getASTContext().Diags;
  
        // Suppress any diagnostics which could be produced by this expression.
        DiagnosticTransaction transaction(diagEngine);
  
        HasError |= ConstraintSystem::preCheckExpression(
            E, DC, /*replaceInvalidRefsWithErrors=*/true,
            /*leaveClosureBodiesUnchecked=*/false);
  
        HasError |= transaction.hasErrors();
  
        if (!HasError)
          HasError |= containsErrorExpr(E);
  
        if (SuppressDiagnostics)
          transaction.abort();
  
        return std::make_pair(false, HasError ? nullptr : E);
      }
    }
  
    std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override {
      // If we see a return statement, note it..
      if (auto returnStmt = dyn_cast<ReturnStmt>(S)) {
        if (!returnStmt->isImplicit()) {
          ReturnStmts.push_back(returnStmt);
          return std::make_pair(false, S);
        }
      }
  
      // Otherwise, recurse into the statement normally.
      return std::make_pair(true, S);
    }
  
    /// Check whether given expression (including single-statement
    /// closures) contains `ErrorExpr` as one of its sub-expressions.
    bool containsErrorExpr(Expr *expr) {
      bool hasError = false;
  
      expr->forEachChildExpr([&](Expr *expr) -> Expr * {
        hasError |= isa<ErrorExpr>(expr);
        if (hasError)
          return nullptr;
  
        if (auto *closure = dyn_cast<ClosureExpr>(expr)) {
          if (closure->hasSingleExpressionBody()) {
            hasError |= containsErrorExpr(closure->getSingleExpressionBody());
            return hasError ? nullptr : expr;
          }
        }
  
        return expr;
      });
  
      return hasError;
    }
  
    /// Ignore patterns.
    std::pair<bool, Pattern*> walkToPatternPre(Pattern *pat) override {
      return { false, pat };
    }
  };
  
  }
  
  ResultBuilderBodyPreCheck PreCheckResultBuilderRequest::evaluate(
      Evaluator &evaluator, PreCheckResultBuilderDescriptor owner) const {
    return PreCheckResultBuilderApplication(
               owner.Fn, /*skipPrecheck=*/false,
               /*suppressDiagnostics=*/owner.SuppressDiagnostics)
        .run();
  }
  
  std::vector<ReturnStmt *> TypeChecker::findReturnStatements(AnyFunctionRef fn) {
    PreCheckResultBuilderApplication precheck(fn, /*skipPreCheck=*/true,
                                                /*SuppressDiagnostics=*/true);
    (void)precheck.run();
    return precheck.getReturnStmts();
  }
  
  bool TypeChecker::typeSupportsBuilderOp(
      Type builderType, DeclContext *dc, Identifier fnName,
      ArrayRef<Identifier> argLabels, SmallVectorImpl<ValueDecl *> *allResults,
      bool checkAvailability) {
    bool foundMatch = false;
    SmallVector<ValueDecl *, 4> foundDecls;
    dc->lookupQualified(
        builderType, DeclNameRef(fnName),
        NL_QualifiedDefault | NL_ProtocolMembers, foundDecls);
    for (auto decl : foundDecls) {
      if (auto func = dyn_cast<FuncDecl>(decl)) {
        // Function must be static.
        if (!func->isStatic())
          continue;
  
        // Function must have the right argument labels, if provided.
        if (!argLabels.empty()) {
          auto funcLabels = func->getName().getArgumentNames();
          if (argLabels.size() > funcLabels.size() ||
              funcLabels.slice(0, argLabels.size()) != argLabels)
            continue;
        }
  
        // If we are checking availability, the candidate must have enough
        // availability in the calling context.
        if (checkAvailability) {
          if (AvailableAttr::isUnavailable(func))
            continue;
          if (TypeChecker::checkDeclarationAvailability(
                  func, ExportContext::forFunctionBody(
                      dc, extractNearestSourceLoc(dc))))
            continue;
        }
  
        foundMatch = true;
        break;
      }
    }
  
    if (allResults)
      allResults->append(foundDecls.begin(), foundDecls.end());
  
    return foundMatch;
  }
  
  Type swift::inferResultBuilderComponentType(NominalTypeDecl *builder) {
    Type componentType;
  
    SmallVector<ValueDecl *, 4> potentialMatches;
    ASTContext &ctx = builder->getASTContext();
    bool supportsBuildBlock = TypeChecker::typeSupportsBuilderOp(
        builder->getDeclaredInterfaceType(), builder, ctx.Id_buildBlock,
        /*argLabels=*/{}, &potentialMatches);
    if (supportsBuildBlock) {
      for (auto decl : potentialMatches) {
        auto func = dyn_cast<FuncDecl>(decl);
        if (!func || !func->isStatic())
          continue;
  
        // If we haven't seen a component type before, gather it.
        if (!componentType) {
          componentType = func->getResultInterfaceType();
          continue;
        }
  
        // If there are inconsistent component types, bail out.
        if (!componentType->isEqual(func->getResultInterfaceType())) {
          componentType = Type();
          break;
        }
      }
    }
  
    return componentType;
  }
  
  std::tuple<SourceLoc, std::string, Type>
  swift::determineResultBuilderBuildFixItInfo(NominalTypeDecl *builder) {
    SourceLoc buildInsertionLoc = builder->getBraces().Start;
    std::string stubIndent;
    Type componentType;
  
    if (buildInsertionLoc.isInvalid())
      return std::make_tuple(buildInsertionLoc, stubIndent, componentType);
  
    ASTContext &ctx = builder->getASTContext();
    buildInsertionLoc = Lexer::getLocForEndOfToken(
        ctx.SourceMgr, buildInsertionLoc);
  
    StringRef extraIndent;
    StringRef currentIndent = Lexer::getIndentationForLine(
        ctx.SourceMgr, buildInsertionLoc, &extraIndent);
    stubIndent = (currentIndent + extraIndent).str();
  
    componentType = inferResultBuilderComponentType(builder);
    return std::make_tuple(buildInsertionLoc, stubIndent, componentType);
  }
  
  void swift::printResultBuilderBuildFunction(
        NominalTypeDecl *builder, Type componentType,
        ResultBuilderBuildFunction function,
        Optional<std::string> stubIndent, llvm::raw_ostream &out) {
    // Render the component type into a string.
    std::string componentTypeString;
    if (componentType)
      componentTypeString = componentType.getString();
    else
      componentTypeString = "<#Component#>";
  
    // Render the code.
    std::string stubIndentStr = stubIndent.getValueOr(std::string());
    ExtraIndentStreamPrinter printer(out, stubIndentStr);
  
    // If we're supposed to provide a full stub, add a newline and the introducer
    // keywords.
    if (stubIndent) {
      printer.printNewline();
  
      if (builder->getFormalAccess() >= AccessLevel::Public)
        printer << "public ";
  
      printer << "static func ";
    }
  
    bool printedResult = false;
    switch (function) {
    case ResultBuilderBuildFunction::BuildBlock:
      printer << "buildBlock(_ components: " << componentTypeString << "...)";
      break;
  
    case ResultBuilderBuildFunction::BuildExpression:
      printer << "buildExpression(_ expression: <#Expression#>)";
      break;
  
    case ResultBuilderBuildFunction::BuildOptional:
      printer << "buildOptional(_ component: " << componentTypeString << "?)";
      break;
  
    case ResultBuilderBuildFunction::BuildEitherFirst:
      printer << "buildEither(first component: " << componentTypeString << ")";
      break;
  
    case ResultBuilderBuildFunction::BuildEitherSecond:
      printer << "buildEither(second component: " << componentTypeString << ")";
      break;
  
    case ResultBuilderBuildFunction::BuildArray:
      printer << "buildArray(_ components: [" << componentTypeString << "])";
      break;
  
    case ResultBuilderBuildFunction::BuildLimitedAvailability:
      printer << "buildLimitedAvailability(_ component: " << componentTypeString
              << ")";
      break;
  
    case ResultBuilderBuildFunction::BuildFinalResult:
      printer << "buildFinalResult(_ component: " << componentTypeString
              << ") -> <#Result#>";
      printedResult = true;
      break;
    }
  
    if (!printedResult)
      printer << " -> " << componentTypeString;
  
    if (stubIndent) {
      printer << " {";
      printer.printNewline();
      printer << "  <#code#>";
      printer.printNewline();
      printer << "}";
    }
  }
  
  ResultBuilder::ResultBuilder(ConstraintSystem *CS, DeclContext *DC,
                               Type builderType)
      : DC(DC), BuilderType(CS ? CS->simplifyType(builderType) : builderType) {
    auto &ctx = DC->getASTContext();
    // Use buildOptional(_:) if available, otherwise fall back to buildIf
    // when available.
    BuildOptionalId =
        (supports(ctx.Id_buildOptional) || !supports(ctx.Id_buildIf))
            ? ctx.Id_buildOptional
            : ctx.Id_buildIf;
  
    if (CS) {
      BuilderSelf = new (ctx) VarDecl(
          /*isStatic=*/false, VarDecl::Introducer::Let,
          /*nameLoc=*/SourceLoc(), ctx.Id_builderSelf, DC);
      BuilderSelf->setImplicit();
      CS->setType(BuilderSelf, MetatypeType::get(BuilderType));
    }
  }
  
  bool ResultBuilder::supports(Identifier fnBaseName,
                               ArrayRef<Identifier> argLabels,
                               bool checkAvailability) {
    DeclName name(DC->getASTContext(), fnBaseName, argLabels);
    auto known = SupportedOps.find(name);
    if (known != SupportedOps.end()) {
      return known->second;
    }
  
    return SupportedOps[name] = TypeChecker::typeSupportsBuilderOp(
               BuilderType, DC, fnBaseName, argLabels, /*allResults*/ {},
               checkAvailability);
  }
  
  Expr *ResultBuilder::buildCall(SourceLoc loc, Identifier fnName,
                                 ArrayRef<Expr *> argExprs,
                                 ArrayRef<Identifier> argLabels) const {
    assert(BuilderSelf);
  
    auto &ctx = DC->getASTContext();
  
    SmallVector<Argument, 4> args;
    for (auto i : indices(argExprs)) {
      auto *expr = argExprs[i];
      auto label = argLabels.empty() ? Identifier() : argLabels[i];
      auto labelLoc = argLabels.empty() ? SourceLoc() : expr->getStartLoc();
      args.emplace_back(labelLoc, label, expr);
    }
  
    auto *baseExpr = new (ctx) DeclRefExpr({BuilderSelf}, DeclNameLoc(loc),
                                           /*isImplicit=*/true);
  
    auto memberRef = new (ctx)
        UnresolvedDotExpr(baseExpr, loc, DeclNameRef(fnName), DeclNameLoc(loc),
                          /*implicit=*/true);
    memberRef->setFunctionRefKind(FunctionRefKind::SingleApply);
  
    auto openLoc = args.empty() ? loc : argExprs.front()->getStartLoc();
    auto closeLoc = args.empty() ? loc : argExprs.back()->getEndLoc();
  
    auto *argList = ArgumentList::createImplicit(ctx, openLoc, args, closeLoc);
    return CallExpr::createImplicit(ctx, memberRef, argList);
  }
  
  VarDecl *ResultBuilder::buildVar(SourceLoc loc) {
    auto &ctx = DC->getASTContext();
    // Create the implicit variable.
    Identifier name =
        ctx.getIdentifier(("$__builder" + Twine(VarCounter++)).str());
    auto var = new (ctx)
        VarDecl(/*isStatic=*/false, VarDecl::Introducer::Var, loc, name, DC);
    var->setImplicit();
    return var;
  }
  
  DeclRefExpr *ResultBuilder::buildVarRef(VarDecl *var, SourceLoc loc) {
    return new (DC->getASTContext())
        DeclRefExpr(var, DeclNameLoc(loc), /*Implicit=*/true);
  }
  