[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)
Doug Wyatt via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 16 10:56:46 PDT 2024
================
@@ -0,0 +1,1199 @@
+//=== EffectAnalysis.cpp - Sema warnings for 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 caller/callee analysis for function effects.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Sema/SemaInternal.h"
+
+#define DEBUG_TYPE "fxanalysis"
+
+using namespace clang;
+
+namespace {
+
+enum class ViolationID : uint8_t {
+ None = 0, // sentinel for an empty Violation
+ Throws,
+ Catches,
+ CallsObjC,
+ AllocatesMemory,
+ HasStaticLocal,
+ AccessesThreadLocal,
+
+ // These only apply to callees, where the analysis stops at the Decl
+ DeclDisallowsInference,
+
+ CallsDeclWithoutEffect,
+ CallsExprWithoutEffect,
+};
+
+// 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;
+ FunctionEffect CalleeEffectPreventingInference; // only for certain IDs
+ ViolationID ID = ViolationID::None;
+ SourceLocation Loc;
+ const Decl *Callee = nullptr; // only valid for Calls*
+
+ Violation() = default;
+
+ Violation(const FunctionEffect &Effect, ViolationID ID, SourceLocation Loc,
+ const Decl *Callee = nullptr,
+ const FunctionEffect *CalleeEffect = nullptr)
+ : Effect(Effect), ID(ID), Loc(Loc), Callee(Callee) {
+ if (CalleeEffect != nullptr)
+ CalleeEffectPreventingInference = *CalleeEffect;
+ }
+};
+
+enum class SpecialFuncType : uint8_t { None, OperatorNew, OperatorDelete };
+enum class CallableType {
+ // 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()->castAs<FunctionProtoType>();
+ if (FPT->isNothrow() || FD->hasAttr<NoThrowAttr>())
+ return true;
+ return false;
+}
+
+// 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 name(Sema &Sem) const {
+ std::string Name;
+ llvm::raw_string_ostream OS(Name);
+
+ if (auto *FD = dyn_cast<FunctionDecl>(CDecl))
+ FD->getNameForDiagnostic(OS, Sem.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 sorted 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.
+ 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>();
+ auto *Iter = _find(Viol.Effect);
----------------
dougsonos wrote:
Done
https://github.com/llvm/llvm-project/pull/99656
More information about the cfe-commits
mailing list