Skip to content

Commit 71d8c31

Browse files
committed
address comments
1 parent abf5363 commit 71d8c31

File tree

5 files changed

+90
-46
lines changed

5 files changed

+90
-46
lines changed

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,9 @@ def Dangling : DiagGroup<"dangling", [DanglingAssignment,
532532
DanglingInitializerList,
533533
DanglingGsl,
534534
ReturnStackAddress]>;
535+
536+
def LifetimeSafety : DiagGroup<"experimental-lifetime-safety">;
537+
535538
def DistributedObjectModifiers : DiagGroup<"distributed-object-modifiers">;
536539
def DllexportExplicitInstantiationDecl : DiagGroup<"dllexport-explicit-instantiation-decl">;
537540
def ExcessInitializers : DiagGroup<"excess-initializers">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10627,6 +10627,10 @@ def warn_dangling_reference_captured_by_unknown : Warning<
1062710627
"object whose reference is captured will be destroyed at the end of "
1062810628
"the full-expression">, InGroup<DanglingCapture>;
1062910629

10630+
def warn_experimental_lifetime_safety_dummy_warning : Warning<
10631+
"todo: remove this warning after we have atleast one warning based on the lifetime analysis">,
10632+
InGroup<LifetimeSafety>, DefaultIgnore;
10633+
1063010634
// For non-floating point, expressions of the form x == x or x != x
1063110635
// should result in a warning, since these always evaluate to a constant.
1063210636
// Array comparisons have similar warnings

