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