<commit_before>//===--- TypeCheckEffects.cpp - Type Checking for Effects Coverage --------===//
//
// 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 semantic analysis to ensure that various effects (such
// as throwing and async) are properly handled.
//
//===----------------------------------------------------------------------===//

#include "TypeChecker.h"
#include "TypeCheckConcurrency.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/DiagnosticsSema.h"
#include "swift/AST/Effects.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/Pattern.h"
#include "swift/AST/PrettyStackTrace.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/TypeCheckRequests.h"

using namespace swift;

static bool hasThrowingFunctionClosureParameter(CanType type) {
  // Only consider throwing function types.
  if (auto fnType = dyn_cast<AnyFunctionType>(type)) {
    return fnType->getExtInfo().isThrowing();
  }

  // Look through tuples.
  if (auto tuple = dyn_cast<TupleType>(type)) {
    for (auto eltType : tuple.getElementTypes()) {
      auto elt = eltType->lookThroughAllOptionalTypes()->getCanonicalType();
      if (hasThrowingFunctionClosureParameter(elt))
        return true;
    }
    return false;
  }

  // Suppress diagnostics in the presence of errors.
  if (type->hasError()) {
    return true;
  }

  return false;
}

static FunctionRethrowingKind
getTypeThrowingKind(Type interfaceTy, GenericSignature genericSig) {
  if (interfaceTy->isTypeParameter()) {
    for (auto proto : genericSig->getRequiredProtocols(interfaceTy)) {
      if (proto->isRethrowingProtocol()) {
        return FunctionRethrowingKind::ByConformance;
      }
    }
  } else if (auto NTD = interfaceTy->getNominalOrBoundGenericNominal()) {
    if (auto genericSig = NTD->getGenericSignature()) {
      for (auto req : genericSig->getRequirements()) {
        if (req.getKind() == RequirementKind::Conformance) {
          if (req.getSecondType()->castTo<ProtocolType>()
                                 ->getDecl()
                                 ->isRethrowingProtocol()) {
            return FunctionRethrowingKind::ByConformance;
          }
        }
      }
    }
  }
  return FunctionRethrowingKind::Invalid;
}

static FunctionRethrowingKind 
getParameterThrowingKind(AbstractFunctionDecl *decl, 
                         GenericSignature genericSig) {
  FunctionRethrowingKind kind = FunctionRethrowingKind::Invalid;
  // check all parameters to determine if any are closures that throw
  bool foundThrowingClosure = false;
  for (auto param : *decl->getParameters()) {
    auto interfaceTy = param->getInterfaceType();
    if (hasThrowingFunctionClosureParameter(interfaceTy
          ->lookThroughAllOptionalTypes()
          ->getCanonicalType())) {
      foundThrowingClosure = true;
    }

    if (kind == FunctionRethrowingKind::Invalid) {
      kind = getTypeThrowingKind(interfaceTy, genericSig);
    }
  }
  if (kind == FunctionRethrowingKind::Invalid &&
      foundThrowingClosure) {
    return FunctionRethrowingKind::ByClosure;
  }
  return kind;
}

ProtocolRethrowsRequirementList
ProtocolRethrowsRequirementsRequest::evaluate(Evaluator &evaluator,
                                              ProtocolDecl *decl) const {
  SmallVector<std::pair<Type, ValueDecl*>, 2> found;
  llvm::DenseSet<ProtocolDecl*> checkedProtocols;

  ASTContext &ctx = decl->getASTContext();

  // only allow rethrowing requirements to be determined from marked protocols
  if (!decl->getAttrs().hasAttribute<swift::AtRethrowsAttr>()) {
    return ProtocolRethrowsRequirementList(ctx.AllocateCopy(found));
  }

  // check if immediate members of protocol are 'rethrows'
  for (auto member : decl->getMembers()) {
    auto fnDecl = dyn_cast<AbstractFunctionDecl>(member);
    // it must be a function
    // it must have a rethrows attribute
    // it must not have any parameters that are closures that cause rethrowing
    if (!fnDecl || 
        !fnDecl->hasThrows()) {
      continue;
    }

    GenericSignature genericSig = fnDecl->getGenericSignature();
    auto kind = getParameterThrowingKind(fnDecl, genericSig);
    // skip closure based rethrowing cases
    if (kind == FunctionRethrowingKind::ByClosure) {
      continue;
    }
    // we now have a protocol member that has a rethrows and no closure 
    // parameters contributing to it's rethrowing-ness
    found.push_back(
      std::pair<Type, ValueDecl*>(decl->getSelfInterfaceType(), fnDecl));
  }
  checkedProtocols.insert(decl);

  // check associated conformances of associated types or inheritance
  for (auto requirement : decl->getRequirementSignature()) {
    if (requirement.getKind() != RequirementKind::Conformance) {
      continue;
    }
    auto protoTy = requirement.getSecondType()->castTo<ProtocolType>();
    auto proto = protoTy->getDecl();
    if (checkedProtocols.count(proto) != 0) {
      continue;
    }
    checkedProtocols.insert(proto);
    for (auto entry : proto->getRethrowingRequirements()) {
      found.emplace_back(requirement.getFirstType(), entry.second);
    }
  }
  
  return ProtocolRethrowsRequirementList(ctx.AllocateCopy(found));
}

FunctionRethrowingKind
FunctionRethrowingKindRequest::evaluate(Evaluator &evaluator,
                                        AbstractFunctionDecl *decl) const {
  if (decl->hasThrows()) {
    auto proto = dyn_cast<ProtocolDecl>(decl->getDeclContext());
    bool fromRethrow = proto != nullptr ? proto->isRethrowingProtocol() : false;
    bool markedRethrows = decl->getAttrs().hasAttribute<RethrowsAttr>();
    if (fromRethrow && !markedRethrows) {
      return FunctionRethrowingKind::ByConformance;
    }
    if (markedRethrows) {
      GenericSignature genericSig = decl->getGenericSignature();
      FunctionRethrowingKind kind = getParameterThrowingKind(decl, genericSig);
      // since we have checked all arguments, if we still havent found anything
      // check the self parameter
      if (kind == FunctionRethrowingKind::Invalid && 
          decl->hasImplicitSelfDecl()) {
        auto selfParam = decl->getImplicitSelfDecl();
        if (selfParam) {
          auto interfaceTy = selfParam->getInterfaceType();
          kind = getTypeThrowingKind(interfaceTy, genericSig);
        }
      }
      return kind;
    }
    return FunctionRethrowingKind::Throws;
  }
  return FunctionRethrowingKind::None;
}

static bool classifyRequirement(ModuleDecl *module, 
                                ProtocolConformance *reqConformance, 
                                ValueDecl *requiredFn) {
  auto declRef = reqConformance->getWitnessDeclRef(requiredFn);
  auto witnessDecl = cast<AbstractFunctionDecl>(declRef.getDecl());
  switch (witnessDecl->getRethrowingKind()) {
    case FunctionRethrowingKind::ByConformance: {
      auto substitutions = reqConformance->getSubstitutions(module);
      for (auto conformanceRef : substitutions.getConformances()) {
        if (conformanceRef.classifyAsThrows()) {
          return true;
        }
      }
      break;
    }
    case FunctionRethrowingKind::None:
      break;
    case FunctionRethrowingKind::Throws:
      return true;
    default:
      return true;
  }
  return false;
}

