Skip to content

Commit 45643cf

Browse files
committed
[clang][dataflow] Centralize expression skipping logic
A follow-up to 62b2a47 to centralize the logic that skips expressions that the CFG does not emit. This allows client code to avoid sprinkling this logic everywhere. Add redirects in the transfer function to similarly skip such expressions by forwarding the visit to the sub-expression. Differential Revision: https://reviews.llvm.org/D124965
1 parent 07c96a3 commit 45643cf

File tree

7 files changed

+72
-68
lines changed

7 files changed

+72
-68
lines changed

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

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,19 @@
3131
namespace clang {
3232
namespace dataflow {
3333

34+
/// Skip past nodes that the CFG does not emit. These nodes are invisible to
35+
/// flow-sensitive analysis, and should be ignored as they will effectively not
36+
/// exist.
37+
///
38+
/// * `ParenExpr` - The CFG takes the operator precedence into account, but
39+
/// otherwise omits the node afterwards.
40+
///
41+
/// * `ExprWithCleanups` - The CFG will generate the appropriate calls to
42+
/// destructors and then omit the node.
43+
///
44+
const Expr &ignoreCFGOmittedNodes(const Expr &E);
45+
const Stmt &ignoreCFGOmittedNodes(const Stmt &S);
46+
3447
/// Owns objects that encompass the state of a program and stores context that
3548
/// is used during dataflow analysis.
3649
class DataflowAnalysisContext {
@@ -95,14 +108,15 @@ class DataflowAnalysisContext {
95108
///
96109
/// `E` must not be assigned a storage location.
97110
void setStorageLocation(const Expr &E, StorageLocation &Loc) {
98-
assert(ExprToLoc.find(&E) == ExprToLoc.end());
99-
ExprToLoc[&E] = &Loc;
111+
const Expr &CanonE = ignoreCFGOmittedNodes(E);
112+
assert(ExprToLoc.find(&CanonE) == ExprToLoc.end());
113+
ExprToLoc[&CanonE] = &Loc;
100114
}
101115

102116
/// Returns the storage location assigned to `E` or null if `E` has no
103117
/// assigned storage location.
104118
StorageLocation *getStorageLocation(const Expr &E) const {
105-
auto It = ExprToLoc.find(&E);
119+
auto It = ExprToLoc.find(&ignoreCFGOmittedNodes(E));
106120
return It == ExprToLoc.end() ? nullptr : It->second;
107121
}
108122

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

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,6 @@ class Environment {
172172
/// Creates a storage location for `E`. Does not assign the returned storage
173173
/// location to `E` in the environment. Does not assign a value to the
174174
/// returned storage location in the environment.
175-
///
176-
/// Requirements:
177-
///
178-
/// `E` must not be a `ExprWithCleanups`.
179175
StorageLocation &createStorageLocation(const Expr &E);
180176

181177
/// Assigns `Loc` as the storage location of `D` in the environment.
@@ -195,16 +191,11 @@ class Environment {
195191
/// Requirements:
196192
///
197193
/// `E` must not be assigned a storage location in the environment.
198-
/// `E` must not be a `ExprWithCleanups`.
199194
void setStorageLocation(const Expr &E, StorageLocation &Loc);
200195

201196
/// Returns the storage location assigned to `E` in the environment, applying
202197
/// the `SP` policy for skipping past indirections, or null if `E` isn't
203198
/// assigned a storage location in the environment.
204-
///
205-
/// Requirements:
206-
///
207-
/// `E` must not be a `ExprWithCleanups`.
208199
StorageLocation *getStorageLocation(const Expr &E, SkipPast SP) const;
209200

210201
/// Returns the storage location assigned to the `this` pointee in the
@@ -235,12 +226,6 @@ class Environment {
235226

236227
/// Equivalent to `getValue(getStorageLocation(E, SP), SkipPast::None)` if `E`
237228
/// is assigned a storage location in the environment, otherwise returns null.
238-
///
239-
/// Requirements:
240-
///
241-
/// `E` must not be a `ExprWithCleanups`.
242-
///
243-
/// FIXME: `Environment` should ignore any `ExprWithCleanups` it sees.
244229
Value *getValue(const Expr &E, SkipPast SP) const;
245230

246231
/// Transfers ownership of `Loc` to the analysis context and returns a

clang/include/clang/Analysis/FlowSensitive/Transfer.h

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,6 @@ class StmtToEnvMap {
3838
/// `S` must not be `ParenExpr` or `ExprWithCleanups`.
3939
void transfer(const StmtToEnvMap &StmtToEnv, const Stmt &S, Environment &Env);
4040

41-
/// Skip past a `ExprWithCleanups` which might surround `E`. Returns null if `E`
42-
/// is null.
43-
///
44-
/// The CFG omits `ExprWithCleanups` nodes (as it does with `ParenExpr`), and so
45-
/// the transfer function doesn't accept them as valid input. Manual traversal
46-
/// of the AST should skip and unwrap any `ExprWithCleanups` it might expect to
47-
/// see. They are safe to skip, as the CFG will emit calls to destructors as
48-
/// appropriate.
49-
const Expr *ignoreExprWithCleanups(const Expr *E);
50-
5141
} // namespace dataflow
5242
} // namespace clang
5343

clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
16+
#include "clang/AST/ExprCXX.h"
1617
#include "clang/Analysis/FlowSensitive/Value.h"
1718
#include <cassert>
1819
#include <memory>
@@ -155,3 +156,22 @@ void DataflowAnalysisContext::addTransitiveFlowConditionConstraints(
155156

156157
} // namespace dataflow
157158
} // namespace clang
159+
160+
using namespace clang;
161+
162+
const Expr &clang::dataflow::ignoreCFGOmittedNodes(const Expr &E) {
163+
const Expr *Current = &E;
164+
if (auto *EWC = dyn_cast<ExprWithCleanups>(Current)) {
165+
Current = EWC->getSubExpr();
166+
assert(Current != nullptr);
167+
}
168+
Current = Current->IgnoreParens();
169+
assert(Current != nullptr);
170+
return *Current;
171+
}
172+
173+
const Stmt &clang::dataflow::ignoreCFGOmittedNodes(const Stmt &S) {
174+
if (auto *E = dyn_cast<Expr>(&S))
175+
return ignoreCFGOmittedNodes(*E);
176+
return S;
177+
}

clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,6 @@ StorageLocation &Environment::createStorageLocation(const VarDecl &D) {
343343
}
344344

345345
StorageLocation &Environment::createStorageLocation(const Expr &E) {
346-
assert(!isa<ExprWithCleanups>(&E));
347346
// Evaluated expressions are always assigned the same storage locations to
348347
// ensure that the environment stabilizes across loop iterations. Storage
349348
// locations for evaluated expressions are stored in the analysis context.
@@ -366,16 +365,15 @@ StorageLocation *Environment::getStorageLocation(const ValueDecl &D,
366365
}
367366

368367
void Environment::setStorageLocation(const Expr &E, StorageLocation &Loc) {
369-
assert(!isa<ExprWithCleanups>(&E));
370-
assert(ExprToLoc.find(&E) == ExprToLoc.end());
371-
ExprToLoc[&E] = &Loc;
368+
const Expr &CanonE = ignoreCFGOmittedNodes(E);
369+
assert(ExprToLoc.find(&CanonE) == ExprToLoc.end());
370+
ExprToLoc[&CanonE] = &Loc;
372371
}
373372

374373
StorageLocation *Environment::getStorageLocation(const Expr &E,
375374
SkipPast SP) const {
376-
assert(!isa<ExprWithCleanups>(&E));
377375
// FIXME: Add a test with parens.
378-
auto It = ExprToLoc.find(E.IgnoreParens());
376+
auto It = ExprToLoc.find(&ignoreCFGOmittedNodes(E));
379377
return It == ExprToLoc.end() ? nullptr : &skip(*It->second, SP);
380378
}
381379

clang/lib/Analysis/FlowSensitive/Transfer.cpp

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -33,27 +33,12 @@
3333
namespace clang {
3434
namespace dataflow {
3535

36-
const Expr *ignoreExprWithCleanups(const Expr *E) {
37-
if (auto *C = dyn_cast_or_null<ExprWithCleanups>(E))
38-
return C->getSubExpr();
39-
return E;
40-
}
41-
4236
static BoolValue &evaluateBooleanEquality(const Expr &LHS, const Expr &RHS,
4337
Environment &Env) {
44-
// Equality of booleans involves implicit integral casts. Ignore these casts
45-
// for now and focus on the values associated with the wrapped expressions.
46-
// FIXME: Consider changing this once the framework offers better support for
47-
// integral casts.
48-
const Expr *LHSNorm = LHS.IgnoreCasts();
49-
const Expr *RHSNorm = RHS.IgnoreCasts();
50-
assert(LHSNorm != nullptr);
51-
assert(RHSNorm != nullptr);
52-
53-
if (auto *LHSValue = dyn_cast_or_null<BoolValue>(
54-
Env.getValue(*LHSNorm, SkipPast::Reference)))
55-
if (auto *RHSValue = dyn_cast_or_null<BoolValue>(
56-
Env.getValue(*RHSNorm, SkipPast::Reference)))
38+
if (auto *LHSValue =
39+
dyn_cast_or_null<BoolValue>(Env.getValue(LHS, SkipPast::Reference)))
40+
if (auto *RHSValue =
41+
dyn_cast_or_null<BoolValue>(Env.getValue(RHS, SkipPast::Reference)))
5742
return Env.makeIff(*LHSValue, *RHSValue);
5843

5944
return Env.makeAtomicBoolValue();
@@ -65,14 +50,10 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
6550
: StmtToEnv(StmtToEnv), Env(Env) {}
6651

6752
void VisitBinaryOperator(const BinaryOperator *S) {
68-
// The CFG does not contain `ParenExpr` as top-level statements in basic
69-
// blocks, however sub-expressions can still be of that type.
70-
assert(S->getLHS() != nullptr);
71-
const Expr *LHS = S->getLHS()->IgnoreParens();
53+
const Expr *LHS = S->getLHS();
7254
assert(LHS != nullptr);
7355

74-
assert(S->getRHS() != nullptr);
75-
const Expr *RHS = S->getRHS()->IgnoreParens();
56+
const Expr *RHS = S->getRHS();
7657
assert(RHS != nullptr);
7758

7859
switch (S->getOpcode()) {
@@ -155,7 +136,7 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
155136
return;
156137
}
157138

158-
InitExpr = ignoreExprWithCleanups(D.getInit());
139+
InitExpr = D.getInit();
159140
assert(InitExpr != nullptr);
160141

161142
if (D.getType()->isReferenceType()) {
@@ -476,8 +457,7 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
476457
assert(S->getArg(0) != nullptr);
477458
// `__builtin_expect` returns by-value, so strip away any potential
478459
// references in the argument.
479-
auto *ArgLoc = Env.getStorageLocation(
480-
*S->getArg(0)->IgnoreParenImpCasts(), SkipPast::Reference);
460+
auto *ArgLoc = Env.getStorageLocation(*S->getArg(0), SkipPast::Reference);
481461
if (ArgLoc == nullptr)
482462
return;
483463
Env.setStorageLocation(*S, *ArgLoc);
@@ -562,6 +542,24 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
562542
Env.setValue(Loc, Env.getBoolLiteralValue(S->getValue()));
563543
}
564544

545+
void VisitParenExpr(const ParenExpr *S) {
546+
// The CFG does not contain `ParenExpr` as top-level statements in basic
547+
// blocks, however manual traversal to sub-expressions may encounter them.
548+
// Redirect to the sub-expression.
549+
auto *SubExpr = S->getSubExpr();
550+
assert(SubExpr != nullptr);
551+
Visit(SubExpr);
552+
}
553+
554+
void VisitExprWithCleanups(const ExprWithCleanups *S) {
555+
// The CFG does not contain `ExprWithCleanups` as top-level statements in
556+
// basic blocks, however manual traversal to sub-expressions may encounter
557+
// them. Redirect to the sub-expression.
558+
auto *SubExpr = S->getSubExpr();
559+
assert(SubExpr != nullptr);
560+
Visit(SubExpr);
561+
}
562+
565563
private:
566564
BoolValue &getLogicOperatorSubExprValue(const Expr &SubExpr) {
567565
// `SubExpr` and its parent logic operator might be part of different basic
@@ -593,7 +591,6 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
593591
};
594592

595593
void transfer(const StmtToEnvMap &StmtToEnv, const Stmt &S, Environment &Env) {
596-
assert(!(isa<ParenExpr, ExprWithCleanups>(&S)));
597594
TransferVisitor(StmtToEnv, Env).Visit(&S);
598595
}
599596

clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class StmtToEnvMapImpl : public StmtToEnvMap {
4646
: CFCtx(CFCtx), BlockToState(BlockToState) {}
4747

4848
const Environment *getEnvironment(const Stmt &S) const override {
49-
auto BlockIT = CFCtx.getStmtToBlock().find(&S);
49+
auto BlockIT = CFCtx.getStmtToBlock().find(&ignoreCFGOmittedNodes(S));
5050
assert(BlockIT != CFCtx.getStmtToBlock().end());
5151
const auto &State = BlockToState[BlockIT->getSecond()->getBlockID()];
5252
assert(State.hasValue());
@@ -77,26 +77,26 @@ class TerminatorVisitor : public ConstStmtVisitor<TerminatorVisitor> {
7777
: StmtToEnv(StmtToEnv), Env(Env), BlockSuccIdx(BlockSuccIdx) {}
7878

7979
void VisitIfStmt(const IfStmt *S) {
80-
auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
80+
auto *Cond = S->getCond();
8181
assert(Cond != nullptr);
8282
extendFlowCondition(*Cond);
8383
}
8484

8585
void VisitWhileStmt(const WhileStmt *S) {
86-
auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
86+
auto *Cond = S->getCond();
8787
assert(Cond != nullptr);
8888
extendFlowCondition(*Cond);
8989
}
9090

9191
void VisitBinaryOperator(const BinaryOperator *S) {
9292
assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
93-
auto *LHS = ignoreExprWithCleanups(S->getLHS())->IgnoreParens();
93+
auto *LHS = S->getLHS();
9494
assert(LHS != nullptr);
9595
extendFlowCondition(*LHS);
9696
}
9797

9898
void VisitConditionalOperator(const ConditionalOperator *S) {
99-
auto *Cond = ignoreExprWithCleanups(S->getCond())->IgnoreParens();
99+
auto *Cond = S->getCond();
100100
assert(Cond != nullptr);
101101
extendFlowCondition(*Cond);
102102
}

0 commit comments

Comments
 (0)