| 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(D: *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(S: 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(S: 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(D: *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(E: *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(D: *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(E: cast<Expr>(Val: 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(D: *ParamDecl, Loc&: createObject(D: *ParamDecl, InitExpr: 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>(Val: 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: *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: *D, Loc&: createObject(D: *D, InitExpr: nullptr)); |
| 582 | } |
| 583 | |
| 584 | for (const FunctionDecl *FD : Referenced.Functions) { |
| 585 | if (getStorageLocation(D: *FD) != nullptr) |
| 586 | continue; |
| 587 | auto &Loc = createStorageLocation(D: *FD); |
| 588 | setStorageLocation(D: *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(RecordPRValue: *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(RecordPRValue: *Call); |
| 629 | Env.LocForRecordReturnVal = &Env.getResultObjectLocation(RecordPRValue: *Call); |
| 630 | |
| 631 | Env.pushCallInternal(FuncDecl: Call->getConstructor(), |
| 632 | Args: 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(D: *Param, Loc&: createObject(D: *Param, InitExpr: 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(E: *Call, Loc&: *CalleeEnv.ReturnLoc); |
| 678 | } else if (!Call->getType()->isVoidType()) { |
| 679 | if (CalleeEnv.ReturnVal != nullptr) |
| 680 | setValue(E: *Call, Val&: *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 | F: [&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(Loc&: FieldRecordLoc, Type: FieldRecordLoc.getType(), |
| 1048 | Visited, Depth: 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(D: *Field, |
| 1067 | Loc: &createLocAndMaybeValue(Ty: FieldType, Visited, Depth: Depth + 1, |
| 1068 | CreatedValuesCount)); |
| 1069 | } else { |
| 1070 | StorageLocation *FieldLoc = Loc.getChild(D: *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(KV: {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 | |