// classify the type requirements of a given protocol type with a function
// requirement as throws or not. This will detect if the signature of the 
// function is throwing or not depending on associated types.
static bool classifyTypeRequirement(ModuleDecl *module, Type protoType, 
                                    ValueDecl *requiredFn, 
                                    ProtocolConformance *conformance,
                                    ProtocolDecl *requiredProtocol) {
  auto reqProtocol = cast<ProtocolDecl>(requiredFn->getDeclContext());
  ProtocolConformance *reqConformance;

  if(protoType->isEqual(reqProtocol->getSelfInterfaceType()) && 
     requiredProtocol == reqProtocol) {
    reqConformance = conformance;
  } else {
    auto reqConformanceRef = 
      conformance->getAssociatedConformance(protoType, reqProtocol);
    if (!reqConformanceRef.isConcrete()) {
      return true;
    }
    reqConformance = reqConformanceRef.getConcrete();
  }

  return classifyRequirement(module, reqConformance, requiredFn);
}

bool
ProtocolConformanceRefClassifyAsThrowsRequest::evaluate(
  Evaluator &evaluator, ProtocolConformanceRef conformanceRef) const {
  auto conformance = conformanceRef.getConcrete();
  auto requiredProtocol = conformanceRef.getRequirement();
  auto module = requiredProtocol->getModuleContext();
  for (auto req : requiredProtocol->getRethrowingRequirements()) {
    if (classifyTypeRequirement(module, req.first, req.second, 
                                conformance, requiredProtocol)) {
      return true;
    }
  }
  return false;
}

namespace {

/// A function reference.
class AbstractFunction {
public:
  enum Kind : uint8_t {
    Opaque, Function, Closure, Parameter,
  };

private:
  union {
    AbstractFunctionDecl *TheFunction;
    AbstractClosureExpr *TheClosure;
    ParamDecl *TheParameter;
    Expr *TheExpr;
  };
  unsigned TheKind : 2;
  unsigned ParamCount : 2;
  FunctionRethrowingKind RethrowingKind;
  SubstitutionMap Substitutions;

public:
  explicit AbstractFunction(Kind kind, Expr *fn)
    : TheKind(kind),
      ParamCount(1),
      RethrowingKind(FunctionRethrowingKind::None) {
    TheExpr = fn;
  }

  explicit AbstractFunction(AbstractFunctionDecl *fn, SubstitutionMap subs)
    : TheKind(Kind::Function),
      ParamCount(fn->getNumCurryLevels()),
      RethrowingKind(fn->getRethrowingKind()),
      Substitutions(subs) {
    TheFunction = fn;
  }

  explicit AbstractFunction(AbstractClosureExpr *closure)
    : TheKind(Kind::Closure),
      ParamCount(1),
      RethrowingKind(FunctionRethrowingKind::None) {
    TheClosure = closure;
  }

  explicit AbstractFunction(ParamDecl *parameter)
    : TheKind(Kind::Parameter),
      ParamCount(1),
      RethrowingKind(FunctionRethrowingKind::None) {
    TheParameter = parameter;
  }

  Kind getKind() const { return Kind(TheKind); }

  /// Whether the function is marked 'rethrows'.
  bool isBodyRethrows() const {
    switch (RethrowingKind) {
    case FunctionRethrowingKind::None:
    case FunctionRethrowingKind::Throws:
      return false;

    case FunctionRethrowingKind::ByClosure:
    case FunctionRethrowingKind::ByConformance:
    case FunctionRethrowingKind::Invalid:
      return true;
    }
  }

  FunctionRethrowingKind getRethrowingKind() const {
    return RethrowingKind;
  }

  unsigned getNumArgumentsForFullApply() const {
    return ParamCount;
  }

  Type getType() const {
    switch (getKind()) {
    case Kind::Opaque: return getOpaqueFunction()->getType();
    case Kind::Function: return getFunction()->getInterfaceType();
    case Kind::Closure: return getClosure()->getType();
    case Kind::Parameter: return getParameter()->getType();
    }
    llvm_unreachable("bad kind");
  }

  bool isAutoClosure() const {
    if (getKind() == Kind::Closure)
      return isa<AutoClosureExpr>(getClosure());
    return false;
  }

  AbstractFunctionDecl *getFunction() const {
    assert(getKind() == Kind::Function);
    return TheFunction;
  }
  AbstractClosureExpr *getClosure() const {
    assert(getKind() == Kind::Closure);
    return TheClosure;
  }
  ParamDecl *getParameter() const {
    assert(getKind() == Kind::Parameter);
    return TheParameter;
  }
  Expr *getOpaqueFunction() const {
    assert(getKind() == Kind::Opaque);
    return TheExpr;
  }

  SubstitutionMap getSubstitutions() const {
    return Substitutions;
  }

  static AbstractFunction decomposeApply(ApplyExpr *apply,
                           SmallVectorImpl<SmallVector<Expr *, 2>> &argLists) {
    Expr *fn;
    do {
      auto *argExpr = apply->getArg();
      if (auto *tupleExpr = dyn_cast<TupleExpr>(argExpr)) {
        auto args = tupleExpr->getElements();
        argLists.emplace_back(args.begin(), args.end());
      } else if (auto *parenExpr = dyn_cast<ParenExpr>(argExpr)) {
        argLists.emplace_back();
        argLists.back().push_back(parenExpr->getSubExpr());
      } else {
        argLists.emplace_back();
        argLists.back().push_back(argExpr);
      }

      fn = apply->getFn()->getValueProvidingExpr();
    } while ((apply = dyn_cast<ApplyExpr>(fn)));

    return decomposeFunction(fn);
  }

  static AbstractFunction decomposeFunction(Expr *fn) {
    assert(fn->getValueProvidingExpr() == fn);

    while (true) {
      // Look through Optional unwraps.
      if (auto conversion = dyn_cast<ForceValueExpr>(fn)) {
        fn = conversion->getSubExpr()->getValueProvidingExpr();
      } else if (auto conversion = dyn_cast<BindOptionalExpr>(fn)) {
        fn = conversion->getSubExpr()->getValueProvidingExpr();
      // Look through optional injections.
      } else if (auto injection = dyn_cast<InjectIntoOptionalExpr>(fn)) {
        fn = injection->getSubExpr()->getValueProvidingExpr();
      // Look through function conversions.
      } else if (auto conversion = dyn_cast<FunctionConversionExpr>(fn)) {
        fn = conversion->getSubExpr()->getValueProvidingExpr();
      // Look through base-ignored qualified references (Module.methodName).
      } else if (auto baseIgnored = dyn_cast<DotSyntaxBaseIgnoredExpr>(fn)) {
        fn = baseIgnored->getRHS();
      // Look through closure capture lists.
      } else if (auto captureList = dyn_cast<CaptureListExpr>(fn)) {
        fn = captureList->getClosureBody();
        // Look through optional evaluations.
      } else if (auto optionalEval = dyn_cast<OptionalEvaluationExpr>(fn)) {
        fn = optionalEval->getSubExpr()->getValueProvidingExpr();
      } else {
        break;
      }
    }
    
    // Constructor delegation.
    if (auto otherCtorDeclRef = dyn_cast<OtherConstructorDeclRefExpr>(fn)) {
      return AbstractFunction(otherCtorDeclRef->getDecl(),
                              otherCtorDeclRef->getDeclRef().getSubstitutions());
    }

    // Normal function references.
    if (auto DRE = dyn_cast<DeclRefExpr>(fn)) {
      ValueDecl *decl = DRE->getDecl();
      if (auto fn = dyn_cast<AbstractFunctionDecl>(decl)) {
        return AbstractFunction(fn, DRE->getDeclRef().getSubstitutions());
      } else if (auto param = dyn_cast<ParamDecl>(decl)) {
        return AbstractFunction(param);
      }

    // Closures.
    } else if (auto closure = dyn_cast<AbstractClosureExpr>(fn)) {
      return AbstractFunction(closure);
    }

    // Everything else is opaque.
    return AbstractFunction(Kind::Opaque, fn);
  }
};

enum ShouldRecurse_t : bool {
  ShouldNotRecurse = false, ShouldRecurse = true
};

/// A CRTP ASTWalker implementation that looks for interesting
/// nodes for effects handling.
template <class Impl>
class EffectsHandlingWalker : public ASTWalker {
  Impl &asImpl() { return *static_cast<Impl*>(this); }
public:
  bool walkToDeclPre(Decl *D) override {
    ShouldRecurse_t recurse = ShouldRecurse;
    // Skip the implementations of all local declarations... except
    // PBD.  We should really just have a PatternBindingStmt.
    if (auto ic = dyn_cast<IfConfigDecl>(D)) {
      recurse = asImpl().checkIfConfig(ic);
    } else if (auto patternBinding = dyn_cast<PatternBindingDecl>(D)) {
      if (patternBinding->isAsyncLet())
        recurse = asImpl().checkAsyncLet(patternBinding);
    } else {
      recurse = ShouldNotRecurse;
    }
    return bool(recurse);
  }

