[clang] Introduce virtual interface for lattices and remove dependency on `llvm::Any`. (PR #120967)
Yitzhak Mandelbaum via cfe-commits
cfe-commits at lists.llvm.org
Mon Dec 30 12:34:33 PST 2024
https://github.com/ymand updated https://github.com/llvm/llvm-project/pull/120967
>From db206514c03a58065e24afccd55886a012b2abcc Mon Sep 17 00:00:00 2001
From: Yitzhak Mandelbaum <yitzhakm at 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.
As a side-effect, we are able to greatly simplify the interface of
`DataflowAnalysis` and collapse the distinction between it and
`TypeErasedDataflowAnalysis`.
---
.../CachedConstAccessorsLattice.h | 29 +++-
.../Analysis/FlowSensitive/DataflowAnalysis.h | 147 +-----------------
.../Analysis/FlowSensitive/DataflowLattice.h | 46 ++++++
.../clang/Analysis/FlowSensitive/Logger.h | 5 +-
.../clang/Analysis/FlowSensitive/MapLattice.h | 26 +++-
.../Models/UncheckedOptionalAccessModel.h | 19 ++-
.../Analysis/FlowSensitive/NoopAnalysis.h | 20 +--
.../Analysis/FlowSensitive/NoopLattice.h | 30 ++--
.../TypeErasedDataflowAnalysis.h | 83 ++++------
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 | 71 ++++-----
.../FlowSensitive/ChromiumCheckModelTest.cpp | 15 +-
.../Analysis/FlowSensitive/LoggerTest.cpp | 50 ++++--
.../Analysis/FlowSensitive/MapLatticeTest.cpp | 40 +++--
.../MultiVarConstantPropagationTest.cpp | 56 ++++---
.../FlowSensitive/SignAnalysisTest.cpp | 17 +-
.../SingleVarConstantPropagationTest.cpp | 52 ++++---
.../Analysis/FlowSensitive/TestingSupport.h | 10 +-
.../FlowSensitive/TransferBranchTest.cpp | 34 ++--
.../TypeErasedDataflowAnalysisTest.cpp | 117 +++++++++-----
24 files changed, 451 insertions(+), 437 deletions(-)
diff --git a/clang/include/clang/Analysis/FlowSensitive/CachedConstAccessorsLattice.h b/clang/include/clang/Analysis/FlowSensitive/CachedConstAccessorsLattice.h
index 48c5287367739a..07478d40a42345 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 {
@@ -45,9 +46,15 @@ namespace dataflow {
/// use(s.getFoo().value()); // unsafe (invalidate cache for s)
/// }
/// }
-template <typename Base> class CachedConstAccessorsLattice : public Base {
+template <typename Base>
+class CachedConstAccessorsLattice
+ : public llvm::RTTIExtends<CachedConstAccessorsLattice<Base>, Base> {
public:
- using Base::Base; // inherit all constructors
+ using Parent = llvm::RTTIExtends<CachedConstAccessorsLattice, Base>;
+ using Parent::Parent; // inherit all constructors
+
+ /// For RTTI.
+ inline static char ID = 0;
/// Creates or returns a previously created `Value` associated with a const
/// method call `obj.getFoo()` where `RecordLoc` is the
@@ -88,7 +95,16 @@ 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 llvm::isa<const CachedConstAccessorsLattice>(Other) &&
+ *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
@@ -145,10 +161,11 @@ joinConstMethodMap(
} // namespace internal
template <typename Base>
-LatticeEffect CachedConstAccessorsLattice<Base>::join(
- const CachedConstAccessorsLattice<Base> &Other) {
+LatticeEffect
+CachedConstAccessorsLattice<Base>::join(const DataflowLattice &L) {
+ LatticeEffect Effect = Base::join(L);
- LatticeEffect Effect = Base::join(Other);
+ const auto &Other = llvm::cast<const CachedConstAccessorsLattice<Base>>(L);
// 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
diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
index e6efde091871fc..12c7968f34b65b 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(
@@ -254,7 +121,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.After(
@@ -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)),
+ std::move(*llvm::cast<typename AnalysisT::Lattice>(
+ State.Lattice.get())),
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.get()),
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.get()),
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..485a1b967653c3 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,48 @@ enum class LatticeEffect {
// DEPRECATED. Use `LatticeEffect`.
using LatticeJoinEffect = LatticeEffect;
+class DataflowLattice
+ : public llvm::RTTIExtends<DataflowLattice, llvm::RTTIRoot> {
+public:
+ /// For RTTI.
+ inline static char ID = 0;
+
+ DataflowLattice() = default;
+
+ /// Joins the object with `Other` by computing their least upper bound,
+ /// modifies the object if necessary, and returns an effect indicating whether
+ /// any changes were made to it.
+ virtual LatticeEffect join(const DataflowLattice &Other) = 0;
+
+ virtual std::unique_ptr<DataflowLattice> clone() = 0;
+
+ /// Replaces this lattice element with one that approximates it, given the
+ /// previous element at the current program point. The approximation should
+ /// be chosen so that the analysis 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`.
+ ///
+ /// Overriding `widen` is optional -- it is only needed to either accelerate
+ /// convergence (for lattices with non-trivial height) or guarantee
+ /// convergence (for lattices with infinite height). The default
+ /// implementation simply checks for equality.
+ ///
+ /// Returns an indication of whether any changes were made to the object. This
+ /// saves a separate call to `isEqual` 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..32719b9170e779 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,8 +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() {}
// At any time during the analysis, we're computing the state for some target
diff --git a/clang/include/clang/Analysis/FlowSensitive/MapLattice.h b/clang/include/clang/Analysis/FlowSensitive/MapLattice.h
index b2d147e4ae444b..d428584ca53ce4 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 llvm::isa<const MapLattice>(Other) &&
+ *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..a65b8fb45e6676 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 {
@@ -53,23 +55,24 @@ using UncheckedOptionalAccessLattice = CachedConstAccessorsLattice<NoopLattice>;
/// 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> {
+class UncheckedOptionalAccessModel : 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..fbee0f4a4bf4fb 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,25 @@ 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; }
+ /// For RTTI.
+ 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 +51,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..684d0a82b5727d 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,73 +39,42 @@ struct DataflowAnalysisOptions {
DataflowAnalysisContext::Options{};
};
-/// Type-erased lattice element container.
-///
-/// Requirements:
-///
-/// The type of the object stored in the container must be a bounded
-/// join-semilattice.
-struct TypeErasedLattice {
- llvm::Any Value;
-};
-
-/// Type-erased base class for dataflow analyses built on a single lattice type.
-class TypeErasedDataflowAnalysis : public Environment::ValueModel {
+/// Base class for dataflow analyses built on a single lattice type. The
+/// framework guarantees that the type of lattice elements passed to `transfer`
+/// and `transferBranch` always matches the type of elements provided by
+/// `initialElement`.
+class DataflowAnalysis : public Environment::ValueModel {
+ ASTContext &Context;
DataflowAnalysisOptions Options;
public:
- TypeErasedDataflowAnalysis() : Options({}) {}
-
- TypeErasedDataflowAnalysis(DataflowAnalysisOptions Options)
- : Options(Options) {}
+ explicit DataflowAnalysis(ASTContext &Context) : Context(Context) {}
- virtual ~TypeErasedDataflowAnalysis() {}
+ explicit DataflowAnalysis(ASTContext &Context,
+ DataflowAnalysisOptions Options)
+ : Context(Context), Options(Options) {}
/// Returns the `ASTContext` that is used by the analysis.
- virtual ASTContext &getASTContext() = 0;
+ ASTContext &getASTContext() { return Context; }
- /// Returns a type-erased lattice element that models the initial state of a
+ /// Returns a 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;
+ virtual DataflowLatticePtr initialElement() = 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;
+ /// 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.
+ /// 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.
- // FIXME: Change `Stmt` argument to a reference.
- virtual void transferBranchTypeErased(bool Branch, const Stmt *,
- TypeErasedLattice &, Environment &) = 0;
+ 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.
@@ -119,16 +87,17 @@ class TypeErasedDataflowAnalysis : public Environment::ValueModel {
/// 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 +128,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..ee9a61fc8185d3 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;
@@ -176,11 +161,10 @@ class JoinedStateBuilder {
std::vector<const TypeErasedDataflowAnalysisState *> All;
std::deque<TypeErasedDataflowAnalysisState> Owned;
- TypeErasedDataflowAnalysisState
- join(const TypeErasedDataflowAnalysisState &L,
- const TypeErasedDataflowAnalysisState &R) {
- return {AC.Analysis.joinTypeErased(L.Lattice, R.Lattice),
- Environment::join(L.Env, R.Env, AC.Analysis, JoinBehavior)};
+ void join(TypeErasedDataflowAnalysisState &L,
+ const TypeErasedDataflowAnalysisState &R) {
+ L.Lattice->join(*R.Lattice);
+ L.Env = Environment::join(L.Env, R.Env, AC.Analysis, JoinBehavior);
}
public:
@@ -200,18 +184,23 @@ 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)};
-
- auto Result = join(*All[0], *All[1]);
+ return {All[0]->Lattice->clone(),
+ Environment::join(All[0]->Env, All[0]->Env, AC.Analysis,
+ JoinBehavior)};
+
+ auto L0 = All[0]->Lattice->clone();
+ L0->join(*All[1]->Lattice);
+ auto Result = TypeErasedDataflowAnalysisState{
+ std::move(L0),
+ Environment::join(All[0]->Env, All[1]->Env, AC.Analysis, JoinBehavior)};
for (unsigned I = 2; I < All.size(); ++I)
- Result = join(Result, *All[I]);
+ join(Result, *All[I]);
return Result;
}
};
@@ -317,8 +306,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 +437,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 +475,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 +498,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 +527,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..47dece3ae29355 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,17 +63,12 @@ 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() {
- return ValueLattice(ValueState::Undefined);
- }
- static constexpr ValueLattice top() {
- return ValueLattice(ValueState::Defined);
- }
+ static ValueLattice bottom() { return ValueLattice(ValueState::Undefined); }
+ static ValueLattice top() { return ValueLattice(ValueState::Defined); }
friend bool operator==(const ValueLattice &Lhs, const ValueLattice &Rhs) {
return Lhs.State == Rhs.State && Lhs.Value == Rhs.Value;
@@ -80,17 +77,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 +128,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..fb4b48fd38e302 100644
--- a/clang/unittests/Analysis/FlowSensitive/SignAnalysisTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/SignAnalysisTest.cpp
@@ -351,19 +351,24 @@ 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 std::make_unique<NoopLattice>();
+ }
- 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,
const Value &Val2, const Environment &Env2, Value &MergedVal,
Environment &MergedEnv) override;
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..a97f2053c2a48b 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,8 @@ 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.get()),
State.Env));
};
}
@@ -266,8 +266,8 @@ 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.get()),
State.Env));
};
}
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferBranchTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferBranchTest.cpp
index ef78131dd72a3a..e9da152efbf221 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,20 @@ 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..8d5b82fd95d30c 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());
}
}
}
@@ -695,11 +728,12 @@ TEST_F(NoreturnDestructorTest, ConditionalOperatorNestedBranchReturns) {
}
// Models an analysis that uses flow conditions.
-class SpecialBoolAnalysis final
- : public DataflowAnalysis<SpecialBoolAnalysis, NoopLattice> {
+class SpecialBoolAnalysis final : 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 +744,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 +844,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 +1561,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;
More information about the cfe-commits
mailing list