Skip to content

Commit d1f5954

Browse files
authored
[clang][dataflow] Add Environment::allows(). (#70046)
This allows querying whether, given the flow condition, a certain formula still has a solution (though it is not necessarily implied by the flow condition, as `flowConditionImplies()` would check). This can be checked today, but only with a double negation, i.e. to check whether, given the flow condition, a formula F has a solution, you can check `!Env.flowConditionImplies(Arena.makeNot(F))`. The double negation makes this hard to reason about, and it would be nicer to have a way of directly checking this. For consistency, this patch also renames `flowConditionImplies()` to `proves()`; the old name is kept around for compatibility but deprecated.
1 parent 00b7979 commit d1f5954

File tree

5 files changed

+70
-21
lines changed

5 files changed

+70
-21
lines changed

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

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,17 @@ class DataflowAnalysisContext {
129129
/// token.
130130
Atom joinFlowConditions(Atom FirstToken, Atom SecondToken);
131131

132-
/// Returns true if and only if the constraints of the flow condition
133-
/// identified by `Token` imply that `Val` is true.
134-
bool flowConditionImplies(Atom Token, const Formula &);
132+
/// Returns true if the constraints of the flow condition identified by
133+
/// `Token` imply that `F` is true.
134+
/// Returns false if the flow condition does not imply `F` or if the solver
135+
/// times out.
136+
bool flowConditionImplies(Atom Token, const Formula &F);
137+
138+
/// Returns true if the constraints of the flow condition identified by
139+
/// `Token` still allow `F` to be true.
140+
/// Returns false if the flow condition implies that `F` is false or if the
141+
/// solver times out.
142+
bool flowConditionAllows(Atom Token, const Formula &F);
135143

136144
/// Returns true if `Val1` is equivalent to `Val2`.
137145
/// Note: This function doesn't take into account constraints on `Val1` and
@@ -184,6 +192,12 @@ class DataflowAnalysisContext {
184192
addTransitiveFlowConditionConstraints(Atom Token,
185193
llvm::SetVector<const Formula *> &Out);
186194

195+
/// Returns true if the solver is able to prove that there is a satisfying
196+
/// assignment for `Constraints`.
197+
bool isSatisfiable(llvm::SetVector<const Formula *> Constraints) {
198+
return querySolver(std::move(Constraints)).getStatus() ==
199+
Solver::Result::Status::Satisfiable;
200+
}
187201

188202
/// Returns true if the solver is able to prove that there is no satisfying
189203
/// assignment for `Constraints`

clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -542,12 +542,29 @@ class Environment {
542542
Atom getFlowConditionToken() const { return FlowConditionToken; }
543543

544544
/// Record a fact that must be true if this point in the program is reached.
545-
void addToFlowCondition(const Formula &);
545+
void assume(const Formula &);
546+
547+
/// Deprecated synonym for `assume()`.
548+
void addToFlowCondition(const Formula &F) { assume(F); }
546549

547550
/// Returns true if the formula is always true when this point is reached.
548-
/// Returns false if the formula may be false, or if the flow condition isn't
549-
/// sufficiently precise to prove that it is true.
550-
bool flowConditionImplies(const Formula &) const;
551+
/// Returns false if the formula may be false (or the flow condition isn't
552+
/// sufficiently precise to prove that it is true) or if the solver times out.
553+
///
554+
/// Note that there is an asymmetry between this function and `allows()` in
555+
/// that they both return false if the solver times out. The assumption is
556+
/// that if `proves()` or `allows()` returns true, this will result in a
557+
/// diagnostic, and we want to bias towards false negatives in the case where
558+
/// the solver times out.
559+
bool proves(const Formula &) const;
560+
561+
/// Returns true if the formula may be true when this point is reached.
562+
/// Returns false if the formula is always false when this point is reached
563+
/// (or the flow condition is overly constraining) or if the solver times out.
564+
bool allows(const Formula &) const;
565+
566+
/// Deprecated synonym for `proves()`.
567+
bool flowConditionImplies(const Formula &F) const { return proves(F); }
551568

552569
/// Returns the `DeclContext` of the block being analysed, if any. Otherwise,
553570
/// returns null.

clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,19 +145,28 @@ Solver::Result DataflowAnalysisContext::querySolver(
145145
}
146146

147147
bool DataflowAnalysisContext::flowConditionImplies(Atom Token,
148-
const Formula &Val) {
148+
const Formula &F) {
149149
// Returns true if and only if truth assignment of the flow condition implies
150-
// that `Val` is also true. We prove whether or not this property holds by
150+
// that `F` is also true. We prove whether or not this property holds by
151151
// reducing the problem to satisfiability checking. In other words, we attempt
152-
// to show that assuming `Val` is false makes the constraints induced by the
152+
// to show that assuming `F` is false makes the constraints induced by the
153153
// flow condition unsatisfiable.
154154
llvm::SetVector<const Formula *> Constraints;
155155
Constraints.insert(&arena().makeAtomRef(Token));
156-
Constraints.insert(&arena().makeNot(Val));
156+
Constraints.insert(&arena().makeNot(F));
157157
addTransitiveFlowConditionConstraints(Token, Constraints);
158158
return isUnsatisfiable(std::move(Constraints));
159159
}
160160

161+
bool DataflowAnalysisContext::flowConditionAllows(Atom Token,
162+
const Formula &F) {
163+
llvm::SetVector<const Formula *> Constraints;
164+
Constraints.insert(&arena().makeAtomRef(Token));
165+
Constraints.insert(&F);
166+
addTransitiveFlowConditionConstraints(Token, Constraints);
167+
return isSatisfiable(std::move(Constraints));
168+
}
169+
161170
bool DataflowAnalysisContext::equivalentFormulas(const Formula &Val1,
162171
const Formula &Val2) {
163172
llvm::SetVector<const Formula *> Constraints;

clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -929,12 +929,16 @@ StorageLocation &Environment::createObjectInternal(const ValueDecl *D,
929929
return Loc;
930930
}
931931

932-
void Environment::addToFlowCondition(const Formula &Val) {
933-
DACtx->addFlowConditionConstraint(FlowConditionToken, Val);
932+
void Environment::assume(const Formula &F) {
933+
DACtx->addFlowConditionConstraint(FlowConditionToken, F);
934934
}
935935

936-
bool Environment::flowConditionImplies(const Formula &Val) const {
937-
return DACtx->flowConditionImplies(FlowConditionToken, Val);
936+
bool Environment::proves(const Formula &F) const {
937+
return DACtx->flowConditionImplies(FlowConditionToken, F);
938+
}
939+
940+
bool Environment::allows(const Formula &F) const {
941+
return DACtx->flowConditionAllows(FlowConditionToken, F);
938942
}
939943

940944
void Environment::dump(raw_ostream &OS) const {

clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,22 @@ TEST_F(EnvironmentTest, FlowCondition) {
3939
Environment Env(DAContext);
4040
auto &A = Env.arena();
4141

42-
EXPECT_TRUE(Env.flowConditionImplies(A.makeLiteral(true)));
43-
EXPECT_FALSE(Env.flowConditionImplies(A.makeLiteral(false)));
42+
EXPECT_TRUE(Env.proves(A.makeLiteral(true)));
43+
EXPECT_TRUE(Env.allows(A.makeLiteral(true)));
44+
EXPECT_FALSE(Env.proves(A.makeLiteral(false)));
45+
EXPECT_FALSE(Env.allows(A.makeLiteral(false)));
4446

4547
auto &X = A.makeAtomRef(A.makeAtom());
46-
EXPECT_FALSE(Env.flowConditionImplies(X));
48+
EXPECT_FALSE(Env.proves(X));
49+
EXPECT_TRUE(Env.allows(X));
4750

48-
Env.addToFlowCondition(X);
49-
EXPECT_TRUE(Env.flowConditionImplies(X));
51+
Env.assume(X);
52+
EXPECT_TRUE(Env.proves(X));
53+
EXPECT_TRUE(Env.allows(X));
5054

5155
auto &NotX = A.makeNot(X);
52-
EXPECT_FALSE(Env.flowConditionImplies(NotX));
56+
EXPECT_FALSE(Env.proves(NotX));
57+
EXPECT_FALSE(Env.allows(NotX));
5358
}
5459

5560
TEST_F(EnvironmentTest, CreateValueRecursiveType) {

0 commit comments

Comments
 (0)