|  | //=== SemaFunctionEffects.cpp - Sema handling of function effects ---------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // This file implements Sema handling of function effects. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "clang/AST/Decl.h" | 
|  | #include "clang/AST/DeclCXX.h" | 
|  | #include "clang/AST/DynamicRecursiveASTVisitor.h" | 
|  | #include "clang/AST/ExprObjC.h" | 
|  | #include "clang/AST/Stmt.h" | 
|  | #include "clang/AST/StmtObjC.h" | 
|  | #include "clang/AST/Type.h" | 
|  | #include "clang/Basic/SourceManager.h" | 
|  | #include "clang/Sema/SemaInternal.h" | 
|  |  | 
|  | #define DEBUG_TYPE "effectanalysis" | 
|  |  | 
|  | using namespace clang; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | enum class ViolationID : uint8_t { | 
|  | None = 0, // Sentinel for an empty Violation. | 
|  | // These first 5 map to a %select{} in one of several FunctionEffects | 
|  | // diagnostics, e.g. warn_func_effect_violation. | 
|  | BaseDiagnosticIndex, | 
|  | AllocatesMemory = BaseDiagnosticIndex, | 
|  | ThrowsOrCatchesExceptions, | 
|  | HasStaticLocalVariable, | 
|  | AccessesThreadLocalVariable, | 
|  | AccessesObjCMethodOrProperty, | 
|  |  | 
|  | // These only apply to callees, where the analysis stops at the Decl. | 
|  | DeclDisallowsInference, | 
|  |  | 
|  | // These both apply to indirect calls. The difference is that sometimes | 
|  | // we have an actual Decl (generally a variable) which is the function | 
|  | // pointer being called, and sometimes, typically due to a cast, we only | 
|  | // have an expression. | 
|  | CallsDeclWithoutEffect, | 
|  | CallsExprWithoutEffect, | 
|  | }; | 
|  |  | 
|  | // Information about the AST context in which a violation was found, so | 
|  | // that diagnostics can point to the correct source. | 
|  | class ViolationSite { | 
|  | public: | 
|  | enum class Kind : uint8_t { | 
|  | Default, // Function body. | 
|  | MemberInitializer, | 
|  | DefaultArgExpr | 
|  | }; | 
|  |  | 
|  | private: | 
|  | llvm::PointerIntPair<CXXDefaultArgExpr *, 2, Kind> Impl; | 
|  |  | 
|  | public: | 
|  | ViolationSite() = default; | 
|  |  | 
|  | explicit ViolationSite(CXXDefaultArgExpr *E) | 
|  | : Impl(E, Kind::DefaultArgExpr) {} | 
|  |  | 
|  | Kind kind() const { return static_cast<Kind>(Impl.getInt()); } | 
|  | CXXDefaultArgExpr *defaultArgExpr() const { return Impl.getPointer(); } | 
|  |  | 
|  | void setKind(Kind K) { Impl.setPointerAndInt(nullptr, K); } | 
|  | }; | 
|  |  | 
|  | // Represents a violation of the rules, potentially for the entire duration of | 
|  | // the analysis phase, in order to refer to it when explaining why a caller has | 
|  | // been made unsafe by a callee. Can be transformed into either a Diagnostic | 
|  | // (warning or a note), depending on whether the violation pertains to a | 
|  | // function failing to be verifed as holding an effect vs. a function failing to | 
|  | // be inferred as holding that effect. | 
|  | struct Violation { | 
|  | FunctionEffect Effect; | 
|  | std::optional<FunctionEffect> | 
|  | CalleeEffectPreventingInference; // Only for certain IDs; can be nullopt. | 
|  | ViolationID ID = ViolationID::None; | 
|  | ViolationSite Site; | 
|  | SourceLocation Loc; | 
|  | const Decl *Callee = | 
|  | nullptr; // Only valid for ViolationIDs Calls{Decl,Expr}WithoutEffect. | 
|  |  | 
|  | Violation(FunctionEffect Effect, ViolationID ID, ViolationSite VS, | 
|  | SourceLocation Loc, const Decl *Callee = nullptr, | 
|  | std::optional<FunctionEffect> CalleeEffect = std::nullopt) | 
|  | : Effect(Effect), CalleeEffectPreventingInference(CalleeEffect), ID(ID), | 
|  | Site(VS), Loc(Loc), Callee(Callee) {} | 
|  |  | 
|  | unsigned diagnosticSelectIndex() const { | 
|  | return unsigned(ID) - unsigned(ViolationID::BaseDiagnosticIndex); | 
|  | } | 
|  | }; | 
|  |  | 
|  | enum class SpecialFuncType : uint8_t { None, OperatorNew, OperatorDelete }; | 
|  | enum class CallableType : uint8_t { | 
|  | // Unknown: probably function pointer. | 
|  | Unknown, | 
|  | Function, | 
|  | Virtual, | 
|  | Block | 
|  | }; | 
|  |  | 
|  | // Return whether a function's effects CAN be verified. | 
|  | // The question of whether it SHOULD be verified is independent. | 
|  | static bool functionIsVerifiable(const FunctionDecl *FD) { | 
|  | if (FD->isTrivial()) { | 
|  | // Otherwise `struct x { int a; };` would have an unverifiable default | 
|  | // constructor. | 
|  | return true; | 
|  | } | 
|  | return FD->hasBody(); | 
|  | } | 
|  |  | 
|  | static bool isNoexcept(const FunctionDecl *FD) { | 
|  | const auto *FPT = FD->getType()->getAs<FunctionProtoType>(); | 
|  | return FPT && (FPT->isNothrow() || FD->hasAttr<NoThrowAttr>()); | 
|  | } | 
|  |  | 
|  | // This list is probably incomplete. | 
|  | // FIXME: Investigate: | 
|  | // __builtin_eh_return? | 
|  | // __builtin_allow_runtime_check? | 
|  | // __builtin_unwind_init and other similar things that sound exception-related. | 
|  | // va_copy? | 
|  | // coroutines? | 
|  | static FunctionEffectKindSet getBuiltinFunctionEffects(unsigned BuiltinID) { | 
|  | FunctionEffectKindSet Result; | 
|  |  | 
|  | switch (BuiltinID) { | 
|  | case 0:  // Not builtin. | 
|  | default: // By default, builtins have no known effects. | 
|  | break; | 
|  |  | 
|  | // These allocate/deallocate heap memory. | 
|  | case Builtin::ID::BI__builtin_calloc: | 
|  | case Builtin::ID::BI__builtin_malloc: | 
|  | case Builtin::ID::BI__builtin_realloc: | 
|  | case Builtin::ID::BI__builtin_free: | 
|  | case Builtin::ID::BI__builtin_operator_delete: | 
|  | case Builtin::ID::BI__builtin_operator_new: | 
|  | case Builtin::ID::BIaligned_alloc: | 
|  | case Builtin::ID::BIcalloc: | 
|  | case Builtin::ID::BImalloc: | 
|  | case Builtin::ID::BImemalign: | 
|  | case Builtin::ID::BIrealloc: | 
|  | case Builtin::ID::BIfree: | 
|  |  | 
|  | case Builtin::ID::BIfopen: | 
|  | case Builtin::ID::BIpthread_create: | 
|  | case Builtin::ID::BI_Block_object_dispose: | 
|  | Result.insert(FunctionEffect(FunctionEffect::Kind::Allocating)); | 
|  | break; | 
|  |  | 
|  | // These block in some other way than allocating memory. | 
|  | // longjmp() and friends are presumed unsafe because they are the moral | 
|  | // equivalent of throwing a C++ exception, which is unsafe. | 
|  | case Builtin::ID::BIlongjmp: | 
|  | case Builtin::ID::BI_longjmp: | 
|  | case Builtin::ID::BIsiglongjmp: | 
|  | case Builtin::ID::BI__builtin_longjmp: | 
|  | case Builtin::ID::BIobjc_exception_throw: | 
|  |  | 
|  | // Objective-C runtime. | 
|  | case Builtin::ID::BIobjc_msgSend: | 
|  | case Builtin::ID::BIobjc_msgSend_fpret: | 
|  | case Builtin::ID::BIobjc_msgSend_fp2ret: | 
|  | case Builtin::ID::BIobjc_msgSend_stret: | 
|  | case Builtin::ID::BIobjc_msgSendSuper: | 
|  | case Builtin::ID::BIobjc_getClass: | 
|  | case Builtin::ID::BIobjc_getMetaClass: | 
|  | case Builtin::ID::BIobjc_enumerationMutation: | 
|  | case Builtin::ID::BIobjc_assign_ivar: | 
|  | case Builtin::ID::BIobjc_assign_global: | 
|  | case Builtin::ID::BIobjc_sync_enter: | 
|  | case Builtin::ID::BIobjc_sync_exit: | 
|  | case Builtin::ID::BINSLog: | 
|  | case Builtin::ID::BINSLogv: | 
|  |  | 
|  | // stdio.h | 
|  | case Builtin::ID::BIfread: | 
|  | case Builtin::ID::BIfwrite: | 
|  |  | 
|  | // stdio.h: printf family. | 
|  | case Builtin::ID::BIprintf: | 
|  | case Builtin::ID::BI__builtin_printf: | 
|  | case Builtin::ID::BIfprintf: | 
|  | case Builtin::ID::BIsnprintf: | 
|  | case Builtin::ID::BIsprintf: | 
|  | case Builtin::ID::BIvprintf: | 
|  | case Builtin::ID::BIvfprintf: | 
|  | case Builtin::ID::BIvsnprintf: | 
|  | case Builtin::ID::BIvsprintf: | 
|  |  | 
|  | // stdio.h: scanf family. | 
|  | case Builtin::ID::BIscanf: | 
|  | case Builtin::ID::BIfscanf: | 
|  | case Builtin::ID::BIsscanf: | 
|  | case Builtin::ID::BIvscanf: | 
|  | case Builtin::ID::BIvfscanf: | 
|  | case Builtin::ID::BIvsscanf: | 
|  | Result.insert(FunctionEffect(FunctionEffect::Kind::Blocking)); | 
|  | break; | 
|  | } | 
|  |  | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | // Transitory, more extended information about a callable, which can be a | 
|  | // function, block, or function pointer. | 
|  | struct CallableInfo { | 
|  | // CDecl holds the function's definition, if any. | 
|  | // FunctionDecl if CallableType::Function or Virtual | 
|  | // BlockDecl if CallableType::Block | 
|  | const Decl *CDecl; | 
|  |  | 
|  | // Remember whether the callable is a function, block, virtual method, | 
|  | // or (presumed) function pointer. | 
|  | CallableType CType = CallableType::Unknown; | 
|  |  | 
|  | // Remember whether the callable is an operator new or delete function, | 
|  | // so that calls to them are reported more meaningfully, as memory | 
|  | // allocations. | 
|  | SpecialFuncType FuncType = SpecialFuncType::None; | 
|  |  | 
|  | // We inevitably want to know the callable's declared effects, so cache them. | 
|  | FunctionEffectKindSet Effects; | 
|  |  | 
|  | CallableInfo(const Decl &CD, SpecialFuncType FT = SpecialFuncType::None) | 
|  | : CDecl(&CD), FuncType(FT) { | 
|  | FunctionEffectsRef DeclEffects; | 
|  | if (auto *FD = dyn_cast<FunctionDecl>(CDecl)) { | 
|  | // Use the function's definition, if any. | 
|  | if (const FunctionDecl *Def = FD->getDefinition()) | 
|  | CDecl = FD = Def; | 
|  | CType = CallableType::Function; | 
|  | if (auto *Method = dyn_cast<CXXMethodDecl>(FD); | 
|  | Method && Method->isVirtual()) | 
|  | CType = CallableType::Virtual; | 
|  | DeclEffects = FD->getFunctionEffects(); | 
|  | } else if (auto *BD = dyn_cast<BlockDecl>(CDecl)) { | 
|  | CType = CallableType::Block; | 
|  | DeclEffects = BD->getFunctionEffects(); | 
|  | } else if (auto *VD = dyn_cast<ValueDecl>(CDecl)) { | 
|  | // ValueDecl is function, enum, or variable, so just look at its type. | 
|  | DeclEffects = FunctionEffectsRef::get(VD->getType()); | 
|  | } | 
|  | Effects = FunctionEffectKindSet(DeclEffects); | 
|  | } | 
|  |  | 
|  | CallableType type() const { return CType; } | 
|  |  | 
|  | bool isCalledDirectly() const { | 
|  | return CType == CallableType::Function || CType == CallableType::Block; | 
|  | } | 
|  |  | 
|  | bool isVerifiable() const { | 
|  | switch (CType) { | 
|  | case CallableType::Unknown: | 
|  | case CallableType::Virtual: | 
|  | return false; | 
|  | case CallableType::Block: | 
|  | return true; | 
|  | case CallableType::Function: | 
|  | return functionIsVerifiable(dyn_cast<FunctionDecl>(CDecl)); | 
|  | } | 
|  | llvm_unreachable("undefined CallableType"); | 
|  | } | 
|  |  | 
|  | /// Generate a name for logging and diagnostics. | 
|  | std::string getNameForDiagnostic(Sema &S) const { | 
|  | std::string Name; | 
|  | llvm::raw_string_ostream OS(Name); | 
|  |  | 
|  | if (auto *FD = dyn_cast<FunctionDecl>(CDecl)) | 
|  | FD->getNameForDiagnostic(OS, S.getPrintingPolicy(), | 
|  | /*Qualified=*/true); | 
|  | else if (auto *BD = dyn_cast<BlockDecl>(CDecl)) | 
|  | OS << "(block " << BD->getBlockManglingNumber() << ")"; | 
|  | else if (auto *VD = dyn_cast<NamedDecl>(CDecl)) | 
|  | VD->printQualifiedName(OS); | 
|  | return Name; | 
|  | } | 
|  | }; | 
|  |  | 
|  | // ---------- | 
|  | // Map effects to single Violations, to hold the first (of potentially many) | 
|  | // violations pertaining to an effect, per function. | 
|  | class EffectToViolationMap { | 
|  | // Since we currently only have a tiny number of effects (typically no more | 
|  | // than 1), use a SmallVector with an inline capacity of 1. Since it | 
|  | // is often empty, use a unique_ptr to the SmallVector. | 
|  | // Note that Violation itself contains a FunctionEffect which is the key. | 
|  | // FIXME: Is there a way to simplify this using existing data structures? | 
|  | using ImplVec = llvm::SmallVector<Violation, 1>; | 
|  | std::unique_ptr<ImplVec> Impl; | 
|  |  | 
|  | public: | 
|  | // Insert a new Violation if we do not already have one for its effect. | 
|  | void maybeInsert(const Violation &Viol) { | 
|  | if (Impl == nullptr) | 
|  | Impl = std::make_unique<ImplVec>(); | 
|  | else if (lookup(Viol.Effect) != nullptr) | 
|  | return; | 
|  |  | 
|  | Impl->push_back(Viol); | 
|  | } | 
|  |  | 
|  | const Violation *lookup(FunctionEffect Key) { | 
|  | if (Impl == nullptr) | 
|  | return nullptr; | 
|  |  | 
|  | auto *Iter = llvm::find_if( | 
|  | *Impl, [&](const auto &Item) { return Item.Effect == Key; }); | 
|  | return Iter != Impl->end() ? &*Iter : nullptr; | 
|  | } | 
|  |  | 
|  | size_t size() const { return Impl ? Impl->size() : 0; } | 
|  | }; | 
|  |  | 
|  | // ---------- | 
|  | // State pertaining to a function whose AST is walked and whose effect analysis | 
|  | // is dependent on a subsequent analysis of other functions. | 
|  | class PendingFunctionAnalysis { | 
|  | friend class CompleteFunctionAnalysis; | 
|  |  | 
|  | public: | 
|  | struct DirectCall { | 
|  | const Decl *Callee; | 
|  | SourceLocation CallLoc; | 
|  | // Not all recursive calls are detected, just enough | 
|  | // to break cycles. | 
|  | bool Recursed = false; | 
|  | ViolationSite VSite; | 
|  |  | 
|  | DirectCall(const Decl *D, SourceLocation CallLoc, ViolationSite VSite) | 
|  | : Callee(D), CallLoc(CallLoc), VSite(VSite) {} | 
|  | }; | 
|  |  | 
|  | // We always have two disjoint sets of effects to verify: | 
|  | // 1. Effects declared explicitly by this function. | 
|  | // 2. All other inferrable effects needing verification. | 
|  | FunctionEffectKindSet DeclaredVerifiableEffects; | 
|  | FunctionEffectKindSet EffectsToInfer; | 
|  |  | 
|  | private: | 
|  | // Violations pertaining to the function's explicit effects. | 
|  | SmallVector<Violation, 0> ViolationsForExplicitEffects; | 
|  |  | 
|  | // Violations pertaining to other, non-explicit, inferrable effects. | 
|  | EffectToViolationMap InferrableEffectToFirstViolation; | 
|  |  | 
|  | // These unverified direct calls are what keeps the analysis "pending", | 
|  | // until the callees can be verified. | 
|  | SmallVector<DirectCall, 0> UnverifiedDirectCalls; | 
|  |  | 
|  | public: | 
|  | PendingFunctionAnalysis(Sema &S, const CallableInfo &CInfo, | 
|  | FunctionEffectKindSet AllInferrableEffectsToVerify) | 
|  | : DeclaredVerifiableEffects(CInfo.Effects) { | 
|  | // Check for effects we are not allowed to infer. | 
|  | FunctionEffectKindSet InferrableEffects; | 
|  |  | 
|  | for (FunctionEffect effect : AllInferrableEffectsToVerify) { | 
|  | std::optional<FunctionEffect> ProblemCalleeEffect = | 
|  | effect.effectProhibitingInference(*CInfo.CDecl, CInfo.Effects); | 
|  | if (!ProblemCalleeEffect) | 
|  | InferrableEffects.insert(effect); | 
|  | else { | 
|  | // Add a Violation for this effect if a caller were to | 
|  | // try to infer it. | 
|  | InferrableEffectToFirstViolation.maybeInsert(Violation( | 
|  | effect, ViolationID::DeclDisallowsInference, ViolationSite{}, | 
|  | CInfo.CDecl->getLocation(), nullptr, ProblemCalleeEffect)); | 
|  | } | 
|  | } | 
|  | // InferrableEffects is now the set of inferrable effects which are not | 
|  | // prohibited. | 
|  | EffectsToInfer = FunctionEffectKindSet::difference( | 
|  | InferrableEffects, DeclaredVerifiableEffects); | 
|  | } | 
|  |  | 
|  | // Hide the way that Violations for explicitly required effects vs. inferred | 
|  | // ones are handled differently. | 
|  | void checkAddViolation(bool Inferring, const Violation &NewViol) { | 
|  | if (!Inferring) | 
|  | ViolationsForExplicitEffects.push_back(NewViol); | 
|  | else | 
|  | InferrableEffectToFirstViolation.maybeInsert(NewViol); | 
|  | } | 
|  |  | 
|  | void addUnverifiedDirectCall(const Decl *D, SourceLocation CallLoc, | 
|  | ViolationSite VSite) { | 
|  | UnverifiedDirectCalls.emplace_back(D, CallLoc, VSite); | 
|  | } | 
|  |  | 
|  | // Analysis is complete when there are no unverified direct calls. | 
|  | bool isComplete() const { return UnverifiedDirectCalls.empty(); } | 
|  |  | 
|  | const Violation *violationForInferrableEffect(FunctionEffect effect) { | 
|  | return InferrableEffectToFirstViolation.lookup(effect); | 
|  | } | 
|  |  | 
|  | // Mutable because caller may need to set a DirectCall's Recursing flag. | 
|  | MutableArrayRef<DirectCall> unverifiedCalls() { | 
|  | assert(!isComplete()); | 
|  | return UnverifiedDirectCalls; | 
|  | } | 
|  |  | 
|  | ArrayRef<Violation> getSortedViolationsForExplicitEffects(SourceManager &SM) { | 
|  | if (!ViolationsForExplicitEffects.empty()) | 
|  | llvm::sort(ViolationsForExplicitEffects, | 
|  | [&SM](const Violation &LHS, const Violation &RHS) { | 
|  | return SM.isBeforeInTranslationUnit(LHS.Loc, RHS.Loc); | 
|  | }); | 
|  | return ViolationsForExplicitEffects; | 
|  | } | 
|  |  | 
|  | void dump(Sema &SemaRef, llvm::raw_ostream &OS) const { | 
|  | OS << "Pending: Declared "; | 
|  | DeclaredVerifiableEffects.dump(OS); | 
|  | OS << ", " << ViolationsForExplicitEffects.size() << " violations; "; | 
|  | OS << " Infer "; | 
|  | EffectsToInfer.dump(OS); | 
|  | OS << ", " << InferrableEffectToFirstViolation.size() << " violations"; | 
|  | if (!UnverifiedDirectCalls.empty()) { | 
|  | OS << "; Calls: "; | 
|  | for (const DirectCall &Call : UnverifiedDirectCalls) { | 
|  | CallableInfo CI(*Call.Callee); | 
|  | OS << " " << CI.getNameForDiagnostic(SemaRef); | 
|  | } | 
|  | } | 
|  | OS << "\n"; | 
|  | } | 
|  | }; | 
|  |  | 
|  | // ---------- | 
|  | class CompleteFunctionAnalysis { | 
|  | // Current size: 2 pointers | 
|  | public: | 
|  | // Has effects which are both the declared ones -- not to be inferred -- plus | 
|  | // ones which have been successfully inferred. These are all considered | 
|  | // "verified" for the purposes of callers; any issue with verifying declared | 
|  | // effects has already been reported and is not the problem of any caller. | 
|  | FunctionEffectKindSet VerifiedEffects; | 
|  |  | 
|  | private: | 
|  | // This is used to generate notes about failed inference. | 
|  | EffectToViolationMap InferrableEffectToFirstViolation; | 
|  |  | 
|  | public: | 
|  | // The incoming Pending analysis is consumed (member(s) are moved-from). | 
|  | CompleteFunctionAnalysis(ASTContext &Ctx, PendingFunctionAnalysis &&Pending, | 
|  | FunctionEffectKindSet DeclaredEffects, | 
|  | FunctionEffectKindSet AllInferrableEffectsToVerify) | 
|  | : VerifiedEffects(DeclaredEffects) { | 
|  | for (FunctionEffect effect : AllInferrableEffectsToVerify) | 
|  | if (Pending.violationForInferrableEffect(effect) == nullptr) | 
|  | VerifiedEffects.insert(effect); | 
|  |  | 
|  | InferrableEffectToFirstViolation = | 
|  | std::move(Pending.InferrableEffectToFirstViolation); | 
|  | } | 
|  |  | 
|  | const Violation *firstViolationForEffect(FunctionEffect Effect) { | 
|  | return InferrableEffectToFirstViolation.lookup(Effect); | 
|  | } | 
|  |  | 
|  | void dump(llvm::raw_ostream &OS) const { | 
|  | OS << "Complete: Verified "; | 
|  | VerifiedEffects.dump(OS); | 
|  | OS << "; Infer "; | 
|  | OS << InferrableEffectToFirstViolation.size() << " violations\n"; | 
|  | } | 
|  | }; | 
|  |  | 
|  | // ========== | 
|  | class Analyzer { | 
|  | Sema &S; | 
|  |  | 
|  | // Subset of Sema.AllEffectsToVerify | 
|  | FunctionEffectKindSet AllInferrableEffectsToVerify; | 
|  |  | 
|  | using FuncAnalysisPtr = | 
|  | llvm::PointerUnion<PendingFunctionAnalysis *, CompleteFunctionAnalysis *>; | 
|  |  | 
|  | // Map all Decls analyzed to FuncAnalysisPtr. Pending state is larger | 
|  | // than complete state, so use different objects to represent them. | 
|  | // The state pointers are owned by the container. | 
|  | class AnalysisMap : llvm::DenseMap<const Decl *, FuncAnalysisPtr> { | 
|  | using Base = llvm::DenseMap<const Decl *, FuncAnalysisPtr>; | 
|  |  | 
|  | public: | 
|  | ~AnalysisMap(); | 
|  |  | 
|  | // Use non-public inheritance in order to maintain the invariant | 
|  | // that lookups and insertions are via the canonical Decls. | 
|  |  | 
|  | FuncAnalysisPtr lookup(const Decl *Key) const { | 
|  | return Base::lookup(Key->getCanonicalDecl()); | 
|  | } | 
|  |  | 
|  | FuncAnalysisPtr &operator[](const Decl *Key) { | 
|  | return Base::operator[](Key->getCanonicalDecl()); | 
|  | } | 
|  |  | 
|  | /// Shortcut for the case where we only care about completed analysis. | 
|  | CompleteFunctionAnalysis *completedAnalysisForDecl(const Decl *D) const { | 
|  | if (FuncAnalysisPtr AP = lookup(D); | 
|  | isa_and_nonnull<CompleteFunctionAnalysis *>(AP)) | 
|  | return cast<CompleteFunctionAnalysis *>(AP); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | void dump(Sema &SemaRef, llvm::raw_ostream &OS) { | 
|  | OS << "\nAnalysisMap:\n"; | 
|  | for (const auto &item : *this) { | 
|  | CallableInfo CI(*item.first); | 
|  | const auto AP = item.second; | 
|  | OS << item.first << " " << CI.getNameForDiagnostic(SemaRef) << " : "; | 
|  | if (AP.isNull()) { | 
|  | OS << "null\n"; | 
|  | } else if (auto *CFA = dyn_cast<CompleteFunctionAnalysis *>(AP)) { | 
|  | OS << CFA << " "; | 
|  | CFA->dump(OS); | 
|  | } else if (auto *PFA = dyn_cast<PendingFunctionAnalysis *>(AP)) { | 
|  | OS << PFA << " "; | 
|  | PFA->dump(SemaRef, OS); | 
|  | } else | 
|  | llvm_unreachable("never"); | 
|  | } | 
|  | OS << "---\n"; | 
|  | } | 
|  | }; | 
|  | AnalysisMap DeclAnalysis; | 
|  |  | 
|  | public: | 
|  | Analyzer(Sema &S) : S(S) {} | 
|  |  | 
|  | void run(const TranslationUnitDecl &TU) { | 
|  | // Gather all of the effects to be verified to see what operations need to | 
|  | // be checked, and to see which ones are inferrable. | 
|  | for (FunctionEffect Effect : S.AllEffectsToVerify) { | 
|  | const FunctionEffect::Flags Flags = Effect.flags(); | 
|  | if (Flags & FunctionEffect::FE_InferrableOnCallees) | 
|  | AllInferrableEffectsToVerify.insert(Effect); | 
|  | } | 
|  | LLVM_DEBUG(llvm::dbgs() << "AllInferrableEffectsToVerify: "; | 
|  | AllInferrableEffectsToVerify.dump(llvm::dbgs()); | 
|  | llvm::dbgs() << "\n";); | 
|  |  | 
|  | // We can use DeclsWithEffectsToVerify as a stack for a | 
|  | // depth-first traversal; there's no need for a second container. But first, | 
|  | // reverse it, so when working from the end, Decls are verified in the order | 
|  | // they are declared. | 
|  | SmallVector<const Decl *> &VerificationQueue = S.DeclsWithEffectsToVerify; | 
|  | std::reverse(VerificationQueue.begin(), VerificationQueue.end()); | 
|  |  | 
|  | while (!VerificationQueue.empty()) { | 
|  | const Decl *D = VerificationQueue.back(); | 
|  | if (FuncAnalysisPtr AP = DeclAnalysis.lookup(D)) { | 
|  | if (auto *Pending = dyn_cast<PendingFunctionAnalysis *>(AP)) { | 
|  | // All children have been traversed; finish analysis. | 
|  | finishPendingAnalysis(D, Pending); | 
|  | } | 
|  | VerificationQueue.pop_back(); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // Not previously visited; begin a new analysis for this Decl. | 
|  | PendingFunctionAnalysis *Pending = verifyDecl(D); | 
|  | if (Pending == nullptr) { | 
|  | // Completed now. | 
|  | VerificationQueue.pop_back(); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // Analysis remains pending because there are direct callees to be | 
|  | // verified first. Push them onto the queue. | 
|  | for (PendingFunctionAnalysis::DirectCall &Call : | 
|  | Pending->unverifiedCalls()) { | 
|  | FuncAnalysisPtr AP = DeclAnalysis.lookup(Call.Callee); | 
|  | if (AP.isNull()) { | 
|  | VerificationQueue.push_back(Call.Callee); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // This indicates recursion (not necessarily direct). For the | 
|  | // purposes of effect analysis, we can just ignore it since | 
|  | // no effects forbid recursion. | 
|  | assert(isa<PendingFunctionAnalysis *>(AP)); | 
|  | Call.Recursed = true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | // Verify a single Decl. Return the pending structure if that was the result, | 
|  | // else null. This method must not recurse. | 
|  | PendingFunctionAnalysis *verifyDecl(const Decl *D) { | 
|  | CallableInfo CInfo(*D); | 
|  | bool isExternC = false; | 
|  |  | 
|  | if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) | 
|  | isExternC = FD->getCanonicalDecl()->isExternCContext(); | 
|  |  | 
|  | // For C++, with non-extern "C" linkage only - if any of the Decl's declared | 
|  | // effects forbid throwing (e.g. nonblocking) then the function should also | 
|  | // be declared noexcept. | 
|  | if (S.getLangOpts().CPlusPlus && !isExternC) { | 
|  | for (FunctionEffect Effect : CInfo.Effects) { | 
|  | if (!(Effect.flags() & FunctionEffect::FE_ExcludeThrow)) | 
|  | continue; | 
|  |  | 
|  | bool IsNoexcept = false; | 
|  | if (auto *FD = D->getAsFunction()) { | 
|  | IsNoexcept = isNoexcept(FD); | 
|  | } else if (auto *BD = dyn_cast<BlockDecl>(D)) { | 
|  | if (auto *TSI = BD->getSignatureAsWritten()) { | 
|  | auto *FPT = TSI->getType()->castAs<FunctionProtoType>(); | 
|  | IsNoexcept = FPT->isNothrow() || BD->hasAttr<NoThrowAttr>(); | 
|  | } | 
|  | } | 
|  | if (!IsNoexcept) | 
|  | S.Diag(D->getBeginLoc(), diag::warn_perf_constraint_implies_noexcept) | 
|  | << GetCallableDeclKind(D, nullptr) << Effect.name(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Build a PendingFunctionAnalysis on the stack. If it turns out to be | 
|  | // complete, we'll have avoided a heap allocation; if it's incomplete, it's | 
|  | // a fairly trivial move to a heap-allocated object. | 
|  | PendingFunctionAnalysis FAnalysis(S, CInfo, AllInferrableEffectsToVerify); | 
|  |  | 
|  | LLVM_DEBUG(llvm::dbgs() | 
|  | << "\nVerifying " << CInfo.getNameForDiagnostic(S) << " "; | 
|  | FAnalysis.dump(S, llvm::dbgs());); | 
|  |  | 
|  | FunctionBodyASTVisitor Visitor(*this, FAnalysis, CInfo); | 
|  |  | 
|  | Visitor.run(); | 
|  | if (FAnalysis.isComplete()) { | 
|  | completeAnalysis(CInfo, std::move(FAnalysis)); | 
|  | return nullptr; | 
|  | } | 
|  | // Move the pending analysis to the heap and save it in the map. | 
|  | PendingFunctionAnalysis *PendingPtr = | 
|  | new PendingFunctionAnalysis(std::move(FAnalysis)); | 
|  | DeclAnalysis[D] = PendingPtr; | 
|  | LLVM_DEBUG(llvm::dbgs() << "inserted pending " << PendingPtr << "\n"; | 
|  | DeclAnalysis.dump(S, llvm::dbgs());); | 
|  | return PendingPtr; | 
|  | } | 
|  |  | 
|  | // Consume PendingFunctionAnalysis, create with it a CompleteFunctionAnalysis, | 
|  | // inserted in the container. | 
|  | void completeAnalysis(const CallableInfo &CInfo, | 
|  | PendingFunctionAnalysis &&Pending) { | 
|  | if (ArrayRef<Violation> Viols = | 
|  | Pending.getSortedViolationsForExplicitEffects(S.getSourceManager()); | 
|  | !Viols.empty()) | 
|  | emitDiagnostics(Viols, CInfo); | 
|  |  | 
|  | CompleteFunctionAnalysis *CompletePtr = new CompleteFunctionAnalysis( | 
|  | S.getASTContext(), std::move(Pending), CInfo.Effects, | 
|  | AllInferrableEffectsToVerify); | 
|  | DeclAnalysis[CInfo.CDecl] = CompletePtr; | 
|  | LLVM_DEBUG(llvm::dbgs() << "inserted complete " << CompletePtr << "\n"; | 
|  | DeclAnalysis.dump(S, llvm::dbgs());); | 
|  | } | 
|  |  | 
|  | // Called after all direct calls requiring inference have been found -- or | 
|  | // not. Repeats calls to FunctionBodyASTVisitor::followCall() but without | 
|  | // the possibility of inference. Deletes Pending. | 
|  | void finishPendingAnalysis(const Decl *D, PendingFunctionAnalysis *Pending) { | 
|  | CallableInfo Caller(*D); | 
|  | LLVM_DEBUG(llvm::dbgs() << "finishPendingAnalysis for " | 
|  | << Caller.getNameForDiagnostic(S) << " : "; | 
|  | Pending->dump(S, llvm::dbgs()); llvm::dbgs() << "\n";); | 
|  | for (const PendingFunctionAnalysis::DirectCall &Call : | 
|  | Pending->unverifiedCalls()) { | 
|  | if (Call.Recursed) | 
|  | continue; | 
|  |  | 
|  | CallableInfo Callee(*Call.Callee); | 
|  | followCall(Caller, *Pending, Callee, Call.CallLoc, | 
|  | /*AssertNoFurtherInference=*/true, Call.VSite); | 
|  | } | 
|  | completeAnalysis(Caller, std::move(*Pending)); | 
|  | delete Pending; | 
|  | } | 
|  |  | 
|  | // Here we have a call to a Decl, either explicitly via a CallExpr or some | 
|  | // other AST construct. PFA pertains to the caller. | 
|  | void followCall(const CallableInfo &Caller, PendingFunctionAnalysis &PFA, | 
|  | const CallableInfo &Callee, SourceLocation CallLoc, | 
|  | bool AssertNoFurtherInference, ViolationSite VSite) { | 
|  | const bool DirectCall = Callee.isCalledDirectly(); | 
|  |  | 
|  | // Initially, the declared effects; inferred effects will be added. | 
|  | FunctionEffectKindSet CalleeEffects = Callee.Effects; | 
|  |  | 
|  | bool IsInferencePossible = DirectCall; | 
|  |  | 
|  | if (DirectCall) | 
|  | if (CompleteFunctionAnalysis *CFA = | 
|  | DeclAnalysis.completedAnalysisForDecl(Callee.CDecl)) { | 
|  | // Combine declared effects with those which may have been inferred. | 
|  | CalleeEffects.insert(CFA->VerifiedEffects); | 
|  | IsInferencePossible = false; // We've already traversed it. | 
|  | } | 
|  |  | 
|  | if (AssertNoFurtherInference) { | 
|  | assert(!IsInferencePossible); | 
|  | } | 
|  |  | 
|  | if (!Callee.isVerifiable()) | 
|  | IsInferencePossible = false; | 
|  |  | 
|  | LLVM_DEBUG(llvm::dbgs() | 
|  | << "followCall from " << Caller.getNameForDiagnostic(S) | 
|  | << " to " << Callee.getNameForDiagnostic(S) | 
|  | << "; verifiable: " << Callee.isVerifiable() << "; callee "; | 
|  | CalleeEffects.dump(llvm::dbgs()); llvm::dbgs() << "\n"; | 
|  | llvm::dbgs() << "  callee " << Callee.CDecl << " canonical " | 
|  | << Callee.CDecl->getCanonicalDecl() << "\n";); | 
|  |  | 
|  | auto Check1Effect = [&](FunctionEffect Effect, bool Inferring) { | 
|  | if (!Effect.shouldDiagnoseFunctionCall(DirectCall, CalleeEffects)) | 
|  | return; | 
|  |  | 
|  | // If inference is not allowed, or the target is indirect (virtual | 
|  | // method/function ptr?), generate a Violation now. | 
|  | if (!IsInferencePossible || | 
|  | !(Effect.flags() & FunctionEffect::FE_InferrableOnCallees)) { | 
|  | if (Callee.FuncType == SpecialFuncType::None) | 
|  | PFA.checkAddViolation(Inferring, | 
|  | {Effect, ViolationID::CallsDeclWithoutEffect, | 
|  | VSite, CallLoc, Callee.CDecl}); | 
|  | else | 
|  | PFA.checkAddViolation( | 
|  | Inferring, | 
|  | {Effect, ViolationID::AllocatesMemory, VSite, CallLoc}); | 
|  | } else { | 
|  | // Inference is allowed and necessary; defer it. | 
|  | PFA.addUnverifiedDirectCall(Callee.CDecl, CallLoc, VSite); | 
|  | } | 
|  | }; | 
|  |  | 
|  | for (FunctionEffect Effect : PFA.DeclaredVerifiableEffects) | 
|  | Check1Effect(Effect, false); | 
|  |  | 
|  | for (FunctionEffect Effect : PFA.EffectsToInfer) | 
|  | Check1Effect(Effect, true); | 
|  | } | 
|  |  | 
|  | // Describe a callable Decl for a diagnostic. | 
|  | // (Not an enum class because the value is always converted to an integer for | 
|  | // use in a diagnostic.) | 
|  | enum CallableDeclKind { | 
|  | CDK_Function, | 
|  | CDK_Constructor, | 
|  | CDK_Destructor, | 
|  | CDK_Lambda, | 
|  | CDK_Block, | 
|  | CDK_MemberInitializer, | 
|  | }; | 
|  |  | 
|  | // Describe a call site or target using an enum mapping to a %select{} | 
|  | // in a diagnostic, e.g. warn_func_effect_violation, | 
|  | // warn_perf_constraint_implies_noexcept, and others. | 
|  | static CallableDeclKind GetCallableDeclKind(const Decl *D, | 
|  | const Violation *V) { | 
|  | if (V != nullptr && | 
|  | V->Site.kind() == ViolationSite::Kind::MemberInitializer) | 
|  | return CDK_MemberInitializer; | 
|  | if (isa<BlockDecl>(D)) | 
|  | return CDK_Block; | 
|  | if (auto *Method = dyn_cast<CXXMethodDecl>(D)) { | 
|  | if (isa<CXXConstructorDecl>(D)) | 
|  | return CDK_Constructor; | 
|  | if (isa<CXXDestructorDecl>(D)) | 
|  | return CDK_Destructor; | 
|  | const CXXRecordDecl *Rec = Method->getParent(); | 
|  | if (Rec->isLambda()) | 
|  | return CDK_Lambda; | 
|  | } | 
|  | return CDK_Function; | 
|  | }; | 
|  |  | 
|  | // Should only be called when function's analysis is determined to be | 
|  | // complete. | 
|  | void emitDiagnostics(ArrayRef<Violation> Viols, const CallableInfo &CInfo) { | 
|  | if (Viols.empty()) | 
|  | return; | 
|  |  | 
|  | auto MaybeAddTemplateNote = [&](const Decl *D) { | 
|  | if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { | 
|  | while (FD != nullptr && FD->isTemplateInstantiation() && | 
|  | FD->getPointOfInstantiation().isValid()) { | 
|  | S.Diag(FD->getPointOfInstantiation(), | 
|  | diag::note_func_effect_from_template); | 
|  | FD = FD->getTemplateInstantiationPattern(); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | // For note_func_effect_call_indirect. | 
|  | enum { Indirect_VirtualMethod, Indirect_FunctionPtr }; | 
|  |  | 
|  | auto MaybeAddSiteContext = [&](const Decl *D, const Violation &V) { | 
|  | // If a violation site is a member initializer, add a note pointing to | 
|  | // the constructor which invoked it. | 
|  | if (V.Site.kind() == ViolationSite::Kind::MemberInitializer) { | 
|  | unsigned ImplicitCtor = 0; | 
|  | if (auto *Ctor = dyn_cast<CXXConstructorDecl>(D); | 
|  | Ctor && Ctor->isImplicit()) | 
|  | ImplicitCtor = 1; | 
|  | S.Diag(D->getLocation(), diag::note_func_effect_in_constructor) | 
|  | << ImplicitCtor; | 
|  | } | 
|  |  | 
|  | // If a violation site is a default argument expression, add a note | 
|  | // pointing to the call site using the default argument. | 
|  | else if (V.Site.kind() == ViolationSite::Kind::DefaultArgExpr) | 
|  | S.Diag(V.Site.defaultArgExpr()->getUsedLocation(), | 
|  | diag::note_in_evaluating_default_argument); | 
|  | }; | 
|  |  | 
|  | // Top-level violations are warnings. | 
|  | for (const Violation &Viol1 : Viols) { | 
|  | StringRef effectName = Viol1.Effect.name(); | 
|  | switch (Viol1.ID) { | 
|  | case ViolationID::None: | 
|  | case ViolationID::DeclDisallowsInference: // Shouldn't happen | 
|  | // here. | 
|  | llvm_unreachable("Unexpected violation kind"); | 
|  | break; | 
|  | case ViolationID::AllocatesMemory: | 
|  | case ViolationID::ThrowsOrCatchesExceptions: | 
|  | case ViolationID::HasStaticLocalVariable: | 
|  | case ViolationID::AccessesThreadLocalVariable: | 
|  | case ViolationID::AccessesObjCMethodOrProperty: | 
|  | S.Diag(Viol1.Loc, diag::warn_func_effect_violation) | 
|  | << GetCallableDeclKind(CInfo.CDecl, &Viol1) << effectName | 
|  | << Viol1.diagnosticSelectIndex(); | 
|  | MaybeAddSiteContext(CInfo.CDecl, Viol1); | 
|  | MaybeAddTemplateNote(CInfo.CDecl); | 
|  | break; | 
|  | case ViolationID::CallsExprWithoutEffect: | 
|  | S.Diag(Viol1.Loc, diag::warn_func_effect_calls_expr_without_effect) | 
|  | << GetCallableDeclKind(CInfo.CDecl, &Viol1) << effectName; | 
|  | MaybeAddSiteContext(CInfo.CDecl, Viol1); | 
|  | MaybeAddTemplateNote(CInfo.CDecl); | 
|  | break; | 
|  |  | 
|  | case ViolationID::CallsDeclWithoutEffect: { | 
|  | CallableInfo CalleeInfo(*Viol1.Callee); | 
|  | std::string CalleeName = CalleeInfo.getNameForDiagnostic(S); | 
|  |  | 
|  | S.Diag(Viol1.Loc, diag::warn_func_effect_calls_func_without_effect) | 
|  | << GetCallableDeclKind(CInfo.CDecl, &Viol1) << effectName | 
|  | << GetCallableDeclKind(CalleeInfo.CDecl, nullptr) << CalleeName; | 
|  | MaybeAddSiteContext(CInfo.CDecl, Viol1); | 
|  | MaybeAddTemplateNote(CInfo.CDecl); | 
|  |  | 
|  | // Emit notes explaining the transitive chain of inferences: Why isn't | 
|  | // the callee safe? | 
|  | for (const Decl *Callee = Viol1.Callee; Callee != nullptr;) { | 
|  | std::optional<CallableInfo> MaybeNextCallee; | 
|  | CompleteFunctionAnalysis *Completed = | 
|  | DeclAnalysis.completedAnalysisForDecl(CalleeInfo.CDecl); | 
|  | if (Completed == nullptr) { | 
|  | // No result - could be | 
|  | // - non-inline and extern | 
|  | // - indirect (virtual or through function pointer) | 
|  | // - effect has been explicitly disclaimed (e.g. "blocking") | 
|  |  | 
|  | CallableType CType = CalleeInfo.type(); | 
|  | if (CType == CallableType::Virtual) | 
|  | S.Diag(Callee->getLocation(), | 
|  | diag::note_func_effect_call_indirect) | 
|  | << Indirect_VirtualMethod << effectName; | 
|  | else if (CType == CallableType::Unknown) | 
|  | S.Diag(Callee->getLocation(), | 
|  | diag::note_func_effect_call_indirect) | 
|  | << Indirect_FunctionPtr << effectName; | 
|  | else if (CalleeInfo.Effects.contains(Viol1.Effect.oppositeKind())) | 
|  | S.Diag(Callee->getLocation(), | 
|  | diag::note_func_effect_call_disallows_inference) | 
|  | << GetCallableDeclKind(CInfo.CDecl, nullptr) << effectName | 
|  | << FunctionEffect(Viol1.Effect.oppositeKind()).name(); | 
|  | else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(Callee); | 
|  | FD == nullptr || FD->getBuiltinID() == 0) { | 
|  | // A builtin callee generally doesn't have a useful source | 
|  | // location at which to insert a note. | 
|  | S.Diag(Callee->getLocation(), diag::note_func_effect_call_extern) | 
|  | << effectName; | 
|  | } | 
|  | break; | 
|  | } | 
|  | const Violation *PtrViol2 = | 
|  | Completed->firstViolationForEffect(Viol1.Effect); | 
|  | if (PtrViol2 == nullptr) | 
|  | break; | 
|  |  | 
|  | const Violation &Viol2 = *PtrViol2; | 
|  | switch (Viol2.ID) { | 
|  | case ViolationID::None: | 
|  | llvm_unreachable("Unexpected violation kind"); | 
|  | break; | 
|  | case ViolationID::DeclDisallowsInference: | 
|  | S.Diag(Viol2.Loc, diag::note_func_effect_call_disallows_inference) | 
|  | << GetCallableDeclKind(CalleeInfo.CDecl, nullptr) << effectName | 
|  | << Viol2.CalleeEffectPreventingInference->name(); | 
|  | break; | 
|  | case ViolationID::CallsExprWithoutEffect: | 
|  | S.Diag(Viol2.Loc, diag::note_func_effect_call_indirect) | 
|  | << Indirect_FunctionPtr << effectName; | 
|  | break; | 
|  | case ViolationID::AllocatesMemory: | 
|  | case ViolationID::ThrowsOrCatchesExceptions: | 
|  | case ViolationID::HasStaticLocalVariable: | 
|  | case ViolationID::AccessesThreadLocalVariable: | 
|  | case ViolationID::AccessesObjCMethodOrProperty: | 
|  | S.Diag(Viol2.Loc, diag::note_func_effect_violation) | 
|  | << GetCallableDeclKind(CalleeInfo.CDecl, &Viol2) << effectName | 
|  | << Viol2.diagnosticSelectIndex(); | 
|  | MaybeAddSiteContext(CalleeInfo.CDecl, Viol2); | 
|  | break; | 
|  | case ViolationID::CallsDeclWithoutEffect: | 
|  | MaybeNextCallee.emplace(*Viol2.Callee); | 
|  | S.Diag(Viol2.Loc, diag::note_func_effect_calls_func_without_effect) | 
|  | << GetCallableDeclKind(CalleeInfo.CDecl, &Viol2) << effectName | 
|  | << GetCallableDeclKind(Viol2.Callee, nullptr) | 
|  | << MaybeNextCallee->getNameForDiagnostic(S); | 
|  | break; | 
|  | } | 
|  | MaybeAddTemplateNote(Callee); | 
|  | Callee = Viol2.Callee; | 
|  | if (MaybeNextCallee) { | 
|  | CalleeInfo = *MaybeNextCallee; | 
|  | CalleeName = CalleeInfo.getNameForDiagnostic(S); | 
|  | } | 
|  | } | 
|  | } break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // ---------- | 
|  | // This AST visitor is used to traverse the body of a function during effect | 
|  | // verification. This happens in 2 situations: | 
|  | //  [1] The function has declared effects which need to be validated. | 
|  | //  [2] The function has not explicitly declared an effect in question, and is | 
|  | //      being checked for implicit conformance. | 
|  | // | 
|  | // Violations are always routed to a PendingFunctionAnalysis. | 
|  | struct FunctionBodyASTVisitor : DynamicRecursiveASTVisitor { | 
|  | Analyzer &Outer; | 
|  | PendingFunctionAnalysis &CurrentFunction; | 
|  | CallableInfo &CurrentCaller; | 
|  | ViolationSite VSite; | 
|  | const Expr *TrailingRequiresClause = nullptr; | 
|  | const Expr *NoexceptExpr = nullptr; | 
|  |  | 
|  | FunctionBodyASTVisitor(Analyzer &Outer, | 
|  | PendingFunctionAnalysis &CurrentFunction, | 
|  | CallableInfo &CurrentCaller) | 
|  | : Outer(Outer), CurrentFunction(CurrentFunction), | 
|  | CurrentCaller(CurrentCaller) { | 
|  | ShouldVisitImplicitCode = true; | 
|  | ShouldWalkTypesOfTypeLocs = false; | 
|  | } | 
|  |  | 
|  | // -- Entry point -- | 
|  | void run() { | 
|  | // The target function may have implicit code paths beyond the | 
|  | // body: member and base destructors. Visit these first. | 
|  | if (auto *Dtor = dyn_cast<CXXDestructorDecl>(CurrentCaller.CDecl)) | 
|  | followDestructor(dyn_cast<CXXRecordDecl>(Dtor->getParent()), Dtor); | 
|  |  | 
|  | if (auto *FD = dyn_cast<FunctionDecl>(CurrentCaller.CDecl)) { | 
|  | TrailingRequiresClause = FD->getTrailingRequiresClause().ConstraintExpr; | 
|  |  | 
|  | // Note that FD->getType->getAs<FunctionProtoType>() can yield a | 
|  | // noexcept Expr which has been boiled down to a constant expression. | 
|  | // Going through the TypeSourceInfo obtains the actual expression which | 
|  | // will be traversed as part of the function -- unless we capture it | 
|  | // here and have TraverseStmt skip it. | 
|  | if (TypeSourceInfo *TSI = FD->getTypeSourceInfo()) { | 
|  | if (FunctionProtoTypeLoc TL = | 
|  | TSI->getTypeLoc().getAs<FunctionProtoTypeLoc>()) | 
|  | if (const FunctionProtoType *FPT = TL.getTypePtr()) | 
|  | NoexceptExpr = FPT->getNoexceptExpr(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Do an AST traversal of the function/block body | 
|  | TraverseDecl(const_cast<Decl *>(CurrentCaller.CDecl)); | 
|  | } | 
|  |  | 
|  | // -- Methods implementing common logic -- | 
|  |  | 
|  | // Handle a language construct forbidden by some effects. Only effects whose | 
|  | // flags include the specified flag receive a violation. \p Flag describes | 
|  | // the construct. | 
|  | void diagnoseLanguageConstruct(FunctionEffect::FlagBit Flag, | 
|  | ViolationID VID, SourceLocation Loc, | 
|  | const Decl *Callee = nullptr) { | 
|  | // If there are any declared verifiable effects which forbid the construct | 
|  | // represented by the flag, store just one violation. | 
|  | for (FunctionEffect Effect : CurrentFunction.DeclaredVerifiableEffects) { | 
|  | if (Effect.flags() & Flag) { | 
|  | addViolation(/*inferring=*/false, Effect, VID, Loc, Callee); | 
|  | break; | 
|  | } | 
|  | } | 
|  | // For each inferred effect which forbids the construct, store a | 
|  | // violation, if we don't already have a violation for that effect. | 
|  | for (FunctionEffect Effect : CurrentFunction.EffectsToInfer) | 
|  | if (Effect.flags() & Flag) | 
|  | addViolation(/*inferring=*/true, Effect, VID, Loc, Callee); | 
|  | } | 
|  |  | 
|  | void addViolation(bool Inferring, FunctionEffect Effect, ViolationID VID, | 
|  | SourceLocation Loc, const Decl *Callee = nullptr) { | 
|  | CurrentFunction.checkAddViolation( | 
|  | Inferring, Violation(Effect, VID, VSite, Loc, Callee)); | 
|  | } | 
|  |  | 
|  | // Here we have a call to a Decl, either explicitly via a CallExpr or some | 
|  | // other AST construct. CallableInfo pertains to the callee. | 
|  | void followCall(CallableInfo &CI, SourceLocation CallLoc) { | 
|  | // Check for a call to a builtin function, whose effects are | 
|  | // handled specially. | 
|  | if (const auto *FD = dyn_cast<FunctionDecl>(CI.CDecl)) { | 
|  | if (unsigned BuiltinID = FD->getBuiltinID()) { | 
|  | CI.Effects = getBuiltinFunctionEffects(BuiltinID); | 
|  | if (CI.Effects.empty()) { | 
|  | // A builtin with no known effects is assumed safe. | 
|  | return; | 
|  | } | 
|  | // A builtin WITH effects doesn't get any special treatment for | 
|  | // being noreturn/noexcept, e.g. longjmp(), so we skip the check | 
|  | // below. | 
|  | } else { | 
|  | // If the callee is both `noreturn` and `noexcept`, it presumably | 
|  | // terminates. Ignore it for the purposes of effect analysis. | 
|  | // If not C++, `noreturn` alone is sufficient. | 
|  | if (FD->isNoReturn() && | 
|  | (!Outer.S.getLangOpts().CPlusPlus || isNoexcept(FD))) | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | Outer.followCall(CurrentCaller, CurrentFunction, CI, CallLoc, | 
|  | /*AssertNoFurtherInference=*/false, VSite); | 
|  | } | 
|  |  | 
|  | void checkIndirectCall(CallExpr *Call, QualType CalleeType) { | 
|  | FunctionEffectKindSet CalleeEffects; | 
|  | if (FunctionEffectsRef Effects = FunctionEffectsRef::get(CalleeType); | 
|  | !Effects.empty()) | 
|  | CalleeEffects.insert(Effects); | 
|  |  | 
|  | auto Check1Effect = [&](FunctionEffect Effect, bool Inferring) { | 
|  | if (Effect.shouldDiagnoseFunctionCall( | 
|  | /*direct=*/false, CalleeEffects)) | 
|  | addViolation(Inferring, Effect, ViolationID::CallsExprWithoutEffect, | 
|  | Call->getBeginLoc()); | 
|  | }; | 
|  |  | 
|  | for (FunctionEffect Effect : CurrentFunction.DeclaredVerifiableEffects) | 
|  | Check1Effect(Effect, false); | 
|  |  | 
|  | for (FunctionEffect Effect : CurrentFunction.EffectsToInfer) | 
|  | Check1Effect(Effect, true); | 
|  | } | 
|  |  | 
|  | // This destructor's body should be followed by the caller, but here we | 
|  | // follow the field and base destructors. | 
|  | void followDestructor(const CXXRecordDecl *Rec, | 
|  | const CXXDestructorDecl *Dtor) { | 
|  | SourceLocation DtorLoc = Dtor->getLocation(); | 
|  | for (const FieldDecl *Field : Rec->fields()) | 
|  | followTypeDtor(Field->getType(), DtorLoc); | 
|  |  | 
|  | if (const auto *Class = dyn_cast<CXXRecordDecl>(Rec)) | 
|  | for (const CXXBaseSpecifier &Base : Class->bases()) | 
|  | followTypeDtor(Base.getType(), DtorLoc); | 
|  | } | 
|  |  | 
|  | void followTypeDtor(QualType QT, SourceLocation CallSite) { | 
|  | const Type *Ty = QT.getTypePtr(); | 
|  | while (Ty->isArrayType()) { | 
|  | const ArrayType *Arr = Ty->getAsArrayTypeUnsafe(); | 
|  | QT = Arr->getElementType(); | 
|  | Ty = QT.getTypePtr(); | 
|  | } | 
|  |  | 
|  | if (Ty->isRecordType()) { | 
|  | if (const CXXRecordDecl *Class = Ty->getAsCXXRecordDecl()) { | 
|  | if (CXXDestructorDecl *Dtor = Class->getDestructor(); | 
|  | Dtor && !Dtor->isDeleted()) { | 
|  | CallableInfo CI(*Dtor); | 
|  | followCall(CI, CallSite); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // -- Methods for use of RecursiveASTVisitor -- | 
|  |  | 
|  | bool VisitCXXThrowExpr(CXXThrowExpr *Throw) override { | 
|  | diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeThrow, | 
|  | ViolationID::ThrowsOrCatchesExceptions, | 
|  | Throw->getThrowLoc()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VisitCXXCatchStmt(CXXCatchStmt *Catch) override { | 
|  | diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeCatch, | 
|  | ViolationID::ThrowsOrCatchesExceptions, | 
|  | Catch->getCatchLoc()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VisitObjCAtThrowStmt(ObjCAtThrowStmt *Throw) override { | 
|  | diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeThrow, | 
|  | ViolationID::ThrowsOrCatchesExceptions, | 
|  | Throw->getThrowLoc()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VisitObjCAtCatchStmt(ObjCAtCatchStmt *Catch) override { | 
|  | diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeCatch, | 
|  | ViolationID::ThrowsOrCatchesExceptions, | 
|  | Catch->getAtCatchLoc()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VisitObjCAtFinallyStmt(ObjCAtFinallyStmt *Finally) override { | 
|  | diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeCatch, | 
|  | ViolationID::ThrowsOrCatchesExceptions, | 
|  | Finally->getAtFinallyLoc()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VisitObjCMessageExpr(ObjCMessageExpr *Msg) override { | 
|  | diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeObjCMessageSend, | 
|  | ViolationID::AccessesObjCMethodOrProperty, | 
|  | Msg->getBeginLoc()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *ARP) override { | 
|  | // Under the hood, @autorelease (potentially?) allocates memory and | 
|  | // invokes ObjC methods. We don't currently have memory allocation as | 
|  | // a "language construct" but we do have ObjC messaging, so diagnose that. | 
|  | diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeObjCMessageSend, | 
|  | ViolationID::AccessesObjCMethodOrProperty, | 
|  | ARP->getBeginLoc()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *Sync) override { | 
|  | // Under the hood, this calls objc_sync_enter and objc_sync_exit, wrapped | 
|  | // in a @try/@finally block. Diagnose this generically as "ObjC | 
|  | // messaging". | 
|  | diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeObjCMessageSend, | 
|  | ViolationID::AccessesObjCMethodOrProperty, | 
|  | Sync->getBeginLoc()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VisitSEHExceptStmt(SEHExceptStmt *Exc) override { | 
|  | diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeCatch, | 
|  | ViolationID::ThrowsOrCatchesExceptions, | 
|  | Exc->getExceptLoc()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VisitCallExpr(CallExpr *Call) override { | 
|  | LLVM_DEBUG(llvm::dbgs() | 
|  | << "VisitCallExpr : " | 
|  | << Call->getBeginLoc().printToString(Outer.S.SourceMgr) | 
|  | << "\n";); | 
|  |  | 
|  | Expr *CalleeExpr = Call->getCallee(); | 
|  | if (const Decl *Callee = CalleeExpr->getReferencedDeclOfCallee()) { | 
|  | CallableInfo CI(*Callee); | 
|  | followCall(CI, Call->getBeginLoc()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (isa<CXXPseudoDestructorExpr>(CalleeExpr)) { | 
|  | // Just destroying a scalar, fine. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // No Decl, just an Expr. Just check based on its type. | 
|  | checkIndirectCall(Call, CalleeExpr->getType()); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VisitVarDecl(VarDecl *Var) override { | 
|  | LLVM_DEBUG(llvm::dbgs() | 
|  | << "VisitVarDecl : " | 
|  | << Var->getBeginLoc().printToString(Outer.S.SourceMgr) | 
|  | << "\n";); | 
|  |  | 
|  | if (Var->isStaticLocal()) | 
|  | diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeStaticLocalVars, | 
|  | ViolationID::HasStaticLocalVariable, | 
|  | Var->getLocation()); | 
|  |  | 
|  | const QualType::DestructionKind DK = | 
|  | Var->needsDestruction(Outer.S.getASTContext()); | 
|  | if (DK == QualType::DK_cxx_destructor) | 
|  | followTypeDtor(Var->getType(), Var->getLocation()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VisitCXXNewExpr(CXXNewExpr *New) override { | 
|  | // RecursiveASTVisitor does not visit the implicit call to operator new. | 
|  | if (FunctionDecl *FD = New->getOperatorNew()) { | 
|  | CallableInfo CI(*FD, SpecialFuncType::OperatorNew); | 
|  | followCall(CI, New->getBeginLoc()); | 
|  | } | 
|  |  | 
|  | // It's a bit excessive to check operator delete here, since it's | 
|  | // just a fallback for operator new followed by a failed constructor. | 
|  | // We could check it via New->getOperatorDelete(). | 
|  |  | 
|  | // It DOES however visit the called constructor | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VisitCXXDeleteExpr(CXXDeleteExpr *Delete) override { | 
|  | // RecursiveASTVisitor does not visit the implicit call to operator | 
|  | // delete. | 
|  | if (FunctionDecl *FD = Delete->getOperatorDelete()) { | 
|  | CallableInfo CI(*FD, SpecialFuncType::OperatorDelete); | 
|  | followCall(CI, Delete->getBeginLoc()); | 
|  | } | 
|  |  | 
|  | // It DOES however visit the called destructor | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VisitCXXConstructExpr(CXXConstructExpr *Construct) override { | 
|  | LLVM_DEBUG(llvm::dbgs() << "VisitCXXConstructExpr : " | 
|  | << Construct->getBeginLoc().printToString( | 
|  | Outer.S.SourceMgr) | 
|  | << "\n";); | 
|  |  | 
|  | // RecursiveASTVisitor does not visit the implicit call to the | 
|  | // constructor. | 
|  | const CXXConstructorDecl *Ctor = Construct->getConstructor(); | 
|  | CallableInfo CI(*Ctor); | 
|  | followCall(CI, Construct->getLocation()); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool TraverseStmt(Stmt *Statement) override { | 
|  | // If this statement is a `requires` clause from the top-level function | 
|  | // being traversed, ignore it, since it's not generating runtime code. | 
|  | // We skip the traversal of lambdas (beyond their captures, see | 
|  | // TraverseLambdaExpr below), so just caching this from our constructor | 
|  | // should suffice. | 
|  | if (Statement != TrailingRequiresClause && Statement != NoexceptExpr) | 
|  | return DynamicRecursiveASTVisitor::TraverseStmt(Statement); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool TraverseConstructorInitializer(CXXCtorInitializer *Init) override { | 
|  | ViolationSite PrevVS = VSite; | 
|  | if (Init->isAnyMemberInitializer()) | 
|  | VSite.setKind(ViolationSite::Kind::MemberInitializer); | 
|  | bool Result = | 
|  | DynamicRecursiveASTVisitor::TraverseConstructorInitializer(Init); | 
|  | VSite = PrevVS; | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | bool TraverseCXXDefaultArgExpr(CXXDefaultArgExpr *E) override { | 
|  | LLVM_DEBUG(llvm::dbgs() | 
|  | << "TraverseCXXDefaultArgExpr : " | 
|  | << E->getUsedLocation().printToString(Outer.S.SourceMgr) | 
|  | << "\n";); | 
|  |  | 
|  | ViolationSite PrevVS = VSite; | 
|  | if (VSite.kind() == ViolationSite::Kind::Default) | 
|  | VSite = ViolationSite{E}; | 
|  |  | 
|  | bool Result = DynamicRecursiveASTVisitor::TraverseCXXDefaultArgExpr(E); | 
|  | VSite = PrevVS; | 
|  | return Result; | 
|  | } | 
|  |  | 
|  | bool TraverseLambdaExpr(LambdaExpr *Lambda) override { | 
|  | // We override this so as to be able to skip traversal of the lambda's | 
|  | // body. We have to explicitly traverse the captures. Why not return | 
|  | // false from shouldVisitLambdaBody()? Because we need to visit a lambda's | 
|  | // body when we are verifying the lambda itself; we only want to skip it | 
|  | // in the context of the outer function. | 
|  | for (unsigned I = 0, N = Lambda->capture_size(); I < N; ++I) | 
|  | TraverseLambdaCapture(Lambda, Lambda->capture_begin() + I, | 
|  | Lambda->capture_init_begin()[I]); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool TraverseBlockExpr(BlockExpr * /*unused*/) override { | 
|  | // As with lambdas, don't traverse the block's body. | 
|  | // TODO: are the capture expressions (ctor call?) safe? | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VisitDeclRefExpr(DeclRefExpr *E) override { | 
|  | const ValueDecl *Val = E->getDecl(); | 
|  | if (const auto *Var = dyn_cast<VarDecl>(Val)) { | 
|  | if (Var->getTLSKind() != VarDecl::TLS_None) { | 
|  | // At least on macOS, thread-local variables are initialized on | 
|  | // first access, including a heap allocation. | 
|  | diagnoseLanguageConstruct(FunctionEffect::FE_ExcludeThreadLocalVars, | 
|  | ViolationID::AccessesThreadLocalVariable, | 
|  | E->getLocation()); | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) override { | 
|  | return TraverseStmt(Node->getResultExpr()); | 
|  | } | 
|  | bool | 
|  | TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) override { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node) override { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node) override { return true; } | 
|  |  | 
|  | bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) override { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) override { return true; } | 
|  |  | 
|  | // Skip concept requirements since they don't generate code. | 
|  | bool TraverseConceptRequirement(concepts::Requirement *R) override { | 
|  | return true; | 
|  | } | 
|  | }; | 
|  | }; | 
|  |  | 
|  | Analyzer::AnalysisMap::~AnalysisMap() { | 
|  | for (const auto &Item : *this) { | 
|  | FuncAnalysisPtr AP = Item.second; | 
|  | if (auto *PFA = dyn_cast<PendingFunctionAnalysis *>(AP)) | 
|  | delete PFA; | 
|  | else | 
|  | delete cast<CompleteFunctionAnalysis *>(AP); | 
|  | } | 
|  | } | 
|  |  | 
|  | } // anonymous namespace | 
|  |  | 
|  | namespace clang { | 
|  |  | 
|  | bool Sema::diagnoseConflictingFunctionEffect( | 
|  | const FunctionEffectsRef &FX, const FunctionEffectWithCondition &NewEC, | 
|  | SourceLocation NewAttrLoc) { | 
|  | // If the new effect has a condition, we can't detect conflicts until the | 
|  | // condition is resolved. | 
|  | if (NewEC.Cond.getCondition() != nullptr) | 
|  | return false; | 
|  |  | 
|  | // Diagnose the new attribute as incompatible with a previous one. | 
|  | auto Incompatible = [&](const FunctionEffectWithCondition &PrevEC) { | 
|  | Diag(NewAttrLoc, diag::err_attributes_are_not_compatible) | 
|  | << ("'" + NewEC.description() + "'") | 
|  | << ("'" + PrevEC.description() + "'") << false; | 
|  | // We don't necessarily have the location of the previous attribute, | 
|  | // so no note. | 
|  | return true; | 
|  | }; | 
|  |  | 
|  | // Compare against previous attributes. | 
|  | FunctionEffect::Kind NewKind = NewEC.Effect.kind(); | 
|  |  | 
|  | for (const FunctionEffectWithCondition &PrevEC : FX) { | 
|  | // Again, can't check yet when the effect is conditional. | 
|  | if (PrevEC.Cond.getCondition() != nullptr) | 
|  | continue; | 
|  |  | 
|  | FunctionEffect::Kind PrevKind = PrevEC.Effect.kind(); | 
|  | // Note that we allow PrevKind == NewKind; it's redundant and ignored. | 
|  |  | 
|  | if (PrevEC.Effect.oppositeKind() == NewKind) | 
|  | return Incompatible(PrevEC); | 
|  |  | 
|  | // A new allocating is incompatible with a previous nonblocking. | 
|  | if (PrevKind == FunctionEffect::Kind::NonBlocking && | 
|  | NewKind == FunctionEffect::Kind::Allocating) | 
|  | return Incompatible(PrevEC); | 
|  |  | 
|  | // A new nonblocking is incompatible with a previous allocating. | 
|  | if (PrevKind == FunctionEffect::Kind::Allocating && | 
|  | NewKind == FunctionEffect::Kind::NonBlocking) | 
|  | return Incompatible(PrevEC); | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void Sema::diagnoseFunctionEffectMergeConflicts( | 
|  | const FunctionEffectSet::Conflicts &Errs, SourceLocation NewLoc, | 
|  | SourceLocation OldLoc) { | 
|  | for (const FunctionEffectSet::Conflict &Conflict : Errs) { | 
|  | Diag(NewLoc, diag::warn_conflicting_func_effects) | 
|  | << Conflict.Kept.description() << Conflict.Rejected.description(); | 
|  | Diag(OldLoc, diag::note_previous_declaration); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Decl should be a FunctionDecl or BlockDecl. | 
|  | void Sema::maybeAddDeclWithEffects(const Decl *D, | 
|  | const FunctionEffectsRef &FX) { | 
|  | if (!D->hasBody()) { | 
|  | if (const auto *FD = D->getAsFunction(); FD && !FD->willHaveBody()) | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (Diags.getIgnoreAllWarnings() || | 
|  | (Diags.getSuppressSystemWarnings() && | 
|  | SourceMgr.isInSystemHeader(D->getLocation()))) | 
|  | return; | 
|  |  | 
|  | if (hasUncompilableErrorOccurred()) | 
|  | return; | 
|  |  | 
|  | // For code in dependent contexts, we'll do this at instantiation time. | 
|  | // Without this check, we would analyze the function based on placeholder | 
|  | // template parameters, and potentially generate spurious diagnostics. | 
|  | if (cast<DeclContext>(D)->isDependentContext()) | 
|  | return; | 
|  |  | 
|  | addDeclWithEffects(D, FX); | 
|  | } | 
|  |  | 
|  | void Sema::addDeclWithEffects(const Decl *D, const FunctionEffectsRef &FX) { | 
|  | // To avoid the possibility of conflict, don't add effects which are | 
|  | // not FE_InferrableOnCallees and therefore not verified; this removes | 
|  | // blocking/allocating but keeps nonblocking/nonallocating. | 
|  | // Also, ignore any conditions when building the list of effects. | 
|  | bool AnyVerifiable = false; | 
|  | for (const FunctionEffectWithCondition &EC : FX) | 
|  | if (EC.Effect.flags() & FunctionEffect::FE_InferrableOnCallees) { | 
|  | AllEffectsToVerify.insert(EC.Effect); | 
|  | AnyVerifiable = true; | 
|  | } | 
|  |  | 
|  | // Record the declaration for later analysis. | 
|  | if (AnyVerifiable) | 
|  | DeclsWithEffectsToVerify.push_back(D); | 
|  | } | 
|  |  | 
|  | void Sema::performFunctionEffectAnalysis(TranslationUnitDecl *TU) { | 
|  | if (hasUncompilableErrorOccurred() || Diags.getIgnoreAllWarnings()) | 
|  | return; | 
|  | if (TU == nullptr) | 
|  | return; | 
|  | Analyzer{*this}.run(*TU); | 
|  | } | 
|  |  | 
|  | Sema::FunctionEffectDiffVector::FunctionEffectDiffVector( | 
|  | const FunctionEffectsRef &Old, const FunctionEffectsRef &New) { | 
|  |  | 
|  | FunctionEffectsRef::iterator POld = Old.begin(); | 
|  | FunctionEffectsRef::iterator OldEnd = Old.end(); | 
|  | FunctionEffectsRef::iterator PNew = New.begin(); | 
|  | FunctionEffectsRef::iterator NewEnd = New.end(); | 
|  |  | 
|  | while (true) { | 
|  | int cmp = 0; | 
|  | if (POld == OldEnd) { | 
|  | if (PNew == NewEnd) | 
|  | break; | 
|  | cmp = 1; | 
|  | } else if (PNew == NewEnd) | 
|  | cmp = -1; | 
|  | else { | 
|  | FunctionEffectWithCondition Old = *POld; | 
|  | FunctionEffectWithCondition New = *PNew; | 
|  | if (Old.Effect.kind() < New.Effect.kind()) | 
|  | cmp = -1; | 
|  | else if (New.Effect.kind() < Old.Effect.kind()) | 
|  | cmp = 1; | 
|  | else { | 
|  | cmp = 0; | 
|  | if (Old.Cond.getCondition() != New.Cond.getCondition()) { | 
|  | // FIXME: Cases where the expressions are equivalent but | 
|  | // don't have the same identity. | 
|  | push_back(FunctionEffectDiff{ | 
|  | Old.Effect.kind(), FunctionEffectDiff::Kind::ConditionMismatch, | 
|  | Old, New}); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (cmp < 0) { | 
|  | // removal | 
|  | FunctionEffectWithCondition Old = *POld; | 
|  | push_back(FunctionEffectDiff{Old.Effect.kind(), | 
|  | FunctionEffectDiff::Kind::Removed, Old, | 
|  | std::nullopt}); | 
|  | ++POld; | 
|  | } else if (cmp > 0) { | 
|  | // addition | 
|  | FunctionEffectWithCondition New = *PNew; | 
|  | push_back(FunctionEffectDiff{New.Effect.kind(), | 
|  | FunctionEffectDiff::Kind::Added, | 
|  | std::nullopt, New}); | 
|  | ++PNew; | 
|  | } else { | 
|  | ++POld; | 
|  | ++PNew; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool Sema::FunctionEffectDiff::shouldDiagnoseConversion( | 
|  | QualType SrcType, const FunctionEffectsRef &SrcFX, QualType DstType, | 
|  | const FunctionEffectsRef &DstFX) const { | 
|  |  | 
|  | switch (EffectKind) { | 
|  | case FunctionEffect::Kind::NonAllocating: | 
|  | // nonallocating can't be added (spoofed) during a conversion, unless we | 
|  | // have nonblocking. | 
|  | if (DiffKind == Kind::Added) { | 
|  | for (const auto &CFE : SrcFX) { | 
|  | if (CFE.Effect.kind() == FunctionEffect::Kind::NonBlocking) | 
|  | return false; | 
|  | } | 
|  | } | 
|  | [[fallthrough]]; | 
|  | case FunctionEffect::Kind::NonBlocking: | 
|  | // nonblocking can't be added (spoofed) during a conversion. | 
|  | switch (DiffKind) { | 
|  | case Kind::Added: | 
|  | return true; | 
|  | case Kind::Removed: | 
|  | return false; | 
|  | case Kind::ConditionMismatch: | 
|  | // FIXME: Condition mismatches are too coarse right now -- expressions | 
|  | // which are equivalent but don't have the same identity are detected as | 
|  | // mismatches. We're going to diagnose those anyhow until expression | 
|  | // matching is better. | 
|  | return true; | 
|  | } | 
|  | break; | 
|  | case FunctionEffect::Kind::Blocking: | 
|  | case FunctionEffect::Kind::Allocating: | 
|  | return false; | 
|  | } | 
|  | llvm_unreachable("unknown effect kind"); | 
|  | } | 
|  |  | 
|  | bool Sema::FunctionEffectDiff::shouldDiagnoseRedeclaration( | 
|  | const FunctionDecl &OldFunction, const FunctionEffectsRef &OldFX, | 
|  | const FunctionDecl &NewFunction, const FunctionEffectsRef &NewFX) const { | 
|  | switch (EffectKind) { | 
|  | case FunctionEffect::Kind::NonAllocating: | 
|  | case FunctionEffect::Kind::NonBlocking: | 
|  | // nonblocking/nonallocating can't be removed in a redeclaration. | 
|  | switch (DiffKind) { | 
|  | case Kind::Added: | 
|  | return false; // No diagnostic. | 
|  | case Kind::Removed: | 
|  | return true; // Issue diagnostic. | 
|  | case Kind::ConditionMismatch: | 
|  | // All these forms of mismatches are diagnosed. | 
|  | return true; | 
|  | } | 
|  | break; | 
|  | case FunctionEffect::Kind::Blocking: | 
|  | case FunctionEffect::Kind::Allocating: | 
|  | return false; | 
|  | } | 
|  | llvm_unreachable("unknown effect kind"); | 
|  | } | 
|  |  | 
|  | Sema::FunctionEffectDiff::OverrideResult | 
|  | Sema::FunctionEffectDiff::shouldDiagnoseMethodOverride( | 
|  | const CXXMethodDecl &OldMethod, const FunctionEffectsRef &OldFX, | 
|  | const CXXMethodDecl &NewMethod, const FunctionEffectsRef &NewFX) const { | 
|  | switch (EffectKind) { | 
|  | case FunctionEffect::Kind::NonAllocating: | 
|  | case FunctionEffect::Kind::NonBlocking: | 
|  | switch (DiffKind) { | 
|  |  | 
|  | // If added on an override, that's fine and not diagnosed. | 
|  | case Kind::Added: | 
|  | return OverrideResult::NoAction; | 
|  |  | 
|  | // If missing from an override (removed), propagate from base to derived. | 
|  | case Kind::Removed: | 
|  | return OverrideResult::Merge; | 
|  |  | 
|  | // If there's a mismatch involving the effect's polarity or condition, | 
|  | // issue a warning. | 
|  | case Kind::ConditionMismatch: | 
|  | return OverrideResult::Warn; | 
|  | } | 
|  | break; | 
|  | case FunctionEffect::Kind::Blocking: | 
|  | case FunctionEffect::Kind::Allocating: | 
|  | return OverrideResult::NoAction; | 
|  | } | 
|  | llvm_unreachable("unknown effect kind"); | 
|  | } | 
|  |  | 
|  | } // namespace clang |