clang 19.0.0git
DataflowEnvironment.cpp
Go to the documentation of this file.
1//===-- DataflowEnvironment.cpp ---------------------------------*- C++ -*-===//
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 defines an Environment class that is used by dataflow analyses
10// that run over Control-Flow Graphs (CFGs) to keep track of the state of the
11// program at given program points.
12//
13//===----------------------------------------------------------------------===//
14
16#include "clang/AST/Decl.h"
17#include "clang/AST/DeclCXX.h"
19#include "clang/AST/Type.h"
23#include "llvm/ADT/DenseMap.h"
24#include "llvm/ADT/DenseSet.h"
25#include "llvm/ADT/MapVector.h"
26#include "llvm/ADT/STLExtras.h"
27#include "llvm/ADT/ScopeExit.h"
28#include "llvm/Support/ErrorHandling.h"
29#include <cassert>
30#include <utility>
31
32#define DEBUG_TYPE "dataflow"
33
34namespace clang {
35namespace dataflow {
36
37// FIXME: convert these to parameters of the analysis or environment. Current
38// settings have been experimentaly validated, but only for a particular
39// analysis.
40static constexpr int MaxCompositeValueDepth = 3;
41static constexpr int MaxCompositeValueSize = 1000;
42
43/// Returns a map consisting of key-value entries that are present in both maps.
44static llvm::DenseMap<const ValueDecl *, StorageLocation *> intersectDeclToLoc(
45 const llvm::DenseMap<const ValueDecl *, StorageLocation *> &DeclToLoc1,
46 const llvm::DenseMap<const ValueDecl *, StorageLocation *> &DeclToLoc2) {
47 llvm::DenseMap<const ValueDecl *, StorageLocation *> Result;
48 for (auto &Entry : DeclToLoc1) {
49 auto It = DeclToLoc2.find(Entry.first);
50 if (It != DeclToLoc2.end() && Entry.second == It->second)
51 Result.insert({Entry.first, Entry.second});
52 }
53 return Result;
54}
55
56// Performs a join on either `ExprToLoc` or `ExprToVal`.
57// The maps must be consistent in the sense that any entries for the same
58// expression must map to the same location / value. This is the case if we are
59// performing a join for control flow within a full-expression (which is the
60// only case when this function should be used).
61template <typename MapT> MapT joinExprMaps(const MapT &Map1, const MapT &Map2) {
62 MapT Result = Map1;
63
64 for (const auto &Entry : Map2) {
65 [[maybe_unused]] auto [It, Inserted] = Result.insert(Entry);
66 // If there was an existing entry, its value should be the same as for the
67 // entry we were trying to insert.
68 assert(It->second == Entry.second);
69 }
70
71 return Result;
72}
73
74// Whether to consider equivalent two values with an unknown relation.
75//
76// FIXME: this function is a hack enabling unsoundness to support
77// convergence. Once we have widening support for the reference/pointer and
78// struct built-in models, this should be unconditionally `false` (and inlined
79// as such at its call sites).
81 switch (K) {
84 return true;
85 default:
86 return false;
87 }
88}
89
91 const Environment &Env1, Value &Val2,
92 const Environment &Env2,
94 // Note: Potentially costly, but, for booleans, we could check whether both
95 // can be proven equivalent in their respective environments.
96
97 // FIXME: move the reference/pointers logic from `areEquivalentValues` to here
98 // and implement separate, join/widen specific handling for
99 // reference/pointers.
100 switch (Model.compare(Type, Val1, Env1, Val2, Env2)) {
102 return true;
104 return false;
106 return equateUnknownValues(Val1.getKind());
107 }
108 llvm_unreachable("All cases covered in switch");
109}
110
111/// Attempts to join distinct values `Val1` and `Val2` in `Env1` and `Env2`,
112/// respectively, of the same type `Type`. Joining generally produces a single
113/// value that (soundly) approximates the two inputs, although the actual
114/// meaning depends on `Model`.
116 const Environment &Env1, Value &Val2,
117 const Environment &Env2,
118 Environment &JoinedEnv,
120 // Join distinct boolean values preserving information about the constraints
121 // in the respective path conditions.
122 if (isa<BoolValue>(&Val1) && isa<BoolValue>(&Val2)) {
123 // FIXME: Checking both values should be unnecessary, since they should have
124 // a consistent shape. However, right now we can end up with BoolValue's in
125 // integer-typed variables due to our incorrect handling of
126 // boolean-to-integer casts (we just propagate the BoolValue to the result
127 // of the cast). So, a join can encounter an integer in one branch but a
128 // bool in the other.
129 // For example:
130 // ```
131 // std::optional<bool> o;
132 // int x;
133 // if (o.has_value())
134 // x = o.value();
135 // ```
136 auto &Expr1 = cast<BoolValue>(Val1).formula();
137 auto &Expr2 = cast<BoolValue>(Val2).formula();
138 auto &A = JoinedEnv.arena();
139 auto &JoinedVal = A.makeAtomRef(A.makeAtom());
140 JoinedEnv.assume(
141 A.makeOr(A.makeAnd(A.makeAtomRef(Env1.getFlowConditionToken()),
142 A.makeEquals(JoinedVal, Expr1)),
143 A.makeAnd(A.makeAtomRef(Env2.getFlowConditionToken()),
144 A.makeEquals(JoinedVal, Expr2))));
145 return &A.makeBoolValue(JoinedVal);
146 }
147
148 Value *JoinedVal = JoinedEnv.createValue(Type);
149 if (JoinedVal)
150 Model.join(Type, Val1, Env1, Val2, Env2, *JoinedVal, JoinedEnv);
151
152 return JoinedVal;
153}
154
156 const Environment &PrevEnv,
157 Value &Current, Environment &CurrentEnv,
159 // Boolean-model widening.
160 if (isa<BoolValue>(Prev) && isa<BoolValue>(Current)) {
161 // FIXME: Checking both values should be unnecessary, but we can currently
162 // end up with `BoolValue`s in integer-typed variables. See comment in
163 // `joinDistinctValues()` for details.
164 auto &PrevBool = cast<BoolValue>(Prev);
165 auto &CurBool = cast<BoolValue>(Current);
166
167 if (isa<TopBoolValue>(Prev))
168 // Safe to return `Prev` here, because Top is never dependent on the
169 // environment.
170 return {&Prev, LatticeEffect::Unchanged};
171
172 // We may need to widen to Top, but before we do so, check whether both
173 // values are implied to be either true or false in the current environment.
174 // In that case, we can simply return a literal instead.
175 bool TruePrev = PrevEnv.proves(PrevBool.formula());
176 bool TrueCur = CurrentEnv.proves(CurBool.formula());
177 if (TruePrev && TrueCur)
178 return {&CurrentEnv.getBoolLiteralValue(true), LatticeEffect::Unchanged};
179 if (!TruePrev && !TrueCur &&
180 PrevEnv.proves(PrevEnv.arena().makeNot(PrevBool.formula())) &&
181 CurrentEnv.proves(CurrentEnv.arena().makeNot(CurBool.formula())))
182 return {&CurrentEnv.getBoolLiteralValue(false), LatticeEffect::Unchanged};
183
184 return {&CurrentEnv.makeTopBoolValue(), LatticeEffect::Changed};
185 }
186
187 // FIXME: Add other built-in model widening.
188
189 // Custom-model widening.
190 if (auto Result = Model.widen(Type, Prev, PrevEnv, Current, CurrentEnv))
191 return *Result;
192
193 return {&Current, equateUnknownValues(Prev.getKind())
196}
197
198// Returns whether the values in `Map1` and `Map2` compare equal for those
199// keys that `Map1` and `Map2` have in common.
200template <typename Key>
201bool compareKeyToValueMaps(const llvm::MapVector<Key, Value *> &Map1,
202 const llvm::MapVector<Key, Value *> &Map2,
203 const Environment &Env1, const Environment &Env2,
205 for (auto &Entry : Map1) {
206 Key K = Entry.first;
207 assert(K != nullptr);
208
209 Value *Val = Entry.second;
210 assert(Val != nullptr);
211
212 auto It = Map2.find(K);
213 if (It == Map2.end())
214 continue;
215 assert(It->second != nullptr);
216
217 if (!areEquivalentValues(*Val, *It->second) &&
218 !compareDistinctValues(K->getType(), *Val, Env1, *It->second, Env2,
219 Model))
220 return false;
221 }
222
223 return true;
224}
225
226// Perform a join on two `LocToVal` maps.
227static llvm::MapVector<const StorageLocation *, Value *>
228joinLocToVal(const llvm::MapVector<const StorageLocation *, Value *> &LocToVal,
229 const llvm::MapVector<const StorageLocation *, Value *> &LocToVal2,
230 const Environment &Env1, const Environment &Env2,
231 Environment &JoinedEnv, Environment::ValueModel &Model) {
232 llvm::MapVector<const StorageLocation *, Value *> Result;
233 for (auto &Entry : LocToVal) {
234 const StorageLocation *Loc = Entry.first;
235 assert(Loc != nullptr);
236
237 Value *Val = Entry.second;
238 assert(Val != nullptr);
239
240 auto It = LocToVal2.find(Loc);
241 if (It == LocToVal2.end())
242 continue;
243 assert(It->second != nullptr);
244
245 if (Value *JoinedVal = Environment::joinValues(
246 Loc->getType(), Val, Env1, It->second, Env2, JoinedEnv, Model)) {
247 Result.insert({Loc, JoinedVal});
248 }
249 }
250
251 return Result;
252}
253
254// Perform widening on either `LocToVal` or `ExprToVal`. `Key` must be either
255// `const StorageLocation *` or `const Expr *`.
256template <typename Key>
257llvm::MapVector<Key, Value *>
258widenKeyToValueMap(const llvm::MapVector<Key, Value *> &CurMap,
259 const llvm::MapVector<Key, Value *> &PrevMap,
260 Environment &CurEnv, const Environment &PrevEnv,
261 Environment::ValueModel &Model, LatticeEffect &Effect) {
262 llvm::MapVector<Key, Value *> WidenedMap;
263 for (auto &Entry : CurMap) {
264 Key K = Entry.first;
265 assert(K != nullptr);
266
267 Value *Val = Entry.second;
268 assert(Val != nullptr);
269
270 auto PrevIt = PrevMap.find(K);
271 if (PrevIt == PrevMap.end())
272 continue;
273 assert(PrevIt->second != nullptr);
274
275 if (areEquivalentValues(*Val, *PrevIt->second)) {
276 WidenedMap.insert({K, Val});
277 continue;
278 }
279
280 auto [WidenedVal, ValEffect] = widenDistinctValues(
281 K->getType(), *PrevIt->second, PrevEnv, *Val, CurEnv, Model);
282 WidenedMap.insert({K, WidenedVal});
283 if (ValEffect == LatticeEffect::Changed)
284 Effect = LatticeEffect::Changed;
285 }
286
287 return WidenedMap;
288}
289
290namespace {
291
292// Visitor that builds a map from record prvalues to result objects.
293// This traverses the body of the function to be analyzed; for each result
294// object that it encounters, it propagates the storage location of the result
295// object to all record prvalues that can initialize it.
296class ResultObjectVisitor : public RecursiveASTVisitor<ResultObjectVisitor> {
297public:
298 // `ResultObjectMap` will be filled with a map from record prvalues to result
299 // object. If the function being analyzed returns a record by value,
300 // `LocForRecordReturnVal` is the location to which this record should be
301 // written; otherwise, it is null.
302 explicit ResultObjectVisitor(
303 llvm::DenseMap<const Expr *, RecordStorageLocation *> &ResultObjectMap,
304 RecordStorageLocation *LocForRecordReturnVal,
305 DataflowAnalysisContext &DACtx)
306 : ResultObjectMap(ResultObjectMap),
307 LocForRecordReturnVal(LocForRecordReturnVal), DACtx(DACtx) {}
308
309 bool shouldVisitImplicitCode() { return true; }
310
311 bool shouldVisitLambdaBody() const { return false; }
312
313 // Traverse all member and base initializers of `Ctor`. This function is not
314 // called by `RecursiveASTVisitor`; it should be called manually if we are
315 // analyzing a constructor. `ThisPointeeLoc` is the storage location that
316 // `this` points to.
317 void TraverseConstructorInits(const CXXConstructorDecl *Ctor,
318 RecordStorageLocation *ThisPointeeLoc) {
319 assert(ThisPointeeLoc != nullptr);
320 for (const CXXCtorInitializer *Init : Ctor->inits()) {
321 Expr *InitExpr = Init->getInit();
322 if (FieldDecl *Field = Init->getMember();
323 Field != nullptr && Field->getType()->isRecordType()) {
324 PropagateResultObject(InitExpr, cast<RecordStorageLocation>(
325 ThisPointeeLoc->getChild(*Field)));
326 } else if (Init->getBaseClass()) {
327 PropagateResultObject(InitExpr, ThisPointeeLoc);
328 }
329
330 // Ensure that any result objects within `InitExpr` (e.g. temporaries)
331 // are also propagated to the prvalues that initialize them.
332 TraverseStmt(InitExpr);
333
334 // If this is a `CXXDefaultInitExpr`, also propagate any result objects
335 // within the default expression.
336 if (auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(InitExpr))
337 TraverseStmt(DefaultInit->getExpr());
338 }
339 }
340
341 bool TraverseDecl(Decl *D) {
342 // Don't traverse nested record or function declarations.
343 // - We won't be analyzing code contained in these anyway
344 // - We don't model fields that are used only in these nested declaration,
345 // so trying to propagate a result object to initializers of such fields
346 // would cause an error.
347 if (isa_and_nonnull<RecordDecl>(D) || isa_and_nonnull<FunctionDecl>(D))
348 return true;
349
351 }
352
353 bool TraverseBindingDecl(BindingDecl *BD) {
354 // `RecursiveASTVisitor` doesn't traverse holding variables for
355 // `BindingDecl`s by itself, so we need to tell it to.
356 if (VarDecl *HoldingVar = BD->getHoldingVar())
357 TraverseDecl(HoldingVar);
358 return RecursiveASTVisitor<ResultObjectVisitor>::TraverseBindingDecl(BD);
359 }
360
361 bool VisitVarDecl(VarDecl *VD) {
362 if (VD->getType()->isRecordType() && VD->hasInit())
363 PropagateResultObject(
364 VD->getInit(),
365 &cast<RecordStorageLocation>(DACtx.getStableStorageLocation(*VD)));
366 return true;
367 }
368
369 bool VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *MTE) {
370 if (MTE->getType()->isRecordType())
371 PropagateResultObject(
372 MTE->getSubExpr(),
373 &cast<RecordStorageLocation>(DACtx.getStableStorageLocation(*MTE)));
374 return true;
375 }
376
377 bool VisitReturnStmt(ReturnStmt *Return) {
378 Expr *RetValue = Return->getRetValue();
379 if (RetValue != nullptr && RetValue->getType()->isRecordType() &&
380 RetValue->isPRValue())
381 PropagateResultObject(RetValue, LocForRecordReturnVal);
382 return true;
383 }
384
385 bool VisitExpr(Expr *E) {
386 // Clang's AST can have record-type prvalues without a result object -- for
387 // example as full-expressions contained in a compound statement or as
388 // arguments of call expressions. We notice this if we get here and a
389 // storage location has not yet been associated with `E`. In this case,
390 // treat this as if it was a `MaterializeTemporaryExpr`.
391 if (E->isPRValue() && E->getType()->isRecordType() &&
392 !ResultObjectMap.contains(E))
393 PropagateResultObject(
394 E, &cast<RecordStorageLocation>(DACtx.getStableStorageLocation(*E)));
395 return true;
396 }
397
398 void
399 PropagateResultObjectToRecordInitList(const RecordInitListHelper &InitList,
400 RecordStorageLocation *Loc) {
401 for (auto [Base, Init] : InitList.base_inits()) {
402 assert(Base->getType().getCanonicalType() ==
403 Init->getType().getCanonicalType());
404
405 // Storage location for the base class is the same as that of the
406 // derived class because we "flatten" the object hierarchy and put all
407 // fields in `RecordStorageLocation` of the derived class.
408 PropagateResultObject(Init, Loc);
409 }
410
411 for (auto [Field, Init] : InitList.field_inits()) {
412 // Fields of non-record type are handled in
413 // `TransferVisitor::VisitInitListExpr()`.
414 if (Field->getType()->isRecordType())
415 PropagateResultObject(
416 Init, cast<RecordStorageLocation>(Loc->getChild(*Field)));
417 }
418 }
419
420 // Assigns `Loc` as the result object location of `E`, then propagates the
421 // location to all lower-level prvalues that initialize the same object as
422 // `E` (or one of its base classes or member variables).
423 void PropagateResultObject(Expr *E, RecordStorageLocation *Loc) {
424 if (!E->isPRValue() || !E->getType()->isRecordType()) {
425 assert(false);
426 // Ensure we don't propagate the result object if we hit this in a
427 // release build.
428 return;
429 }
430
431 ResultObjectMap[E] = Loc;
432
433 // The following AST node kinds are "original initializers": They are the
434 // lowest-level AST node that initializes a given object, and nothing
435 // below them can initialize the same object (or part of it).
436 if (isa<CXXConstructExpr>(E) || isa<CallExpr>(E) || isa<LambdaExpr>(E) ||
437 isa<CXXDefaultArgExpr>(E) || isa<CXXDefaultInitExpr>(E) ||
438 isa<CXXStdInitializerListExpr>(E) ||
439 // We treat `BuiltinBitCastExpr` as an "original initializer" too as
440 // it may not even be casting from a record type -- and even if it is,
441 // the two objects are in general of unrelated type.
442 isa<BuiltinBitCastExpr>(E)) {
443 return;
444 }
445 if (auto *Op = dyn_cast<BinaryOperator>(E);
446 Op && Op->getOpcode() == BO_Cmp) {
447 // Builtin `<=>` returns a `std::strong_ordering` object.
448 return;
449 }
450
451 if (auto *InitList = dyn_cast<InitListExpr>(E)) {
452 if (!InitList->isSemanticForm())
453 return;
454 if (InitList->isTransparent()) {
455 PropagateResultObject(InitList->getInit(0), Loc);
456 return;
457 }
458
459 PropagateResultObjectToRecordInitList(RecordInitListHelper(InitList),
460 Loc);
461 return;
462 }
463
464 if (auto *ParenInitList = dyn_cast<CXXParenListInitExpr>(E)) {
465 PropagateResultObjectToRecordInitList(RecordInitListHelper(ParenInitList),
466 Loc);
467 return;
468 }
469
470 if (auto *Op = dyn_cast<BinaryOperator>(E); Op && Op->isCommaOp()) {
471 PropagateResultObject(Op->getRHS(), Loc);
472 return;
473 }
474
475 if (auto *Cond = dyn_cast<AbstractConditionalOperator>(E)) {
476 PropagateResultObject(Cond->getTrueExpr(), Loc);
477 PropagateResultObject(Cond->getFalseExpr(), Loc);
478 return;
479 }
480
481 if (auto *SE = dyn_cast<StmtExpr>(E)) {
482 PropagateResultObject(cast<Expr>(SE->getSubStmt()->body_back()), Loc);
483 return;
484 }
485
486 // All other expression nodes that propagate a record prvalue should have
487 // exactly one child.
488 SmallVector<Stmt *, 1> Children(E->child_begin(), E->child_end());
489 LLVM_DEBUG({
490 if (Children.size() != 1)
491 E->dump();
492 });
493 assert(Children.size() == 1);
494 for (Stmt *S : Children)
495 PropagateResultObject(cast<Expr>(S), Loc);
496 }
497
498private:
499 llvm::DenseMap<const Expr *, RecordStorageLocation *> &ResultObjectMap;
500 RecordStorageLocation *LocForRecordReturnVal;
501 DataflowAnalysisContext &DACtx;
502};
503
504} // namespace
505
507 : DACtx(&DACtx),
508 FlowConditionToken(DACtx.arena().makeFlowConditionToken()) {}
509
511 const DeclContext &DeclCtx)
512 : Environment(DACtx) {
513 CallStack.push_back(&DeclCtx);
514}
515
517 const DeclContext *DeclCtx = getDeclCtx();
518 if (DeclCtx == nullptr)
519 return;
520
521 const auto *FuncDecl = dyn_cast<FunctionDecl>(DeclCtx);
522 if (FuncDecl == nullptr)
523 return;
524
525 assert(FuncDecl->doesThisDeclarationHaveABody());
526
527 initFieldsGlobalsAndFuncs(FuncDecl);
528
529 for (const auto *ParamDecl : FuncDecl->parameters()) {
530 assert(ParamDecl != nullptr);
531 setStorageLocation(*ParamDecl, createObject(*ParamDecl, nullptr));
532 }
533
534 if (FuncDecl->getReturnType()->isRecordType())
535 LocForRecordReturnVal = &cast<RecordStorageLocation>(
536 createStorageLocation(FuncDecl->getReturnType()));
537
538 if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(DeclCtx)) {
539 auto *Parent = MethodDecl->getParent();
540 assert(Parent != nullptr);
541
542 if (Parent->isLambda()) {
543 for (const auto &Capture : Parent->captures()) {
544 if (Capture.capturesVariable()) {
545 const auto *VarDecl = Capture.getCapturedVar();
546 assert(VarDecl != nullptr);
548 } else if (Capture.capturesThis()) {
549 const auto *SurroundingMethodDecl =
550 cast<CXXMethodDecl>(DeclCtx->getNonClosureAncestor());
551 QualType ThisPointeeType =
552 SurroundingMethodDecl->getFunctionObjectParameterType();
554 cast<RecordStorageLocation>(createObject(ThisPointeeType)));
555 }
556 }
557 } else if (MethodDecl->isImplicitObjectMemberFunction()) {
558 QualType ThisPointeeType = MethodDecl->getFunctionObjectParameterType();
559 auto &ThisLoc =
560 cast<RecordStorageLocation>(createStorageLocation(ThisPointeeType));
562 // Initialize fields of `*this` with values, but only if we're not
563 // analyzing a constructor; after all, it's the constructor's job to do
564 // this (and we want to be able to test that).
565 if (!isa<CXXConstructorDecl>(MethodDecl))
567 }
568 }
569
570 // We do this below the handling of `CXXMethodDecl` above so that we can
571 // be sure that the storage location for `this` has been set.
572 ResultObjectMap = std::make_shared<PrValueToResultObject>(
573 buildResultObjectMap(DACtx, FuncDecl, getThisPointeeStorageLocation(),
574 LocForRecordReturnVal));
575}
576
577// FIXME: Add support for resetting globals after function calls to enable
578// the implementation of sound analyses.
579void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) {
580 assert(FuncDecl->doesThisDeclarationHaveABody());
581
582 ReferencedDecls Referenced = getReferencedDecls(*FuncDecl);
583
584 // These have to be added before the lines that follow to ensure that
585 // `create*` work correctly for structs.
586 DACtx->addModeledFields(Referenced.Fields);
587
588 for (const VarDecl *D : Referenced.Globals) {
589 if (getStorageLocation(*D) != nullptr)
590 continue;
591
592 // We don't run transfer functions on the initializers of global variables,
593 // so they won't be associated with a value or storage location. We
594 // therefore intentionally don't pass an initializer to `createObject()`;
595 // in particular, this ensures that `createObject()` will initialize the
596 // fields of record-type variables with values.
597 setStorageLocation(*D, createObject(*D, nullptr));
598 }
599
600 for (const FunctionDecl *FD : Referenced.Functions) {
601 if (getStorageLocation(*FD) != nullptr)
602 continue;
603 auto &Loc = createStorageLocation(*FD);
604 setStorageLocation(*FD, Loc);
605 }
606}
607
609 Environment Copy(*this);
610 Copy.FlowConditionToken = DACtx->forkFlowCondition(FlowConditionToken);
611 return Copy;
612}
613
614bool Environment::canDescend(unsigned MaxDepth,
615 const DeclContext *Callee) const {
616 return CallStack.size() <= MaxDepth && !llvm::is_contained(CallStack, Callee);
617}
618
620 Environment Env(*this);
621
622 if (const auto *MethodCall = dyn_cast<CXXMemberCallExpr>(Call)) {
623 if (const Expr *Arg = MethodCall->getImplicitObjectArgument()) {
624 if (!isa<CXXThisExpr>(Arg))
625 Env.ThisPointeeLoc =
626 cast<RecordStorageLocation>(getStorageLocation(*Arg));
627 // Otherwise (when the argument is `this`), retain the current
628 // environment's `ThisPointeeLoc`.
629 }
630 }
631
632 if (Call->getType()->isRecordType() && Call->isPRValue())
633 Env.LocForRecordReturnVal = &Env.getResultObjectLocation(*Call);
634
635 Env.pushCallInternal(Call->getDirectCallee(),
636 llvm::ArrayRef(Call->getArgs(), Call->getNumArgs()));
637
638 return Env;
639}
640
642 Environment Env(*this);
643
644 Env.ThisPointeeLoc = &Env.getResultObjectLocation(*Call);
645 Env.LocForRecordReturnVal = &Env.getResultObjectLocation(*Call);
646
647 Env.pushCallInternal(Call->getConstructor(),
648 llvm::ArrayRef(Call->getArgs(), Call->getNumArgs()));
649
650 return Env;
651}
652
653void Environment::pushCallInternal(const FunctionDecl *FuncDecl,
655 // Canonicalize to the definition of the function. This ensures that we're
656 // putting arguments into the same `ParamVarDecl`s` that the callee will later
657 // be retrieving them from.
658 assert(FuncDecl->getDefinition() != nullptr);
659 FuncDecl = FuncDecl->getDefinition();
660
661 CallStack.push_back(FuncDecl);
662
663 initFieldsGlobalsAndFuncs(FuncDecl);
664
665 const auto *ParamIt = FuncDecl->param_begin();
666
667 // FIXME: Parameters don't always map to arguments 1:1; examples include
668 // overloaded operators implemented as member functions, and parameter packs.
669 for (unsigned ArgIndex = 0; ArgIndex < Args.size(); ++ParamIt, ++ArgIndex) {
670 assert(ParamIt != FuncDecl->param_end());
671 const VarDecl *Param = *ParamIt;
672 setStorageLocation(*Param, createObject(*Param, Args[ArgIndex]));
673 }
674
675 ResultObjectMap = std::make_shared<PrValueToResultObject>(
676 buildResultObjectMap(DACtx, FuncDecl, getThisPointeeStorageLocation(),
677 LocForRecordReturnVal));
678}
679
680void Environment::popCall(const CallExpr *Call, const Environment &CalleeEnv) {
681 // We ignore some entries of `CalleeEnv`:
682 // - `DACtx` because is already the same in both
683 // - We don't want the callee's `DeclCtx`, `ReturnVal`, `ReturnLoc` or
684 // `ThisPointeeLoc` because they don't apply to us.
685 // - `DeclToLoc`, `ExprToLoc`, and `ExprToVal` capture information from the
686 // callee's local scope, so when popping that scope, we do not propagate
687 // the maps.
688 this->LocToVal = std::move(CalleeEnv.LocToVal);
689 this->FlowConditionToken = std::move(CalleeEnv.FlowConditionToken);
690
691 if (Call->isGLValue()) {
692 if (CalleeEnv.ReturnLoc != nullptr)
693 setStorageLocation(*Call, *CalleeEnv.ReturnLoc);
694 } else if (!Call->getType()->isVoidType()) {
695 if (CalleeEnv.ReturnVal != nullptr)
696 setValue(*Call, *CalleeEnv.ReturnVal);
697 }
698}
699
701 const Environment &CalleeEnv) {
702 // See also comment in `popCall(const CallExpr *, const Environment &)` above.
703 this->LocToVal = std::move(CalleeEnv.LocToVal);
704 this->FlowConditionToken = std::move(CalleeEnv.FlowConditionToken);
705}
706
708 Environment::ValueModel &Model) const {
709 assert(DACtx == Other.DACtx);
710
711 if (ReturnVal != Other.ReturnVal)
712 return false;
713
714 if (ReturnLoc != Other.ReturnLoc)
715 return false;
716
717 if (LocForRecordReturnVal != Other.LocForRecordReturnVal)
718 return false;
719
720 if (ThisPointeeLoc != Other.ThisPointeeLoc)
721 return false;
722
723 if (DeclToLoc != Other.DeclToLoc)
724 return false;
725
726 if (ExprToLoc != Other.ExprToLoc)
727 return false;
728
729 if (!compareKeyToValueMaps(ExprToVal, Other.ExprToVal, *this, Other, Model))
730 return false;
731
732 if (!compareKeyToValueMaps(LocToVal, Other.LocToVal, *this, Other, Model))
733 return false;
734
735 return true;
736}
737
740 assert(DACtx == PrevEnv.DACtx);
741 assert(ReturnVal == PrevEnv.ReturnVal);
742 assert(ReturnLoc == PrevEnv.ReturnLoc);
743 assert(LocForRecordReturnVal == PrevEnv.LocForRecordReturnVal);
744 assert(ThisPointeeLoc == PrevEnv.ThisPointeeLoc);
745 assert(CallStack == PrevEnv.CallStack);
746 assert(ResultObjectMap == PrevEnv.ResultObjectMap);
747
748 auto Effect = LatticeEffect::Unchanged;
749
750 // By the API, `PrevEnv` is a previous version of the environment for the same
751 // block, so we have some guarantees about its shape. In particular, it will
752 // be the result of a join or widen operation on previous values for this
753 // block. For `DeclToLoc`, `ExprToVal`, and `ExprToLoc`, join guarantees that
754 // these maps are subsets of the maps in `PrevEnv`. So, as long as we maintain
755 // this property here, we don't need change their current values to widen.
756 assert(DeclToLoc.size() <= PrevEnv.DeclToLoc.size());
757 assert(ExprToVal.size() <= PrevEnv.ExprToVal.size());
758 assert(ExprToLoc.size() <= PrevEnv.ExprToLoc.size());
759
760 ExprToVal = widenKeyToValueMap(ExprToVal, PrevEnv.ExprToVal, *this, PrevEnv,
761 Model, Effect);
762
763 LocToVal = widenKeyToValueMap(LocToVal, PrevEnv.LocToVal, *this, PrevEnv,
764 Model, Effect);
765 if (DeclToLoc.size() != PrevEnv.DeclToLoc.size() ||
766 ExprToLoc.size() != PrevEnv.ExprToLoc.size() ||
767 ExprToVal.size() != PrevEnv.ExprToVal.size() ||
768 LocToVal.size() != PrevEnv.LocToVal.size())
769 Effect = LatticeEffect::Changed;
770
771 return Effect;
772}
773
776 ExprJoinBehavior ExprBehavior) {
777 assert(EnvA.DACtx == EnvB.DACtx);
778 assert(EnvA.LocForRecordReturnVal == EnvB.LocForRecordReturnVal);
779 assert(EnvA.ThisPointeeLoc == EnvB.ThisPointeeLoc);
780 assert(EnvA.CallStack == EnvB.CallStack);
781 assert(EnvA.ResultObjectMap == EnvB.ResultObjectMap);
782
783 Environment JoinedEnv(*EnvA.DACtx);
784
785 JoinedEnv.CallStack = EnvA.CallStack;
786 JoinedEnv.ResultObjectMap = EnvA.ResultObjectMap;
787 JoinedEnv.LocForRecordReturnVal = EnvA.LocForRecordReturnVal;
788 JoinedEnv.ThisPointeeLoc = EnvA.ThisPointeeLoc;
789
790 if (EnvA.CallStack.empty()) {
791 JoinedEnv.ReturnVal = nullptr;
792 } else {
793 // FIXME: Make `CallStack` a vector of `FunctionDecl` so we don't need this
794 // cast.
795 auto *Func = dyn_cast<FunctionDecl>(EnvA.CallStack.back());
796 assert(Func != nullptr);
797 JoinedEnv.ReturnVal =
798 joinValues(Func->getReturnType(), EnvA.ReturnVal, EnvA, EnvB.ReturnVal,
799 EnvB, JoinedEnv, Model);
800 }
801
802 if (EnvA.ReturnLoc == EnvB.ReturnLoc)
803 JoinedEnv.ReturnLoc = EnvA.ReturnLoc;
804 else
805 JoinedEnv.ReturnLoc = nullptr;
806
807 JoinedEnv.DeclToLoc = intersectDeclToLoc(EnvA.DeclToLoc, EnvB.DeclToLoc);
808
809 // FIXME: update join to detect backedges and simplify the flow condition
810 // accordingly.
811 JoinedEnv.FlowConditionToken = EnvA.DACtx->joinFlowConditions(
812 EnvA.FlowConditionToken, EnvB.FlowConditionToken);
813
814 JoinedEnv.LocToVal =
815 joinLocToVal(EnvA.LocToVal, EnvB.LocToVal, EnvA, EnvB, JoinedEnv, Model);
816
817 if (ExprBehavior == KeepExprState) {
818 JoinedEnv.ExprToVal = joinExprMaps(EnvA.ExprToVal, EnvB.ExprToVal);
819 JoinedEnv.ExprToLoc = joinExprMaps(EnvA.ExprToLoc, EnvB.ExprToLoc);
820 }
821
822 return JoinedEnv;
823}
824
826 const Environment &Env1, Value *Val2,
827 const Environment &Env2, Environment &JoinedEnv,
829 if (Val1 == nullptr || Val2 == nullptr)
830 // We can't say anything about the joined value -- even if one of the values
831 // is non-null, we don't want to simply propagate it, because it would be
832 // too specific: Because the other value is null, that means we have no
833 // information at all about the value (i.e. the value is unconstrained).
834 return nullptr;
835
836 if (areEquivalentValues(*Val1, *Val2))
837 // Arbitrarily return one of the two values.
838 return Val1;
839
840 return joinDistinctValues(Ty, *Val1, Env1, *Val2, Env2, JoinedEnv, Model);
841}
842
844 return DACtx->createStorageLocation(Type);
845}
846
848 // Evaluated declarations are always assigned the same storage locations to
849 // ensure that the environment stabilizes across loop iterations. Storage
850 // locations for evaluated declarations are stored in the analysis context.
851 return DACtx->getStableStorageLocation(D);
852}
853
855 // Evaluated expressions are always assigned the same storage locations to
856 // ensure that the environment stabilizes across loop iterations. Storage
857 // locations for evaluated expressions are stored in the analysis context.
858 return DACtx->getStableStorageLocation(E);
859}
860
862 assert(!DeclToLoc.contains(&D));
863 // The only kinds of declarations that may have a "variable" storage location
864 // are declarations of reference type and `BindingDecl`. For all other
865 // declaration, the storage location should be the stable storage location
866 // returned by `createStorageLocation()`.
867 assert(D.getType()->isReferenceType() || isa<BindingDecl>(D) ||
868 &Loc == &createStorageLocation(D));
869 DeclToLoc[&D] = &Loc;
870}
871
873 auto It = DeclToLoc.find(&D);
874 if (It == DeclToLoc.end())
875 return nullptr;
876
877 StorageLocation *Loc = It->second;
878
879 return Loc;
880}
881
882void Environment::removeDecl(const ValueDecl &D) { DeclToLoc.erase(&D); }
883
885 // `DeclRefExpr`s to builtin function types aren't glvalues, for some reason,
886 // but we still want to be able to associate a `StorageLocation` with them,
887 // so allow these as an exception.
888 assert(E.isGLValue() ||
889 E.getType()->isSpecificBuiltinType(BuiltinType::BuiltinFn));
890 const Expr &CanonE = ignoreCFGOmittedNodes(E);
891 assert(!ExprToLoc.contains(&CanonE));
892 ExprToLoc[&CanonE] = &Loc;
893}
894
896 // See comment in `setStorageLocation()`.
897 assert(E.isGLValue() ||
898 E.getType()->isSpecificBuiltinType(BuiltinType::BuiltinFn));
899 auto It = ExprToLoc.find(&ignoreCFGOmittedNodes(E));
900 return It == ExprToLoc.end() ? nullptr : &*It->second;
901}
902
904Environment::getResultObjectLocation(const Expr &RecordPRValue) const {
905 assert(RecordPRValue.getType()->isRecordType());
906 assert(RecordPRValue.isPRValue());
907
908 assert(ResultObjectMap != nullptr);
909 RecordStorageLocation *Loc = ResultObjectMap->lookup(&RecordPRValue);
910 assert(Loc != nullptr);
911 // In release builds, use the "stable" storage location if the map lookup
912 // failed.
913 if (Loc == nullptr)
914 return cast<RecordStorageLocation>(
915 DACtx->getStableStorageLocation(RecordPRValue));
916 return *Loc;
917}
918
920 return DACtx->getOrCreateNullPointerValue(PointeeType);
921}
922
924 QualType Type) {
926 int CreatedValuesCount = 0;
927 initializeFieldsWithValues(Loc, Type, Visited, 0, CreatedValuesCount);
928 if (CreatedValuesCount > MaxCompositeValueSize) {
929 llvm::errs() << "Attempting to initialize a huge value of type: " << Type
930 << '\n';
931 }
932}
933
935 // Records should not be associated with values.
936 assert(!isa<RecordStorageLocation>(Loc));
937 LocToVal[&Loc] = &Val;
938}
939
940void Environment::setValue(const Expr &E, Value &Val) {
941 const Expr &CanonE = ignoreCFGOmittedNodes(E);
942
943 assert(CanonE.isPRValue());
944 // Records should not be associated with values.
945 assert(!CanonE.getType()->isRecordType());
946 ExprToVal[&CanonE] = &Val;
947}
948
950 // Records should not be associated with values.
951 assert(!isa<RecordStorageLocation>(Loc));
952 return LocToVal.lookup(&Loc);
953}
954
956 auto *Loc = getStorageLocation(D);
957 if (Loc == nullptr)
958 return nullptr;
959 return getValue(*Loc);
960}
961
963 // Records should not be associated with values.
964 assert(!E.getType()->isRecordType());
965
966 if (E.isPRValue()) {
967 auto It = ExprToVal.find(&ignoreCFGOmittedNodes(E));
968 return It == ExprToVal.end() ? nullptr : It->second;
969 }
970
971 auto It = ExprToLoc.find(&ignoreCFGOmittedNodes(E));
972 if (It == ExprToLoc.end())
973 return nullptr;
974 return getValue(*It->second);
975}
976
979 int CreatedValuesCount = 0;
980 Value *Val = createValueUnlessSelfReferential(Type, Visited, /*Depth=*/0,
981 CreatedValuesCount);
982 if (CreatedValuesCount > MaxCompositeValueSize) {
983 llvm::errs() << "Attempting to initialize a huge value of type: " << Type
984 << '\n';
985 }
986 return Val;
987}
988
989Value *Environment::createValueUnlessSelfReferential(
991 int &CreatedValuesCount) {
992 assert(!Type.isNull());
993 assert(!Type->isReferenceType());
994 assert(!Type->isRecordType());
995
996 // Allow unlimited fields at depth 1; only cap at deeper nesting levels.
997 if ((Depth > 1 && CreatedValuesCount > MaxCompositeValueSize) ||
999 return nullptr;
1000
1001 if (Type->isBooleanType()) {
1002 CreatedValuesCount++;
1003 return &makeAtomicBoolValue();
1004 }
1005
1006 if (Type->isIntegerType()) {
1007 // FIXME: consider instead `return nullptr`, given that we do nothing useful
1008 // with integers, and so distinguishing them serves no purpose, but could
1009 // prevent convergence.
1010 CreatedValuesCount++;
1011 return &arena().create<IntegerValue>();
1012 }
1013
1014 if (Type->isPointerType()) {
1015 CreatedValuesCount++;
1016 QualType PointeeType = Type->getPointeeType();
1017 StorageLocation &PointeeLoc =
1018 createLocAndMaybeValue(PointeeType, Visited, Depth, CreatedValuesCount);
1019
1020 return &arena().create<PointerValue>(PointeeLoc);
1021 }
1022
1023 return nullptr;
1024}
1025
1026StorageLocation &
1027Environment::createLocAndMaybeValue(QualType Ty,
1029 int Depth, int &CreatedValuesCount) {
1030 if (!Visited.insert(Ty.getCanonicalType()).second)
1031 return createStorageLocation(Ty.getNonReferenceType());
1032 auto EraseVisited = llvm::make_scope_exit(
1033 [&Visited, Ty] { Visited.erase(Ty.getCanonicalType()); });
1034
1035 Ty = Ty.getNonReferenceType();
1036
1037 if (Ty->isRecordType()) {
1038 auto &Loc = cast<RecordStorageLocation>(createStorageLocation(Ty));
1039 initializeFieldsWithValues(Loc, Ty, Visited, Depth, CreatedValuesCount);
1040 return Loc;
1041 }
1042
1043 StorageLocation &Loc = createStorageLocation(Ty);
1044
1045 if (Value *Val = createValueUnlessSelfReferential(Ty, Visited, Depth,
1046 CreatedValuesCount))
1047 setValue(Loc, *Val);
1048
1049 return Loc;
1050}
1051
1052void Environment::initializeFieldsWithValues(RecordStorageLocation &Loc,
1053 QualType Type,
1055 int Depth,
1056 int &CreatedValuesCount) {
1057 auto initField = [&](QualType FieldType, StorageLocation &FieldLoc) {
1058 if (FieldType->isRecordType()) {
1059 auto &FieldRecordLoc = cast<RecordStorageLocation>(FieldLoc);
1060 initializeFieldsWithValues(FieldRecordLoc, FieldRecordLoc.getType(),
1061 Visited, Depth + 1, CreatedValuesCount);
1062 } else {
1063 if (getValue(FieldLoc) != nullptr)
1064 return;
1065 if (!Visited.insert(FieldType.getCanonicalType()).second)
1066 return;
1067 if (Value *Val = createValueUnlessSelfReferential(
1068 FieldType, Visited, Depth + 1, CreatedValuesCount))
1069 setValue(FieldLoc, *Val);
1070 Visited.erase(FieldType.getCanonicalType());
1071 }
1072 };
1073
1074 for (const FieldDecl *Field : DACtx->getModeledFields(Type)) {
1075 assert(Field != nullptr);
1076 QualType FieldType = Field->getType();
1077
1078 if (FieldType->isReferenceType()) {
1079 Loc.setChild(*Field,
1080 &createLocAndMaybeValue(FieldType, Visited, Depth + 1,
1081 CreatedValuesCount));
1082 } else {
1083 StorageLocation *FieldLoc = Loc.getChild(*Field);
1084 assert(FieldLoc != nullptr);
1085 initField(FieldType, *FieldLoc);
1086 }
1087 }
1088 for (const auto &[FieldName, FieldType] : DACtx->getSyntheticFields(Type)) {
1089 // Synthetic fields cannot have reference type, so we don't need to deal
1090 // with this case.
1091 assert(!FieldType->isReferenceType());
1092 initField(FieldType, Loc.getSyntheticField(FieldName));
1093 }
1094}
1095
1096StorageLocation &Environment::createObjectInternal(const ValueDecl *D,
1097 QualType Ty,
1098 const Expr *InitExpr) {
1099 if (Ty->isReferenceType()) {
1100 // Although variables of reference type always need to be initialized, it
1101 // can happen that we can't see the initializer, so `InitExpr` may still
1102 // be null.
1103 if (InitExpr) {
1104 if (auto *InitExprLoc = getStorageLocation(*InitExpr))
1105 return *InitExprLoc;
1106 }
1107
1108 // Even though we have an initializer, we might not get an
1109 // InitExprLoc, for example if the InitExpr is a CallExpr for which we
1110 // don't have a function body. In this case, we just invent a storage
1111 // location and value -- it's the best we can do.
1112 return createObjectInternal(D, Ty.getNonReferenceType(), nullptr);
1113 }
1114
1115 StorageLocation &Loc =
1117
1118 if (Ty->isRecordType()) {
1119 auto &RecordLoc = cast<RecordStorageLocation>(Loc);
1120 if (!InitExpr)
1122 } else {
1123 Value *Val = nullptr;
1124 if (InitExpr)
1125 // In the (few) cases where an expression is intentionally
1126 // "uninterpreted", `InitExpr` is not associated with a value. There are
1127 // two ways to handle this situation: propagate the status, so that
1128 // uninterpreted initializers result in uninterpreted variables, or
1129 // provide a default value. We choose the latter so that later refinements
1130 // of the variable can be used for reasoning about the surrounding code.
1131 // For this reason, we let this case be handled by the `createValue()`
1132 // call below.
1133 //
1134 // FIXME. If and when we interpret all language cases, change this to
1135 // assert that `InitExpr` is interpreted, rather than supplying a
1136 // default value (assuming we don't update the environment API to return
1137 // references).
1138 Val = getValue(*InitExpr);
1139 if (!Val)
1140 Val = createValue(Ty);
1141 if (Val)
1142 setValue(Loc, *Val);
1143 }
1144
1145 return Loc;
1146}
1147
1149 DACtx->addFlowConditionConstraint(FlowConditionToken, F);
1150}
1151
1152bool Environment::proves(const Formula &F) const {
1153 return DACtx->flowConditionImplies(FlowConditionToken, F);
1154}
1155
1156bool Environment::allows(const Formula &F) const {
1157 return DACtx->flowConditionAllows(FlowConditionToken, F);
1158}
1159
1160void Environment::dump(raw_ostream &OS) const {
1161 llvm::DenseMap<const StorageLocation *, std::string> LocToName;
1162 if (LocForRecordReturnVal != nullptr)
1163 LocToName[LocForRecordReturnVal] = "(returned record)";
1164 if (ThisPointeeLoc != nullptr)
1165 LocToName[ThisPointeeLoc] = "this";
1166
1167 OS << "DeclToLoc:\n";
1168 for (auto [D, L] : DeclToLoc) {
1169 auto Iter = LocToName.insert({L, D->getNameAsString()}).first;
1170 OS << " [" << Iter->second << ", " << L << "]\n";
1171 }
1172 OS << "ExprToLoc:\n";
1173 for (auto [E, L] : ExprToLoc)
1174 OS << " [" << E << ", " << L << "]\n";
1175
1176 OS << "ExprToVal:\n";
1177 for (auto [E, V] : ExprToVal)
1178 OS << " [" << E << ", " << V << ": " << *V << "]\n";
1179
1180 OS << "LocToVal:\n";
1181 for (auto [L, V] : LocToVal) {
1182 OS << " [" << L;
1183 if (auto Iter = LocToName.find(L); Iter != LocToName.end())
1184 OS << " (" << Iter->second << ")";
1185 OS << ", " << V << ": " << *V << "]\n";
1186 }
1187
1188 if (const FunctionDecl *Func = getCurrentFunc()) {
1189 if (Func->getReturnType()->isReferenceType()) {
1190 OS << "ReturnLoc: " << ReturnLoc;
1191 if (auto Iter = LocToName.find(ReturnLoc); Iter != LocToName.end())
1192 OS << " (" << Iter->second << ")";
1193 OS << "\n";
1194 } else if (Func->getReturnType()->isRecordType() ||
1195 isa<CXXConstructorDecl>(Func)) {
1196 OS << "LocForRecordReturnVal: " << LocForRecordReturnVal << "\n";
1197 } else if (!Func->getReturnType()->isVoidType()) {
1198 if (ReturnVal == nullptr)
1199 OS << "ReturnVal: nullptr\n";
1200 else
1201 OS << "ReturnVal: " << *ReturnVal << "\n";
1202 }
1203
1204 if (isa<CXXMethodDecl>(Func)) {
1205 OS << "ThisPointeeLoc: " << ThisPointeeLoc << "\n";
1206 }
1207 }
1208
1209 OS << "\n";
1210 DACtx->dumpFlowCondition(FlowConditionToken, OS);
1211}
1212
1213void Environment::dump() const { dump(llvm::dbgs()); }
1214
1215Environment::PrValueToResultObject Environment::buildResultObjectMap(
1216 DataflowAnalysisContext *DACtx, const FunctionDecl *FuncDecl,
1217 RecordStorageLocation *ThisPointeeLoc,
1218 RecordStorageLocation *LocForRecordReturnVal) {
1219 assert(FuncDecl->doesThisDeclarationHaveABody());
1220
1221 PrValueToResultObject Map;
1222
1223 ResultObjectVisitor Visitor(Map, LocForRecordReturnVal, *DACtx);
1224 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(FuncDecl))
1225 Visitor.TraverseConstructorInits(Ctor, ThisPointeeLoc);
1226 Visitor.TraverseStmt(FuncDecl->getBody());
1227
1228 return Map;
1229}
1230
1232 const Environment &Env) {
1233 Expr *ImplicitObject = MCE.getImplicitObjectArgument();
1234 if (ImplicitObject == nullptr)
1235 return nullptr;
1236 if (ImplicitObject->getType()->isPointerType()) {
1237 if (auto *Val = Env.get<PointerValue>(*ImplicitObject))
1238 return &cast<RecordStorageLocation>(Val->getPointeeLoc());
1239 return nullptr;
1240 }
1241 return cast_or_null<RecordStorageLocation>(
1242 Env.getStorageLocation(*ImplicitObject));
1243}
1244
1246 const Environment &Env) {
1247 Expr *Base = ME.getBase();
1248 if (Base == nullptr)
1249 return nullptr;
1250 if (ME.isArrow()) {
1251 if (auto *Val = Env.get<PointerValue>(*Base))
1252 return &cast<RecordStorageLocation>(Val->getPointeeLoc());
1253 return nullptr;
1254 }
1255 return Env.get<RecordStorageLocation>(*Base);
1256}
1257
1258} // namespace dataflow
1259} // namespace clang
#define V(N, I)
Definition: ASTContext.h:3284
NodeId Parent
Definition: ASTDiff.cpp:191
MatchType Type
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
static void initField(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable, bool IsActive, const Descriptor *D, unsigned FieldOffset)
Definition: Descriptor.cpp:139
const Environment & Env
Definition: HTMLLogger.cpp:148
llvm::DenseSet< const void * > Visited
Definition: HTMLLogger.cpp:146
unsigned Iter
Definition: HTMLLogger.cpp:154
static bool RetValue(InterpState &S, CodePtr &Pt, APValue &Result)
Definition: Interp.cpp:32
llvm::MachO::RecordLoc RecordLoc
Definition: MachO.h:40
C Language Family Type Representation.
Represents a call to a C++ constructor.
Definition: ExprCXX.h:1542
Represents a call to a member function that may be written either with member call syntax (e....
Definition: ExprCXX.h:176
Expr * getImplicitObjectArgument() const
Retrieve the implicit object argument for the member call.
Definition: ExprCXX.cpp:654
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2820
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Definition: DeclBase.h:1436
Decl * getNonClosureAncestor()
Find the nearest non-closure ancestor of this context, i.e.
Definition: DeclBase.cpp:1179
This represents one expression.
Definition: Expr.h:110
bool isGLValue() const
Definition: Expr.h:280
bool isPRValue() const
Definition: Expr.h:278
QualType getType() const
Definition: Expr.h:142
Represents a function declaration or definition.
Definition: Decl.h:1971
Stmt * getBody(const FunctionDecl *&Definition) const
Retrieve the body (definition) of the function.
Definition: Decl.cpp:3236
param_iterator param_end()
Definition: Decl.h:2697
param_iterator param_begin()
Definition: Decl.h:2696
bool doesThisDeclarationHaveABody() const
Returns whether this specific declaration of the function has a body.
Definition: Decl.h:2296
FunctionDecl * getDefinition()
Get the definition for this declaration.
Definition: Decl.h:2252
MemberExpr - [C99 6.5.2.3] Structure and Union Members.
Definition: Expr.h:3172
Expr * getBase() const
Definition: Expr.h:3249
bool isArrow() const
Definition: Expr.h:3356
A (possibly-)qualified type.
Definition: Type.h:940
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
bool TraverseDecl(Decl *D)
Recursively visit a declaration, by dispatching to Traverse*Decl() based on the argument's dynamic ty...
The base class of the type hierarchy.
Definition: Type.h:1813
bool isBooleanType() const
Definition: Type.h:8033
bool isPointerType() const
Definition: Type.h:7612
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition: Type.h:7945
bool isReferenceType() const
Definition: Type.h:7624
bool isSpecificBuiltinType(unsigned K) const
Test for a particular builtin type.
Definition: Type.h:7874
bool isRecordType() const
Definition: Type.h:7706
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition: Decl.h:706
QualType getType() const
Definition: Decl.h:717
Represents a variable declaration or definition.
Definition: Decl.h:918
const Formula & makeAtomRef(Atom A)
Returns a formula for the variable A.
Definition: Arena.cpp:34
const Formula & makeNot(const Formula &Val)
Returns a formula for the negation of Val.
Definition: Arena.cpp:67
std::enable_if_t< std::is_base_of< StorageLocation, T >::value, T & > create(Args &&...args)
Creates a T (some subclass of StorageLocation), forwarding args to the constructor,...
Definition: Arena.h:36
Owns objects that encompass the state of a program and stores context that is used during dataflow an...
Atom joinFlowConditions(Atom FirstToken, Atom SecondToken)
Creates a new flow condition that represents the disjunction of the flow conditions identified by Fir...
void addFlowConditionConstraint(Atom Token, const Formula &Constraint)
Adds Constraint to the flow condition identified by Token.
Atom forkFlowCondition(Atom Token)
Creates a new flow condition with the same constraints as the flow condition identified by Token and ...
StorageLocation & getStableStorageLocation(const ValueDecl &D)
Returns a stable storage location for D.
bool flowConditionImplies(Atom Token, const Formula &F)
Returns true if the constraints of the flow condition identified by Token imply that F is true.
bool flowConditionAllows(Atom Token, const Formula &F)
Returns true if the constraints of the flow condition identified by Token still allow F to be true.
PointerValue & getOrCreateNullPointerValue(QualType PointeeType)
Returns a pointer value that represents a null pointer.
llvm::StringMap< QualType > getSyntheticFields(QualType Type)
Returns the names and types of the synthetic fields for the given record type.
StorageLocation & createStorageLocation(QualType Type)
Returns a new storage location appropriate for Type.
FieldSet getModeledFields(QualType Type)
Returns the fields of Type, limited to the set of fields modeled by this context.
LLVM_DUMP_METHOD void dumpFlowCondition(Atom Token, llvm::raw_ostream &OS=llvm::dbgs())
Supplements Environment with non-standard comparison and join operations.
virtual std::optional< WidenResult > widen(QualType Type, Value &Prev, const Environment &PrevEnv, Value &Current, Environment &CurrentEnv)
This function may widen the current value – replace it with an approximation that can reach a fixed p...
virtual void join(QualType Type, const Value &Val1, const Environment &Env1, const Value &Val2, const Environment &Env2, Value &JoinedVal, Environment &JoinedEnv)
Modifies JoinedVal to approximate both Val1 and Val2.
virtual ComparisonResult compare(QualType Type, const Value &Val1, const Environment &Env1, const Value &Val2, const Environment &Env2)
Returns: Same: Val1 is equivalent to Val2, according to the model.
Holds the state of the program (store and heap) at a given program point.
bool allows(const Formula &) const
Returns true if the formula may be true when this point is reached.
LatticeEffect widen(const Environment &PrevEnv, Environment::ValueModel &Model)
Widens the environment point-wise, using PrevEnv as needed to inform the approximation.
PointerValue & getOrCreateNullPointerValue(QualType PointeeType)
Returns a pointer value that represents a null pointer.
RecordStorageLocation * getThisPointeeStorageLocation() const
Returns the storage location assigned to the this pointee in the environment or null if the this poin...
Environment pushCall(const CallExpr *Call) const
Creates and returns an environment to use for an inline analysis of the callee.
StorageLocation * getStorageLocation(const ValueDecl &D) const
Returns the storage location assigned to D in the environment, or null if D isn't assigned a storage ...
LLVM_DUMP_METHOD void dump() const
BoolValue & makeTopBoolValue() const
Returns a unique instance of boolean Top.
void initializeFieldsWithValues(RecordStorageLocation &Loc, QualType Type)
Initializes the fields (including synthetic fields) of Loc with values, unless values of the field ty...
StorageLocation & createStorageLocation(QualType Type)
Creates a storage location appropriate for Type.
Environment fork() const
Returns a new environment that is a copy of this one.
void popCall(const CallExpr *Call, const Environment &CalleeEnv)
Moves gathered information back into this from a CalleeEnv created via pushCall.
Environment(DataflowAnalysisContext &DACtx)
Creates an environment that uses DACtx to store objects that encompass the state of a program.
bool equivalentTo(const Environment &Other, Environment::ValueModel &Model) const
Returns true if and only if the environment is equivalent to Other, i.e the two environments:
BoolValue & makeAtomicBoolValue() const
Returns an atomic boolean value.
bool proves(const Formula &) const
Returns true if the formula is always true when this point is reached.
Value * getValue(const StorageLocation &Loc) const
Returns the value assigned to Loc in the environment or null if Loc isn't assigned a value in the env...
bool canDescend(unsigned MaxDepth, const DeclContext *Callee) const
Returns whether this Environment can be extended to analyze the given Callee (i.e.
const FunctionDecl * getCurrentFunc() const
Returns the function currently being analyzed, or null if the code being analyzed isn't part of a fun...
BoolValue & getBoolLiteralValue(bool Value) const
Returns a symbolic boolean value that models a boolean literal equal to Value
StorageLocation & createObject(QualType Ty, const Expr *InitExpr=nullptr)
Creates an object (i.e.
void assume(const Formula &)
Record a fact that must be true if this point in the program is reached.
static Value * joinValues(QualType Ty, Value *Val1, const Environment &Env1, Value *Val2, const Environment &Env2, Environment &JoinedEnv, Environment::ValueModel &Model)
Returns a value that approximates both Val1 and Val2, or null if no such value can be produced.
void setStorageLocation(const ValueDecl &D, StorageLocation &Loc)
Assigns Loc as the storage location of D in the environment.
void removeDecl(const ValueDecl &D)
Removes the location assigned to D in the environment (if any).
RecordStorageLocation & getResultObjectLocation(const Expr &RecordPRValue) const
Returns the location of the result object for a record-type prvalue.
ExprJoinBehavior
How to treat expression state (ExprToLoc and ExprToVal) in a join.
static Environment join(const Environment &EnvA, const Environment &EnvB, Environment::ValueModel &Model, ExprJoinBehavior ExprBehavior)
Joins two environments by taking the intersection of storage locations and values that are stored in ...
Value * createValue(QualType Type)
Creates a value appropriate for Type, if Type is supported, otherwise returns null.
void setValue(const StorageLocation &Loc, Value &Val)
Assigns Val as the value of Loc in the environment.
void setThisPointeeStorageLocation(RecordStorageLocation &Loc)
Sets the storage location assigned to the this pointee in the environment.
Atom getFlowConditionToken() const
Returns a boolean variable that identifies the flow condition (FC).
const DeclContext * getDeclCtx() const
Returns the DeclContext of the block being analysed, if any.
std::enable_if_t< std::is_base_of_v< StorageLocation, T >, T * > get(const ValueDecl &D) const
Returns the result of casting getStorageLocation(...) to a subclass of StorageLocation (using cast_or...
void initialize()
Assigns storage locations and values to all parameters, captures, global variables,...
Models a symbolic pointer. Specifically, any value of type T*.
Definition: Value.h:170
A storage location for a record (struct, class, or union).
Base class for elements of the local variable store and of the heap.
Base class for all values computed by abstract interpretation.
Definition: Value.h:33
Kind getKind() const
Definition: Value.h:55
llvm::MapVector< Key, Value * > widenKeyToValueMap(const llvm::MapVector< Key, Value * > &CurMap, const llvm::MapVector< Key, Value * > &PrevMap, Environment &CurEnv, const Environment &PrevEnv, Environment::ValueModel &Model, LatticeEffect &Effect)
bool compareKeyToValueMaps(const llvm::MapVector< Key, Value * > &Map1, const llvm::MapVector< Key, Value * > &Map2, const Environment &Env1, const Environment &Env2, Environment::ValueModel &Model)
static llvm::DenseMap< const ValueDecl *, StorageLocation * > intersectDeclToLoc(const llvm::DenseMap< const ValueDecl *, StorageLocation * > &DeclToLoc1, const llvm::DenseMap< const ValueDecl *, StorageLocation * > &DeclToLoc2)
Returns a map consisting of key-value entries that are present in both maps.
static bool equateUnknownValues(Value::Kind K)
bool areEquivalentValues(const Value &Val1, const Value &Val2)
An equivalence relation for values.
Definition: Value.cpp:29
static constexpr int MaxCompositeValueDepth
MapT joinExprMaps(const MapT &Map1, const MapT &Map2)
static constexpr int MaxCompositeValueSize
ReferencedDecls getReferencedDecls(const FunctionDecl &FD)
Returns declarations that are declared in or referenced from FD.
Definition: ASTOps.cpp:246
RecordStorageLocation * getImplicitObjectLocation(const CXXMemberCallExpr &MCE, const Environment &Env)
Returns the storage location for the implicit object of a CXXMemberCallExpr, or null if none is defin...
static WidenResult widenDistinctValues(QualType Type, Value &Prev, const Environment &PrevEnv, Value &Current, Environment &CurrentEnv, Environment::ValueModel &Model)
const Expr & ignoreCFGOmittedNodes(const Expr &E)
Skip past nodes that the CFG does not emit.
Definition: ASTOps.cpp:34
static llvm::MapVector< const StorageLocation *, Value * > joinLocToVal(const llvm::MapVector< const StorageLocation *, Value * > &LocToVal, const llvm::MapVector< const StorageLocation *, Value * > &LocToVal2, const Environment &Env1, const Environment &Env2, Environment &JoinedEnv, Environment::ValueModel &Model)
static bool compareDistinctValues(QualType Type, Value &Val1, const Environment &Env1, Value &Val2, const Environment &Env2, Environment::ValueModel &Model)
RecordStorageLocation * getBaseObjectLocation(const MemberExpr &ME, const Environment &Env)
Returns the storage location for the base object of a MemberExpr, or null if none is defined in the e...
static Value * joinDistinctValues(QualType Type, Value &Val1, const Environment &Env1, Value &Val2, const Environment &Env2, Environment &JoinedEnv, Environment::ValueModel &Model)
Attempts to join distinct values Val1 and Val2 in Env1 and Env2, respectively, of the same type Type.
LatticeEffect
Effect indicating whether a lattice operation resulted in a new value.
The JSON file list parser is used to communicate input to InstallAPI.
@ Result
The result type of a method or function.
@ Other
Other implicit parameter.
A collection of several types of declarations, all referenced from the same function.
Definition: ASTOps.h:85
llvm::DenseSet< const VarDecl * > Globals
All variables with static storage duration, notably including static member variables and static vari...
Definition: ASTOps.h:90
llvm::DenseSet< const FunctionDecl * > Functions
Free functions and member functions which are referenced (but not necessarily called).
Definition: ASTOps.h:93
FieldSet Fields
Non-static member variables.
Definition: ASTOps.h:87
The result of a widen operation.