  std::pair<bool, Expr*> walkToExprPre(Expr *E) override {
    ShouldRecurse_t recurse = ShouldRecurse;
    if (isa<ErrorExpr>(E)) {
      asImpl().flagInvalidCode();
    } else if (auto closure = dyn_cast<ClosureExpr>(E)) {
      recurse = asImpl().checkClosure(closure);
    } else if (auto autoclosure = dyn_cast<AutoClosureExpr>(E)) {
      recurse = asImpl().checkAutoClosure(autoclosure);
    } else if (auto awaitExpr = dyn_cast<AwaitExpr>(E)) {
      recurse = asImpl().checkAwait(awaitExpr);
    } else if (auto tryExpr = dyn_cast<TryExpr>(E)) {
      recurse = asImpl().checkTry(tryExpr);
    } else if (auto forceTryExpr = dyn_cast<ForceTryExpr>(E)) {
      recurse = asImpl().checkForceTry(forceTryExpr);
    } else if (auto optionalTryExpr = dyn_cast<OptionalTryExpr>(E)) {
      recurse = asImpl().checkOptionalTry(optionalTryExpr);
    } else if (auto apply = dyn_cast<ApplyExpr>(E)) {
      recurse = asImpl().checkApply(apply);
    } else if (auto declRef = dyn_cast<DeclRefExpr>(E)) {
      recurse = asImpl().checkDeclRef(declRef);
    } else if (auto interpolated = dyn_cast<InterpolatedStringLiteralExpr>(E)) {
      recurse = asImpl().checkInterpolatedStringLiteral(interpolated);
    }
    // Error handling validation (via checkTopLevelEffects) happens after
    // type checking. If an unchecked expression is still around, the code was
    // invalid.
#define UNCHECKED_EXPR(KIND, BASE) \
    else if (isa<KIND##Expr>(E)) return {false, nullptr};
#include "swift/AST/ExprNodes.def"

    return {bool(recurse), E};
  }

  std::pair<bool, Stmt*> walkToStmtPre(Stmt *S) override {
    ShouldRecurse_t recurse = ShouldRecurse;
    if (auto doCatch = dyn_cast<DoCatchStmt>(S)) {
      recurse = asImpl().checkDoCatch(doCatch);
    } else if (auto thr = dyn_cast<ThrowStmt>(S)) {
      recurse = asImpl().checkThrow(thr);
    } else if (auto forEach = dyn_cast<ForEachStmt>(S)) {
      recurse = asImpl().checkForEach(forEach);
    }
    return {bool(recurse), S};
  }

  ShouldRecurse_t checkDoCatch(DoCatchStmt *S) {
    auto bodyResult = (S->isSyntacticallyExhaustive()
        ? asImpl().checkExhaustiveDoBody(S)
        : asImpl().checkNonExhaustiveDoBody(S));
    for (auto clause : S->getCatches()) {
      asImpl().checkCatch(clause, bodyResult);
    }
    return ShouldNotRecurse;
  }

  ShouldRecurse_t checkForEach(ForEachStmt *S) {
    return ShouldRecurse;
  }
};

/// A potential reason why something might throw.
class PotentialThrowReason {
public:
  enum class Kind : uint8_t {
    /// The function throws unconditionally.
    Throw,

    /// The function calls an unconditionally throwing function.
    CallThrows,

    /// The initializer of an 'async let' unconditionally throws.
    AsyncLetThrows,

    /// The function is 'rethrows', and it was passed an explicit
    /// argument that was not rethrowing-only in this context.
    CallRethrowsWithExplicitThrowingArgument,

    /// The function is 'rethrows', and it was passed a default
    /// argument that was not rethrowing-only in this context.
    CallRethrowsWithDefaultThrowingArgument,

    /// The the function is 'rethrows', and it is a member that
    /// is a conformance to a rethrowing protocol.
    CallRethrowsWithConformance,
  };

  static StringRef kindToString(Kind k) {
    switch (k) {
      case Kind::Throw: return "Throw";
      case Kind::CallThrows: return "CallThrows";
      case Kind::AsyncLetThrows: return "AsyncLetThrows";
      case Kind::CallRethrowsWithExplicitThrowingArgument:
        return "CallRethrowsWithExplicitThrowingArgument";
      case Kind::CallRethrowsWithDefaultThrowingArgument: 
        return "CallRethrowsWithDefaultThrowingArgument";
      case Kind::CallRethrowsWithConformance:
        return "CallRethrowsWithConformance";
    }
  }

private:
  Expr *TheExpression;
  Kind TheKind;

  explicit PotentialThrowReason(Kind kind) : TheKind(kind) {}
public:
  static PotentialThrowReason forRethrowsArgument(Expr *E) {
    PotentialThrowReason result(Kind::CallRethrowsWithExplicitThrowingArgument);
    result.TheExpression = E;
    return result;
  }
  static PotentialThrowReason forDefaultArgument() {
    return PotentialThrowReason(Kind::CallRethrowsWithDefaultThrowingArgument);
  }
  static PotentialThrowReason forRethrowsConformance(Expr *E) {
    PotentialThrowReason result(Kind::CallRethrowsWithConformance);
    result.TheExpression = E;
    return result;
  }
  static PotentialThrowReason forThrowingApply() {
    return PotentialThrowReason(Kind::CallThrows);
  }
  static PotentialThrowReason forThrow() {
    return PotentialThrowReason(Kind::Throw);
  }
  static PotentialThrowReason forThrowingAsyncLet() {
    return PotentialThrowReason(Kind::AsyncLetThrows);
  }

  Kind getKind() const { return TheKind; }

  /// Is this a throw expression?
  bool isThrow() const { return getKind() == Kind::Throw; }
  bool isRethrowsCall() const {
    return (getKind() == Kind::CallRethrowsWithExplicitThrowingArgument ||
            getKind() == Kind::CallRethrowsWithDefaultThrowingArgument ||
            getKind() == Kind::CallRethrowsWithConformance);
  }

  /// If this was built with forRethrowsArgument, return the expression.
  Expr *getThrowingArgument() const {
    assert(getKind() == Kind::CallRethrowsWithExplicitThrowingArgument);
    return TheExpression;
  }
};

enum class ThrowingKind {
  /// The call/function can't throw.
  None,

