Skip to content

Commit 834cb91

Browse files
authored
[clang][dataflow] Remove declarations from DeclToLoc when their lifetime ends. (#67300)
After https://reviews.llvm.org/D153273, we're now able to use `CFGLifetimeEnds` together with the other CFG options we use.
1 parent b0e28eb commit 834cb91

File tree

6 files changed

+41
-21
lines changed

6 files changed

+41
-21
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,13 @@ class Environment {
260260
/// if `D` isn't assigned a storage location in the environment.
261261
StorageLocation *getStorageLocation(const ValueDecl &D) const;
262262

263+
/// Removes the location assigned to `D` in the environment.
264+
///
265+
/// Requirements:
266+
///
267+
/// `D` must have a storage location assigned in the environment.
268+
void removeDecl(const ValueDecl &D);
269+
263270
/// Assigns `Loc` as the storage location of the glvalue `E` in the
264271
/// environment.
265272
///

clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ ControlFlowContext::build(const Decl &D, Stmt &S, ASTContext &C) {
9797
Options.AddTemporaryDtors = true;
9898
Options.AddInitializers = true;
9999
Options.AddCXXDefaultInitExprInCtors = true;
100+
Options.AddLifetime = true;
100101

101102
// Ensure that all sub-expressions in basic blocks are evaluated.
102103
Options.setAllAlwaysAdd();

clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,20 @@ namespace dataflow {
3535
static constexpr int MaxCompositeValueDepth = 3;
3636
static constexpr int MaxCompositeValueSize = 1000;
3737

38+
/// Returns whether all declarations that `DeclToLoc1` and `DeclToLoc2` have in
39+
/// common map to the same storage location in both maps.
40+
bool declToLocConsistent(
41+
const llvm::DenseMap<const ValueDecl *, StorageLocation *> &DeclToLoc1,
42+
const llvm::DenseMap<const ValueDecl *, StorageLocation *> &DeclToLoc2) {
43+
for (auto &Entry : DeclToLoc1) {
44+
auto It = DeclToLoc2.find(Entry.first);
45+
if (It != DeclToLoc2.end() && Entry.second != It->second)
46+
return false;
47+
}
48+
49+
return true;
50+
}
51+
3852
/// Returns a map consisting of key-value entries that are present in both maps.
3953
template <typename K, typename V>
4054
llvm::DenseMap<K, V> intersectDenseMaps(const llvm::DenseMap<K, V> &Map1,
@@ -636,10 +650,7 @@ Environment Environment::join(const Environment &EnvA, const Environment &EnvB,
636650
else
637651
JoinedEnv.ReturnLoc = nullptr;
638652

639-
// FIXME: Once we're able to remove declarations from `DeclToLoc` when their
640-
// lifetime ends, add an assertion that there aren't any entries in
641-
// `DeclToLoc` and `Other.DeclToLoc` that map the same declaration to
642-
// different storage locations.
653+
assert(declToLocConsistent(EnvA.DeclToLoc, EnvB.DeclToLoc));
643654
JoinedEnv.DeclToLoc = intersectDenseMaps(EnvA.DeclToLoc, EnvB.DeclToLoc);
644655

645656
JoinedEnv.ExprToLoc = intersectDenseMaps(EnvA.ExprToLoc, EnvB.ExprToLoc);
@@ -691,6 +702,11 @@ StorageLocation *Environment::getStorageLocation(const ValueDecl &D) const {
691702
return Loc;
692703
}
693704

705+
void Environment::removeDecl(const ValueDecl &D) {
706+
assert(DeclToLoc.contains(&D));
707+
DeclToLoc.erase(&D);
708+
}
709+
694710
void Environment::setStorageLocation(const Expr &E, StorageLocation &Loc) {
695711
// `DeclRefExpr`s to builtin function types aren't glvalues, for some reason,
696712
// but we still want to be able to associate a `StorageLocation` with them,

clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -439,19 +439,17 @@ static void builtinTransfer(const CFGElement &Elt,
439439
case CFGElement::Initializer:
440440
builtinTransferInitializer(Elt.castAs<CFGInitializer>(), State);
441441
break;
442+
case CFGElement::LifetimeEnds:
443+
// Removing declarations when their lifetime ends serves two purposes:
444+
// - Eliminate unnecessary clutter from `Environment::DeclToLoc`
445+
// - Allow us to assert that, when joining two `Environment`s, the two
446+
// `DeclToLoc` maps never contain entries that map the same declaration to
447+
// different storage locations.
448+
if (const ValueDecl *VD = Elt.castAs<CFGLifetimeEnds>().getVarDecl())
449+
State.Env.removeDecl(*VD);
450+
break;
442451
default:
443-
// FIXME: Evaluate other kinds of `CFGElement`, including:
444-
// - When encountering `CFGLifetimeEnds`, remove the declaration from
445-
// `Environment::DeclToLoc`. This would serve two purposes:
446-
// a) Eliminate unnecessary clutter from `Environment::DeclToLoc`
447-
// b) Allow us to implement an assertion that, when joining two
448-
// `Environments`, the two `DeclToLoc` maps never contain entries that
449-
// map the same declaration to different storage locations.
450-
// Unfortunately, however, we can't currently process `CFGLifetimeEnds`
451-
// because the corresponding CFG option `AddLifetime` is incompatible with
452-
// the option 'AddImplicitDtors`, which we already use. We will first
453-
// need to modify the CFG implementation to make these two options
454-
// compatible before we can process `CFGLifetimeEnds`.
452+
// FIXME: Evaluate other kinds of `CFGElement`
455453
break;
456454
}
457455
}

clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2957,10 +2957,7 @@ TEST(TransferTest, VarDeclInDoWhile) {
29572957
const auto *BarVal = cast<IntegerValue>(EnvInLoop.getValue(*BarDecl));
29582958
EXPECT_EQ(BarVal, FooPointeeVal);
29592959

2960-
// FIXME: This assertion documents current behavior, but we would prefer
2961-
// declarations to be removed from the environment when their lifetime
2962-
// ends. Once this is the case, change this assertion accordingly.
2963-
ASSERT_THAT(EnvAfterLoop.getValue(*BarDecl), BarVal);
2960+
ASSERT_THAT(EnvAfterLoop.getValue(*BarDecl), IsNull());
29642961
});
29652962
}
29662963

clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ TEST(DataflowAnalysisTest, DiagnoseFunctionDiagnoserCalledOnEachElement) {
108108
// `diagnoseFunction` provides no guarantees about the order in which elements
109109
// are visited, so we use `UnorderedElementsAre`.
110110
EXPECT_THAT_EXPECTED(Result, llvm::HasValue(UnorderedElementsAre(
111-
"0\n", "int x = 0;\n", "x\n", "++x\n")));
111+
"0\n", "int x = 0;\n", "x\n", "++x\n",
112+
" (Lifetime ends)\n")));
112113
}
113114

114115
struct NonConvergingLattice {

0 commit comments

Comments
 (0)