From 58abe36ae7654987f5af793e3e261ac0b43c870b Mon Sep 17 00:00:00 2001
From: Eric Li
Date: Wed, 4 May 2022 17:15:00 +0000
Subject: [PATCH] [clang][dataflow] Add flowConditionIsTautology function
Provide a way for users to check if a flow condition is
unconditionally true.
Differential Revision: https://reviews.llvm.org/D124943
---
.../FlowSensitive/DataflowAnalysisContext.h | 4 +++
.../FlowSensitive/DataflowAnalysisContext.cpp | 13 ++++++++++
.../FlowSensitive/DataflowAnalysisContextTest.cpp | 29 ++++++++++++++++++++++
3 files changed, 46 insertions(+)
diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
index 5cf681e..a762cb9 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -173,6 +173,10 @@ public:
/// identified by `Token` imply that `Val` is true.
bool flowConditionImplies(AtomicBoolValue &Token, BoolValue &Val);
+ /// Returns true if and only if the constraints of the flow condition
+ /// identified by `Token` are always true.
+ bool flowConditionIsTautology(AtomicBoolValue &Token);
+
private:
/// Adds all constraints of the flow condition identified by `Token` and all
/// of its transitive dependencies to `Constraints`. `VisitedTokens` is used
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
index 4274fca..b29983e 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -117,6 +117,19 @@ bool DataflowAnalysisContext::flowConditionImplies(AtomicBoolValue &Token,
return S->solve(std::move(Constraints)) == Solver::Result::Unsatisfiable;
}
+bool DataflowAnalysisContext::flowConditionIsTautology(AtomicBoolValue &Token) {
+ // Returns true if and only if we cannot prove that the flow condition can
+ // ever be false.
+ llvm::DenseSet Constraints = {
+ &getBoolLiteralValue(true),
+ &getOrCreateNegationValue(getBoolLiteralValue(false)),
+ &getOrCreateNegationValue(Token),
+ };
+ llvm::DenseSet VisitedTokens;
+ addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
+ return S->solve(std::move(Constraints)) == Solver::Result::Unsatisfiable;
+}
+
void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
AtomicBoolValue &Token, llvm::DenseSet &Constraints,
llvm::DenseSet &VisitedTokens) const {
diff --git a/clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp b/clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
index b4b9b16..b8a9ef5 100644
--- a/clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -140,4 +140,33 @@ TEST_F(DataflowAnalysisContextTest, JoinFlowConditions) {
EXPECT_TRUE(Context.flowConditionImplies(FC3, C3));
}
+TEST_F(DataflowAnalysisContextTest, FlowConditionTautologies) {
+ // Fresh flow condition with empty/no constraints is always true.
+ auto &FC1 = Context.makeFlowConditionToken();
+ EXPECT_TRUE(Context.flowConditionIsTautology(FC1));
+
+ // Literal `true` is always true.
+ auto &FC2 = Context.makeFlowConditionToken();
+ Context.addFlowConditionConstraint(FC2, Context.getBoolLiteralValue(true));
+ EXPECT_TRUE(Context.flowConditionIsTautology(FC2));
+
+ // Literal `false` is never true.
+ auto &FC3 = Context.makeFlowConditionToken();
+ Context.addFlowConditionConstraint(FC3, Context.getBoolLiteralValue(false));
+ EXPECT_FALSE(Context.flowConditionIsTautology(FC3));
+
+ // We can't prove that an arbitrary bool A is always true...
+ auto &C1 = Context.createAtomicBoolValue();
+ auto &FC4 = Context.makeFlowConditionToken();
+ Context.addFlowConditionConstraint(FC4, C1);
+ EXPECT_FALSE(Context.flowConditionIsTautology(FC4));
+
+ // ... but we can prove A || !A is true.
+ auto &FC5 = Context.makeFlowConditionToken();
+ Context.addFlowConditionConstraint(
+ FC5, Context.getOrCreateDisjunctionValue(
+ C1, Context.getOrCreateNegationValue(C1)));
+ EXPECT_TRUE(Context.flowConditionIsTautology(FC5));
+}
+
} // namespace
--
2.7.4