  /// The call/function can only throw if one of the parameters in
  /// the current rethrows context can throw.
  RethrowingOnly,

  /// The call/function can throw.
  Throws,
};

/// A type expressing the result of classifying whether a call or function
/// throws or is async.
class Classification {
  bool IsInvalid = false;  // The AST is malformed.  Don't diagnose.
  bool IsAsync = false;
  ThrowingKind Result = ThrowingKind::None;
  Optional<PotentialThrowReason> Reason;
  
  void print(raw_ostream &out) const {
    out << "{ IsInvalid = " << IsInvalid
        << ", IsAsync = " << IsAsync
        << ", Result = ThrowingKind::";
    
    switch(Result) {
      case ThrowingKind::None:            out << "None"; break;
      case ThrowingKind::RethrowingOnly:  out << "RethrowingOnly"; break;
      case ThrowingKind::Throws:          out << "Throws"; break;
    }
         
    out << ", Reason = ";
    if (!Reason)
      out << "nil";
    else
      out << PotentialThrowReason::kindToString(Reason.getValue().getKind());

    out << " }";
  }
  
public:
  Classification() : Result(ThrowingKind::None) {}
  explicit Classification(ThrowingKind result, PotentialThrowReason reason,
                          bool isAsync)
      : IsAsync(isAsync), Result(result) {
    if (result == ThrowingKind::Throws ||
        result == ThrowingKind::RethrowingOnly) {
      Reason = reason;
    }
  }

  /// Return a classification saying that there's an unconditional
  /// throw site.
  static Classification forThrow(PotentialThrowReason reason, bool isAsync) {
    Classification result;
    result.Result = ThrowingKind::Throws;
    result.Reason = reason;
    result.IsAsync = isAsync;
    return result;
  }

  /// Return a classification saying that there's an unconditional
  /// throw site.
  static Classification forAsync() {
    Classification result;
    result.IsAsync = true;
    return result;
  }
  
  static Classification forInvalidCode() {
    Classification result;
    result.IsInvalid = true;
    return result;
  }

  static Classification forRethrowingOnly(PotentialThrowReason reason, bool isAsync) {
    Classification result;
    result.Result = ThrowingKind::RethrowingOnly;
    result.Reason = reason;
    result.IsAsync = isAsync;
    return result;
  }

  void merge(Classification other) {
    bool oldAsync = IsAsync;
    
    if (other.getResult() > getResult())
      *this = other;

    IsAsync = oldAsync | other.IsAsync;
  }

  bool isInvalid() const { return IsInvalid; }
  ThrowingKind getResult() const { return Result; }
  PotentialThrowReason getThrowsReason() const {
    assert(getResult() == ThrowingKind::Throws ||
           getResult() == ThrowingKind::RethrowingOnly);
    return *Reason;
  }
  
  bool isAsync() const { return IsAsync; }

#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
  LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); }
#endif
};


/// A class for collecting information about rethrowing functions.
class ApplyClassifier {
  /// The key to this cache is the function decl or closure being analyzed.  The
  /// value in this cache is nil when the body has an error detected in it.
  llvm::DenseMap<void*, Optional<ThrowingKind>> Cache;

public:
  DeclContext *RethrowsDC = nullptr;
  bool inRethrowsContext() const { return RethrowsDC != nullptr; }

