https://github.com/ymand created https://github.com/llvm/llvm-project/pull/120967
This PR has 2 related goals: *Remove dependency on `llvm::Any`*. For some platforms, use of `llvm::Any` forces users to explicitly define internal details related to `Any`. Aside from aesthetics, this places an obscure requirement on lattice implementers. We prefer to use LLVM's (explicit) RTTI framework instead. *Introduce virtual interface for lattices*. Currently, we implicitly define the interface for lattices, because the interface to analyses is fully captured in templates. This PR moves to an explicit, virtual interface. We combine these two changes in a single PR because they are closely related: we use the new lattice interface as the basis of an open type hierarchy that embeds RTTI for safe interfacing with the dataflow engine. >From 43385b7341ef6d3bd5f689670ca407f6e5f91949 Mon Sep 17 00:00:00 2001 From: Yitzhak Mandelbaum <yitzh...@google.com> Date: Mon, 23 Dec 2024 13:42:21 +0000 Subject: [PATCH] Introduce virtual interface for lattices and remove dependency on `llvm::Any`. This PR has 2 related goals: *Remove dependency on `llvm::Any`*. For some platforms, use of `llvm::Any` forces users to explicitly define internal details related to `Any`. Aside from aesthetics, this places an obscure requirement on lattice implementers. We prefer to use LLVM's (explicit) RTTI framework instead. *Introduce virtual interface for lattices*. Currently, we implicitly define the interface for lattices, because the interface to analyses is fully captured in templates. This PR moves to an explicit, virtual interface. We combine these two changes in a single PR because they are closely related: we use the new lattice interface as the basis of an open type hierarchy that embeds RTTI for safe interfacing with the dataflow engine. --- .../CachedConstAccessorsLattice.h | 20 ++- .../Analysis/FlowSensitive/DataflowAnalysis.h | 151 +----------------- .../Analysis/FlowSensitive/DataflowLattice.h | 38 +++++ .../clang/Analysis/FlowSensitive/Logger.h | 4 +- .../clang/Analysis/FlowSensitive/MapLattice.h | 26 ++- .../Models/UncheckedOptionalAccessModel.h | 24 ++- .../Analysis/FlowSensitive/NoopAnalysis.h | 20 +-- .../Analysis/FlowSensitive/NoopLattice.h | 29 ++-- .../TypeErasedDataflowAnalysis.h | 144 +++++++++-------- clang/lib/Analysis/FlowSensitive/Arena.cpp | 4 +- .../lib/Analysis/FlowSensitive/HTMLLogger.cpp | 3 +- clang/lib/Analysis/FlowSensitive/Logger.cpp | 4 +- .../Models/UncheckedOptionalAccessModel.cpp | 9 +- clang/lib/Analysis/FlowSensitive/Transfer.cpp | 1 + .../TypeErasedDataflowAnalysis.cpp | 56 +++---- .../FlowSensitive/ChromiumCheckModelTest.cpp | 15 +- .../Analysis/FlowSensitive/LoggerTest.cpp | 50 ++++-- .../Analysis/FlowSensitive/MapLatticeTest.cpp | 40 +++-- .../MultiVarConstantPropagationTest.cpp | 52 +++--- .../FlowSensitive/SignAnalysisTest.cpp | 14 +- .../SingleVarConstantPropagationTest.cpp | 52 +++--- .../Analysis/FlowSensitive/TestingSupport.h | 8 +- .../FlowSensitive/TransferBranchTest.cpp | 33 ++-- .../TypeErasedDataflowAnalysisTest.cpp | 116 ++++++++++---- 24 files changed, 475 insertions(+), 438 deletions(-) diff --git a/clang/include/clang/Analysis/FlowSensitive/CachedConstAccessorsLattice.h b/clang/include/clang/Analysis/FlowSensitive/CachedConstAccessorsLattice.h index 48c5287367739a..8dd9cd546be160 100644 --- a/clang/include/clang/Analysis/FlowSensitive/CachedConstAccessorsLattice.h +++ b/clang/include/clang/Analysis/FlowSensitive/CachedConstAccessorsLattice.h @@ -20,6 +20,7 @@ #include "clang/Analysis/FlowSensitive/Value.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLFunctionalExtras.h" +#include "llvm/Support/ExtensibleRTTI.h" namespace clang { namespace dataflow { @@ -88,7 +89,15 @@ template <typename Base> class CachedConstAccessorsLattice : public Base { return Base::operator==(Other); } - LatticeJoinEffect join(const CachedConstAccessorsLattice &Other); + std::unique_ptr<DataflowLattice> clone() override { + return std::make_unique<CachedConstAccessorsLattice>(*this); + } + + bool isEqual(const DataflowLattice &Other) const override { + return *this == llvm::cast<const CachedConstAccessorsLattice>(Other); + } + + LatticeEffect join(const DataflowLattice &Other) override; private: // Maps a record storage location and const method to the value to return @@ -146,22 +155,23 @@ joinConstMethodMap( template <typename Base> LatticeEffect CachedConstAccessorsLattice<Base>::join( - const CachedConstAccessorsLattice<Base> &Other) { - + const DataflowLattice &Other) { LatticeEffect Effect = Base::join(Other); + const auto &OtherL = llvm::cast<const CachedConstAccessorsLattice<Base>>(Other); + // For simplicity, we only retain values that are identical, but not ones that // are non-identical but equivalent. This is likely to be sufficient in // practice, and it reduces implementation complexity considerably. ConstMethodReturnValues = clang::dataflow::internal::joinConstMethodMap<dataflow::Value>( - ConstMethodReturnValues, Other.ConstMethodReturnValues, Effect); + ConstMethodReturnValues, OtherL.ConstMethodReturnValues, Effect); ConstMethodReturnStorageLocations = clang::dataflow::internal::joinConstMethodMap<dataflow::StorageLocation>( ConstMethodReturnStorageLocations, - Other.ConstMethodReturnStorageLocations, Effect); + OtherL.ConstMethodReturnStorageLocations, Effect); return Effect; } diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h index e6efde091871fc..494a2d5cd1e8a7 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h @@ -16,7 +16,6 @@ #include <iterator> #include <optional> -#include <type_traits> #include <utility> #include <vector> @@ -37,138 +36,6 @@ namespace clang { namespace dataflow { -/// Base class template for dataflow analyses built on a single lattice type. -/// -/// Requirements: -/// -/// `Derived` must be derived from a specialization of this class template and -/// must provide the following public members: -/// * `LatticeT initialElement()` - returns a lattice element that models the -/// initial state of a basic block; -/// * `void transfer(const CFGElement &, LatticeT &, Environment &)` - applies -/// the analysis transfer function for a given CFG element and lattice -/// element. -/// -/// `Derived` can optionally provide the following members: -/// * `void transferBranch(bool Branch, const Stmt *Stmt, TypeErasedLattice &E, -/// Environment &Env)` - applies the analysis transfer -/// function for a given edge from a CFG block of a conditional statement. -/// -/// `Derived` can optionally override the virtual functions in the -/// `Environment::ValueModel` interface (which is an indirect base class of -/// this class). -/// -/// `LatticeT` is a bounded join-semilattice that is used by `Derived` and must -/// provide the following public members: -/// * `LatticeJoinEffect join(const LatticeT &)` - joins the object and the -/// argument by computing their least upper bound, modifies the object if -/// necessary, and returns an effect indicating whether any changes were -/// made to it; -/// FIXME: make it `static LatticeT join(const LatticeT&, const LatticeT&)` -/// * `bool operator==(const LatticeT &) const` - returns true if and only if -/// the object is equal to the argument. -/// -/// `LatticeT` can optionally provide the following members: -/// * `LatticeJoinEffect widen(const LatticeT &Previous)` - replaces the -/// lattice element with an approximation that can reach a fixed point more -/// quickly than iterated application of the transfer function alone. The -/// previous value is provided to inform the choice of widened value. The -/// function must also serve as a comparison operation, by indicating whether -/// the widened value is equivalent to the previous value with the returned -/// `LatticeJoinEffect`. -template <typename Derived, typename LatticeT> -class DataflowAnalysis : public TypeErasedDataflowAnalysis { -public: - /// Bounded join-semilattice that is used in the analysis. - using Lattice = LatticeT; - - explicit DataflowAnalysis(ASTContext &Context) : Context(Context) {} - - explicit DataflowAnalysis(ASTContext &Context, - DataflowAnalysisOptions Options) - : TypeErasedDataflowAnalysis(Options), Context(Context) {} - - ASTContext &getASTContext() final { return Context; } - - TypeErasedLattice typeErasedInitialElement() final { - return {static_cast<Derived *>(this)->initialElement()}; - } - - TypeErasedLattice joinTypeErased(const TypeErasedLattice &E1, - const TypeErasedLattice &E2) final { - // FIXME: change the signature of join() to avoid copying here. - Lattice L1 = llvm::any_cast<const Lattice &>(E1.Value); - const Lattice &L2 = llvm::any_cast<const Lattice &>(E2.Value); - L1.join(L2); - return {std::move(L1)}; - } - - LatticeJoinEffect widenTypeErased(TypeErasedLattice &Current, - const TypeErasedLattice &Previous) final { - Lattice &C = llvm::any_cast<Lattice &>(Current.Value); - const Lattice &P = llvm::any_cast<const Lattice &>(Previous.Value); - return widenInternal(Rank0{}, C, P); - } - - bool isEqualTypeErased(const TypeErasedLattice &E1, - const TypeErasedLattice &E2) final { - const Lattice &L1 = llvm::any_cast<const Lattice &>(E1.Value); - const Lattice &L2 = llvm::any_cast<const Lattice &>(E2.Value); - return L1 == L2; - } - - void transferTypeErased(const CFGElement &Element, TypeErasedLattice &E, - Environment &Env) final { - Lattice &L = llvm::any_cast<Lattice &>(E.Value); - static_cast<Derived *>(this)->transfer(Element, L, Env); - } - - void transferBranchTypeErased(bool Branch, const Stmt *Stmt, - TypeErasedLattice &E, Environment &Env) final { - transferBranchInternal(Rank0{}, *static_cast<Derived *>(this), Branch, Stmt, - E, Env); - } - -private: - // These `Rank` structs are used for template metaprogramming to choose - // between overloads. - struct Rank1 {}; - struct Rank0 : Rank1 {}; - - // The first-choice implementation: use `widen` when it is available. - template <typename T> - static auto widenInternal(Rank0, T &Current, const T &Prev) - -> decltype(Current.widen(Prev)) { - return Current.widen(Prev); - } - - // The second-choice implementation: `widen` is unavailable. Widening is - // merged with equality checking, so when widening is unimplemented, we - // default to equality checking. - static LatticeJoinEffect widenInternal(Rank1, const Lattice &Current, - const Lattice &Prev) { - return Prev == Current ? LatticeJoinEffect::Unchanged - : LatticeJoinEffect::Changed; - } - - // The first-choice implementation: `transferBranch` is implemented. - template <typename Analysis> - static auto transferBranchInternal(Rank0, Analysis &A, bool Branch, - const Stmt *Stmt, TypeErasedLattice &L, - Environment &Env) - -> std::void_t<decltype(A.transferBranch( - Branch, Stmt, std::declval<LatticeT &>(), Env))> { - A.transferBranch(Branch, Stmt, llvm::any_cast<Lattice &>(L.Value), Env); - } - - // The second-choice implementation: `transferBranch` is unimplemented. No-op. - template <typename Analysis> - static void transferBranchInternal(Rank1, Analysis &A, bool, const Stmt *, - TypeErasedLattice &, Environment &) {} - - ASTContext &Context; -}; - // Model of the program at a given program point. template <typename LatticeT> struct DataflowAnalysisState { // Model of a program property. @@ -241,7 +108,7 @@ runDataflowAnalysis(const AdornedCFG &ACFG, AnalysisT &Analysis, [&PostAnalysisCallbacks](const CFGElement &Element, const TypeErasedDataflowAnalysisState &State) { auto *Lattice = - llvm::any_cast<typename AnalysisT::Lattice>(&State.Lattice.Value); + llvm::cast<typename AnalysisT::Lattice>(State.Lattice.get()); // FIXME: we should not be copying the environment here! // Ultimately the `CFGEltCallback` only gets a const reference anyway. PostAnalysisCallbacks.Before( @@ -253,13 +120,13 @@ runDataflowAnalysis(const AdornedCFG &ACFG, AnalysisT &Analysis, TypeErasedCallbacks.After = [&PostAnalysisCallbacks](const CFGElement &Element, const TypeErasedDataflowAnalysisState &State) { - auto *Lattice = - llvm::any_cast<typename AnalysisT::Lattice>(&State.Lattice.Value); + auto &Lattice = + llvm::cast<typename AnalysisT::Lattice>(*State.Lattice); // FIXME: we should not be copying the environment here! // Ultimately the `CFGEltCallback` only gets a const reference anyway. PostAnalysisCallbacks.After( Element, DataflowAnalysisState<typename AnalysisT::Lattice>{ - *Lattice, State.Env.fork()}); + Lattice, State.Env.fork()}); }; } @@ -278,8 +145,8 @@ runDataflowAnalysis(const AdornedCFG &ACFG, AnalysisT &Analysis, return llvm::transformOptional( std::move(OptState), [](TypeErasedDataflowAnalysisState &&State) { return DataflowAnalysisState<typename AnalysisT::Lattice>{ - llvm::any_cast<typename AnalysisT::Lattice>( - std::move(State.Lattice.Value)), + llvm::cast<typename AnalysisT::Lattice>( + std::move(*State.Lattice)), std::move(State.Env)}; }); }); @@ -341,8 +208,7 @@ diagnoseFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx, auto EltDiagnostics = Diagnoser.Before( Elt, ASTCtx, TransferStateForDiagnostics<typename AnalysisT::Lattice>( - llvm::any_cast<const typename AnalysisT::Lattice &>( - State.Lattice.Value), + llvm::cast<typename AnalysisT::Lattice>(*State.Lattice), State.Env)); llvm::move(EltDiagnostics, std::back_inserter(Diagnostics)); }; @@ -355,8 +221,7 @@ diagnoseFunction(const FunctionDecl &FuncDecl, ASTContext &ASTCtx, auto EltDiagnostics = Diagnoser.After( Elt, ASTCtx, TransferStateForDiagnostics<typename AnalysisT::Lattice>( - llvm::any_cast<const typename AnalysisT::Lattice &>( - State.Lattice.Value), + llvm::cast<typename AnalysisT::Lattice>(*State.Lattice), State.Env)); llvm::move(EltDiagnostics, std::back_inserter(Diagnostics)); }; diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowLattice.h b/clang/include/clang/Analysis/FlowSensitive/DataflowLattice.h index b262732804ed69..f773c0bf9fefe5 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowLattice.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowLattice.h @@ -14,6 +14,10 @@ #ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_DATAFLOWLATTICE_H #define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_DATAFLOWLATTICE_H +#include "llvm/Support/Casting.h" +#include "llvm/Support/ExtensibleRTTI.h" +#include <memory> + namespace clang { namespace dataflow { @@ -25,6 +29,40 @@ enum class LatticeEffect { // DEPRECATED. Use `LatticeEffect`. using LatticeJoinEffect = LatticeEffect; +class DataflowLattice + : public llvm::RTTIExtends<DataflowLattice, llvm::RTTIRoot> { +public: + inline static char ID = 0; + DataflowLattice() = default; + + /// Joins two type-erased lattice elements by computing their least upper + /// bound. + virtual LatticeEffect join(const DataflowLattice &) = 0; + + virtual std::unique_ptr<DataflowLattice> clone() = 0; + + /// Chooses a lattice element that approximates the current element at a + /// program point, given the previous element at that point. Places the + /// widened result in the current element (`Current`). Widening is optional -- + /// it is only needed to either accelerate convergence (for lattices with + /// non-trivial height) or guarantee convergence (for lattices with infinite + /// height). + /// + /// Returns an indication of whether any changes were made to `Current` in + /// order to widen. This saves a separate call to `isEqualTypeErased` after + /// the widening. + virtual LatticeEffect widen(const DataflowLattice &Previous) { + return isEqual(Previous) ? LatticeEffect::Unchanged + : LatticeEffect::Changed; + } + + /// Returns true if and only if the two given type-erased lattice elements are + /// equal. + virtual bool isEqual(const DataflowLattice &) const = 0; +}; + +using DataflowLatticePtr = std::unique_ptr<DataflowLattice>; + } // namespace dataflow } // namespace clang diff --git a/clang/include/clang/Analysis/FlowSensitive/Logger.h b/clang/include/clang/Analysis/FlowSensitive/Logger.h index f2e64810d00506..34d5865f6bb244 100644 --- a/clang/include/clang/Analysis/FlowSensitive/Logger.h +++ b/clang/include/clang/Analysis/FlowSensitive/Logger.h @@ -16,7 +16,7 @@ namespace clang::dataflow { // Forward declarations so we can use Logger anywhere in the framework. class AdornedCFG; -class TypeErasedDataflowAnalysis; +class DataflowAnalysis; struct TypeErasedDataflowAnalysisState; /// A logger is notified as the analysis progresses. @@ -40,7 +40,7 @@ class Logger { /// Called by the framework as we start analyzing a new function or statement. /// Forms a pair with endAnalysis(). - virtual void beginAnalysis(const AdornedCFG &, TypeErasedDataflowAnalysis &) { + virtual void beginAnalysis(const AdornedCFG &, DataflowAnalysis &) { } virtual void endAnalysis() {} diff --git a/clang/include/clang/Analysis/FlowSensitive/MapLattice.h b/clang/include/clang/Analysis/FlowSensitive/MapLattice.h index b2d147e4ae444b..0321d55b9863eb 100644 --- a/clang/include/clang/Analysis/FlowSensitive/MapLattice.h +++ b/clang/include/clang/Analysis/FlowSensitive/MapLattice.h @@ -36,11 +36,15 @@ namespace dataflow { /// /// Requirements on `ElementLattice`: /// * Provides standard declarations of a bounded semi-lattice. -template <typename Key, typename ElementLattice> class MapLattice { +template <typename Key, typename ElementLattice> +class MapLattice : public llvm::RTTIExtends<MapLattice<Key, ElementLattice>, + DataflowLattice> { using Container = llvm::DenseMap<Key, ElementLattice>; Container C; public: + inline static char ID = 0; + using key_type = Key; using mapped_type = ElementLattice; using value_type = typename Container::value_type; @@ -89,18 +93,28 @@ template <typename Key, typename ElementLattice> class MapLattice { mapped_type &operator[](const key_type &K) { return C[K]; } + + DataflowLatticePtr clone() override { + return std::make_unique<MapLattice>(*this); + } + + bool isEqual(const DataflowLattice &Other) const override { + return *this == llvm::cast<const MapLattice>(Other); + } + /// If an entry exists in one map but not the other, the missing entry is /// treated as implicitly mapping to `bottom`. So, the joined map contains the /// entry as it was in the source map. - LatticeJoinEffect join(const MapLattice &Other) { - LatticeJoinEffect Effect = LatticeJoinEffect::Unchanged; + LatticeEffect join(const DataflowLattice &L) override { + const auto &Other = llvm::cast<MapLattice>(L); + LatticeEffect Effect = LatticeEffect::Unchanged; for (const auto &O : Other.C) { auto It = C.find(O.first); if (It == C.end()) { C.insert(O); - Effect = LatticeJoinEffect::Changed; - } else if (It->second.join(O.second) == LatticeJoinEffect::Changed) - Effect = LatticeJoinEffect::Changed; + Effect = LatticeEffect::Changed; + } else if (It->second.join(O.second) == LatticeEffect::Changed) + Effect = LatticeEffect::Changed; } return Effect; } diff --git a/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h b/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h index 713494178b97bd..654c76efec823b 100644 --- a/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h +++ b/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h @@ -23,6 +23,8 @@ #include "clang/Analysis/FlowSensitive/NoopLattice.h" #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/ExtensibleRTTI.h" +#include <memory> namespace clang { namespace dataflow { @@ -48,28 +50,34 @@ struct UncheckedOptionalAccessModelOptions { bool IgnoreSmartPointerDereference = false; }; -using UncheckedOptionalAccessLattice = CachedConstAccessorsLattice<NoopLattice>; +struct UncheckedOptionalAccessLattice + : public llvm::RTTIExtends<UncheckedOptionalAccessLattice, + CachedConstAccessorsLattice<NoopLattice>> { + inline static char ID = 0; +}; /// Dataflow analysis that models whether optionals hold values or not. /// /// Models the `std::optional`, `absl::optional`, and `base::Optional` types. class UncheckedOptionalAccessModel - : public DataflowAnalysis<UncheckedOptionalAccessModel, - UncheckedOptionalAccessLattice> { + : public DataflowAnalysis { public: + using Lattice = UncheckedOptionalAccessLattice; + UncheckedOptionalAccessModel(ASTContext &Ctx, dataflow::Environment &Env); /// Returns a matcher for the optional classes covered by this model. static ast_matchers::DeclarationMatcher optionalClassDecl(); - static UncheckedOptionalAccessLattice initialElement() { return {}; } + std::unique_ptr<DataflowLattice> initialElement() override { + return std::make_unique<Lattice>(); + } - void transfer(const CFGElement &Elt, UncheckedOptionalAccessLattice &L, - Environment &Env); + void transfer(const CFGElement &Elt, DataflowLattice &L, + Environment &Env) override; private: - CFGMatchSwitch<TransferState<UncheckedOptionalAccessLattice>> - TransferMatchSwitch; + CFGMatchSwitch<TransferState<Lattice>> TransferMatchSwitch; }; class UncheckedOptionalAccessDiagnoser { diff --git a/clang/include/clang/Analysis/FlowSensitive/NoopAnalysis.h b/clang/include/clang/Analysis/FlowSensitive/NoopAnalysis.h index 393f68300cb80c..a5efb001358514 100644 --- a/clang/include/clang/Analysis/FlowSensitive/NoopAnalysis.h +++ b/clang/include/clang/Analysis/FlowSensitive/NoopAnalysis.h @@ -13,26 +13,26 @@ #ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_NOOPANALYSIS_H #define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_NOOPANALYSIS_H -#include "clang/AST/ASTContext.h" #include "clang/Analysis/CFG.h" -#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h" #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" #include "clang/Analysis/FlowSensitive/NoopLattice.h" +#include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h" +#include <memory> namespace clang { namespace dataflow { -class NoopAnalysis : public DataflowAnalysis<NoopAnalysis, NoopLattice> { +class NoopAnalysis : public DataflowAnalysis { public: - NoopAnalysis(ASTContext &Context) - : DataflowAnalysis<NoopAnalysis, NoopLattice>(Context) {} + using DataflowAnalysis::DataflowAnalysis; + using Lattice = NoopLattice; - NoopAnalysis(ASTContext &Context, DataflowAnalysisOptions Options) - : DataflowAnalysis<NoopAnalysis, NoopLattice>(Context, Options) {} + std::unique_ptr<DataflowLattice> initialElement() override { + return std::make_unique<NoopLattice>(); + } - static NoopLattice initialElement() { return {}; } - - void transfer(const CFGElement &E, NoopLattice &L, Environment &Env) {} + void transfer(const CFGElement &E, DataflowLattice &L, + Environment &Env) override {} }; } // namespace dataflow diff --git a/clang/include/clang/Analysis/FlowSensitive/NoopLattice.h b/clang/include/clang/Analysis/FlowSensitive/NoopLattice.h index 96c695473b67a1..e5201d6910ecbf 100644 --- a/clang/include/clang/Analysis/FlowSensitive/NoopLattice.h +++ b/clang/include/clang/Analysis/FlowSensitive/NoopLattice.h @@ -14,8 +14,7 @@ #define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_NOOP_LATTICE_H #include "clang/Analysis/FlowSensitive/DataflowLattice.h" -#include "clang/Support/Compiler.h" -#include "llvm/ADT/Any.h" +#include <memory> #include <ostream> namespace clang { @@ -24,13 +23,24 @@ namespace dataflow { /// Trivial lattice for dataflow analysis with exactly one element. /// /// Useful for analyses that only need the Environment and nothing more. -class NoopLattice { +class NoopLattice : public llvm::RTTIExtends<NoopLattice, DataflowLattice> { public: - bool operator==(const NoopLattice &Other) const { return true; } + inline static char ID = 0; + + bool isEqual(const DataflowLattice &Other) const override { + return llvm::isa<NoopLattice>(Other); + } + + std::unique_ptr<DataflowLattice> clone() override { + return std::make_unique<NoopLattice>(); + } - LatticeJoinEffect join(const NoopLattice &Other) { + LatticeEffect join(const DataflowLattice &Other) override { + assert(llvm::isa<NoopLattice>(Other)); return LatticeJoinEffect::Unchanged; } + + bool operator==(const NoopLattice &Other) const { return true; } }; inline std::ostream &operator<<(std::ostream &OS, const NoopLattice &) { @@ -40,13 +50,4 @@ inline std::ostream &operator<<(std::ostream &OS, const NoopLattice &) { } // namespace dataflow } // namespace clang -namespace llvm { -// This needs to be exported for ClangAnalysisFlowSensitiveTests so any_cast -// uses the correct address of Any::TypeId from the clang shared library instead -// of creating one in the test executable. when building with -// CLANG_LINK_CLANG_DYLIB -extern template struct CLANG_TEMPLATE_ABI - Any::TypeId<clang::dataflow::NoopLattice>; -} // namespace llvm - #endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_NOOP_LATTICE_H diff --git a/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h b/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h index 512453e2be67ad..dbad9d334e2696 100644 --- a/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h +++ b/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h @@ -25,7 +25,6 @@ #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" #include "clang/Analysis/FlowSensitive/DataflowLattice.h" -#include "llvm/ADT/Any.h" #include "llvm/Support/Error.h" namespace clang { @@ -40,95 +39,100 @@ struct DataflowAnalysisOptions { DataflowAnalysisContext::Options{}; }; -/// Type-erased lattice element container. +/// Base class template for dataflow analyses built on a single lattice type. /// /// Requirements: /// -/// The type of the object stored in the container must be a bounded -/// join-semilattice. -struct TypeErasedLattice { - llvm::Any Value; -}; +/// `Derived` must be derived from a specialization of this class template and +/// must provide the following public members: +/// * `LatticeT initialElement()` - returns a lattice element that models the +/// initial state of a basic block; +/// * `void transfer(const CFGElement &, LatticeT &, Environment &)` - applies +/// the analysis transfer function for a given CFG element and lattice +/// element. +/// +/// `Derived` can optionally provide the following members: +/// * `void transferBranch(bool Branch, const Stmt *Stmt, TypeErasedLattice &E, +/// Environment &Env)` - applies the analysis transfer +/// function for a given edge from a CFG block of a conditional statement. +/// +/// `Derived` can optionally override the virtual functions in the +/// `Environment::ValueModel` interface (which is an indirect base class of +/// this class). +/// +/// `LatticeT` is a bounded join-semilattice that is used by `Derived` and must +/// provide the following public members: +/// * `LatticeJoinEffect join(const LatticeT &)` - joins the object and the +/// argument by computing their least upper bound, modifies the object if +/// necessary, and returns an effect indicating whether any changes were +/// made to it; +/// FIXME: make it `static LatticeT join(const LatticeT&, const LatticeT&)` +/// * `bool operator==(const LatticeT &) const` - returns true if and only if +/// the object is equal to the argument. +/// +/// `LatticeT` can optionally provide the following members: +/// * `LatticeJoinEffect widen(const LatticeT &Previous)` - replaces the +/// lattice element with an approximation that can reach a fixed point more +/// quickly than iterated application of the transfer function alone. The +/// previous value is provided to inform the choice of widened value. The +/// function must also serve as a comparison operation, by indicating whether +/// the widened value is equivalent to the previous value with the returned +/// `LatticeJoinEffect`. /// Type-erased base class for dataflow analyses built on a single lattice type. -class TypeErasedDataflowAnalysis : public Environment::ValueModel { +class DataflowAnalysis : public Environment::ValueModel { + ASTContext &Context; DataflowAnalysisOptions Options; public: - TypeErasedDataflowAnalysis() : Options({}) {} - - TypeErasedDataflowAnalysis(DataflowAnalysisOptions Options) - : Options(Options) {} - - virtual ~TypeErasedDataflowAnalysis() {} - - /// Returns the `ASTContext` that is used by the analysis. - virtual ASTContext &getASTContext() = 0; - - /// Returns a type-erased lattice element that models the initial state of a - /// basic block. - virtual TypeErasedLattice typeErasedInitialElement() = 0; - - /// Joins two type-erased lattice elements by computing their least upper - /// bound. Places the join result in the left element and returns an effect - /// indicating whether any changes were made to it. - virtual TypeErasedLattice joinTypeErased(const TypeErasedLattice &, - const TypeErasedLattice &) = 0; - - /// Chooses a lattice element that approximates the current element at a - /// program point, given the previous element at that point. Places the - /// widened result in the current element (`Current`). Widening is optional -- - /// it is only needed to either accelerate convergence (for lattices with - /// non-trivial height) or guarantee convergence (for lattices with infinite - /// height). - /// - /// Returns an indication of whether any changes were made to `Current` in - /// order to widen. This saves a separate call to `isEqualTypeErased` after - /// the widening. - virtual LatticeJoinEffect - widenTypeErased(TypeErasedLattice &Current, - const TypeErasedLattice &Previous) = 0; - - /// Returns true if and only if the two given type-erased lattice elements are - /// equal. - virtual bool isEqualTypeErased(const TypeErasedLattice &, - const TypeErasedLattice &) = 0; - - /// Applies the analysis transfer function for a given control flow graph - /// element and type-erased lattice element. - virtual void transferTypeErased(const CFGElement &, TypeErasedLattice &, - Environment &) = 0; - - /// Applies the analysis transfer function for a given edge from a CFG block - /// of a conditional statement. - /// @param Stmt The condition which is responsible for the split in the CFG. - /// @param Branch True if the edge goes to the basic block where the - /// condition is true. - // FIXME: Change `Stmt` argument to a reference. - virtual void transferBranchTypeErased(bool Branch, const Stmt *, - TypeErasedLattice &, Environment &) = 0; - - /// If the built-in model is enabled, returns the options to be passed to - /// them. Otherwise returns empty. - const std::optional<DataflowAnalysisContext::Options> & - builtinOptions() const { - return Options.BuiltinOpts; + explicit DataflowAnalysis(ASTContext &Context) : Context(Context) {} + + explicit DataflowAnalysis(ASTContext &Context, DataflowAnalysisOptions Options) + : Context(Context), Options(Options) {} + + /// Returns the `ASTContext` that is used by the analysis. + ASTContext &getASTContext() { return Context; } + + /// Returns a lattice element that models the initial state of a + /// basic block. + virtual DataflowLatticePtr initialElement() = 0; + + /// Applies the analysis transfer function for a given control flow graph + /// element and type-erased lattice element. The derived type of Lattice will + /// always match the type of the lattice returned by `initialElement`. + virtual void transfer(const CFGElement &, DataflowLattice &Lattice, + Environment &) = 0; + + /// Applies the analysis transfer function for a given edge from a CFG block + /// of a conditional statement. This method is optional and so the default + /// implementation is a no-op. + /// @param Stmt The condition which is responsible for the split in the CFG. + /// @param Branch True if the edge goes to the basic block where the + /// condition is true. + virtual void transferBranch(bool Branch, const Stmt &Stmt, DataflowLattice &, + Environment &) {}; + + /// If the built-in model is enabled, returns the options to be passed to + /// them. Otherwise returns empty. + const std::optional<DataflowAnalysisContext::Options> &builtinOptions() const { + return Options.BuiltinOpts; } }; /// Type-erased model of the program at a given program point. struct TypeErasedDataflowAnalysisState { /// Type-erased model of a program property. - TypeErasedLattice Lattice; + std::unique_ptr<DataflowLattice> Lattice; /// Model of the state of the program (store and heap). Environment Env; - TypeErasedDataflowAnalysisState(TypeErasedLattice Lattice, Environment Env) + TypeErasedDataflowAnalysisState(std::unique_ptr<DataflowLattice> Lattice, + Environment Env) : Lattice(std::move(Lattice)), Env(std::move(Env)) {} TypeErasedDataflowAnalysisState fork() const { - return TypeErasedDataflowAnalysisState(Lattice, Env.fork()); + return TypeErasedDataflowAnalysisState(Lattice->clone(), Env.fork()); } }; @@ -159,7 +163,7 @@ struct CFGEltCallbacksTypeErased { /// from converging. llvm::Expected<std::vector<std::optional<TypeErasedDataflowAnalysisState>>> runTypeErasedDataflowAnalysis( - const AdornedCFG &ACFG, TypeErasedDataflowAnalysis &Analysis, + const AdornedCFG &ACFG, DataflowAnalysis &Analysis, const Environment &InitEnv, const CFGEltCallbacksTypeErased &PostAnalysisCallbacks, std::int32_t MaxBlockVisits); diff --git a/clang/lib/Analysis/FlowSensitive/Arena.cpp b/clang/lib/Analysis/FlowSensitive/Arena.cpp index 7542a137c735e0..59d0fb2aa4208d 100644 --- a/clang/lib/Analysis/FlowSensitive/Arena.cpp +++ b/clang/lib/Analysis/FlowSensitive/Arena.cpp @@ -180,7 +180,7 @@ class FormulaParseError : public llvm::ErrorInfo<FormulaParseError> { unsigned Offset; public: - static char ID; + inline static char ID; FormulaParseError(llvm::StringRef Formula, unsigned Offset) : Formula(Formula), Offset(Offset) {} @@ -195,8 +195,6 @@ class FormulaParseError : public llvm::ErrorInfo<FormulaParseError> { } }; -char FormulaParseError::ID = 0; - } // namespace llvm::Expected<const Formula &> Arena::parseFormula(llvm::StringRef In) { diff --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp index 557df218837941..ecec08be5febe5 100644 --- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp +++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp @@ -175,8 +175,7 @@ class HTMLLogger : public Logger { public: explicit HTMLLogger(StreamFactory Streams) : Streams(std::move(Streams)) {} - void beginAnalysis(const AdornedCFG &ACFG, - TypeErasedDataflowAnalysis &A) override { + void beginAnalysis(const AdornedCFG &ACFG, DataflowAnalysis &A) override { OS = Streams(); this->ACFG = &ACFG; *OS << llvm::StringRef(HTMLLogger_html).split("<?INJECT?>").first; diff --git a/clang/lib/Analysis/FlowSensitive/Logger.cpp b/clang/lib/Analysis/FlowSensitive/Logger.cpp index 8f40768171c94e..a2afe43e5a9b88 100644 --- a/clang/lib/Analysis/FlowSensitive/Logger.cpp +++ b/clang/lib/Analysis/FlowSensitive/Logger.cpp @@ -28,13 +28,13 @@ struct TextualLogger final : Logger { unsigned CurrentElementIndex; bool ShowColors; llvm::DenseMap<const CFGBlock *, unsigned> VisitCount; - TypeErasedDataflowAnalysis *CurrentAnalysis; + DataflowAnalysis *CurrentAnalysis; TextualLogger(llvm::raw_ostream &OS) : OS(OS), ShowColors(llvm::WithColor::defaultAutoDetectFunction()(OS)) {} virtual void beginAnalysis(const AdornedCFG &ACFG, - TypeErasedDataflowAnalysis &Analysis) override { + DataflowAnalysis &Analysis) override { { llvm::WithColor Header(OS, llvm::raw_ostream::Colors::RED, /*Bold=*/true); OS << "=== Beginning data flow analysis ===\n"; diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp index da5dda063344f9..7c1372f434fa9a 100644 --- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp +++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp @@ -1102,9 +1102,7 @@ UncheckedOptionalAccessModel::optionalClassDecl() { UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx, Environment &Env) - : DataflowAnalysis<UncheckedOptionalAccessModel, - UncheckedOptionalAccessLattice>(Ctx), - TransferMatchSwitch(buildTransferMatchSwitch()) { + : DataflowAnalysis(Ctx), TransferMatchSwitch(buildTransferMatchSwitch()) { Env.getDataflowAnalysisContext().setSyntheticFieldCallback( [&Ctx](QualType Ty) -> llvm::StringMap<QualType> { const CXXRecordDecl *Optional = @@ -1117,9 +1115,10 @@ UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx, } void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt, - UncheckedOptionalAccessLattice &L, + DataflowLattice &L, Environment &Env) { - LatticeTransferState State(L, Env); + LatticeTransferState State(llvm::cast<UncheckedOptionalAccessLattice>(L), + Env); TransferMatchSwitch(Elt, getASTContext(), State); } diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp index 9c54eb16d22246..6c00640768e532 100644 --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -22,6 +22,7 @@ #include "clang/AST/StmtVisitor.h" #include "clang/Analysis/FlowSensitive/ASTOps.h" #include "clang/Analysis/FlowSensitive/AdornedCFG.h" +#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h" #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" #include "clang/Analysis/FlowSensitive/NoopAnalysis.h" diff --git a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp index 32b3886439495f..98c2acec9ff295 100644 --- a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp +++ b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp @@ -26,11 +26,10 @@ #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" #include "clang/Analysis/FlowSensitive/DataflowLattice.h" #include "clang/Analysis/FlowSensitive/DataflowWorklist.h" -#include "clang/Analysis/FlowSensitive/RecordOps.h" +#include "clang/Analysis/FlowSensitive/Logger.h" #include "clang/Analysis/FlowSensitive/Transfer.h" #include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h" #include "clang/Analysis/FlowSensitive/Value.h" -#include "clang/Support/Compiler.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Debug.h" @@ -38,27 +37,13 @@ #define DEBUG_TYPE "clang-dataflow" -namespace clang { -namespace dataflow { -class NoopLattice; -} -} // namespace clang - -namespace llvm { -// This needs to be exported for ClangAnalysisFlowSensitiveTests so any_cast -// uses the correct address of Any::TypeId from the clang shared library instead -// of creating one in the test executable. when building with -// CLANG_LINK_CLANG_DYLIB -template struct CLANG_EXPORT_TEMPLATE Any::TypeId<clang::dataflow::NoopLattice>; -} // namespace llvm - namespace clang { namespace dataflow { /// Returns the index of `Block` in the successors of `Pred`. static int blockIndexInPredecessor(const CFGBlock &Pred, const CFGBlock &Block) { - auto BlockPos = llvm::find_if( + const auto *BlockPos = llvm::find_if( Pred.succs(), [&Block](const CFGBlock::AdjacentBlock &Succ) { return Succ && Succ->getBlockID() == Block.getBlockID(); }); @@ -102,7 +87,7 @@ class TerminatorVisitor /// Holds data structures required for running dataflow analysis. struct AnalysisContext { - AnalysisContext(const AdornedCFG &ACFG, TypeErasedDataflowAnalysis &Analysis, + AnalysisContext(const AdornedCFG &ACFG, DataflowAnalysis &Analysis, const Environment &InitEnv, llvm::ArrayRef<std::optional<TypeErasedDataflowAnalysisState>> BlockStates) @@ -116,7 +101,7 @@ struct AnalysisContext { /// Contains the CFG being analyzed. const AdornedCFG &ACFG; /// The analysis to be run. - TypeErasedDataflowAnalysis &Analysis; + DataflowAnalysis &Analysis; /// Initial state to start the analysis. const Environment &InitEnv; Logger &Log; @@ -179,7 +164,9 @@ class JoinedStateBuilder { TypeErasedDataflowAnalysisState join(const TypeErasedDataflowAnalysisState &L, const TypeErasedDataflowAnalysisState &R) { - return {AC.Analysis.joinTypeErased(L.Lattice, R.Lattice), + auto L2 = L.Lattice->clone(); + L2->join(*R.Lattice); + return {std::move(L2), Environment::join(L.Env, R.Env, AC.Analysis, JoinBehavior)}; } @@ -200,14 +187,15 @@ class JoinedStateBuilder { // FIXME: Consider passing `Block` to Analysis.typeErasedInitialElement // to enable building analyses like computation of dominators that // initialize the state of each basic block differently. - return {AC.Analysis.typeErasedInitialElement(), AC.InitEnv.fork()}; + return {AC.Analysis.initialElement(), AC.InitEnv.fork()}; if (All.size() == 1) // Join the environment with itself so that we discard expression state if // desired. // FIXME: We could consider writing special-case code for this that only // does the discarding, but it's not clear if this is worth it. - return {All[0]->Lattice, Environment::join(All[0]->Env, All[0]->Env, - AC.Analysis, JoinBehavior)}; + return {All[0]->Lattice->clone(), + Environment::join(All[0]->Env, All[0]->Env, AC.Analysis, + JoinBehavior)}; auto Result = join(*All[0], *All[1]); for (unsigned I = 2; I < All.size(); ++I) @@ -317,8 +305,7 @@ computeBlockInputState(const CFGBlock &Block, AnalysisContext &AC) { BranchVal ? CondVal : &Copy.Env.makeNot(*CondVal); Copy.Env.assume(AssertedVal->formula()); } - AC.Analysis.transferBranchTypeErased(BranchVal, Cond, Copy.Lattice, - Copy.Env); + AC.Analysis.transferBranch(BranchVal, *Cond, *Copy.Lattice, Copy.Env); Builder.addOwned(std::move(Copy)); } return std::move(Builder).take(); @@ -449,7 +436,7 @@ transferCFGBlock(const CFGBlock &Block, AnalysisContext &AC, } // User-provided analysis - AC.Analysis.transferTypeErased(Element, State.Lattice, State.Env); + AC.Analysis.transfer(Element, *State.Lattice, State.Env); if (PostAnalysisCallbacks.After) { PostAnalysisCallbacks.After(Element, State); @@ -487,7 +474,7 @@ transferCFGBlock(const CFGBlock &Block, AnalysisContext &AC, llvm::Expected<std::vector<std::optional<TypeErasedDataflowAnalysisState>>> runTypeErasedDataflowAnalysis( - const AdornedCFG &ACFG, TypeErasedDataflowAnalysis &Analysis, + const AdornedCFG &ACFG, DataflowAnalysis &Analysis, const Environment &InitEnv, const CFGEltCallbacksTypeErased &PostAnalysisCallbacks, std::int32_t MaxBlockVisits) { @@ -510,7 +497,7 @@ runTypeErasedDataflowAnalysis( // The entry basic block doesn't contain statements so it can be skipped. const CFGBlock &Entry = CFG.getEntry(); - BlockStates[Entry.getBlockID()] = {Analysis.typeErasedInitialElement(), + BlockStates[Entry.getBlockID()] = {Analysis.initialElement(), StartingEnv.fork()}; Worklist.enqueueSuccessors(&Entry); @@ -539,19 +526,18 @@ runTypeErasedDataflowAnalysis( OldBlockState->Env.dump(); }); if (isBackedgeNode(*Block)) { - LatticeJoinEffect Effect1 = Analysis.widenTypeErased( - NewBlockState.Lattice, OldBlockState->Lattice); - LatticeJoinEffect Effect2 = + LatticeEffect Effect1 = + NewBlockState.Lattice->widen(*OldBlockState->Lattice); + LatticeEffect Effect2 = NewBlockState.Env.widen(OldBlockState->Env, Analysis); - if (Effect1 == LatticeJoinEffect::Unchanged && - Effect2 == LatticeJoinEffect::Unchanged) { + if (Effect1 == LatticeEffect::Unchanged && + Effect2 == LatticeEffect::Unchanged) { // The state of `Block` didn't change from widening so there's no need // to revisit its successors. AC.Log.blockConverged(); continue; } - } else if (Analysis.isEqualTypeErased(OldBlockState->Lattice, - NewBlockState.Lattice) && + } else if (OldBlockState->Lattice->isEqual(*NewBlockState.Lattice) && OldBlockState->Env.equivalentTo(NewBlockState.Env, Analysis)) { // The state of `Block` didn't change after transfer so there's no need // to revisit its successors. diff --git a/clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp b/clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp index a2762046665a2c..532ab4716bd761 100644 --- a/clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp @@ -108,16 +108,19 @@ std::string ReplacePattern(std::string S, const std::string &Pattern, return S; } -template <typename Model> -class ModelAdaptorAnalysis - : public DataflowAnalysis<ModelAdaptorAnalysis<Model>, NoopLattice> { +template <typename Model> class ModelAdaptorAnalysis : public DataflowAnalysis { public: + using Lattice = NoopLattice; + explicit ModelAdaptorAnalysis(ASTContext &Context) - : DataflowAnalysis<ModelAdaptorAnalysis, NoopLattice>(Context) {} + : DataflowAnalysis(Context) {} - static NoopLattice initialElement() { return NoopLattice(); } + std::unique_ptr<DataflowLattice> initialElement() override { + return std::make_unique<Lattice>(); + } - void transfer(const CFGElement &E, NoopLattice &, Environment &Env) { + void transfer(const CFGElement &E, DataflowLattice &, + Environment &Env) override { M.transfer(E, Env); } diff --git a/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp b/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp index 88630119ba8a1a..c9cf006d0ff814 100644 --- a/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp @@ -1,8 +1,9 @@ #include "TestingSupport.h" #include "clang/ASTMatchers/ASTMatchers.h" -#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h" #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" #include "clang/Analysis/FlowSensitive/DataflowLattice.h" +#include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h" +#include "llvm/Support/ExtensibleRTTI.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" @@ -10,43 +11,59 @@ namespace clang::dataflow::test { namespace { using testing::HasSubstr; -struct TestLattice { +struct TestLattice : public llvm::RTTIExtends<TestLattice, DataflowLattice> { + inline static char ID = 0; + int Elements = 0; int Branches = 0; int Joins = 0; - LatticeJoinEffect join(const TestLattice &Other) { + DataflowLatticePtr clone() override { + return std::make_unique<TestLattice>(*this); + } + + bool isEqual(const DataflowLattice &Other) const override { + return *this == llvm::cast<const TestLattice>(Other); + } + + LatticeEffect join(const DataflowLattice &L) override { + const auto &Other = llvm::cast<const TestLattice>(L); if (Joins < 3) { ++Joins; Elements += Other.Elements; Branches += Other.Branches; - return LatticeJoinEffect::Changed; + return LatticeEffect::Changed; } - return LatticeJoinEffect::Unchanged; + return LatticeEffect::Unchanged; } - friend bool operator==(const TestLattice &LHS, const TestLattice &RHS) { - return std::tie(LHS.Elements, LHS.Branches, LHS.Joins) == + + bool operator==(const TestLattice &RHS) const { + return std::tie(Elements, Branches, Joins) == std::tie(RHS.Elements, RHS.Branches, RHS.Joins); } }; -class TestAnalysis : public DataflowAnalysis<TestAnalysis, TestLattice> { +class TestAnalysis : public DataflowAnalysis { public: + using Lattice = TestLattice; using DataflowAnalysis::DataflowAnalysis; - static TestLattice initialElement() { return TestLattice{}; } - void transfer(const CFGElement &, TestLattice &L, Environment &E) { + std::unique_ptr<DataflowLattice> initialElement() override { + return std::make_unique<Lattice>(); + } + void transfer(const CFGElement &, DataflowLattice &L, + Environment &E) override { E.getDataflowAnalysisContext().getOptions().Log->log( [](llvm::raw_ostream &OS) { OS << "transfer()"; }); - ++L.Elements; + ++llvm::cast<Lattice>(L).Elements; } - void transferBranch(bool Branch, const Stmt *S, TestLattice &L, - Environment &E) { + void transferBranch(bool Branch, const Stmt &, DataflowLattice &L, + Environment &E) override { E.getDataflowAnalysisContext().getOptions().Log->log( [&](llvm::raw_ostream &OS) { OS << "transferBranch(" << Branch << ")"; }); - ++L.Branches; + ++llvm::cast<Lattice>(L).Branches; } }; @@ -57,8 +74,7 @@ class TestLogger : public Logger { private: llvm::raw_string_ostream OS; - void beginAnalysis(const AdornedCFG &, - TypeErasedDataflowAnalysis &) override { + void beginAnalysis(const AdornedCFG &, DataflowAnalysis &) override { logText("beginAnalysis()"); } void endAnalysis() override { logText("\nendAnalysis()"); } @@ -76,7 +92,7 @@ class TestLogger : public Logger { OS << "enterElement(" << llvm::StringRef(S).trim() << ")\n"; } void recordState(TypeErasedDataflowAnalysisState &S) override { - const TestLattice &L = llvm::any_cast<TestLattice>(S.Lattice.Value); + const TestLattice &L = llvm::cast<TestLattice>(*S.Lattice); OS << "recordState(Elements=" << L.Elements << ", Branches=" << L.Branches << ", Joins=" << L.Joins << ")\n"; } diff --git a/clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp b/clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp index f6fe81e0dfc5f6..e03d611443daaa 100644 --- a/clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/MapLatticeTest.cpp @@ -1,6 +1,7 @@ #include "clang/Analysis/FlowSensitive/MapLattice.h" #include "clang/Analysis/FlowSensitive/DataflowLattice.h" #include "llvm/Support/Error.h" +#include "llvm/Support/ExtensibleRTTI.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include <ostream> @@ -10,7 +11,7 @@ using namespace dataflow; namespace { // A simple lattice for basic tests. -class BooleanLattice { +class BooleanLattice : public DataflowLattice { public: BooleanLattice() : Value(false) {} explicit BooleanLattice(bool B) : Value(B) {} @@ -19,7 +20,16 @@ class BooleanLattice { static BooleanLattice top() { return BooleanLattice(true); } - LatticeJoinEffect join(BooleanLattice Other) { + DataflowLatticePtr clone() override { + return std::make_unique<BooleanLattice>(*this); + } + + bool isEqual(const DataflowLattice &Other) const override { + return *this == llvm::cast<const BooleanLattice>(Other); + } + + LatticeEffect join(const DataflowLattice &L) override { + const auto &Other = llvm::cast<BooleanLattice>(L); auto Prev = Value; Value = Value || Other.Value; return Prev == Value ? LatticeJoinEffect::Unchanged @@ -44,6 +54,8 @@ class BooleanLattice { private: bool Value; }; + +using BoolMapLattice = MapLattice<int, BooleanLattice>; } // namespace static constexpr int Key1 = 0; @@ -55,7 +67,7 @@ using ::testing::Pair; using ::testing::UnorderedElementsAre; TEST(MapLatticeTest, InsertWorks) { - MapLattice<int, BooleanLattice> Lattice; + BoolMapLattice Lattice; EXPECT_THAT(Lattice.insert({Key1, BooleanLattice(false)}), Pair(_, true)); EXPECT_THAT(Lattice.insert({Key2, BooleanLattice(false)}), Pair(_, true)); @@ -68,10 +80,10 @@ TEST(MapLatticeTest, InsertWorks) { } TEST(MapLatticeTest, ComparisonWorks) { - MapLattice<int, BooleanLattice> Lattice1; + BoolMapLattice Lattice1; Lattice1.insert({Key1, BooleanLattice(true)}); Lattice1.insert({Key2, BooleanLattice(false)}); - MapLattice<int, BooleanLattice> Lattice2 = Lattice1; + BoolMapLattice Lattice2 = Lattice1; EXPECT_EQ(Lattice1, Lattice2); Lattice2.find(Key2)->second = BooleanLattice(true); @@ -79,11 +91,11 @@ TEST(MapLatticeTest, ComparisonWorks) { } TEST(MapLatticeTest, JoinChange) { - MapLattice<int, BooleanLattice> Lattice1; + BoolMapLattice Lattice1; Lattice1.insert({Key1, BooleanLattice(false)}); Lattice1.insert({Key2, BooleanLattice(false)}); - MapLattice<int, BooleanLattice> Lattice2; + BoolMapLattice Lattice2; Lattice2.insert({Key1, BooleanLattice(true)}); Lattice2.insert({Key2, BooleanLattice(true)}); @@ -97,7 +109,7 @@ TEST(MapLatticeTest, JoinChange) { } TEST(MapLatticeTest, JoinEqNoChange) { - MapLattice<int, BooleanLattice> Lattice; + BoolMapLattice Lattice; Lattice.insert({Key1, BooleanLattice(false)}); Lattice.insert({Key2, BooleanLattice(false)}); @@ -107,11 +119,11 @@ TEST(MapLatticeTest, JoinEqNoChange) { } TEST(MapLatticeTest, JoinLtNoChange) { - MapLattice<int, BooleanLattice> Lattice1; + BoolMapLattice Lattice1; Lattice1.insert({Key1, BooleanLattice(false)}); Lattice1.insert({Key2, BooleanLattice(false)}); - MapLattice<int, BooleanLattice> Lattice2; + BoolMapLattice Lattice2; Lattice2.insert({Key1, BooleanLattice(true)}); Lattice2.insert({Key2, BooleanLattice(true)}); @@ -128,9 +140,9 @@ TEST(MapLatticeTest, JoinLtNoChange) { } TEST(MapLatticeTest, JoinDifferentDomainsProducesUnion) { - MapLattice<int, BooleanLattice> Lattice1; + BoolMapLattice Lattice1; Lattice1.insert({Key1, BooleanLattice(true)}); - MapLattice<int, BooleanLattice> Lattice2; + BoolMapLattice Lattice2; Lattice2.insert({Key2, BooleanLattice(true)}); ASSERT_EQ(Lattice1.join(Lattice2), LatticeJoinEffect::Changed); @@ -139,7 +151,7 @@ TEST(MapLatticeTest, JoinDifferentDomainsProducesUnion) { } TEST(MapLatticeTest, FindWorks) { - MapLattice<int, BooleanLattice> Lattice; + BoolMapLattice Lattice; Lattice.insert({Key1, BooleanLattice(true)}); Lattice.insert({Key2, BooleanLattice(false)}); @@ -153,7 +165,7 @@ TEST(MapLatticeTest, FindWorks) { } TEST(MapLatticeTest, ContainsWorks) { - MapLattice<int, BooleanLattice> Lattice; + BoolMapLattice Lattice; Lattice.insert({Key1, BooleanLattice(true)}); EXPECT_TRUE(Lattice.contains(Key1)); EXPECT_FALSE(Lattice.contains(Key2)); diff --git a/clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp b/clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp index ed95887a45f1a3..a7f2e4900b438c 100644 --- a/clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp @@ -45,7 +45,9 @@ using namespace ast_matchers; // Models the value of an expression at a program point, for all paths through // the program. -struct ValueLattice { +struct ValueLattice : public llvm::RTTIExtends<ValueLattice, DataflowLattice> { + inline static char ID = 0; + // FIXME: change the internal representation to use a `std::variant`, once // clang admits C++17 constructs. enum class ValueState : bool { @@ -61,15 +63,14 @@ struct ValueLattice { // `State`. std::optional<int64_t> Value; - constexpr ValueLattice() - : State(ValueState::Undefined), Value(std::nullopt) {} - constexpr ValueLattice(int64_t V) : State(ValueState::Defined), Value(V) {} - constexpr ValueLattice(ValueState S) : State(S), Value(std::nullopt) {} + ValueLattice() : State(ValueState::Undefined), Value(std::nullopt) {} + explicit ValueLattice(int64_t V) : State(ValueState::Defined), Value(V) {} + explicit ValueLattice(ValueState S) : State(S), Value(std::nullopt) {} - static constexpr ValueLattice bottom() { + static ValueLattice bottom() { return ValueLattice(ValueState::Undefined); } - static constexpr ValueLattice top() { + static ValueLattice top() { return ValueLattice(ValueState::Defined); } @@ -80,17 +81,26 @@ struct ValueLattice { return !(Lhs == Rhs); } - LatticeJoinEffect join(const ValueLattice &Other) { + DataflowLatticePtr clone() override { + return std::make_unique<ValueLattice>(*this); + } + + bool isEqual(const DataflowLattice &Other) const override { + return *this == llvm::cast<const ValueLattice>(Other); + } + + LatticeEffect join(const DataflowLattice &L) override { + const auto &Other = llvm::cast<ValueLattice>(L); if (*this == Other || Other == bottom() || *this == top()) - return LatticeJoinEffect::Unchanged; + return LatticeEffect::Unchanged; if (*this == bottom()) { *this = Other; - return LatticeJoinEffect::Changed; + return LatticeEffect::Changed; } *this = top(); - return LatticeJoinEffect::Changed; + return LatticeEffect::Changed; } }; @@ -122,24 +132,24 @@ auto refToVar() { return declRefExpr(to(varDecl().bind(kVar))); } // not account for the variable's address possibly escaping, which would // invalidate the analysis. It also could be optimized to drop out-of-scope // variables from the map. -class ConstantPropagationAnalysis - : public DataflowAnalysis<ConstantPropagationAnalysis, - ConstantPropagationLattice> { +class ConstantPropagationAnalysis : public DataflowAnalysis { public: + using Lattice = ConstantPropagationLattice; + explicit ConstantPropagationAnalysis(ASTContext &Context) - : DataflowAnalysis<ConstantPropagationAnalysis, - ConstantPropagationLattice>(Context) {} + : DataflowAnalysis(Context) {} - static ConstantPropagationLattice initialElement() { - return ConstantPropagationLattice::bottom(); + std::unique_ptr<DataflowLattice> initialElement() override { + return std::make_unique<Lattice>(ConstantPropagationLattice::bottom()); } - void transfer(const CFGElement &E, ConstantPropagationLattice &Vars, - Environment &Env) { + void transfer(const CFGElement &E, DataflowLattice &L, + Environment &Env) override { + auto &Vars = llvm::cast<ConstantPropagationLattice>(L); auto CS = E.getAs<CFGStmt>(); if (!CS) return; - auto S = CS->getStmt(); + auto *S = CS->getStmt(); auto matcher = stmt(anyOf(declStmt(hasSingleDecl( varDecl(decl().bind(kVar), hasType(isInteger()), diff --git a/clang/unittests/Analysis/FlowSensitive/SignAnalysisTest.cpp b/clang/unittests/Analysis/FlowSensitive/SignAnalysisTest.cpp index b8fc528dbdce07..9e2166d6bc0edb 100644 --- a/clang/unittests/Analysis/FlowSensitive/SignAnalysisTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/SignAnalysisTest.cpp @@ -351,17 +351,19 @@ auto buildTransferMatchSwitch() { .Build(); } -class SignPropagationAnalysis - : public DataflowAnalysis<SignPropagationAnalysis, NoopLattice> { +class SignPropagationAnalysis : public DataflowAnalysis { public: + using Lattice = NoopLattice; + SignPropagationAnalysis(ASTContext &Context) - : DataflowAnalysis<SignPropagationAnalysis, NoopLattice>(Context), + : DataflowAnalysis(Context), TransferMatchSwitch(buildTransferMatchSwitch()) {} - static NoopLattice initialElement() { return {}; } + std::unique_ptr<DataflowLattice> initialElement() override { return {}; } - void transfer(const CFGElement &Elt, NoopLattice &L, Environment &Env) { - LatticeTransferState State(L, Env); + void transfer(const CFGElement &Elt, DataflowLattice &L, + Environment &Env) override { + LatticeTransferState State(llvm::cast<NoopLattice>(L), Env); TransferMatchSwitch(Elt, getASTContext(), State); } void join(QualType Type, const Value &Val1, const Environment &Env1, diff --git a/clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp b/clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp index b76ce4fa426427..9670cfa344b5b5 100644 --- a/clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp @@ -46,7 +46,10 @@ using namespace ast_matchers; // A semi-lattice for dataflow analysis that tracks the value of a single // integer variable. If it can be identified with a single (constant) value, // then that value is stored. -struct ConstantPropagationLattice { +struct ConstantPropagationLattice + : public llvm::RTTIExtends<ConstantPropagationLattice, DataflowLattice> { + inline static char ID = 0; + // A null `Var` represents "top": either more than one value is possible or // more than one variable was encountered. Otherwise, `Data` indicates that // `Var` has the given `Value` at the program point with which this lattice @@ -62,11 +65,14 @@ struct ConstantPropagationLattice { // `std::nullopt` is "bottom". std::optional<VarValue> Data; - static constexpr ConstantPropagationLattice bottom() { - return {std::nullopt}; + explicit ConstantPropagationLattice(std::optional<VarValue> Data) + : Data(std::move(Data)) {} + + static ConstantPropagationLattice bottom() { + return ConstantPropagationLattice{std::nullopt}; } - static constexpr ConstantPropagationLattice top() { - return {VarValue{nullptr, 0}}; + static ConstantPropagationLattice top() { + return ConstantPropagationLattice{VarValue{nullptr, 0}}; } friend bool operator==(const ConstantPropagationLattice &Lhs, @@ -74,17 +80,26 @@ struct ConstantPropagationLattice { return Lhs.Data == Rhs.Data; } - LatticeJoinEffect join(const ConstantPropagationLattice &Other) { + DataflowLatticePtr clone() override { + return std::make_unique<ConstantPropagationLattice>(*this); + } + + bool isEqual(const DataflowLattice &Other) const override { + return *this == llvm::cast<const ConstantPropagationLattice>(Other); + } + + LatticeEffect join(const DataflowLattice &L) override { + const auto &Other = llvm::cast<ConstantPropagationLattice>(L); if (*this == Other || Other == bottom() || *this == top()) - return LatticeJoinEffect::Unchanged; + return LatticeEffect::Unchanged; if (*this == bottom()) { *this = Other; - return LatticeJoinEffect::Changed; + return LatticeEffect::Changed; } *this = top(); - return LatticeJoinEffect::Changed; + return LatticeEffect::Changed; } }; @@ -112,24 +127,23 @@ namespace { // details needed for a real analysis in production. Most notably, the transfer // function does not account for the variable's address possibly escaping, which // would invalidate the analysis. -class ConstantPropagationAnalysis - : public DataflowAnalysis<ConstantPropagationAnalysis, - ConstantPropagationLattice> { +class ConstantPropagationAnalysis : public DataflowAnalysis { public: + using Lattice = ConstantPropagationLattice; explicit ConstantPropagationAnalysis(ASTContext &Context) - : DataflowAnalysis<ConstantPropagationAnalysis, - ConstantPropagationLattice>(Context) {} + : DataflowAnalysis(Context) {} - static ConstantPropagationLattice initialElement() { - return ConstantPropagationLattice::bottom(); + std::unique_ptr<DataflowLattice> initialElement() override { + return std::make_unique<Lattice>(ConstantPropagationLattice::bottom()); } - void transfer(const CFGElement &E, ConstantPropagationLattice &Element, - Environment &Env) { + void transfer(const CFGElement &E, DataflowLattice &L, + Environment &Env) override { + auto &Element = llvm::cast<ConstantPropagationLattice>(L); auto CS = E.getAs<CFGStmt>(); if (!CS) return; - auto S = CS->getStmt(); + auto *S = CS->getStmt(); auto matcher = stmt( anyOf(declStmt(hasSingleDecl(varDecl(hasType(isInteger()), hasInitializer(expr().bind(kInit))) diff --git a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h index 2ba84373531c6d..13482f2aad075f 100644 --- a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h +++ b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h @@ -102,7 +102,7 @@ struct AnalysisOutputs { /// function and is analyzed. const AdornedCFG &ACFG; /// The analysis to be run. - TypeErasedDataflowAnalysis &Analysis; + DataflowAnalysis &Analysis; /// Initial state to start the analysis. const Environment &InitEnv; // Stores the state of a CFG block if it has been evaluated by the analysis. @@ -254,8 +254,7 @@ checkDataflow(AnalysisInputs<AnalysisT> AI, AI.Callbacks.Before( Context, Element, TransferStateForDiagnostics<typename AnalysisT::Lattice>( - llvm::any_cast<const typename AnalysisT::Lattice &>( - State.Lattice.Value), + llvm::cast<const typename AnalysisT::Lattice>(State.Lattice), State.Env)); }; } @@ -266,8 +265,7 @@ checkDataflow(AnalysisInputs<AnalysisT> AI, AI.Callbacks.After( Context, Element, TransferStateForDiagnostics<typename AnalysisT::Lattice>( - llvm::any_cast<const typename AnalysisT::Lattice &>( - State.Lattice.Value), + llvm::cast<const typename AnalysisT::Lattice>(State.Lattice), State.Env)); }; } diff --git a/clang/unittests/Analysis/FlowSensitive/TransferBranchTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferBranchTest.cpp index ef78131dd72a3a..651541923fb658 100644 --- a/clang/unittests/Analysis/FlowSensitive/TransferBranchTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferBranchTest.cpp @@ -25,12 +25,22 @@ namespace { using namespace ast_matchers; -struct TestLattice { +struct TestLattice : public llvm::RTTIExtends<TestLattice, DataflowLattice> { + inline static char ID = 0; + std::optional<bool> Branch; static TestLattice bottom() { return {}; } + DataflowLatticePtr clone() override { + return std::make_unique<TestLattice>(*this); + } + + bool isEqual(const DataflowLattice &Other) const override { + return *this == llvm::cast<const TestLattice>(Other); + } + // Does not matter for this test, but we must provide some definition of join. - LatticeJoinEffect join(const TestLattice &Other) { + LatticeJoinEffect join(const DataflowLattice &) override { return LatticeJoinEffect::Unchanged; } friend bool operator==(const TestLattice &Lhs, const TestLattice &Rhs) { @@ -38,16 +48,19 @@ struct TestLattice { } }; -class TestPropagationAnalysis - : public DataflowAnalysis<TestPropagationAnalysis, TestLattice> { +class TestPropagationAnalysis : public DataflowAnalysis { public: + using Lattice = TestLattice; + explicit TestPropagationAnalysis(ASTContext &Context) - : DataflowAnalysis<TestPropagationAnalysis, TestLattice>(Context) {} - static TestLattice initialElement() { return TestLattice::bottom(); } - void transfer(const CFGElement &, TestLattice &, Environment &) {} - void transferBranch(bool Branch, const Stmt *S, TestLattice &L, - Environment &Env) { - L.Branch = Branch; + : DataflowAnalysis(Context) {} + std::unique_ptr<DataflowLattice> initialElement() override { + return std::make_unique<Lattice>(TestLattice::bottom()); + } + void transfer(const CFGElement &, DataflowLattice &, Environment &) override {} + void transferBranch(bool Branch, const Stmt &S, DataflowLattice &L, + Environment &Env) override { + llvm::cast<Lattice>(L).Branch = Branch; } }; diff --git a/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp b/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp index 8717d9753d161b..ce0af893a8fee3 100644 --- a/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp @@ -20,6 +20,7 @@ #include "clang/Analysis/FlowSensitive/DataflowLattice.h" #include "clang/Analysis/FlowSensitive/DebugSupport.h" #include "clang/Analysis/FlowSensitive/NoopAnalysis.h" +#include "clang/Analysis/FlowSensitive/NoopLattice.h" #include "clang/Analysis/FlowSensitive/Value.h" #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h" #include "clang/Tooling/Tooling.h" @@ -28,6 +29,7 @@ #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" +#include "llvm/Support/ExtensibleRTTI.h" #include "llvm/Testing/ADT/StringMapEntry.h" #include "llvm/Testing/Support/Error.h" #include "gmock/gmock.h" @@ -407,34 +409,50 @@ TEST_F(DiscardExprStateTest, CallWithParenExprTreatedCorrectly) { EXPECT_NE(CallExpectState.Env.getValue(FnToPtrDecay), nullptr); } -struct NonConvergingLattice { +struct NonConvergingLattice + : public llvm::RTTIExtends<NonConvergingLattice, DataflowLattice> { + inline static char ID = 0; int State; + explicit NonConvergingLattice(int S) : State(S) {} + bool operator==(const NonConvergingLattice &Other) const { return State == Other.State; } - LatticeJoinEffect join(const NonConvergingLattice &Other) { - if (Other.State == 0) + LatticeEffect join(const DataflowLattice &Other) override { + const auto &L = llvm::cast<const NonConvergingLattice>(Other); + if (L.State == 0) return LatticeJoinEffect::Unchanged; - State += Other.State; + State += L.State; return LatticeJoinEffect::Changed; } + + bool isEqual(const DataflowLattice &Other) const override { + return *this == llvm::cast<const NonConvergingLattice>(Other); + } + + std::unique_ptr<DataflowLattice> clone() override { + return std::make_unique<NonConvergingLattice>(*this); + } }; -class NonConvergingAnalysis - : public DataflowAnalysis<NonConvergingAnalysis, NonConvergingLattice> { +class NonConvergingAnalysis : public DataflowAnalysis { public: + using Lattice = NonConvergingLattice; + explicit NonConvergingAnalysis(ASTContext &Context) - : DataflowAnalysis<NonConvergingAnalysis, NonConvergingLattice>( - Context, - // Don't apply builtin transfer function. - DataflowAnalysisOptions{std::nullopt}) {} + : DataflowAnalysis(Context, + // Don't apply builtin transfer function. + DataflowAnalysisOptions{std::nullopt}) {} - static NonConvergingLattice initialElement() { return {0}; } + std::unique_ptr<DataflowLattice> initialElement() override { + return std::make_unique<Lattice>(0); + } - void transfer(const CFGElement &, NonConvergingLattice &E, Environment &) { - ++E.State; + void transfer(const CFGElement &, DataflowLattice &L, + Environment &) override { + ++llvm::cast<NonConvergingLattice>(L).State; } }; @@ -481,7 +499,9 @@ TEST_F(DataflowAnalysisTest, JoinBoolLValues) { llvm::Succeeded()); } -struct FunctionCallLattice { +struct FunctionCallLattice + : public llvm::RTTIExtends<FunctionCallLattice, DataflowLattice> { + inline static char ID = 0; using FunctionSet = llvm::SmallSet<std::string, 8>; FunctionSet CalledFunctions; @@ -489,7 +509,8 @@ struct FunctionCallLattice { return CalledFunctions == Other.CalledFunctions; } - LatticeJoinEffect join(const FunctionCallLattice &Other) { + LatticeEffect join(const DataflowLattice &L) override { + const auto &Other = llvm::cast<const FunctionCallLattice>(L); if (Other.CalledFunctions.empty()) return LatticeJoinEffect::Unchanged; const size_t size_before = CalledFunctions.size(); @@ -498,6 +519,13 @@ struct FunctionCallLattice { return CalledFunctions.size() == size_before ? LatticeJoinEffect::Unchanged : LatticeJoinEffect::Changed; } + + std::unique_ptr<DataflowLattice> clone() override { + return std::make_unique<FunctionCallLattice>(*this); + } + bool isEqual(const DataflowLattice &Other) const override { + return *this == llvm::cast<const FunctionCallLattice>(Other); + } }; std::ostream &operator<<(std::ostream &OS, const FunctionCallLattice &L) { @@ -507,22 +535,27 @@ std::ostream &operator<<(std::ostream &OS, const FunctionCallLattice &L) { return OS << "{" << S << "}"; } -class FunctionCallAnalysis - : public DataflowAnalysis<FunctionCallAnalysis, FunctionCallLattice> { +class FunctionCallAnalysis : public DataflowAnalysis { public: + using Lattice = FunctionCallLattice; + explicit FunctionCallAnalysis(ASTContext &Context) - : DataflowAnalysis<FunctionCallAnalysis, FunctionCallLattice>(Context) {} + : DataflowAnalysis(Context) {} - static FunctionCallLattice initialElement() { return {}; } + std::unique_ptr<DataflowLattice> initialElement() override { + return std::make_unique<Lattice>(); + } - void transfer(const CFGElement &Elt, FunctionCallLattice &E, Environment &) { + void transfer(const CFGElement &Elt, DataflowLattice &L, + Environment &) override { + auto &FCL = llvm::cast<FunctionCallLattice>(L); auto CS = Elt.getAs<CFGStmt>(); if (!CS) return; const auto *S = CS->getStmt(); if (auto *C = dyn_cast<CallExpr>(S)) { if (auto *F = dyn_cast<FunctionDecl>(C->getCalleeDecl())) { - E.CalledFunctions.insert(F->getNameInfo().getAsString()); + FCL.CalledFunctions.insert(F->getNameInfo().getAsString()); } } } @@ -696,10 +729,12 @@ TEST_F(NoreturnDestructorTest, ConditionalOperatorNestedBranchReturns) { // Models an analysis that uses flow conditions. class SpecialBoolAnalysis final - : public DataflowAnalysis<SpecialBoolAnalysis, NoopLattice> { + : public DataflowAnalysis { public: + using Lattice = NoopLattice; + explicit SpecialBoolAnalysis(ASTContext &Context, Environment &Env) - : DataflowAnalysis<SpecialBoolAnalysis, NoopLattice>(Context) { + : DataflowAnalysis(Context) { Env.getDataflowAnalysisContext().setSyntheticFieldCallback( [](QualType Ty) -> llvm::StringMap<QualType> { RecordDecl *RD = Ty->getAsRecordDecl(); @@ -710,9 +745,12 @@ class SpecialBoolAnalysis final }); } - static NoopLattice initialElement() { return {}; } + std::unique_ptr<DataflowLattice> initialElement() override { + return std::make_unique<Lattice>(); + } - void transfer(const CFGElement &Elt, NoopLattice &, Environment &Env) { + void transfer(const CFGElement &Elt, DataflowLattice &, + Environment &Env) override { auto CS = Elt.getAs<CFGStmt>(); if (!CS) return; @@ -807,15 +845,19 @@ TEST_F(JoinFlowConditionsTest, JoinDistinctButProvablyEquivalentValues) { }); } -class NullPointerAnalysis final - : public DataflowAnalysis<NullPointerAnalysis, NoopLattice> { +class NullPointerAnalysis final : public DataflowAnalysis { public: + using Lattice = NoopLattice; + explicit NullPointerAnalysis(ASTContext &Context) - : DataflowAnalysis<NullPointerAnalysis, NoopLattice>(Context) {} + : DataflowAnalysis(Context) {} - static NoopLattice initialElement() { return {}; } + std::unique_ptr<DataflowLattice> initialElement() override { + return std::make_unique<Lattice>(); + } - void transfer(const CFGElement &Elt, NoopLattice &, Environment &Env) { + void transfer(const CFGElement &Elt, DataflowLattice &, + Environment &Env) override { auto CS = Elt.getAs<CFGStmt>(); if (!CS) return; @@ -1520,14 +1562,18 @@ TEST_F(FlowConditionTest, PointerToBoolImplicitCast) { }); } -class TopAnalysis final : public DataflowAnalysis<TopAnalysis, NoopLattice> { +class TopAnalysis final : public DataflowAnalysis { public: - explicit TopAnalysis(ASTContext &Context) - : DataflowAnalysis<TopAnalysis, NoopLattice>(Context) {} + using Lattice = NoopLattice; - static NoopLattice initialElement() { return {}; } + explicit TopAnalysis(ASTContext &Context) : DataflowAnalysis(Context) {} + + std::unique_ptr<DataflowLattice> initialElement() override { + return std::make_unique<Lattice>(); + } - void transfer(const CFGElement &Elt, NoopLattice &, Environment &Env) { + void transfer(const CFGElement &Elt, DataflowLattice &, + Environment &Env) override { auto CS = Elt.getAs<CFGStmt>(); if (!CS) return; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits