clang 22.0.0git
UninitializedValues.cpp
Go to the documentation of this file.
1//===- UninitializedValues.cpp - Find Uninitialized Values ----------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements uninitialized values analysis for source-level CFGs.
10//
11//===----------------------------------------------------------------------===//
12
14#include "clang/AST/Attr.h"
15#include "clang/AST/Decl.h"
16#include "clang/AST/DeclBase.h"
17#include "clang/AST/Expr.h"
19#include "clang/AST/Stmt.h"
20#include "clang/AST/StmtObjC.h"
22#include "clang/AST/Type.h"
24#include "clang/Analysis/CFG.h"
27#include "clang/Basic/LLVM.h"
28#include "llvm/ADT/BitVector.h"
29#include "llvm/ADT/DenseMap.h"
30#include "llvm/ADT/PackedVector.h"
31#include "llvm/ADT/SmallBitVector.h"
32#include "llvm/ADT/SmallVector.h"
33#include <algorithm>
34#include <cassert>
35#include <optional>
36
37using namespace clang;
38
39#define DEBUG_LOGGING 0
40
41static bool recordIsNotEmpty(const RecordDecl *RD) {
42 // We consider a record decl to be empty if it contains only unnamed bit-
43 // fields, zero-width fields, and fields of empty record type.
44 for (const auto *FD : RD->fields()) {
45 if (FD->isUnnamedBitField())
46 continue;
47 if (FD->isZeroSize(FD->getASTContext()))
48 continue;
49 // The only case remaining to check is for a field declaration of record
50 // type and whether that record itself is empty.
51 if (const auto *FieldRD = FD->getType()->getAsRecordDecl();
52 !FieldRD || recordIsNotEmpty(FieldRD))
53 return true;
54 }
55 return false;
56}
57
58static bool isTrackedVar(const VarDecl *vd, const DeclContext *dc) {
59 if (vd->isLocalVarDecl() && !vd->hasGlobalStorage() &&
60 !vd->isExceptionVariable() && !vd->isInitCapture() && !vd->isImplicit() &&
61 vd->getDeclContext() == dc) {
62 QualType ty = vd->getType();
63 if (const auto *RD = ty->getAsRecordDecl())
64 return recordIsNotEmpty(RD);
65 return ty->isScalarType() || ty->isVectorType() || ty->isRVVSizelessBuiltinType();
66 }
67 return false;
68}
69
70//------------------------------------------------------------------------====//
71// DeclToIndex: a mapping from Decls we track to value indices.
72//====------------------------------------------------------------------------//
73
74namespace {
75
76class DeclToIndex {
77 llvm::DenseMap<const VarDecl *, unsigned> map;
78
79public:
80 DeclToIndex() = default;
81
82 /// Compute the actual mapping from declarations to bits.
83 void computeMap(const DeclContext &dc);
84
85 /// Return the number of declarations in the map.
86 unsigned size() const { return map.size(); }
87
88 /// Returns the bit vector index for a given declaration.
89 std::optional<unsigned> getValueIndex(const VarDecl *d) const;
90};
91
92} // namespace
93
94void DeclToIndex::computeMap(const DeclContext &dc) {
95 unsigned count = 0;
97 E(dc.decls_end());
98 for ( ; I != E; ++I) {
99 const VarDecl *vd = *I;
100 if (isTrackedVar(vd, &dc))
101 map[vd] = count++;
102 }
103}
104
105std::optional<unsigned> DeclToIndex::getValueIndex(const VarDecl *d) const {
106 llvm::DenseMap<const VarDecl *, unsigned>::const_iterator I = map.find(d);
107 if (I == map.end())
108 return std::nullopt;
109 return I->second;
110}
111
112//------------------------------------------------------------------------====//
113// CFGBlockValues: dataflow values for CFG blocks.
114//====------------------------------------------------------------------------//
115
116// These values are defined in such a way that a merge can be done using
117// a bitwise OR.
118enum Value { Unknown = 0x0, /* 00 */
119 Initialized = 0x1, /* 01 */
120 Uninitialized = 0x2, /* 10 */
121 MayUninitialized = 0x3 /* 11 */ };
122
123static bool isUninitialized(const Value v) {
124 return v >= Uninitialized;
125}
126
127static bool isAlwaysUninit(const Value v) {
128 return v == Uninitialized;
129}
130
131namespace {
132
133using ValueVector = llvm::PackedVector<Value, 2, llvm::SmallBitVector>;
134
135class CFGBlockValues {
136 const CFG &cfg;
138 ValueVector scratch;
139 DeclToIndex declToIndex;
140
141public:
142 CFGBlockValues(const CFG &cfg);
143
144 unsigned getNumEntries() const { return declToIndex.size(); }
145
146 void computeSetOfDeclarations(const DeclContext &dc);
147
148 ValueVector &getValueVector(const CFGBlock *block) {
149 return vals[block->getBlockID()];
150 }
151
152 void setAllScratchValues(Value V);
153 void mergeIntoScratch(ValueVector const &source, bool isFirst);
154 bool updateValueVectorWithScratch(const CFGBlock *block);
155
156 bool hasNoDeclarations() const {
157 return declToIndex.size() == 0;
158 }
159
160 void resetScratch();
161
162 ValueVector::reference operator[](const VarDecl *vd);
163
164 Value getValue(const CFGBlock *block, const VarDecl *vd) {
165 std::optional<unsigned> idx = declToIndex.getValueIndex(vd);
166 return getValueVector(block)[*idx];
167 }
168};
169
170} // namespace
171
172CFGBlockValues::CFGBlockValues(const CFG &c) : cfg(c), vals(0) {}
173
174void CFGBlockValues::computeSetOfDeclarations(const DeclContext &dc) {
175 declToIndex.computeMap(dc);
176 unsigned decls = declToIndex.size();
177 scratch.resize(decls);
178 unsigned n = cfg.getNumBlockIDs();
179 if (!n)
180 return;
181 vals.resize(n);
182 for (auto &val : vals)
183 val.resize(decls);
184}
185
186#if DEBUG_LOGGING
187static void printVector(const CFGBlock *block, ValueVector &bv,
188 unsigned num) {
189 llvm::errs() << block->getBlockID() << " :";
190 for (const auto &i : bv)
191 llvm::errs() << ' ' << i;
192 llvm::errs() << " : " << num << '\n';
193}
194#endif
195
196void CFGBlockValues::setAllScratchValues(Value V) {
197 for (unsigned I = 0, E = scratch.size(); I != E; ++I)
198 scratch[I] = V;
199}
200
201void CFGBlockValues::mergeIntoScratch(ValueVector const &source,
202 bool isFirst) {
203 if (isFirst)
204 scratch = source;
205 else
206 scratch |= source;
207}
208
209bool CFGBlockValues::updateValueVectorWithScratch(const CFGBlock *block) {
210 ValueVector &dst = getValueVector(block);
211 bool changed = (dst != scratch);
212 if (changed)
213 dst = scratch;
214#if DEBUG_LOGGING
215 printVector(block, scratch, 0);
216#endif
217 return changed;
218}
219
220void CFGBlockValues::resetScratch() {
221 scratch.reset();
222}
223
224ValueVector::reference CFGBlockValues::operator[](const VarDecl *vd) {
225 return scratch[*declToIndex.getValueIndex(vd)];
226}
227
228//------------------------------------------------------------------------====//
229// Classification of DeclRefExprs as use or initialization.
230//====------------------------------------------------------------------------//
231
232namespace {
233
234class FindVarResult {
235 const VarDecl *vd;
236 const DeclRefExpr *dr;
237
238public:
239 FindVarResult(const VarDecl *vd, const DeclRefExpr *dr) : vd(vd), dr(dr) {}
240
241 const DeclRefExpr *getDeclRefExpr() const { return dr; }
242 const VarDecl *getDecl() const { return vd; }
243};
244
245} // namespace
246
247static const Expr *stripCasts(ASTContext &C, const Expr *Ex) {
248 while (Ex) {
249 Ex = Ex->IgnoreParenNoopCasts(C);
250 if (const auto *CE = dyn_cast<CastExpr>(Ex)) {
251 if (CE->getCastKind() == CK_LValueBitCast) {
252 Ex = CE->getSubExpr();
253 continue;
254 }
255 }
256 break;
257 }
258 return Ex;
259}
260
261/// If E is an expression comprising a reference to a single variable, find that
262/// variable.
263static FindVarResult findVar(const Expr *E, const DeclContext *DC) {
264 if (const auto *DRE =
265 dyn_cast<DeclRefExpr>(stripCasts(DC->getParentASTContext(), E)))
266 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()))
267 if (isTrackedVar(VD, DC))
268 return FindVarResult(VD, DRE);
269 return FindVarResult(nullptr, nullptr);
270}
271
272namespace {
273
274/// Classify each DeclRefExpr as an initialization or a use. Any
275/// DeclRefExpr which isn't explicitly classified will be assumed to have
276/// escaped the analysis and will be treated as an initialization.
277class ClassifyRefs : public StmtVisitor<ClassifyRefs> {
278public:
279 enum Class { Init, Use, SelfInit, ConstRefUse, ConstPtrUse, Ignore };
280
281private:
282 const DeclContext *DC;
283 llvm::DenseMap<const DeclRefExpr *, Class> Classification;
284
285 bool isTrackedVar(const VarDecl *VD) const {
286 return ::isTrackedVar(VD, DC);
287 }
288
289 void classify(const Expr *E, Class C);
290
291public:
292 ClassifyRefs(AnalysisDeclContext &AC) : DC(cast<DeclContext>(AC.getDecl())) {}
293
294 void VisitDeclStmt(DeclStmt *DS);
295 void VisitUnaryOperator(UnaryOperator *UO);
296 void VisitBinaryOperator(BinaryOperator *BO);
297 void VisitCallExpr(CallExpr *CE);
298 void VisitCastExpr(CastExpr *CE);
299 void VisitOMPExecutableDirective(OMPExecutableDirective *ED);
300
301 void operator()(Stmt *S) { Visit(S); }
302
303 Class get(const DeclRefExpr *DRE) const {
304 llvm::DenseMap<const DeclRefExpr*, Class>::const_iterator I
305 = Classification.find(DRE);
306 if (I != Classification.end())
307 return I->second;
308
309 const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
310 if (!VD || !isTrackedVar(VD))
311 return Ignore;
312
313 return Init;
314 }
315};
316
317} // namespace
318
320 if (VD->getType()->isRecordType())
321 return nullptr;
322 if (Expr *Init = VD->getInit()) {
323 const auto *DRE =
324 dyn_cast<DeclRefExpr>(stripCasts(VD->getASTContext(), Init));
325 if (DRE && DRE->getDecl() == VD)
326 return DRE;
327 }
328 return nullptr;
329}
330
331void ClassifyRefs::classify(const Expr *E, Class C) {
332 // The result of a ?: could also be an lvalue.
333 E = E->IgnoreParens();
334 if (const auto *CO = dyn_cast<ConditionalOperator>(E)) {
335 classify(CO->getTrueExpr(), C);
336 classify(CO->getFalseExpr(), C);
337 return;
338 }
339
340 if (const auto *BCO = dyn_cast<BinaryConditionalOperator>(E)) {
341 classify(BCO->getFalseExpr(), C);
342 return;
343 }
344
345 if (const auto *OVE = dyn_cast<OpaqueValueExpr>(E)) {
346 classify(OVE->getSourceExpr(), C);
347 return;
348 }
349
350 if (const auto *ME = dyn_cast<MemberExpr>(E)) {
351 if (const auto *VD = dyn_cast<VarDecl>(ME->getMemberDecl())) {
352 if (!VD->isStaticDataMember())
353 classify(ME->getBase(), C);
354 }
355 return;
356 }
357
358 if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
359 switch (BO->getOpcode()) {
360 case BO_PtrMemD:
361 case BO_PtrMemI:
362 classify(BO->getLHS(), C);
363 return;
364 case BO_Comma:
365 classify(BO->getRHS(), C);
366 return;
367 default:
368 return;
369 }
370 }
371
372 FindVarResult Var = findVar(E, DC);
373 if (const DeclRefExpr *DRE = Var.getDeclRefExpr()) {
374 auto &Class = Classification[DRE];
375 Class = std::max(Class, C);
376 }
377}
378
379void ClassifyRefs::VisitDeclStmt(DeclStmt *DS) {
380 for (auto *DI : DS->decls()) {
381 auto *VD = dyn_cast<VarDecl>(DI);
382 if (VD && isTrackedVar(VD))
383 if (const DeclRefExpr *DRE = getSelfInitExpr(VD))
384 Classification[DRE] = SelfInit;
385 }
386}
387
388void ClassifyRefs::VisitBinaryOperator(BinaryOperator *BO) {
389 // Ignore the evaluation of a DeclRefExpr on the LHS of an assignment. If this
390 // is not a compound-assignment, we will treat it as initializing the variable
391 // when TransferFunctions visits it. A compound-assignment does not affect
392 // whether a variable is uninitialized, and there's no point counting it as a
393 // use.
394 if (BO->isCompoundAssignmentOp())
395 classify(BO->getLHS(), Use);
396 else if (BO->getOpcode() == BO_Assign || BO->getOpcode() == BO_Comma)
397 classify(BO->getLHS(), Ignore);
398}
399
400void ClassifyRefs::VisitUnaryOperator(UnaryOperator *UO) {
401 // Increment and decrement are uses despite there being no lvalue-to-rvalue
402 // conversion.
403 if (UO->isIncrementDecrementOp())
404 classify(UO->getSubExpr(), Use);
405}
406
407void ClassifyRefs::VisitOMPExecutableDirective(OMPExecutableDirective *ED) {
409 classify(cast<Expr>(S), Use);
410}
411
412static bool isPointerToConst(const QualType &QT) {
413 return QT->isAnyPointerType() && QT->getPointeeType().isConstQualified();
414}
415
416static bool hasTrivialBody(CallExpr *CE) {
417 if (FunctionDecl *FD = CE->getDirectCallee()) {
418 if (FunctionTemplateDecl *FTD = FD->getPrimaryTemplate())
419 return FTD->getTemplatedDecl()->hasTrivialBody();
420 return FD->hasTrivialBody();
421 }
422 return false;
423}
424
425void ClassifyRefs::VisitCallExpr(CallExpr *CE) {
426 // Classify arguments to std::move as used.
427 if (CE->isCallToStdMove()) {
428 // RecordTypes are handled in SemaDeclCXX.cpp.
429 if (!CE->getArg(0)->getType()->isRecordType())
430 classify(CE->getArg(0), Use);
431 return;
432 }
433 bool isTrivialBody = hasTrivialBody(CE);
434 // If a value is passed by const pointer to a function,
435 // we should not assume that it is initialized by the call, and we
436 // conservatively do not assume that it is used.
437 // If a value is passed by const reference to a function,
438 // it should already be initialized.
439 for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end();
440 I != E; ++I) {
441 if ((*I)->isGLValue()) {
442 if ((*I)->getType().isConstQualified())
443 classify((*I), isTrivialBody ? Ignore : ConstRefUse);
444 } else if (isPointerToConst((*I)->getType())) {
445 const Expr *Ex = stripCasts(DC->getParentASTContext(), *I);
446 const auto *UO = dyn_cast<UnaryOperator>(Ex);
447 if (UO && UO->getOpcode() == UO_AddrOf)
448 classify(UO->getSubExpr(), isTrivialBody ? Ignore : ConstPtrUse);
449 }
450 }
451}
452
453void ClassifyRefs::VisitCastExpr(CastExpr *CE) {
454 if (CE->getCastKind() == CK_LValueToRValue)
455 classify(CE->getSubExpr(), Use);
456 else if (const auto *CSE = dyn_cast<CStyleCastExpr>(CE)) {
457 if (CSE->getType()->isVoidType()) {
458 // Squelch any detected load of an uninitialized value if
459 // we cast it to void.
460 // e.g. (void) x;
461 classify(CSE->getSubExpr(), Ignore);
462 }
463 }
464}
465
466//------------------------------------------------------------------------====//
467// Transfer function for uninitialized values analysis.
468//====------------------------------------------------------------------------//
469
470namespace {
471
472class TransferFunctions : public StmtVisitor<TransferFunctions> {
473 CFGBlockValues &vals;
474 const CFG &cfg;
475 const CFGBlock *block;
477 const ClassifyRefs &classification;
478 ObjCNoReturn objCNoRet;
479 UninitVariablesHandler &handler;
480
481public:
482 TransferFunctions(CFGBlockValues &vals, const CFG &cfg,
483 const CFGBlock *block, AnalysisDeclContext &ac,
484 const ClassifyRefs &classification,
485 UninitVariablesHandler &handler)
486 : vals(vals), cfg(cfg), block(block), ac(ac),
487 classification(classification), objCNoRet(ac.getASTContext()),
488 handler(handler) {}
489
490 void reportUse(const Expr *ex, const VarDecl *vd);
491 void reportConstRefUse(const Expr *ex, const VarDecl *vd);
492 void reportConstPtrUse(const Expr *ex, const VarDecl *vd);
493
494 void VisitBinaryOperator(BinaryOperator *bo);
495 void VisitBlockExpr(BlockExpr *be);
496 void VisitCallExpr(CallExpr *ce);
497 void VisitDeclRefExpr(DeclRefExpr *dr);
498 void VisitDeclStmt(DeclStmt *ds);
499 void VisitGCCAsmStmt(GCCAsmStmt *as);
500 void VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS);
501 void VisitObjCMessageExpr(ObjCMessageExpr *ME);
502 void VisitOMPExecutableDirective(OMPExecutableDirective *ED);
503
504 bool isTrackedVar(const VarDecl *vd) {
505 return ::isTrackedVar(vd, cast<DeclContext>(ac.getDecl()));
506 }
507
508 FindVarResult findVar(const Expr *ex) {
509 return ::findVar(ex, cast<DeclContext>(ac.getDecl()));
510 }
511
512 UninitUse getUninitUse(const Expr *ex, const VarDecl *vd, Value v) {
513 UninitUse Use(ex, isAlwaysUninit(v));
514
515 assert(isUninitialized(v));
516 if (Use.getKind() == UninitUse::Always)
517 return Use;
518
519 // If an edge which leads unconditionally to this use did not initialize
520 // the variable, we can say something stronger than 'may be uninitialized':
521 // we can say 'either it's used uninitialized or you have dead code'.
522 //
523 // We track the number of successors of a node which have been visited, and
524 // visit a node once we have visited all of its successors. Only edges where
525 // the variable might still be uninitialized are followed. Since a variable
526 // can't transfer from being initialized to being uninitialized, this will
527 // trace out the subgraph which inevitably leads to the use and does not
528 // initialize the variable. We do not want to skip past loops, since their
529 // non-termination might be correlated with the initialization condition.
530 //
531 // For example:
532 //
533 // void f(bool a, bool b) {
534 // block1: int n;
535 // if (a) {
536 // block2: if (b)
537 // block3: n = 1;
538 // block4: } else if (b) {
539 // block5: while (!a) {
540 // block6: do_work(&a);
541 // n = 2;
542 // }
543 // }
544 // block7: if (a)
545 // block8: g();
546 // block9: return n;
547 // }
548 //
549 // Starting from the maybe-uninitialized use in block 9:
550 // * Block 7 is not visited because we have only visited one of its two
551 // successors.
552 // * Block 8 is visited because we've visited its only successor.
553 // From block 8:
554 // * Block 7 is visited because we've now visited both of its successors.
555 // From block 7:
556 // * Blocks 1, 2, 4, 5, and 6 are not visited because we didn't visit all
557 // of their successors (we didn't visit 4, 3, 5, 6, and 5, respectively).
558 // * Block 3 is not visited because it initializes 'n'.
559 // Now the algorithm terminates, having visited blocks 7 and 8, and having
560 // found the frontier is blocks 2, 4, and 5.
561 //
562 // 'n' is definitely uninitialized for two edges into block 7 (from blocks 2
563 // and 4), so we report that any time either of those edges is taken (in
564 // each case when 'b == false'), 'n' is used uninitialized.
566 SmallVector<unsigned, 32> SuccsVisited(cfg.getNumBlockIDs(), 0);
567 Queue.push_back(block);
568 // Specify that we've already visited all successors of the starting block.
569 // This has the dual purpose of ensuring we never add it to the queue, and
570 // of marking it as not being a candidate element of the frontier.
571 SuccsVisited[block->getBlockID()] = block->succ_size();
572 while (!Queue.empty()) {
573 const CFGBlock *B = Queue.pop_back_val();
574
575 // If the use is always reached from the entry block, make a note of that.
576 if (B == &cfg.getEntry())
577 Use.setUninitAfterCall();
578
580 I != E; ++I) {
581 const CFGBlock *Pred = *I;
582 if (!Pred)
583 continue;
584
585 Value AtPredExit = vals.getValue(Pred, vd);
586 if (AtPredExit == Initialized)
587 // This block initializes the variable.
588 continue;
589 if (AtPredExit == MayUninitialized &&
590 vals.getValue(B, vd) == Uninitialized) {
591 // This block declares the variable (uninitialized), and is reachable
592 // from a block that initializes the variable. We can't guarantee to
593 // give an earlier location for the diagnostic (and it appears that
594 // this code is intended to be reachable) so give a diagnostic here
595 // and go no further down this path.
596 Use.setUninitAfterDecl();
597 continue;
598 }
599
600 unsigned &SV = SuccsVisited[Pred->getBlockID()];
601 if (!SV) {
602 // When visiting the first successor of a block, mark all NULL
603 // successors as having been visited.
605 SE = Pred->succ_end();
606 SI != SE; ++SI)
607 if (!*SI)
608 ++SV;
609 }
610
611 if (++SV == Pred->succ_size())
612 // All paths from this block lead to the use and don't initialize the
613 // variable.
614 Queue.push_back(Pred);
615 }
616 }
617
618 // Scan the frontier, looking for blocks where the variable was
619 // uninitialized.
620 for (const auto *Block : cfg) {
621 if (vals.getValue(Block, vd) != Uninitialized)
622 continue;
623 unsigned BlockID = Block->getBlockID();
624 const Stmt *Term = Block->getTerminatorStmt();
625 if (SuccsVisited[BlockID] && SuccsVisited[BlockID] < Block->succ_size() &&
626 Term) {
627 // This block inevitably leads to the use. If we have an edge from here
628 // to a post-dominator block, and the variable is uninitialized on that
629 // edge, we have found a bug.
631 E = Block->succ_end(); I != E; ++I) {
632 const CFGBlock *Succ = *I;
633 if (Succ && SuccsVisited[Succ->getBlockID()] >= Succ->succ_size()) {
634 // Switch cases are a special case: report the label to the caller
635 // as the 'terminator', not the switch statement itself. Suppress
636 // situations where no label matched: we can't be sure that's
637 // possible.
638 if (isa<SwitchStmt>(Term)) {
639 const Stmt *Label = Succ->getLabel();
640 if (!Label || !isa<SwitchCase>(Label))
641 // Might not be possible.
642 continue;
643 UninitUse::Branch Branch;
644 Branch.Terminator = Label;
645 Branch.Output = 0; // Ignored.
646 Use.addUninitBranch(Branch);
647 } else {
648 UninitUse::Branch Branch;
649 Branch.Terminator = Term;
650 Branch.Output = I - Block->succ_begin();
651 Use.addUninitBranch(Branch);
652 }
653 }
654 }
655 }
656 }
657
658 return Use;
659 }
660};
661
662} // namespace
663
664void TransferFunctions::reportUse(const Expr *ex, const VarDecl *vd) {
665 Value v = vals[vd];
666 if (isUninitialized(v))
667 handler.handleUseOfUninitVariable(vd, getUninitUse(ex, vd, v));
668}
669
670void TransferFunctions::reportConstRefUse(const Expr *ex, const VarDecl *vd) {
671 Value v = vals[vd];
672 if (isAlwaysUninit(v)) {
673 auto use = getUninitUse(ex, vd, v);
674 use.setConstRefUse();
675 handler.handleUseOfUninitVariable(vd, use);
676 }
677}
678
679void TransferFunctions::reportConstPtrUse(const Expr *ex, const VarDecl *vd) {
680 Value v = vals[vd];
681 if (isAlwaysUninit(v)) {
682 auto use = getUninitUse(ex, vd, v);
683 use.setConstPtrUse();
684 handler.handleUseOfUninitVariable(vd, use);
685 }
686}
687
688void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS) {
689 // This represents an initialization of the 'element' value.
690 if (const auto *DS = dyn_cast<DeclStmt>(FS->getElement())) {
691 const auto *VD = cast<VarDecl>(DS->getSingleDecl());
692 if (isTrackedVar(VD))
693 vals[VD] = Initialized;
694 }
695}
696
697void TransferFunctions::VisitOMPExecutableDirective(
700 assert(S && "Expected non-null used-in-clause child.");
701 Visit(S);
702 }
703 if (!ED->isStandaloneDirective())
704 Visit(ED->getStructuredBlock());
705}
706
707void TransferFunctions::VisitBlockExpr(BlockExpr *be) {
708 const BlockDecl *bd = be->getBlockDecl();
709 for (const auto &I : bd->captures()) {
710 const VarDecl *vd = I.getVariable();
711 if (!isTrackedVar(vd))
712 continue;
713 if (I.isByRef()) {
714 vals[vd] = Initialized;
715 continue;
716 }
717 reportUse(be, vd);
718 }
719}
720
721void TransferFunctions::VisitCallExpr(CallExpr *ce) {
722 if (Decl *Callee = ce->getCalleeDecl()) {
723 if (Callee->hasAttr<ReturnsTwiceAttr>()) {
724 // After a call to a function like setjmp or vfork, any variable which is
725 // initialized anywhere within this function may now be initialized. For
726 // now, just assume such a call initializes all variables. FIXME: Only
727 // mark variables as initialized if they have an initializer which is
728 // reachable from here.
729 vals.setAllScratchValues(Initialized);
730 }
731 else if (Callee->hasAttr<AnalyzerNoReturnAttr>()) {
732 // Functions labeled like "analyzer_noreturn" are often used to denote
733 // "panic" functions that in special debug situations can still return,
734 // but for the most part should not be treated as returning. This is a
735 // useful annotation borrowed from the static analyzer that is useful for
736 // suppressing branch-specific false positives when we call one of these
737 // functions but keep pretending the path continues (when in reality the
738 // user doesn't care).
739 vals.setAllScratchValues(Unknown);
740 }
741 }
742}
743
744void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) {
745 switch (classification.get(dr)) {
746 case ClassifyRefs::Ignore:
747 break;
748 case ClassifyRefs::Use:
749 reportUse(dr, cast<VarDecl>(dr->getDecl()));
750 break;
751 case ClassifyRefs::Init:
752 vals[cast<VarDecl>(dr->getDecl())] = Initialized;
753 break;
754 case ClassifyRefs::SelfInit:
755 handler.handleSelfInit(cast<VarDecl>(dr->getDecl()));
756 break;
757 case ClassifyRefs::ConstRefUse:
758 reportConstRefUse(dr, cast<VarDecl>(dr->getDecl()));
759 break;
760 case ClassifyRefs::ConstPtrUse:
761 reportConstPtrUse(dr, cast<VarDecl>(dr->getDecl()));
762 break;
763 }
764}
765
766void TransferFunctions::VisitBinaryOperator(BinaryOperator *BO) {
767 if (BO->getOpcode() == BO_Assign) {
768 FindVarResult Var = findVar(BO->getLHS());
769 if (const VarDecl *VD = Var.getDecl())
770 vals[VD] = Initialized;
771 }
772}
773
774void TransferFunctions::VisitDeclStmt(DeclStmt *DS) {
775 for (auto *DI : DS->decls()) {
776 auto *VD = dyn_cast<VarDecl>(DI);
777 if (VD && isTrackedVar(VD)) {
778 if (getSelfInitExpr(VD)) {
779 // If the initializer consists solely of a reference to itself, we
780 // explicitly mark the variable as uninitialized. This allows code
781 // like the following:
782 //
783 // int x = x;
784 //
785 // to deliberately leave a variable uninitialized. Different analysis
786 // clients can detect this pattern and adjust their reporting
787 // appropriately, but we need to continue to analyze subsequent uses
788 // of the variable.
789 vals[VD] = Uninitialized;
790 } else if (VD->getInit()) {
791 // Treat the new variable as initialized.
792 vals[VD] = Initialized;
793 } else {
794 // No initializer: the variable is now uninitialized. This matters
795 // for cases like:
796 // while (...) {
797 // int n;
798 // use(n);
799 // n = 0;
800 // }
801 // FIXME: Mark the variable as uninitialized whenever its scope is
802 // left, since its scope could be re-entered by a jump over the
803 // declaration.
804 vals[VD] = Uninitialized;
805 }
806 }
807 }
808}
809
810void TransferFunctions::VisitGCCAsmStmt(GCCAsmStmt *as) {
811 // An "asm goto" statement is a terminator that may initialize some variables.
812 if (!as->isAsmGoto())
813 return;
814
815 ASTContext &C = ac.getASTContext();
816 for (const Expr *O : as->outputs()) {
817 const Expr *Ex = stripCasts(C, O);
818
819 // Strip away any unary operators. Invalid l-values are reported by other
820 // semantic analysis passes.
821 while (const auto *UO = dyn_cast<UnaryOperator>(Ex))
822 Ex = stripCasts(C, UO->getSubExpr());
823
824 // Mark the variable as potentially uninitialized for those cases where
825 // it's used on an indirect path, where it's not guaranteed to be
826 // defined.
827 if (const VarDecl *VD = findVar(Ex).getDecl())
828 if (vals[VD] != Initialized)
829 vals[VD] = MayUninitialized;
830 }
831}
832
833void TransferFunctions::VisitObjCMessageExpr(ObjCMessageExpr *ME) {
834 // If the Objective-C message expression is an implicit no-return that
835 // is not modeled in the CFG, set the tracked dataflow values to Unknown.
836 if (objCNoRet.isImplicitNoReturn(ME)) {
837 vals.setAllScratchValues(Unknown);
838 }
839}
840
841//------------------------------------------------------------------------====//
842// High-level "driver" logic for uninitialized values analysis.
843//====------------------------------------------------------------------------//
844
845static bool runOnBlock(const CFGBlock *block, const CFG &cfg,
846 AnalysisDeclContext &ac, CFGBlockValues &vals,
847 const ClassifyRefs &classification,
848 llvm::BitVector &wasAnalyzed,
849 UninitVariablesHandler &handler) {
850 wasAnalyzed[block->getBlockID()] = true;
851 vals.resetScratch();
852 // Merge in values of predecessor blocks.
853 bool isFirst = true;
855 E = block->pred_end(); I != E; ++I) {
856 const CFGBlock *pred = *I;
857 if (!pred)
858 continue;
859 if (wasAnalyzed[pred->getBlockID()]) {
860 vals.mergeIntoScratch(vals.getValueVector(pred), isFirst);
861 isFirst = false;
862 }
863 }
864 // Apply the transfer function.
865 TransferFunctions tf(vals, cfg, block, ac, classification, handler);
866 for (const auto &I : *block) {
867 if (std::optional<CFGStmt> cs = I.getAs<CFGStmt>())
868 tf.Visit(const_cast<Stmt *>(cs->getStmt()));
869 }
870 CFGTerminator terminator = block->getTerminator();
871 if (auto *as = dyn_cast_or_null<GCCAsmStmt>(terminator.getStmt()))
872 if (as->isAsmGoto())
873 tf.Visit(as);
874 return vals.updateValueVectorWithScratch(block);
875}
876
877namespace {
878
879/// PruneBlocksHandler is a special UninitVariablesHandler that is used
880/// to detect when a CFGBlock has any *potential* use of an uninitialized
881/// variable. It is mainly used to prune out work during the final
882/// reporting pass.
883struct PruneBlocksHandler : public UninitVariablesHandler {
884 /// Records if a CFGBlock had a potential use of an uninitialized variable.
885 llvm::BitVector hadUse;
886
887 /// Records if any CFGBlock had a potential use of an uninitialized variable.
888 bool hadAnyUse = false;
889
890 /// The current block to scribble use information.
891 unsigned currentBlock = 0;
892
893 PruneBlocksHandler(unsigned numBlocks) : hadUse(numBlocks, false) {}
894
895 ~PruneBlocksHandler() override = default;
896
897 void handleUseOfUninitVariable(const VarDecl *vd,
898 const UninitUse &use) override {
899 hadUse[currentBlock] = true;
900 hadAnyUse = true;
901 }
902
903 /// Called when the uninitialized variable analysis detects the
904 /// idiom 'int x = x'. All other uses of 'x' within the initializer
905 /// are handled by handleUseOfUninitVariable.
906 void handleSelfInit(const VarDecl *vd) override {
907 hadUse[currentBlock] = true;
908 hadAnyUse = true;
909 }
910};
911
912} // namespace
913
915 const DeclContext &dc,
916 const CFG &cfg,
918 UninitVariablesHandler &handler,
920 CFGBlockValues vals(cfg);
921 vals.computeSetOfDeclarations(dc);
922 if (vals.hasNoDeclarations())
923 return;
924
925 stats.NumVariablesAnalyzed = vals.getNumEntries();
926
927 // Precompute which expressions are uses and which are initializations.
928 ClassifyRefs classification(ac);
929 cfg.VisitBlockStmts(classification);
930
931 // Mark all variables uninitialized at the entry.
932 const CFGBlock &entry = cfg.getEntry();
933 ValueVector &vec = vals.getValueVector(&entry);
934 const unsigned n = vals.getNumEntries();
935 for (unsigned j = 0; j < n; ++j) {
936 vec[j] = Uninitialized;
937 }
938
939 // Proceed with the workist.
940 ForwardDataflowWorklist worklist(cfg, ac);
941 llvm::BitVector previouslyVisited(cfg.getNumBlockIDs());
942 worklist.enqueueSuccessors(&cfg.getEntry());
943 llvm::BitVector wasAnalyzed(cfg.getNumBlockIDs(), false);
944 wasAnalyzed[cfg.getEntry().getBlockID()] = true;
945 PruneBlocksHandler PBH(cfg.getNumBlockIDs());
946
947 while (const CFGBlock *block = worklist.dequeue()) {
948 PBH.currentBlock = block->getBlockID();
949
950 // Did the block change?
951 bool changed = runOnBlock(block, cfg, ac, vals,
952 classification, wasAnalyzed, PBH);
953 ++stats.NumBlockVisits;
954 if (changed || !previouslyVisited[block->getBlockID()])
955 worklist.enqueueSuccessors(block);
956 previouslyVisited[block->getBlockID()] = true;
957 }
958
959 if (!PBH.hadAnyUse)
960 return;
961
962 // Run through the blocks one more time, and report uninitialized variables.
963 for (const auto *block : cfg)
964 if (PBH.hadUse[block->getBlockID()]) {
965 runOnBlock(block, cfg, ac, vals, classification, wasAnalyzed, handler);
966 ++stats.NumBlockVisits;
967 }
968}
969
#define V(N, I)
Definition: ASTContext.h:3597
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
Expr * E
const CFGBlock * Block
Definition: HTMLLogger.cpp:152
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
Defines the Objective-C statement AST node classes.
C Language Family Type Representation.
static bool isAlwaysUninit(const Value v)
static bool isTrackedVar(const VarDecl *vd, const DeclContext *dc)
static const Expr * stripCasts(ASTContext &C, const Expr *Ex)
static bool isUninitialized(const Value v)
@ Uninitialized
@ MayUninitialized
static bool isPointerToConst(const QualType &QT)
static FindVarResult findVar(const Expr *E, const DeclContext *DC)
If E is an expression comprising a reference to a single variable, find that variable.
static bool hasTrivialBody(CallExpr *CE)
static bool recordIsNotEmpty(const RecordDecl *RD)
static const DeclRefExpr * getSelfInitExpr(VarDecl *VD)
static bool runOnBlock(const CFGBlock *block, const CFG &cfg, AnalysisDeclContext &ac, CFGBlockValues &vals, const ClassifyRefs &classification, llvm::BitVector &wasAnalyzed, UninitVariablesHandler &handler)
std::string Label
__device__ __2f16 float c
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:188
AnalysisDeclContext contains the context data for the function, method or block under analysis.
const Decl * getDecl() const
ASTContext & getASTContext() const
outputs_range outputs()
Definition: Stmt.h:3338
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3974
Expr * getLHS() const
Definition: Expr.h:4024
static bool isCompoundAssignmentOp(Opcode Opc)
Definition: Expr.h:4115
Opcode getOpcode() const
Definition: Expr.h:4019
Represents a block literal declaration, which is like an unnamed FunctionDecl.
Definition: Decl.h:4630
ArrayRef< Capture > captures() const
Definition: Decl.h:4757
BlockExpr - Adaptor class for mixing a BlockDecl with expressions.
Definition: Expr.h:6560
const BlockDecl * getBlockDecl() const
Definition: Expr.h:6572
Represents a single basic block in a source-level CFG.
Definition: CFG.h:605
pred_iterator pred_end()
Definition: CFG.h:973
succ_iterator succ_end()
Definition: CFG.h:991
Stmt * getLabel()
Definition: CFG.h:1106
CFGTerminator getTerminator() const
Definition: CFG.h:1085
succ_iterator succ_begin()
Definition: CFG.h:990
Stmt * getTerminatorStmt()
Definition: CFG.h:1087
AdjacentBlocks::const_iterator const_pred_iterator
Definition: CFG.h:959
pred_iterator pred_begin()
Definition: CFG.h:972
unsigned getBlockID() const
Definition: CFG.h:1111
AdjacentBlocks::const_iterator const_succ_iterator
Definition: CFG.h:966
unsigned succ_size() const
Definition: CFG.h:1008
Represents CFGBlock terminator statement.
Definition: CFG.h:532
Stmt * getStmt()
Definition: CFG.h:564
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
Definition: CFG.h:1222
void VisitBlockStmts(Callback &O) const
Definition: CFG.h:1396
CFGBlock & getEntry()
Definition: CFG.h:1330
unsigned getNumBlockIDs() const
Returns the total number of BlockIDs allocated (which start at 0).
Definition: CFG.h:1409
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2879
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:3083
arg_iterator arg_begin()
Definition: Expr.h:3136
arg_iterator arg_end()
Definition: Expr.h:3139
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition: Expr.h:3062
bool isCallToStdMove() const
Definition: Expr.cpp:3578
Decl * getCalleeDecl()
Definition: Expr.h:3056
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
Definition: Expr.h:3612
CastKind getCastKind() const
Definition: Expr.h:3656
Expr * getSubExpr()
Definition: Expr.h:3662
const CFGBlock * dequeue()
specific_decl_iterator - Iterates over a subrange of declarations stored in a DeclContext,...
Definition: DeclBase.h:2393
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Definition: DeclBase.h:1449
ASTContext & getParentASTContext() const
Definition: DeclBase.h:2138
decl_iterator decls_end() const
Definition: DeclBase.h:2375
decl_iterator decls_begin() const
Definition: DeclBase.cpp:1649
A reference to a declared variable, function, enum, etc.
Definition: Expr.h:1272
ValueDecl * getDecl()
Definition: Expr.h:1340
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
Definition: Stmt.h:1622
decl_range decls()
Definition: Stmt.h:1670
const Decl * getSingleDecl() const
Definition: Stmt.h:1637
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:524
bool isImplicit() const
isImplicit - Indicates whether the declaration was implicitly generated by the implementation.
Definition: DeclBase.h:593
DeclContext * getDeclContext()
Definition: DeclBase.h:448
This represents one expression.
Definition: Expr.h:112
Expr * IgnoreParenNoopCasts(const ASTContext &Ctx) LLVM_READONLY
Skip past any parentheses and casts which do not change the value (including ptr->int casts of the sa...
Definition: Expr.cpp:3100
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Definition: Expr.cpp:3069
QualType getType() const
Definition: Expr.h:144
Represents a function declaration or definition.
Definition: Decl.h:1999
Declaration of a template function.
Definition: DeclTemplate.h:952
This represents a GCC inline-assembly statement extension.
Definition: Stmt.h:3364
bool isAsmGoto() const
Definition: Stmt.h:3510
This is a basic class for representing single OpenMP executable directive.
Definition: StmtOpenMP.h:266
ArrayRef< OMPClause * > clauses() const
Definition: StmtOpenMP.h:572
const Stmt * getStructuredBlock() const
Returns the AST node representing OpenMP structured-block of this OpenMP executable directive,...
Definition: StmtOpenMP.h:587
bool isStandaloneDirective() const
Returns whether or not this is a Standalone directive.
Definition: StmtOpenMP.cpp:58
static llvm::iterator_range< used_clauses_child_iterator > used_clauses_children(ArrayRef< OMPClause * > Clauses)
Definition: StmtOpenMP.h:401
Represents Objective-C's collection statement.
Definition: StmtObjC.h:23
An expression that sends a message to the given Objective-C object or class.
Definition: ExprObjC.h:940
bool isImplicitNoReturn(const ObjCMessageExpr *ME)
Return true if the given message expression is known to never return.
A (possibly-)qualified type.
Definition: TypeBase.h:937
bool isConstQualified() const
Determine whether this type is const-qualified.
Definition: TypeBase.h:8416
Represents a struct/union/class.
Definition: Decl.h:4305
field_range fields() const
Definition: Decl.h:4508
RetTy Visit(PTR(Stmt) S, ParamTys... P)
Definition: StmtVisitor.h:45
StmtVisitor - This class implements a simple visitor for Stmt subclasses.
Definition: StmtVisitor.h:186
Stmt - This represents one statement.
Definition: Stmt.h:85
RecordDecl * getAsRecordDecl() const
Retrieves the RecordDecl this type refers to.
Definition: Type.h:41
bool isScalarType() const
Definition: TypeBase.h:9038
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition: Type.cpp:752
bool isVectorType() const
Definition: TypeBase.h:8719
bool isRVVSizelessBuiltinType() const
Returns true for RVV scalable vector types.
Definition: Type.cpp:2599
bool isAnyPointerType() const
Definition: TypeBase.h:8588
bool isRecordType() const
Definition: TypeBase.h:8707
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Definition: Expr.h:2246
Expr * getSubExpr() const
Definition: Expr.h:2287
Opcode getOpcode() const
Definition: Expr.h:2282
static bool isIncrementDecrementOp(Opcode Op)
Definition: Expr.h:2342
A use of a variable, which might be uninitialized.
@ Always
The use is always uninitialized.
virtual void handleUseOfUninitVariable(const VarDecl *vd, const UninitUse &use)
Called when the uninitialized variable is used at the given expression.
virtual void handleSelfInit(const VarDecl *vd)
Called when the uninitialized variable analysis detects the idiom 'int x = x'.
QualType getType() const
Definition: Decl.h:722
Represents a variable declaration or definition.
Definition: Decl.h:925
bool isInitCapture() const
Whether this variable is the implicit variable for a lambda init-capture.
Definition: Decl.h:1577
bool hasGlobalStorage() const
Returns true for all variables that do not have local storage.
Definition: Decl.h:1225
bool isExceptionVariable() const
Determine whether this variable is the exception variable in a C++ catch statememt or an Objective-C ...
Definition: Decl.h:1493
const Expr * getInit() const
Definition: Decl.h:1367
bool isLocalVarDecl() const
Returns true for local variable declarations other than parameters.
Definition: Decl.h:1252
BlockID
The various types of blocks that can occur within a API notes file.
The JSON file list parser is used to communicate input to InstallAPI.
void runUninitializedVariablesAnalysis(const DeclContext &dc, const CFG &cfg, AnalysisDeclContext &ac, UninitVariablesHandler &handler, UninitVariablesAnalysisStats &stats)
U cast(CodeGen::Address addr)
Definition: Address.h:327
@ Class
The "class" keyword introduces the elaborated-type-specifier.
const half4 dst(half4 Src0, half4 Src1)
#define false
Definition: stdbool.h:26
A worklist implementation for forward dataflow analysis.
void enqueueSuccessors(const CFGBlock *Block)
Iterator for iterating over Stmt * arrays that contain only T *.
Definition: Stmt.h:1441