clang/lib/Analysis/LifetimeSafety.cpp

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,10 @@ template <typename Tag> struct ID {
4545
bool operator==(const ID<Tag> &Other) const { return Value == Other.Value; }
4646
bool operator!=(const ID<Tag> &Other) const { return !(*this == Other); }
4747
bool operator<(const ID<Tag> &Other) const { return Value < Other.Value; }
48-
ID<Tag> &operator++() {
48+
ID<Tag> operator++(int) {
49+
ID<Tag> Tmp = *this;
4950
++Value;
50-
return *this;
51+
return Tmp;
5152
}
5253
void Profile(llvm::FoldingSetNodeID &IDBuilder) const {
5354
IDBuilder.AddInteger(Value);
@@ -59,11 +60,8 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID<Tag> ID) {
5960
return OS << ID.Value;
6061
}
6162

62-
struct LoanTag {};
63-
struct OriginTag {};
64-
65-
using LoanID = ID<LoanTag>;
66-
using OriginID = ID<OriginTag>;
63+
using LoanID = ID<struct LoanTag>;
64+
using OriginID = ID<struct OriginTag>;
6765

6866
/// Information about a single borrow, or "Loan". A loan is created when a
6967
/// reference or pointer is taken.
@@ -81,7 +79,12 @@ struct Loan {
8179

8280
/// An Origin is a symbolic identifier that represents the set of possible
8381
/// loans a pointer-like object could hold at any given time.
84-
/// TODO: Also represent Origins of complex types (fields, inner types).
82+
/// TODO: Enhance the origin model to handle complex types, pointer
83+
/// indirection and reborrowing. The plan is to move from a single origin per
84+
/// variable/expression to a "list of origins" governed by the Type.
85+
/// For example, the type 'int**' would have two origins.
86+
/// See discussion:
87+
/// https://github.com/llvm/llvm-project/pull/142313/commits/0cd187b01e61b200d92ca0b640789c1586075142#r2137644238
8588
struct Origin {
8689
OriginID ID;
8790
llvm::PointerUnion<const clang::ValueDecl *, const clang::Expr *> Ptr;
@@ -101,19 +104,20 @@ class LoanManager {
101104
public:
102105
LoanManager() = default;
103106

104-
Loan &addLoan(AccessPath path, SourceLocation loc) {
105-
++NextLoanID;
106-
AllLoans.emplace_back(NextLoanID, path, loc);
107+
Loan &addLoan(AccessPath Path, SourceLocation Loc) {
108+
AllLoans.emplace_back(getNextLoanID(), Path, Loc);
107109
return AllLoans.back();
108110
}
109111

110-
const Loan &getLoan(LoanID id) const {
111-
assert(id.Value < AllLoans.size());
112-
return AllLoans[id.Value];
112+
const Loan &getLoan(LoanID ID) const {
113+
assert(ID.Value < AllLoans.size());
114+
return AllLoans[ID.Value];
113115
}
114116
llvm::ArrayRef<Loan> getLoans() const { return AllLoans; }
115117

116118
private:
119+
LoanID getNextLoanID() { return NextLoanID++; }
120+
117121
LoanID NextLoanID{0};
118122
/// TODO(opt): Profile and evaluate the usefullness of small buffer
119123
/// optimisation.
@@ -124,23 +128,27 @@ class OriginManager {
124128
public:
125129
OriginManager() = default;
126130

127-
OriginID getNextOriginID() { return ++NextOriginID; }
128-
Origin &addOrigin(OriginID id, const clang::ValueDecl &D) {
129-
AllOrigins.emplace_back(id, &D);
131+
Origin &addOrigin(OriginID ID, const clang::ValueDecl &D) {
132+
AllOrigins.emplace_back(ID, &D);
130133
return AllOrigins.back();
131134
}
132-
Origin &addOrigin(OriginID id, const clang::Expr &E) {
133-
AllOrigins.emplace_back(id, &E);
135+
Origin &addOrigin(OriginID ID, const clang::Expr &E) {
136+
AllOrigins.emplace_back(ID, &E);
134137
return AllOrigins.back();
135138
}
136139

137140
OriginID get(const Expr &E) {
141+
// Origin of DeclRefExpr is that of the declaration it refers to.
138142
if (const auto *DRE = dyn_cast<DeclRefExpr>(&E)) {
139-
// Origin of DeclRefExpr is that of the declaration it refers to.
140143
return get(*DRE->getDecl());
141144
}
142145
auto It = ExprToOriginID.find(&E);
143-
assert(It != ExprToOriginID.end());
146+
// TODO: This should be an assert(It != ExprToOriginID.end()). The current
147+
// implementation falls back to getOrCreate to avoid crashing on
148+
// yet-unhandled pointer expressions, creating an empty origin for them.
149+
if (It == ExprToOriginID.end())
150+
return getOrCreate(E);
151+
144152
return It->second;
145153
}
146154

@@ -183,6 +191,8 @@ class OriginManager {
183191
}
184192

185193
private:
194+
OriginID getNextOriginID() { return NextOriginID++; }
195+
186196
OriginID NextOriginID{0};
187197
/// TODO(opt): Profile and evaluate the usefullness of small buffer
188198
/// optimisation.
@@ -321,10 +331,7 @@ class FactManager {
321331
llvm::dbgs() << "Function: " << ND->getQualifiedNameAsString() << "\n";
322332
}
323333
// Print blocks in the order as they appear in code for a stable ordering.
324-
ForwardDataflowWorklist worklist(Cfg, AC);
325-
for (const CFGBlock *B : Cfg.const_nodes())
326-
worklist.enqueueBlock(B);
327-
while (const CFGBlock *B = worklist.dequeue()) {
334+
for (const CFGBlock *B : *AC.getAnalysis<PostOrderCFGView>()) {
328335
llvm::dbgs() << " Block B" << B->getBlockID() << ":\n";
329336
auto It = BlockToFactsMap.find(B);
330337
if (It != BlockToFactsMap.end()) {
@@ -351,19 +358,14 @@ class FactManager {
351358
class FactGenerator : public ConstStmtVisitor<FactGenerator> {
352359

353360
public:
354-
FactGenerator(const CFG &Cfg, FactManager &FactMgr, AnalysisDeclContext &AC)
355-
: FactMgr(FactMgr), Cfg(Cfg), AC(AC) {}
361+
FactGenerator(FactManager &FactMgr, AnalysisDeclContext &AC)
362+
: FactMgr(FactMgr), AC(AC) {}
356363

357364
void run() {
358365
llvm::TimeTraceScope TimeProfile("FactGenerator");
359366
// Iterate through the CFG blocks in reverse post-order to ensure that
360367
// initializations and destructions are processed in the correct sequence.
361-
// TODO: A reverse post-order traversal utility should be provided by
362-
// Dataflow framework.
363-
ForwardDataflowWorklist Worklist(Cfg, AC);
364-
for (const CFGBlock *B : Cfg.const_nodes())
365-
Worklist.enqueueBlock(B);
366-
while (const CFGBlock *Block = Worklist.dequeue()) {
368+
for (const CFGBlock *Block : *AC.getAnalysis<PostOrderCFGView>()) {
367369
CurrentBlockFacts.clear();
368370
for (unsigned I = 0; I < Block->size(); ++I) {
369371
const CFGElement &Element = Block->Elements[I];
@@ -386,7 +388,7 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
386388
}
387389

388390
void VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *N) {
389-
/// TODO: Handle nullptr expr as a special 'null' loan. Uninintialed
391+
/// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
390392
/// pointers can use the same type of loan.
391393
FactMgr.getOriginMgr().getOrCreate(*N);
392394
}
@@ -484,7 +486,6 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
484486
}
485487

486488
FactManager &FactMgr;
487-
const CFG &Cfg;
488489
AnalysisDeclContext &AC;
489490
llvm::SmallVector<Fact *> CurrentBlockFacts;
490491
};
@@ -537,6 +538,9 @@ struct LifetimeLattice {
537538

538539
/// Computes the union of two lattices by performing a key-wise join of
539540
/// their OriginLoanMaps.
541+
// TODO(opt): This key-wise join is a performance bottleneck. A more
542+
// efficient merge could be implemented using a Patricia Trie or HAMT
543+
// instead of the current AVL-tree-based ImmutableMap.
540544
LifetimeLattice join(const LifetimeLattice &Other,
541545
LifetimeFactory &Factory) const {
542546
/// Merge the smaller map into the larger one ensuring we iterate over the
@@ -652,7 +656,8 @@ class Transferer {
652656
/// Orchestrates the analysis by iterating over the CFG using a worklist
653657
/// algorithm. It computes a fixed point by propagating the LifetimeLattice
654658
/// state through each block until the state no longer changes.
655-
/// TODO: Maybe use the dataflow framework!
659+
/// TODO: Maybe use the dataflow framework! The framework might need changes
660+
/// to support the current comparison done at block-entry.
656661
class LifetimeDataflow {
657662
const CFG &Cfg;
658663
AnalysisDeclContext &AC;
@@ -688,6 +693,9 @@ class LifetimeDataflow {
688693
: LifetimeLattice{};
689694
LifetimeLattice NewSuccEntryState =
690695
OldSuccEntryState.join(ExitState, LifetimeFact);
696+
// Enqueue the successor if its entry state has changed.
697+
// TODO(opt): Consider changing 'join' to report a change if !=
698+
// comparison is found expensive.
691699
if (SuccIt == BlockEntryStates.end() ||
692700
NewSuccEntryState != OldSuccEntryState) {
693701
BlockEntryStates[Successor] = NewSuccEntryState;
@@ -733,7 +741,7 @@ void runLifetimeAnalysis(const DeclContext &DC, const CFG &Cfg,
733741
DEBUG_WITH_TYPE("PrintCFG", Cfg.dump(AC.getASTContext().getLangOpts(),
734742
/*ShowColors=*/true));
735743
FactManager FactMgr;
736-
FactGenerator FactGen(Cfg, FactMgr, AC);
744+
FactGenerator FactGen(FactMgr, AC);
737745
FactGen.run();
738746
DEBUG_WITH_TYPE("LifetimeFacts", FactMgr.dump(Cfg, AC));
739747

clang/lib/Sema/AnalysisBasedWarnings.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2868,14 +2868,14 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
28682868
}
28692869
}
28702870

2871-
DEBUG_WITH_TYPE(
2872-
"ExperimentalLifetimeAnalysis",
2873-
// TODO: Enable for other languages once the analysis is stable.
2874-
if (S.getLangOpts().CPlusPlus) {
2875-
if (CFG *cfg = AC.getCFG()) {
2876-
runLifetimeAnalysis(*cast<DeclContext>(D), *cfg, AC);
2877-
}
2878-
});
2871+
// TODO: Enable lifetime safety analysis for other languages once it is
2872+
// stable.
2873+
if (!Diags.isIgnored(diag::warn_experimental_lifetime_safety_dummy_warning,
2874+
D->getBeginLoc()) &&
2875+
S.getLangOpts().CPlusPlus) {
2876+
if (CFG *cfg = AC.getCFG())
2877+
runLifetimeAnalysis(*cast<DeclContext>(D), *cfg, AC);
2878+
}
28792879
// Check for violations of "called once" parameter properties.
28802880
if (S.getLangOpts().ObjC && !S.getLangOpts().CPlusPlus &&
28812881
shouldAnalyzeCalledOnceParameters(Diags, D->getBeginLoc())) {

clang/test/Sema/warn-lifetime-safety-dataflow.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -mllvm -debug-only=ExperimentalLifetimeAnalysis,LifetimeFacts,LifetimeDataflow %s 2>&1 | FileCheck %s
1+
// RUN: %clang_cc1 -mllvm -debug-only=LifetimeFacts,LifetimeDataflow -Wexperimental-lifetime-safety %s 2>&1 | FileCheck %s
22

33
struct MyObj {
44
int id;
@@ -117,14 +117,20 @@ void conditional(bool condition) {
117117
int *q = p;
118118
// CHECK: AssignOrigin (DestID: [[O_P_RVAL:[0-9]+]], SrcID: [[O_P]])
119119
// CHECK: AssignOrigin (DestID: [[O_Q:[0-9]+]], SrcID: [[O_P_RVAL]])
120+
int *q = p;
121+
// CHECK: AssignOrigin (DestID: [[O_P_RVAL:[0-9]+]], SrcID: [[O_P]])
122+
// CHECK: AssignOrigin (DestID: [[O_Q:[0-9]+]], SrcID: [[O_P_RVAL]])
120123
}
121124
// CHECK: Dataflow results:
122125
// CHECK-DAG: Origin [[O_ADDR_A]] contains Loan [[L_A]]
123126
// CHECK-DAG: Origin [[O_ADDR_B]] contains Loan [[L_B]]
124127
// CHECK-DAG: Origin [[O_P]] contains Loan [[L_A]]
128+
// CHECK-DAG: Origin [[O_P]] contains Loan [[L_A]]
125129
// CHECK-DAG: Origin [[O_P]] contains Loan [[L_B]]
126130
// CHECK-DAG: Origin [[O_Q]] contains Loan [[L_A]]
127131
// CHECK-DAG: Origin [[O_Q]] contains Loan [[L_B]]
132+
// CHECK-DAG: Origin [[O_Q]] contains Loan [[L_A]]
133+
// CHECK-DAG: Origin [[O_Q]] contains Loan [[L_B]]
128134

129135

130136
// CHECK-LABEL: Function: pointers_in_a_cycle
@@ -249,6 +255,8 @@ void assign_in_switch(int mode) {
249255
MyObj* p = nullptr;
250256
// CHECK: Block B{{[0-9]+}}:
251257
// CHECK: AssignOrigin (DestID: [[O_NULLPTR_CAST:[0-9]+]], SrcID: [[O_NULLPTR:[0-9]+]])
258+
// CHECK: AssignOrigin (DestID: [[O_P:[0-9]+]], SrcID: [[O_NULLPTR_CAST]])
259+
// CHECK: AssignOrigin (DestID: [[O_NULLPTR_CAST:[0-9]+]], SrcID: [[O_NULLPTR:[0-9]+]])
252260
// CHECK: AssignOrigin (DestID: [[O_P:[0-9]+]], SrcID: [[O_NULLPTR_CAST]])
253261
switch (mode) {
254262
case 1:
@@ -289,6 +297,8 @@ void loan_in_loop(bool condition) {
289297
MyObj* p = nullptr;
290298
// CHECK: AssignOrigin (DestID: [[O_NULLPTR_CAST:[0-9]+]], SrcID: [[O_NULLPTR:[0-9]+]])
291299
// CHECK: AssignOrigin (DestID: [[O_P:[0-9]+]], SrcID: [[O_NULLPTR_CAST]])
300+
// CHECK: AssignOrigin (DestID: [[O_NULLPTR_CAST:[0-9]+]], SrcID: [[O_NULLPTR:[0-9]+]])
301+
// CHECK: AssignOrigin (DestID: [[O_P:[0-9]+]], SrcID: [[O_NULLPTR_CAST]])
292302
while (condition) {
293303
MyObj inner;
294304
p = &inner;
@@ -337,6 +347,8 @@ void nested_scopes() {
337347
MyObj* p = nullptr;
338348
// CHECK: Block B{{[0-9]+}}:
339349
// CHECK: AssignOrigin (DestID: [[O_NULLPTR_CAST:[0-9]+]], SrcID: [[O_NULLPTR:[0-9]+]])
350+
// CHECK: AssignOrigin (DestID: [[O_P:[0-9]+]], SrcID: [[O_NULLPTR_CAST]])
351+
// CHECK: AssignOrigin (DestID: [[O_NULLPTR_CAST:[0-9]+]], SrcID: [[O_NULLPTR:[0-9]+]])
340352
// CHECK: AssignOrigin (DestID: [[O_P:[0-9]+]], SrcID: [[O_NULLPTR_CAST]])
341353
{
342354
MyObj outer;
@@ -358,3 +370,20 @@ void nested_scopes() {
358370
// CHECK-DAG: Origin [[O_P]] contains Loan [[L_INNER]]
359371
// CHECK-DAG: Origin [[O_ADDR_INNER]] contains Loan [[L_INNER]]
360372
// CHECK-DAG: Origin [[O_ADDR_OUTER]] contains Loan [[L_OUTER]]
373+
374+
375+
// CHECK-LABEL: Function: pointer_indirection
376+
void pointer_indirection() {
377+
int a;
378+
int *p = &a;
379+
// CHECK: Block B1:
380+
// CHECK: Issue (LoanID: [[L_A:[0-9]+]], OriginID: [[O_ADDR_A:[0-9]+]])
381+
// CHECK: AssignOrigin (DestID: [[O_P:[0-9]+]], SrcID: [[O_ADDR_A]])
382+
int **pp = &p;
383+
// CHECK: Issue (LoanID: [[L_P:[0-9]+]], OriginID: [[O_ADDR_P:[0-9]+]])
384+
// CHECK: AssignOrigin (DestID: [[O_PP:[0-9]+]], SrcID: [[O_ADDR_P]])
385+
386+
// FIXME: The Origin for the RHS is broken
387+
int *q = *pp;
388+
// CHECK: AssignOrigin (DestID: [[O_Q:[0-9]+]], SrcID: {{[0-9]+}})
389+
}

0 commit comments

Comments
 (0)