  /// Check to see if the given function application throws or is async.
  Classification classifyApply(ApplyExpr *E) {
    // An apply expression is a potential throw site if the function throws.
    // But if the expression didn't type-check, suppress diagnostics.
    if (!E->getType() || E->getType()->hasError())
      return Classification::forInvalidCode();

    auto type = E->getFn()->getType();
    if (!type) return Classification::forInvalidCode();
    auto fnType = type->getAs<AnyFunctionType>();
    if (!fnType) return Classification::forInvalidCode();

    bool isAsync = fnType->isAsync() || E->implicitlyAsync();
    // Decompose the application.
    SmallVector<SmallVector<Expr *, 2>, 2> argLists;
    auto fnRef = AbstractFunction::decomposeApply(E, argLists);

    // If any of the arguments didn't type check, fail.
    for (auto argList : argLists) {
      for (auto *arg : argList) {
        if (!arg->getType() || arg->getType()->hasError())
          return Classification::forInvalidCode();
      }
    }

    if (fnRef.getRethrowingKind() == FunctionRethrowingKind::ByConformance) {
      auto substitutions = fnRef.getSubstitutions();
      bool classifiedAsThrows = false;
      for (auto conformanceRef : substitutions.getConformances()) {
        if (conformanceRef.classifyAsThrows()) {
          classifiedAsThrows = true;
          break;
        }
      }

      if (classifiedAsThrows) {
        // multiple passes can occur, so ensure that any sub-expressions of this
        // call are marked as throws to mimic the closure variant.
        if (auto subExpr = dyn_cast<ApplyExpr>(E->getFn())) {
          if (!subExpr->isThrowsSet()) {
            subExpr->setThrows(true);
          }
        }
        return Classification::forRethrowingOnly(
          PotentialThrowReason::forRethrowsConformance(E), isAsync);
      }
    } else if (fnRef.isBodyRethrows() && 
               fnRef.getRethrowingKind() == FunctionRethrowingKind::Throws) {
      return Classification::forThrow(PotentialThrowReason::forThrowingApply(),
                                      isAsync);
    } else if (fnRef.isBodyRethrows() &&
               fnRef.getRethrowingKind() == FunctionRethrowingKind::None) {
      return isAsync ? Classification::forAsync() : Classification();
    }

    // If the function doesn't throw at all, we're done here.
    if (!fnType->isThrowing()) {
      return isAsync ? Classification::forAsync() : Classification();
    }

    // If we're applying more arguments than the natural argument
    // count, then this is a call to the opaque value returned from
    // the function.
    if (argLists.size() != fnRef.getNumArgumentsForFullApply()) {
      // Special case: a reference to an operator within a type might be
      // missing 'self'.
      // FIXME: The issue here is that this is an ill-formed expression, but
      // we don't know it from the structure of the expression.
      if (argLists.size() == 1 && fnRef.getKind() == AbstractFunction::Function &&
          isa<FuncDecl>(fnRef.getFunction()) &&
          cast<FuncDecl>(fnRef.getFunction())->isOperator() &&
          fnRef.getNumArgumentsForFullApply() == 2 &&
          fnRef.getFunction()->getDeclContext()->isTypeContext()) {
        // Can only happen with invalid code.
        assert(fnRef.getFunction()->getASTContext().Diags.hadAnyError());
        return Classification::forInvalidCode();
      }

      assert(argLists.size() > fnRef.getNumArgumentsForFullApply() &&
             "partial application was throwing?");
      return Classification::forThrow(PotentialThrowReason::forThrowingApply(),
                                      isAsync);
    }

    // If the function's body is 'rethrows' for the number of
    // arguments we gave it, apply the rethrows logic.
    if (fnRef.isBodyRethrows()) {
      // We need to walk the original parameter types in parallel
      // because it only counts for 'rethrows' purposes if it lines up
      // with a throwing function parameter in the original type.
      Type type = fnRef.getType();
      if (!type) return Classification::forInvalidCode();

      // Use the most significant result from the arguments.
      Classification result = isAsync ? Classification::forAsync() 
                                      : Classification();
      for (auto argList : llvm::reverse(argLists)) {
        auto fnType = type->getAs<AnyFunctionType>();
        if (!fnType)
          return Classification::forInvalidCode();

        auto params = fnType->getParams();
        if (params.size() != argList.size())
          return Classification::forInvalidCode();

        for (unsigned i = 0, e = params.size(); i < e; ++i) {
          result.merge(classifyRethrowsArgument(argList[i],
                                                params[i].getParameterType()));
        }

        type = fnType->getResult();<commit_msg>Sema: Streamline conformance rethrows check<commit_after>//===--- TypeCheckEffects.cpp - Type Checking for Effects Coverage --------===//
//
// 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 semantic analysis to ensure that various effects (such
// as throwing and async) are properly handled.
//
//===----------------------------------------------------------------------===//

#include "TypeChecker.h"
#include "TypeCheckConcurrency.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/DiagnosticsSema.h"
#include "swift/AST/Effects.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/Pattern.h"
#include "swift/AST/PrettyStackTrace.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/TypeCheckRequests.h"

using namespace swift;

static bool hasThrowingFunctionClosureParameter(CanType type) {
  // Only consider throwing function types.
  if (auto fnType = dyn_cast<AnyFunctionType>(type)) {
    return fnType->getExtInfo().isThrowing();
  }

  // Look through tuples.
  if (auto tuple = dyn_cast<TupleType>(type)) {
    for (auto eltType : tuple.getElementTypes()) {
      auto elt = eltType->lookThroughAllOptionalTypes()->getCanonicalType();
      if (hasThrowingFunctionClosureParameter(elt))
        return true;
    }
    return false;
  }

  // Suppress diagnostics in the presence of errors.
  if (type->hasError()) {
    return true;
  }

  return false;
}

static FunctionRethrowingKind
getTypeThrowingKind(Type interfaceTy, GenericSignature genericSig) {
  if (interfaceTy->isTypeParameter()) {
    for (auto proto : genericSig->getRequiredProtocols(interfaceTy)) {
      if (proto->isRethrowingProtocol()) {
        return FunctionRethrowingKind::ByConformance;
      }
    }
  } else if (auto NTD = interfaceTy->getNominalOrBoundGenericNominal()) {
    if (auto genericSig = NTD->getGenericSignature()) {
      for (auto req : genericSig->getRequirements()) {
        if (req.getKind() == RequirementKind::Conformance) {
          if (req.getSecondType()->castTo<ProtocolType>()
                                 ->getDecl()
                                 ->isRethrowingProtocol()) {
            return FunctionRethrowingKind::ByConformance;
          }
        }
      }
    }
  }
  return FunctionRethrowingKind::Invalid;
}

static FunctionRethrowingKind 
getParameterThrowingKind(AbstractFunctionDecl *decl, 
                         GenericSignature genericSig) {
  FunctionRethrowingKind kind = FunctionRethrowingKind::Invalid;
  // check all parameters to determine if any are closures that throw
  bool foundThrowingClosure = false;
  for (auto param : *decl->getParameters()) {
    auto interfaceTy = param->getInterfaceType();
    if (hasThrowingFunctionClosureParameter(interfaceTy
          ->lookThroughAllOptionalTypes()
          ->getCanonicalType())) {
      foundThrowingClosure = true;
    }

    if (kind == FunctionRethrowingKind::Invalid) {
      kind = getTypeThrowingKind(interfaceTy, genericSig);
    }
  }
  if (kind == FunctionRethrowingKind::Invalid &&
      foundThrowingClosure) {
    return FunctionRethrowingKind::ByClosure;
  }
  return kind;
}

ProtocolRethrowsRequirementList
ProtocolRethrowsRequirementsRequest::evaluate(Evaluator &evaluator,
                                              ProtocolDecl *decl) const {
  SmallVector<std::pair<Type, ValueDecl*>, 2> found;
  llvm::DenseSet<ProtocolDecl*> checkedProtocols;

  ASTContext &ctx = decl->getASTContext();

  // only allow rethrowing requirements to be determined from marked protocols
  if (!decl->getAttrs().hasAttribute<swift::AtRethrowsAttr>()) {
    return ProtocolRethrowsRequirementList(ctx.AllocateCopy(found));
  }

  // check if immediate members of protocol are 'rethrows'
  for (auto member : decl->getMembers()) {
    auto fnDecl = dyn_cast<AbstractFunctionDecl>(member);
    // it must be a function
    // it must have a rethrows attribute
    // it must not have any parameters that are closures that cause rethrowing
    if (!fnDecl || 
        !fnDecl->hasThrows()) {
      continue;
    }

    GenericSignature genericSig = fnDecl->getGenericSignature();
    auto kind = getParameterThrowingKind(fnDecl, genericSig);
    // skip closure based rethrowing cases
    if (kind == FunctionRethrowingKind::ByClosure) {
      continue;
    }
    // we now have a protocol member that has a rethrows and no closure 
    // parameters contributing to it's rethrowing-ness
    found.push_back(
      std::pair<Type, ValueDecl*>(decl->getSelfInterfaceType(), fnDecl));
  }
  checkedProtocols.insert(decl);

  // check associated conformances of associated types or inheritance
  for (auto requirement : decl->getRequirementSignature()) {
    if (requirement.getKind() != RequirementKind::Conformance) {
      continue;
    }
    auto protoTy = requirement.getSecondType()->castTo<ProtocolType>();
    auto proto = protoTy->getDecl();
    if (checkedProtocols.count(proto) != 0) {
      continue;
    }
    checkedProtocols.insert(proto);
    for (auto entry : proto->getRethrowingRequirements()) {
      found.emplace_back(requirement.getFirstType(), entry.second);
    }
  }
  
  return ProtocolRethrowsRequirementList(ctx.AllocateCopy(found));
}

FunctionRethrowingKind
FunctionRethrowingKindRequest::evaluate(Evaluator &evaluator,
                                        AbstractFunctionDecl *decl) const {
  if (decl->hasThrows()) {
    auto proto = dyn_cast<ProtocolDecl>(decl->getDeclContext());
    bool fromRethrow = proto != nullptr ? proto->isRethrowingProtocol() : false;
    bool markedRethrows = decl->getAttrs().hasAttribute<RethrowsAttr>();
    if (fromRethrow && !markedRethrows) {
      return FunctionRethrowingKind::ByConformance;
    }
    if (markedRethrows) {
      GenericSignature genericSig = decl->getGenericSignature();
      FunctionRethrowingKind kind = getParameterThrowingKind(decl, genericSig);
      // since we have checked all arguments, if we still havent found anything
      // check the self parameter
      if (kind == FunctionRethrowingKind::Invalid && 
          decl->hasImplicitSelfDecl()) {
        auto selfParam = decl->getImplicitSelfDecl();
        if (selfParam) {
          auto interfaceTy = selfParam->getInterfaceType();
          kind = getTypeThrowingKind(interfaceTy, genericSig);
        }
      }
      return kind;
    }
    return FunctionRethrowingKind::Throws;
  }
  return FunctionRethrowingKind::None;
}

static bool classifyRequirement(ModuleDecl *module, 
                                ProtocolConformance *reqConformance, 
                                ValueDecl *requiredFn) {
  auto declRef = reqConformance->getWitnessDeclRef(requiredFn);
  auto witnessDecl = cast<AbstractFunctionDecl>(declRef.getDecl());
  switch (witnessDecl->getRethrowingKind()) {
    case FunctionRethrowingKind::ByConformance: {
      auto substitutions = reqConformance->getSubstitutions(module);
      for (auto conformanceRef : substitutions.getConformances()) {
        if (conformanceRef.classifyAsThrows()) {
          return true;
        }
      }
      break;
    }
    case FunctionRethrowingKind::None:
      break;
    case FunctionRethrowingKind::Throws:
      return true;
    default:
      return true;
  }
  return false;
}

// classify the type requirements of a given protocol type with a function
// requirement as throws or not. This will detect if the signature of the 
// function is throwing or not depending on associated types.
static bool classifyTypeRequirement(ModuleDecl *module, Type protoType, 
                                    ValueDecl *requiredFn, 
                                    ProtocolConformance *conformance,
                                    ProtocolDecl *requiredProtocol) {
  auto reqProtocol = cast<ProtocolDecl>(requiredFn->getDeclContext());
  ProtocolConformance *reqConformance;

  if(protoType->isEqual(reqProtocol->getSelfInterfaceType()) && 
     requiredProtocol == reqProtocol) {
    reqConformance = conformance;
  } else {
    auto reqConformanceRef = 
      conformance->getAssociatedConformance(protoType, reqProtocol);
    if (!reqConformanceRef.isConcrete()) {
      return true;
    }
    reqConformance = reqConformanceRef.getConcrete();
  }

  return classifyRequirement(module, reqConformance, requiredFn);
}

bool
ProtocolConformanceRefClassifyAsThrowsRequest::evaluate(
  Evaluator &evaluator, ProtocolConformanceRef conformanceRef) const {
  auto conformance = conformanceRef.getConcrete();
  auto requiredProtocol = conformanceRef.getRequirement();
  auto module = requiredProtocol->getModuleContext();
  for (auto req : requiredProtocol->getRethrowingRequirements()) {
    if (classifyTypeRequirement(module, req.first, req.second, 
                                conformance, requiredProtocol)) {
      return true;
    }
  }
  return false;
}

namespace {

/// A function reference.
class AbstractFunction {
public:
  enum Kind : uint8_t {
    Opaque, Function, Closure, Parameter,
  };

private:
  union {
    AbstractFunctionDecl *TheFunction;
    AbstractClosureExpr *TheClosure;
    ParamDecl *TheParameter;
    Expr *TheExpr;
  };
  unsigned TheKind : 2;
  unsigned ParamCount : 2;
  FunctionRethrowingKind RethrowingKind;
  SubstitutionMap Substitutions;

public:
  explicit AbstractFunction(Kind kind, Expr *fn)
    : TheKind(kind),
      ParamCount(1),
      RethrowingKind(FunctionRethrowingKind::None) {
    TheExpr = fn;
  }

  explicit AbstractFunction(AbstractFunctionDecl *fn, SubstitutionMap subs)
    : TheKind(Kind::Function),
      ParamCount(fn->getNumCurryLevels()),
      RethrowingKind(fn->getRethrowingKind()),
      Substitutions(subs) {
    TheFunction = fn;
  }

  explicit AbstractFunction(AbstractClosureExpr *closure)
    : TheKind(Kind::Closure),
      ParamCount(1),
      RethrowingKind(FunctionRethrowingKind::None) {
    TheClosure = closure;
  }

  explicit AbstractFunction(ParamDecl *parameter)
    : TheKind(Kind::Parameter),
      ParamCount(1),
      RethrowingKind(FunctionRethrowingKind::None) {
    TheParameter = parameter;
  }

  Kind getKind() const { return Kind(TheKind); }

  /// Whether the function is marked 'rethrows'.
  bool isBodyRethrows() const {
    switch (RethrowingKind) {
    case FunctionRethrowingKind::None:
    case FunctionRethrowingKind::Throws:
      return false;

    case FunctionRethrowingKind::ByClosure:
    case FunctionRethrowingKind::ByConformance:
    case FunctionRethrowingKind::Invalid:
      return true;
    }
  }

  FunctionRethrowingKind getRethrowingKind() const {
    return RethrowingKind;
  }

  unsigned getNumArgumentsForFullApply() const {
    return ParamCount;
  }

  Type getType() const {
    switch (getKind()) {
    case Kind::Opaque: return getOpaqueFunction()->getType();
    case Kind::Function: return getFunction()->getInterfaceType();
    case Kind::Closure: return getClosure()->getType();
    case Kind::Parameter: return getParameter()->getType();
    }
    llvm_unreachable("bad kind");
  }

  bool isAutoClosure() const {
    if (getKind() == Kind::Closure)
      return isa<AutoClosureExpr>(getClosure());
    return false;
  }

  AbstractFunctionDecl *getFunction() const {
    assert(getKind() == Kind::Function);
    return TheFunction;
  }
  AbstractClosureExpr *getClosure() const {
    assert(getKind() == Kind::Closure);
    return TheClosure;
  }
  ParamDecl *getParameter() const {
    assert(getKind() == Kind::Parameter);
    return TheParameter;
  }
  Expr *getOpaqueFunction() const {
    assert(getKind() == Kind::Opaque);
    return TheExpr;
  }

  SubstitutionMap getSubstitutions() const {
    return Substitutions;
  }

  static AbstractFunction decomposeApply(ApplyExpr *apply,
                           SmallVectorImpl<SmallVector<Expr *, 2>> &argLists) {
    Expr *fn;
    do {
      auto *argExpr = apply->getArg();
      if (auto *tupleExpr = dyn_cast<TupleExpr>(argExpr)) {
        auto args = tupleExpr->getElements();
        argLists.emplace_back(args.begin(), args.end());
      } else if (auto *parenExpr = dyn_cast<ParenExpr>(argExpr)) {
        argLists.emplace_back();
        argLists.back().push_back(parenExpr->getSubExpr());
      } else {
        argLists.emplace_back();
        argLists.back().push_back(argExpr);
      }

      fn = apply->getFn()->getValueProvidingExpr();
    } while ((apply = dyn_cast<ApplyExpr>(fn)));

    return decomposeFunction(fn);
  }

  static AbstractFunction decomposeFunction(Expr *fn) {
    assert(fn->getValueProvidingExpr() == fn);

    while (true) {
      // Look through Optional unwraps.
      if (auto conversion = dyn_cast<ForceValueExpr>(fn)) {
        fn = conversion->getSubExpr()->getValueProvidingExpr();
      } else if (auto conversion = dyn_cast<BindOptionalExpr>(fn)) {
        fn = conversion->getSubExpr()->getValueProvidingExpr();
      // Look through optional injections.
      } else if (auto injection = dyn_cast<InjectIntoOptionalExpr>(fn)) {
        fn = injection->getSubExpr()->getValueProvidingExpr();
      // Look through function conversions.
      } else if (auto conversion = dyn_cast<FunctionConversionExpr>(fn)) {
        fn = conversion->getSubExpr()->getValueProvidingExpr();
      // Look through base-ignored qualified references (Module.methodName).
      } else if (auto baseIgnored = dyn_cast<DotSyntaxBaseIgnoredExpr>(fn)) {
        fn = baseIgnored->getRHS();
      // Look through closure capture lists.
      } else if (auto captureList = dyn_cast<CaptureListExpr>(fn)) {
        fn = captureList->getClosureBody();
        // Look through optional evaluations.
      } else if (auto optionalEval = dyn_cast<OptionalEvaluationExpr>(fn)) {
        fn = optionalEval->getSubExpr()->getValueProvidingExpr();
      } else {
        break;
      }
    }
    
    // Constructor delegation.
    if (auto otherCtorDeclRef = dyn_cast<OtherConstructorDeclRefExpr>(fn)) {
      return AbstractFunction(otherCtorDeclRef->getDecl(),
                              otherCtorDeclRef->getDeclRef().getSubstitutions());
    }

    // Normal function references.
    if (auto DRE = dyn_cast<DeclRefExpr>(fn)) {
      ValueDecl *decl = DRE->getDecl();
      if (auto fn = dyn_cast<AbstractFunctionDecl>(decl)) {
        return AbstractFunction(fn, DRE->getDeclRef().getSubstitutions());
      } else if (auto param = dyn_cast<ParamDecl>(decl)) {
        return AbstractFunction(param);
      }

    // Closures.
    } else if (auto closure = dyn_cast<AbstractClosureExpr>(fn)) {
      return AbstractFunction(closure);
    }

    // Everything else is opaque.
    return AbstractFunction(Kind::Opaque, fn);
  }
};

enum ShouldRecurse_t : bool {
  ShouldNotRecurse = false, ShouldRecurse = true
};

/// A CRTP ASTWalker implementation that looks for interesting
/// nodes for effects handling.
template <class Impl>
class EffectsHandlingWalker : public ASTWalker {
  Impl &asImpl() { return *static_cast<Impl*>(this); }
public:
  bool walkToDeclPre(Decl *D) override {
    ShouldRecurse_t recurse = ShouldRecurse;
    // Skip the implementations of all local declarations... except
    // PBD.  We should really just have a PatternBindingStmt.
    if (auto ic = dyn_cast<IfConfigDecl>(D)) {
      recurse = asImpl().checkIfConfig(ic);
    } else if (auto patternBinding = dyn_cast<PatternBindingDecl>(D)) {
      if (patternBinding->isAsyncLet())
        recurse = asImpl().checkAsyncLet(patternBinding);
    } else {
      recurse = ShouldNotRecurse;
    }
    return bool(recurse);
  }

  std::pair<bool, Expr*> walkToExprPre(Expr *E) override {
    ShouldRecurse_t recurse = ShouldRecurse;
    if (isa<ErrorExpr>(E)) {
      asImpl().flagInvalidCode();
    } else if (auto closure = dyn_cast<ClosureExpr>(E)) {
      recurse = asImpl().checkClosure(closure);
    } else if (auto autoclosure = dyn_cast<AutoClosureExpr>(E)) {
      recurse = asImpl().checkAutoClosure(autoclosure);
    } else if (auto awaitExpr = dyn_cast<AwaitExpr>(E)) {
      recurse = asImpl().checkAwait(awaitExpr);
    } else if (auto tryExpr = dyn_cast<TryExpr>(E)) {
      recurse = asImpl().checkTry(tryExpr);
    } else if (auto forceTryExpr = dyn_cast<ForceTryExpr>(E)) {
      recurse = asImpl().checkForceTry(forceTryExpr);
    } else if (auto optionalTryExpr = dyn_cast<OptionalTryExpr>(E)) {
      recurse = asImpl().checkOptionalTry(optionalTryExpr);
    } else if (auto apply = dyn_cast<ApplyExpr>(E)) {
      recurse = asImpl().checkApply(apply);
    } else if (auto declRef = dyn_cast<DeclRefExpr>(E)) {
      recurse = asImpl().checkDeclRef(declRef);
    } else if (auto interpolated = dyn_cast<InterpolatedStringLiteralExpr>(E)) {
      recurse = asImpl().checkInterpolatedStringLiteral(interpolated);
    }
    // Error handling validation (via checkTopLevelEffects) happens after
    // type checking. If an unchecked expression is still around, the code was
    // invalid.
#define UNCHECKED_EXPR(KIND, BASE) \
    else if (isa<KIND##Expr>(E)) return {false, nullptr};
#include "swift/AST/ExprNodes.def"

    return {bool(recurse), E};
  }

  std::pair<bool, Stmt*> walkToStmtPre(Stmt *S) override {
    ShouldRecurse_t recurse = ShouldRecurse;
    if (auto doCatch = dyn_cast<DoCatchStmt>(S)) {
      recurse = asImpl().checkDoCatch(doCatch);
    } else if (auto thr = dyn_cast<ThrowStmt>(S)) {
      recurse = asImpl().checkThrow(thr);
    } else if (auto forEach = dyn_cast<ForEachStmt>(S)) {
      recurse = asImpl().checkForEach(forEach);
    }
    return {bool(recurse), S};
  }

  ShouldRecurse_t checkDoCatch(DoCatchStmt *S) {
    auto bodyResult = (S->isSyntacticallyExhaustive()
        ? asImpl().checkExhaustiveDoBody(S)
        : asImpl().checkNonExhaustiveDoBody(S));
    for (auto clause : S->getCatches()) {
      asImpl().checkCatch(clause, bodyResult);
    }
    return ShouldNotRecurse;
  }

  ShouldRecurse_t checkForEach(ForEachStmt *S) {
    return ShouldRecurse;
  }
};

/// A potential reason why something might throw.
class PotentialThrowReason {
public:
  enum class Kind : uint8_t {
    /// The function throws unconditionally.
    Throw,

    /// The function calls an unconditionally throwing function.
    CallThrows,

    /// The initializer of an 'async let' unconditionally throws.
    AsyncLetThrows,

    /// The function is 'rethrows', and it was passed an explicit
    /// argument that was not rethrowing-only in this context.
    CallRethrowsWithExplicitThrowingArgument,

    /// The function is 'rethrows', and it was passed a default
    /// argument that was not rethrowing-only in this context.
    CallRethrowsWithDefaultThrowingArgument,

    /// The the function is 'rethrows', and it is a member that
    /// is a conformance to a rethrowing protocol.
    CallRethrowsWithConformance,
  };

  static StringRef kindToString(Kind k) {
    switch (k) {
      case Kind::Throw: return "Throw";
      case Kind::CallThrows: return "CallThrows";
      case Kind::AsyncLetThrows: return "AsyncLetThrows";
      case Kind::CallRethrowsWithExplicitThrowingArgument:
        return "CallRethrowsWithExplicitThrowingArgument";
      case Kind::CallRethrowsWithDefaultThrowingArgument: 
        return "CallRethrowsWithDefaultThrowingArgument";
      case Kind::CallRethrowsWithConformance:
        return "CallRethrowsWithConformance";
    }
  }

private:
  Expr *TheExpression;
  Kind TheKind;

  explicit PotentialThrowReason(Kind kind) : TheKind(kind) {}
public:
  static PotentialThrowReason forRethrowsArgument(Expr *E) {
    PotentialThrowReason result(Kind::CallRethrowsWithExplicitThrowingArgument);
    result.TheExpression = E;
    return result;
  }
  static PotentialThrowReason forDefaultArgument() {
    return PotentialThrowReason(Kind::CallRethrowsWithDefaultThrowingArgument);
  }
  static PotentialThrowReason forRethrowsConformance(Expr *E) {
    PotentialThrowReason result(Kind::CallRethrowsWithConformance);
    result.TheExpression = E;
    return result;
  }
  static PotentialThrowReason forThrowingApply() {
    return PotentialThrowReason(Kind::CallThrows);
  }
  static PotentialThrowReason forThrow() {
    return PotentialThrowReason(Kind::Throw);
  }
  static PotentialThrowReason forThrowingAsyncLet() {
    return PotentialThrowReason(Kind::AsyncLetThrows);
  }

  Kind getKind() const { return TheKind; }

  /// Is this a throw expression?
  bool isThrow() const { return getKind() == Kind::Throw; }
  bool isRethrowsCall() const {
    return (getKind() == Kind::CallRethrowsWithExplicitThrowingArgument ||
            getKind() == Kind::CallRethrowsWithDefaultThrowingArgument ||
            getKind() == Kind::CallRethrowsWithConformance);
  }

  /// If this was built with forRethrowsArgument, return the expression.
  Expr *getThrowingArgument() const {
    assert(getKind() == Kind::CallRethrowsWithExplicitThrowingArgument);
    return TheExpression;
  }
};

enum class ThrowingKind {
  /// The call/function can't throw.
  None,

  /// The call/function can only throw if one of the parameters in
  /// the current rethrows context can throw.
  RethrowingOnly,

  /// The call/function can throw.
  Throws,
};

/// A type expressing the result of classifying whether a call or function
/// throws or is async.
class Classification {
  bool IsInvalid = false;  // The AST is malformed.  Don't diagnose.
  bool IsAsync = false;
  ThrowingKind Result = ThrowingKind::None;
  Optional<PotentialThrowReason> Reason;
  
  void print(raw_ostream &out) const {
    out << "{ IsInvalid = " << IsInvalid
        << ", IsAsync = " << IsAsync
        << ", Result = ThrowingKind::";
    
    switch(Result) {
      case ThrowingKind::None:            out << "None"; break;
      case ThrowingKind::RethrowingOnly:  out << "RethrowingOnly"; break;
      case ThrowingKind::Throws:          out << "Throws"; break;
    }
         
    out << ", Reason = ";
    if (!Reason)
      out << "nil";
    else
      out << PotentialThrowReason::kindToString(Reason.getValue().getKind());

    out << " }";
  }
  
public:
  Classification() : Result(ThrowingKind::None) {}
  explicit Classification(ThrowingKind result, PotentialThrowReason reason,
                          bool isAsync)
      : IsAsync(isAsync), Result(result) {
    if (result == ThrowingKind::Throws ||
        result == ThrowingKind::RethrowingOnly) {
      Reason = reason;
    }
  }

  /// Return a classification saying that there's an unconditional
  /// throw site.
  static Classification forThrow(PotentialThrowReason reason, bool isAsync) {
    Classification result;
    result.Result = ThrowingKind::Throws;
    result.Reason = reason;
    result.IsAsync = isAsync;
    return result;
  }

  /// Return a classification saying that there's an unconditional
  /// throw site.
  static Classification forAsync() {
    Classification result;
    result.IsAsync = true;
    return result;
  }
  
  static Classification forInvalidCode() {
    Classification result;
    result.IsInvalid = true;
    return result;
  }

  static Classification forRethrowingOnly(PotentialThrowReason reason, bool isAsync) {
    Classification result;
    result.Result = ThrowingKind::RethrowingOnly;
    result.Reason = reason;
    result.IsAsync = isAsync;
    return result;
  }

  void merge(Classification other) {
    bool oldAsync = IsAsync;
    
    if (other.getResult() > getResult())
      *this = other;

    IsAsync = oldAsync | other.IsAsync;
  }

  bool isInvalid() const { return IsInvalid; }
  ThrowingKind getResult() const { return Result; }
  PotentialThrowReason getThrowsReason() const {
    assert(getResult() == ThrowingKind::Throws ||
           getResult() == ThrowingKind::RethrowingOnly);
    return *Reason;
  }
  
  bool isAsync() const { return IsAsync; }

#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
  LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); }
#endif
};


/// A class for collecting information about rethrowing functions.
class ApplyClassifier {
  /// The key to this cache is the function decl or closure being analyzed.  The
  /// value in this cache is nil when the body has an error detected in it.
  llvm::DenseMap<void*, Optional<ThrowingKind>> Cache;

public:
  DeclContext *RethrowsDC = nullptr;
  bool inRethrowsContext() const { return RethrowsDC != nullptr; }

  /// Check to see if the given function application throws or is async.
  Classification classifyApply(ApplyExpr *E) {
    // An apply expression is a potential throw site if the function throws.
    // But if the expression didn't type-check, suppress diagnostics.
    if (!E->getType() || E->getType()->hasError())
      return Classification::forInvalidCode();

    auto type = E->getFn()->getType();
    if (!type) return Classification::forInvalidCode();
    auto fnType = type->getAs<AnyFunctionType>();
    if (!fnType) return Classification::forInvalidCode();

    bool isAsync = fnType->isAsync() || E->implicitlyAsync();
    // Decompose the application.
    SmallVector<SmallVector<Expr *, 2>, 2> argLists;
    auto fnRef = AbstractFunction::decomposeApply(E, argLists);

    // If any of the arguments didn't type check, fail.
    for (auto argList : argLists) {
      for (auto *arg : argList) {
        if (!arg->getType() || arg->getType()->hasError())
          return Classification::forInvalidCode();
      }
    }

    // If the function doesn't throw at all, we're done here.
    if (!fnType->isThrowing()) {
      return isAsync ? Classification::forAsync() : Classification();
    }

    // If we're applying more arguments than the natural argument
    // count, then this is a call to the opaque value returned from
    // the function.
    if (argLists.size() != fnRef.getNumArgumentsForFullApply()) {
      // Special case: a reference to an operator within a type might be
      // missing 'self'.
      // FIXME: The issue here is that this is an ill-formed expression, but
      // we don't know it from the structure of the expression.
      if (argLists.size() == 1 && fnRef.getKind() == AbstractFunction::Function &&
          isa<FuncDecl>(fnRef.getFunction()) &&
          cast<FuncDecl>(fnRef.getFunction())->isOperator() &&
          fnRef.getNumArgumentsForFullApply() == 2 &&
          fnRef.getFunction()->getDeclContext()->isTypeContext()) {
        // Can only happen with invalid code.
        assert(fnRef.getFunction()->getASTContext().Diags.hadAnyError());
        return Classification::forInvalidCode();
      }

      assert(argLists.size() > fnRef.getNumArgumentsForFullApply() &&
             "partial application was throwing?");
      return Classification::forThrow(PotentialThrowReason::forThrowingApply(),
                                      isAsync);
    }

    if (fnRef.getRethrowingKind() == FunctionRethrowingKind::ByConformance) {
      auto substitutions = fnRef.getSubstitutions();
      for (auto conformanceRef : substitutions.getConformances()) {
        if (conformanceRef.classifyAsThrows()) {
          return Classification::forRethrowingOnly(
            PotentialThrowReason::forRethrowsConformance(E), isAsync);
        }
      }
    }

    // If the function's body is 'rethrows' for the number of
    // arguments we gave it, apply the rethrows logic.
    if (fnRef.isBodyRethrows()) {
      // We need to walk the original parameter types in parallel
      // because it only counts for 'rethrows' purposes if it lines up
      // with a throwing function parameter in the original type.
      Type type = fnRef.getType();
      if (!type) return Classification::forInvalidCode();

      // Use the most significant result from the arguments.
      Classification result = isAsync ? Classification::forAsync() 
                                      : Classification();
      for (auto argList : llvm::reverse(argLists)) {
        auto fnType = type->getAs<AnyFunctionType>();
        if (!fnType)
          return Classification::forInvalidCode();

        auto params = fnType->getParams();
        if (params.size() != argList.size())
          return Classification::forInvalidCode();

        for (unsigned i = 0, e = params.size(); i < e; ++i) {
          result.merge(classifyRethrowsArgument(argList[i],
                                                params[i].getParameterType()));
        }

        type = fnType->getResult();
{'fraction_non_alphanumeric': 0.11472002748196496, 'fraction_numerical': 0.0008588114050154586, 'mean_word_length': 3.189766839378238, 'pattern_counts': {'":': 0, '<': 176, '<?xml version=': 0, '>': 321, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 25, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '97538', 'n_tokens_mistral': 16901, 'n_tokens_neox': 16239, 'n_words': 4050}