Skip to content

Commit 7b8f162

Browse files
committed
Fixed implementation
1 parent e60da46 commit 7b8f162

File tree

2 files changed

+189
-31
lines changed

2 files changed

+189
-31
lines changed

clang/lib/Sema/AnalysisBasedWarnings.cpp

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include "clang/Sema/SemaInternal.h"
4848
#include "llvm/ADT/ArrayRef.h"
4949
#include "llvm/ADT/BitVector.h"
50+
#include "llvm/ADT/DenseMap.h"
5051
#include "llvm/ADT/MapVector.h"
5152
#include "llvm/ADT/STLFunctionalExtras.h"
5253
#include "llvm/ADT/SmallVector.h"
@@ -434,6 +435,8 @@ struct TransferFunctions : public StmtVisitor<TransferFunctions> {
434435

435436
TransferFunctions(const VarDecl *VD) : Var(VD) {}
436437

438+
void reset() { AllValuesAreNoReturn = std::nullopt; }
439+
437440
void VisitDeclStmt(DeclStmt *DS) {
438441
for (auto *DI : DS->decls())
439442
if (auto *VD = dyn_cast<VarDecl>(DI))
@@ -477,28 +480,63 @@ struct TransferFunctions : public StmtVisitor<TransferFunctions> {
477480
static bool areAllValuesNoReturn(const VarDecl *VD, const CFGBlock &VarBlk,
478481
AnalysisDeclContext &AC) {
479482
// The set of possible values of a constant variable is determined by
480-
// its initializer.
481-
if (VD->getType().isConstant(AC.getASTContext())) {
483+
// its initializer, unless it is a function parameter.
484+
if (!isa<ParmVarDecl>(VD) && VD->getType().isConstant(AC.getASTContext())) {
482485
if (const VarDecl *Def = VD->getDefinition())
483486
return isInitializedWithNoReturn(Def);
484487
return false;
485488
}
486489

487-
// Scan function statements for definitions of the given variable.
490+
// In multithreaded environment the value of a global variable may be changed
491+
// asynchronously.
492+
if (!VD->getDeclContext()->isFunctionOrMethod())
493+
return false;
494+
495+
// Check the condition "all values are noreturn". It is satisfied if the
496+
// variable is set to "noreturn" value in the current block or all its
497+
// predecessors satisfies the condition.
498+
using MapTy = llvm::DenseMap<const CFGBlock *, std::optional<bool>>;
499+
using ValueTy = MapTy::value_type;
500+
MapTy BlocksToCheck;
501+
BlocksToCheck[&VarBlk] = std::nullopt;
502+
const auto BlockSatisfiesCondition = [](ValueTy Item) {
503+
return Item.getSecond().value_or(false);
504+
};
505+
488506
TransferFunctions TF(VD);
489507
BackwardDataflowWorklist Worklist(*AC.getCFG(), AC);
490508
Worklist.enqueueBlock(&VarBlk);
491509
while (const CFGBlock *B = Worklist.dequeue()) {
510+
// First check the current block.
492511
for (CFGBlock::const_reverse_iterator ri = B->rbegin(), re = B->rend();
493512
ri != re; ++ri) {
494513
if (std::optional<CFGStmt> cs = ri->getAs<CFGStmt>()) {
495514
const Stmt *S = cs->getStmt();
515+
TF.reset();
496516
TF.Visit(const_cast<Stmt *>(S));
497-
if (TF.AllValuesAreNoReturn)
498-
return *TF.AllValuesAreNoReturn;
517+
if (TF.AllValuesAreNoReturn) {
518+
if (!TF.AllValuesAreNoReturn.value())
519+
return false;
520+
BlocksToCheck[B] = true;
521+
break;
522+
}
499523
}
500524
}
501-
Worklist.enqueuePredecessors(B);
525+
526+
// If all checked blocks satisfy the condition, the check is finished.
527+
if (std::all_of(BlocksToCheck.begin(), BlocksToCheck.end(),
528+
BlockSatisfiesCondition))
529+
return true;
530+
531+
// If this block does not contain the variable definition, check
532+
// its predecessors.
533+
if (!BlocksToCheck[B]) {
534+
Worklist.enqueuePredecessors(B);
535+
BlocksToCheck.erase(B);
536+
for (const auto &PredBlk : B->preds())
537+
if (!BlocksToCheck.contains(PredBlk))
538+
BlocksToCheck[PredBlk] = std::nullopt;
539+
}
502540
}
503541

504542
return false;

clang/test/SemaCXX/noreturn-vars.cpp

Lines changed: 145 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -43,50 +43,171 @@ void (*global_fptr)() = noret;
4343
[[noreturn]] void test_global_assign() {
4444
global_fptr = noret;
4545
global_fptr();
46+
} // expected-warning {{function declared 'noreturn' should not return}}
47+
48+
// Local variable assignment.
49+
50+
[[noreturn]] void test_init() {
51+
func_type func_ptr = noret;
52+
func_ptr();
4653
}
4754

48-
[[noreturn]] void test_global_override() {
49-
global_fptr = ordinary;
50-
global_fptr = noret;
51-
global_fptr();
55+
[[noreturn]] void test_assign() {
56+
void (*func_ptr)(void);
57+
func_ptr = noret;
58+
func_ptr();
59+
}
60+
61+
[[noreturn]] void test_override() {
62+
func_type func_ptr;
63+
func_ptr = ordinary;
64+
func_ptr = noret;
65+
func_ptr();
66+
}
67+
68+
[[noreturn]] void test_if_all(int x) {
69+
func_type func_ptr;
70+
if (x > 0)
71+
func_ptr = noret;
72+
else
73+
func_ptr = noret2;
74+
func_ptr();
75+
}
76+
77+
[[noreturn]] void test_if_mix(int x) {
78+
func_type func_ptr;
79+
if (x > 0)
80+
func_ptr = noret;
81+
else
82+
func_ptr = ordinary;
83+
func_ptr();
84+
} // expected-warning {{function declared 'noreturn' should not return}}
85+
86+
[[noreturn]] void test_if_opt(int x) {
87+
func_type func_ptr = noret;
88+
if (x > 0)
89+
func_ptr = ordinary;
90+
func_ptr();
91+
} // expected-warning {{function declared 'noreturn' should not return}}
92+
93+
[[noreturn]] void test_if_opt2(int x) {
94+
func_type func_ptr = ordinary;
95+
if (x > 0)
96+
func_ptr = noret;
97+
func_ptr();
98+
} // expected-warning {{function declared 'noreturn' should not return}}
99+
100+
[[noreturn]] void test_if_nest_all(int x, int y) {
101+
func_type func_ptr;
102+
if (x > 0) {
103+
if (y > 0)
104+
func_ptr = noret;
105+
else
106+
func_ptr = noret2;
107+
} else {
108+
if (y < 0)
109+
func_ptr = noret2;
110+
else
111+
func_ptr = noret;
112+
}
113+
func_ptr();
114+
}
115+
116+
[[noreturn]] void test_if_nest_mix(int x, int y) {
117+
func_type func_ptr;
118+
if (x > 0) {
119+
if (y > 0)
120+
func_ptr = noret;
121+
else
122+
func_ptr = noret2;
123+
} else {
124+
if (y < 0)
125+
func_ptr = ordinary;
126+
else
127+
func_ptr = noret;
128+
}
129+
func_ptr();
130+
} // expected-warning {{function declared 'noreturn' should not return}}
131+
132+
[[noreturn]] void test_switch_all(int x) {
133+
func_type func_ptr;
134+
switch(x) {
135+
case 1:
136+
func_ptr = noret;
137+
break;
138+
default:
139+
func_ptr = noret2;
140+
break;
141+
}
142+
func_ptr();
52143
}
53144

54-
[[noreturn]] void test_global_switch_01(int x) {
145+
[[noreturn]] void test_switch_mix(int x) {
146+
func_type func_ptr;
55147
switch(x) {
56148
case 1:
57-
global_fptr = noret;
58-
break;
149+
func_ptr = ordinary;
150+
break;
59151
default:
60-
global_fptr = noret2;
61-
break;
152+
func_ptr = noret;
153+
break;
62154
}
63-
global_fptr();
155+
func_ptr();
156+
} // expected-warning {{function declared 'noreturn' should not return}}
157+
158+
[[noreturn]] void test_switch_fall(int x) {
159+
func_type func_ptr;
160+
switch(x) {
161+
case 1:
162+
func_ptr = ordinary;
163+
default:
164+
func_ptr = noret;
165+
break;
166+
}
167+
func_ptr();
64168
}
65169

66-
[[noreturn]] void test_global_switch_02(int x) {
170+
[[noreturn]] void test_switch_all_nest(int x, int y) {
171+
func_type func_ptr;
67172
switch(x) {
68173
case 1:
69-
global_fptr = ordinary;
70-
break;
174+
func_ptr = noret;
175+
break;
71176
default:
72-
global_fptr = noret;
73-
break;
177+
if (y > 0)
178+
func_ptr = noret2;
179+
else
180+
func_ptr = noret;
181+
break;
74182
}
75-
global_fptr();
183+
func_ptr();
76184
}
77185

78-
// Local variable assignment.
186+
[[noreturn]] void test_switch_mix_nest(int x, int y) {
187+
func_type func_ptr;
188+
switch(x) {
189+
case 1:
190+
func_ptr = noret;
191+
break;
192+
default:
193+
if (y > 0)
194+
func_ptr = noret2;
195+
else
196+
func_ptr = ordinary;
197+
break;
198+
}
199+
func_ptr();
200+
} // expected-warning {{function declared 'noreturn' should not return}}
79201

80-
[[noreturn]] void test_local_init() {
81-
func_type func_ptr = noret;
202+
// Function parameters.
203+
204+
[[noreturn]] void test_param(void (*func_ptr)() = noret) {
82205
func_ptr();
83-
}
206+
} // expected-warning {{function declared 'noreturn' should not return}}
84207

85-
[[noreturn]] void test_local_assign() {
86-
void (*func_ptr)(void);
87-
func_ptr = noret;
208+
[[noreturn]] void test_const_param(void (* const func_ptr)() = noret) {
88209
func_ptr();
89-
}
210+
} // expected-warning {{function declared 'noreturn' should not return}}
90211

91212
// Escaped value.
92213

@@ -104,4 +225,3 @@ extern void abc_02(func_type *);
104225
abc_02(&func_ptr);
105226
func_ptr();
106227
} // expected-warning {{function declared 'noreturn' should not return}}
107-

0 commit comments

Comments
 (0)