Skip to content

Commit 58abe36

Browse files
committed
[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
1 parent 6641c57 commit 58abe36

File tree

3 files changed

+46
-0
lines changed

3 files changed

+46
-0
lines changed

clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ class DataflowAnalysisContext {
173173
/// identified by `Token` imply that `Val` is true.
174174
bool flowConditionImplies(AtomicBoolValue &Token, BoolValue &Val);
175175

176+
/// Returns true if and only if the constraints of the flow condition
177+
/// identified by `Token` are always true.
178+
bool flowConditionIsTautology(AtomicBoolValue &Token);
179+
176180
private:
177181
/// Adds all constraints of the flow condition identified by `Token` and all
178182
/// of its transitive dependencies to `Constraints`. `VisitedTokens` is used

clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,19 @@ bool DataflowAnalysisContext::flowConditionImplies(AtomicBoolValue &Token,
117117
return S->solve(std::move(Constraints)) == Solver::Result::Unsatisfiable;
118118
}
119119

120+
bool DataflowAnalysisContext::flowConditionIsTautology(AtomicBoolValue &Token) {
121+
// Returns true if and only if we cannot prove that the flow condition can
122+
// ever be false.
123+
llvm::DenseSet<BoolValue *> Constraints = {
124+
&getBoolLiteralValue(true),
125+
&getOrCreateNegationValue(getBoolLiteralValue(false)),
126+
&getOrCreateNegationValue(Token),
127+
};
128+
llvm::DenseSet<AtomicBoolValue *> VisitedTokens;
129+
addTransitiveFlowConditionConstraints(Token, Constraints, VisitedTokens);
130+
return S->solve(std::move(Constraints)) == Solver::Result::Unsatisfiable;
131+
}
132+
120133
void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
121134
AtomicBoolValue &Token, llvm::DenseSet<BoolValue *> &Constraints,
122135
llvm::DenseSet<AtomicBoolValue *> &VisitedTokens) const {

clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,33 @@ TEST_F(DataflowAnalysisContextTest, JoinFlowConditions) {
140140
EXPECT_TRUE(Context.flowConditionImplies(FC3, C3));
141141
}
142142

143+
TEST_F(DataflowAnalysisContextTest, FlowConditionTautologies) {
144+
// Fresh flow condition with empty/no constraints is always true.
145+
auto &FC1 = Context.makeFlowConditionToken();
146+
EXPECT_TRUE(Context.flowConditionIsTautology(FC1));
147+
148+
// Literal `true` is always true.
149+
auto &FC2 = Context.makeFlowConditionToken();
150+
Context.addFlowConditionConstraint(FC2, Context.getBoolLiteralValue(true));
151+
EXPECT_TRUE(Context.flowConditionIsTautology(FC2));
152+
153+
// Literal `false` is never true.
154+
auto &FC3 = Context.makeFlowConditionToken();
155+
Context.addFlowConditionConstraint(FC3, Context.getBoolLiteralValue(false));
156+
EXPECT_FALSE(Context.flowConditionIsTautology(FC3));
157+
158+
// We can't prove that an arbitrary bool A is always true...
159+
auto &C1 = Context.createAtomicBoolValue();
160+
auto &FC4 = Context.makeFlowConditionToken();
161+
Context.addFlowConditionConstraint(FC4, C1);
162+
EXPECT_FALSE(Context.flowConditionIsTautology(FC4));
163+
164+
// ... but we can prove A || !A is true.
165+
auto &FC5 = Context.makeFlowConditionToken();
166+
Context.addFlowConditionConstraint(
167+
FC5, Context.getOrCreateDisjunctionValue(
168+
C1, Context.getOrCreateNegationValue(C1)));
169+
EXPECT_TRUE(Context.flowConditionIsTautology(FC5));
170+
}
171+
143172
} // namespace

0 commit comments

Comments
 (0)