1 //===-- Transfer.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 transfer functions that evaluate program statements and 10 // update an environment accordingly. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/Analysis/FlowSensitive/Transfer.h" 15 #include "clang/AST/Decl.h" 16 #include "clang/AST/DeclBase.h" 17 #include "clang/AST/DeclCXX.h" 18 #include "clang/AST/Expr.h" 19 #include "clang/AST/ExprCXX.h" 20 #include "clang/AST/OperationKinds.h" 21 #include "clang/AST/Stmt.h" 22 #include "clang/AST/StmtVisitor.h" 23 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" 24 #include "clang/Analysis/FlowSensitive/Value.h" 25 #include "clang/Basic/Builtins.h" 26 #include "clang/Basic/OperatorKinds.h" 27 #include "llvm/ADT/STLExtras.h" 28 #include "llvm/Support/Casting.h" 29 #include <cassert> 30 #include <memory> 31 #include <tuple> 32 33 namespace clang { 34 namespace dataflow { 35 36 static BoolValue &evaluateBooleanEquality(const Expr &LHS, const Expr &RHS, 37 Environment &Env) { 38 if (auto *LHSValue = 39 dyn_cast_or_null<BoolValue>(Env.getValue(LHS, SkipPast::Reference))) 40 if (auto *RHSValue = 41 dyn_cast_or_null<BoolValue>(Env.getValue(RHS, SkipPast::Reference))) 42 return Env.makeIff(*LHSValue, *RHSValue); 43 44 return Env.makeAtomicBoolValue(); 45 } 46 47 class TransferVisitor : public ConstStmtVisitor<TransferVisitor> { 48 public: 49 TransferVisitor(const StmtToEnvMap &StmtToEnv, Environment &Env) 50 : StmtToEnv(StmtToEnv), Env(Env) {} 51 52 void VisitBinaryOperator(const BinaryOperator *S) { 53 const Expr *LHS = S->getLHS(); 54 assert(LHS != nullptr); 55 56 const Expr *RHS = S->getRHS(); 57 assert(RHS != nullptr); 58 59 switch (S->getOpcode()) { 60 case BO_Assign: { 61 auto *LHSLoc = Env.getStorageLocation(*LHS, SkipPast::Reference); 62 if (LHSLoc == nullptr) 63 break; 64 65 auto *RHSVal = Env.getValue(*RHS, SkipPast::Reference); 66 if (RHSVal == nullptr) 67 break; 68 69 // Assign a value to the storage location of the left-hand side. 70 Env.setValue(*LHSLoc, *RHSVal); 71 72 // Assign a storage location for the whole expression. 73 Env.setStorageLocation(*S, *LHSLoc); 74 break; 75 } 76 case BO_LAnd: 77 case BO_LOr: { 78 BoolValue &LHSVal = getLogicOperatorSubExprValue(*LHS); 79 BoolValue &RHSVal = getLogicOperatorSubExprValue(*RHS); 80 81 auto &Loc = Env.createStorageLocation(*S); 82 Env.setStorageLocation(*S, Loc); 83 if (S->getOpcode() == BO_LAnd) 84 Env.setValue(Loc, Env.makeAnd(LHSVal, RHSVal)); 85 else 86 Env.setValue(Loc, Env.makeOr(LHSVal, RHSVal)); 87 break; 88 } 89 case BO_NE: 90 case BO_EQ: { 91 auto &LHSEqRHSValue = evaluateBooleanEquality(*LHS, *RHS, Env); 92 auto &Loc = Env.createStorageLocation(*S); 93 Env.setStorageLocation(*S, Loc); 94 Env.setValue(Loc, S->getOpcode() == BO_EQ ? LHSEqRHSValue 95 : Env.makeNot(LHSEqRHSValue)); 96 break; 97 } 98 default: 99 break; 100 } 101 } 102 103 void VisitDeclRefExpr(const DeclRefExpr *S) { 104 assert(S->getDecl() != nullptr); 105 auto *DeclLoc = Env.getStorageLocation(*S->getDecl(), SkipPast::None); 106 if (DeclLoc == nullptr) 107 return; 108 109 if (S->getDecl()->getType()->isReferenceType()) { 110 Env.setStorageLocation(*S, *DeclLoc); 111 } else { 112 auto &Loc = Env.createStorageLocation(*S); 113 auto &Val = Env.takeOwnership(std::make_unique<ReferenceValue>(*DeclLoc)); 114 Env.setStorageLocation(*S, Loc); 115 Env.setValue(Loc, Val); 116 } 117 } 118 119 void VisitDeclStmt(const DeclStmt *S) { 120 // Group decls are converted into single decls in the CFG so the cast below 121 // is safe. 122 const auto &D = *cast<VarDecl>(S->getSingleDecl()); 123 124 // Static local vars are already initialized in `Environment`. 125 if (D.hasGlobalStorage()) 126 return; 127 128 auto &Loc = Env.createStorageLocation(D); 129 Env.setStorageLocation(D, Loc); 130 131 const Expr *InitExpr = D.getInit(); 132 if (InitExpr == nullptr) { 133 // No initializer expression - associate `Loc` with a new value. 134 if (Value *Val = Env.createValue(D.getType())) 135 Env.setValue(Loc, *Val); 136 return; 137 } 138 139 if (D.getType()->isReferenceType()) { 140 // Initializing a reference variable - do not create a reference to 141 // reference. 142 if (auto *InitExprLoc = 143 Env.getStorageLocation(*InitExpr, SkipPast::Reference)) { 144 auto &Val = 145 Env.takeOwnership(std::make_unique<ReferenceValue>(*InitExprLoc)); 146 Env.setValue(Loc, Val); 147 } 148 } else if (auto *InitExprVal = Env.getValue(*InitExpr, SkipPast::None)) { 149 Env.setValue(Loc, *InitExprVal); 150 } 151 152 if (Env.getValue(Loc) == nullptr) { 153 // We arrive here in (the few) cases where an expression is intentionally 154 // "uninterpreted". There are two ways to handle this situation: propagate 155 // the status, so that uninterpreted initializers result in uninterpreted 156 // variables, or provide a default value. We choose the latter so that 157 // later refinements of the variable can be used for reasoning about the 158 // surrounding code. 159 // 160 // FIXME. If and when we interpret all language cases, change this to 161 // assert that `InitExpr` is interpreted, rather than supplying a default 162 // value (assuming we don't update the environment API to return 163 // references). 164 if (Value *Val = Env.createValue(D.getType())) 165 Env.setValue(Loc, *Val); 166 } 167 168 if (const auto *Decomp = dyn_cast<DecompositionDecl>(&D)) { 169 // If VarDecl is a DecompositionDecl, evaluate each of its bindings. This 170 // needs to be evaluated after initializing the values in the storage for 171 // VarDecl, as the bindings refer to them. 172 // FIXME: Add support for ArraySubscriptExpr. 173 // FIXME: Consider adding AST nodes that are used for structured bindings 174 // to the CFG. 175 for (const auto *B : Decomp->bindings()) { 176 auto *ME = dyn_cast_or_null<MemberExpr>(B->getBinding()); 177 if (ME == nullptr) 178 continue; 179 180 auto *DE = dyn_cast_or_null<DeclRefExpr>(ME->getBase()); 181 if (DE == nullptr) 182 continue; 183 184 // ME and its base haven't been visited because they aren't included in 185 // the statements of the CFG basic block. 186 VisitDeclRefExpr(DE); 187 VisitMemberExpr(ME); 188 189 if (auto *Loc = Env.getStorageLocation(*ME, SkipPast::Reference)) 190 Env.setStorageLocation(*B, *Loc); 191 } 192 } 193 } 194 195 void VisitImplicitCastExpr(const ImplicitCastExpr *S) { 196 const Expr *SubExpr = S->getSubExpr(); 197 assert(SubExpr != nullptr); 198 199 switch (S->getCastKind()) { 200 case CK_IntegralToBoolean: { 201 // This cast creates a new, boolean value from the integral value. We 202 // model that with a fresh value in the environment, unless it's already a 203 // boolean. 204 auto &Loc = Env.createStorageLocation(*S); 205 Env.setStorageLocation(*S, Loc); 206 if (auto *SubExprVal = dyn_cast_or_null<BoolValue>( 207 Env.getValue(*SubExpr, SkipPast::Reference))) 208 Env.setValue(Loc, *SubExprVal); 209 else 210 // FIXME: If integer modeling is added, then update this code to create 211 // the boolean based on the integer model. 212 Env.setValue(Loc, Env.makeAtomicBoolValue()); 213 break; 214 } 215 216 case CK_LValueToRValue: { 217 auto *SubExprVal = Env.getValue(*SubExpr, SkipPast::Reference); 218 if (SubExprVal == nullptr) 219 break; 220 221 auto &ExprLoc = Env.createStorageLocation(*S); 222 Env.setStorageLocation(*S, ExprLoc); 223 Env.setValue(ExprLoc, *SubExprVal); 224 break; 225 } 226 227 case CK_IntegralCast: 228 // FIXME: This cast creates a new integral value from the 229 // subexpression. But, because we don't model integers, we don't 230 // distinguish between this new value and the underlying one. If integer 231 // modeling is added, then update this code to create a fresh location and 232 // value. 233 case CK_UncheckedDerivedToBase: 234 case CK_ConstructorConversion: 235 case CK_UserDefinedConversion: 236 // FIXME: Add tests that excercise CK_UncheckedDerivedToBase, 237 // CK_ConstructorConversion, and CK_UserDefinedConversion. 238 case CK_NoOp: { 239 // FIXME: Consider making `Environment::getStorageLocation` skip noop 240 // expressions (this and other similar expressions in the file) instead of 241 // assigning them storage locations. 242 auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None); 243 if (SubExprLoc == nullptr) 244 break; 245 246 Env.setStorageLocation(*S, *SubExprLoc); 247 break; 248 } 249 default: 250 break; 251 } 252 } 253 254 void VisitUnaryOperator(const UnaryOperator *S) { 255 const Expr *SubExpr = S->getSubExpr(); 256 assert(SubExpr != nullptr); 257 258 switch (S->getOpcode()) { 259 case UO_Deref: { 260 // Skip past a reference to handle dereference of a dependent pointer. 261 const auto *SubExprVal = cast_or_null<PointerValue>( 262 Env.getValue(*SubExpr, SkipPast::Reference)); 263 if (SubExprVal == nullptr) 264 break; 265 266 auto &Loc = Env.createStorageLocation(*S); 267 Env.setStorageLocation(*S, Loc); 268 Env.setValue(Loc, Env.takeOwnership(std::make_unique<ReferenceValue>( 269 SubExprVal->getPointeeLoc()))); 270 break; 271 } 272 case UO_AddrOf: { 273 // Do not form a pointer to a reference. If `SubExpr` is assigned a 274 // `ReferenceValue` then form a value that points to the location of its 275 // pointee. 276 StorageLocation *PointeeLoc = 277 Env.getStorageLocation(*SubExpr, SkipPast::Reference); 278 if (PointeeLoc == nullptr) 279 break; 280 281 auto &PointerLoc = Env.createStorageLocation(*S); 282 auto &PointerVal = 283 Env.takeOwnership(std::make_unique<PointerValue>(*PointeeLoc)); 284 Env.setStorageLocation(*S, PointerLoc); 285 Env.setValue(PointerLoc, PointerVal); 286 break; 287 } 288 case UO_LNot: { 289 auto *SubExprVal = 290 dyn_cast_or_null<BoolValue>(Env.getValue(*SubExpr, SkipPast::None)); 291 if (SubExprVal == nullptr) 292 break; 293 294 auto &ExprLoc = Env.createStorageLocation(*S); 295 Env.setStorageLocation(*S, ExprLoc); 296 Env.setValue(ExprLoc, Env.makeNot(*SubExprVal)); 297 break; 298 } 299 default: 300 break; 301 } 302 } 303 304 void VisitCXXThisExpr(const CXXThisExpr *S) { 305 auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation(); 306 if (ThisPointeeLoc == nullptr) 307 // Unions are not supported yet, and will not have a location for the 308 // `this` expression's pointee. 309 return; 310 311 auto &Loc = Env.createStorageLocation(*S); 312 Env.setStorageLocation(*S, Loc); 313 Env.setValue(Loc, Env.takeOwnership( 314 std::make_unique<PointerValue>(*ThisPointeeLoc))); 315 } 316 317 void VisitMemberExpr(const MemberExpr *S) { 318 ValueDecl *Member = S->getMemberDecl(); 319 assert(Member != nullptr); 320 321 // FIXME: Consider assigning pointer values to function member expressions. 322 if (Member->isFunctionOrFunctionTemplate()) 323 return; 324 325 if (auto *D = dyn_cast<VarDecl>(Member)) { 326 if (D->hasGlobalStorage()) { 327 auto *VarDeclLoc = Env.getStorageLocation(*D, SkipPast::None); 328 if (VarDeclLoc == nullptr) 329 return; 330 331 if (VarDeclLoc->getType()->isReferenceType()) { 332 Env.setStorageLocation(*S, *VarDeclLoc); 333 } else { 334 auto &Loc = Env.createStorageLocation(*S); 335 Env.setStorageLocation(*S, Loc); 336 Env.setValue(Loc, Env.takeOwnership( 337 std::make_unique<ReferenceValue>(*VarDeclLoc))); 338 } 339 return; 340 } 341 } 342 343 // The receiver can be either a value or a pointer to a value. Skip past the 344 // indirection to handle both cases. 345 auto *BaseLoc = cast_or_null<AggregateStorageLocation>( 346 Env.getStorageLocation(*S->getBase(), SkipPast::ReferenceThenPointer)); 347 if (BaseLoc == nullptr) 348 return; 349 350 // FIXME: Add support for union types. 351 if (BaseLoc->getType()->isUnionType()) 352 return; 353 354 auto &MemberLoc = BaseLoc->getChild(*Member); 355 if (MemberLoc.getType()->isReferenceType()) { 356 Env.setStorageLocation(*S, MemberLoc); 357 } else { 358 auto &Loc = Env.createStorageLocation(*S); 359 Env.setStorageLocation(*S, Loc); 360 Env.setValue( 361 Loc, Env.takeOwnership(std::make_unique<ReferenceValue>(MemberLoc))); 362 } 363 } 364 365 void VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *S) { 366 const Expr *InitExpr = S->getExpr(); 367 assert(InitExpr != nullptr); 368 369 Value *InitExprVal = Env.getValue(*InitExpr, SkipPast::None); 370 if (InitExprVal == nullptr) 371 return; 372 373 const FieldDecl *Field = S->getField(); 374 assert(Field != nullptr); 375 376 auto &ThisLoc = 377 *cast<AggregateStorageLocation>(Env.getThisPointeeStorageLocation()); 378 auto &FieldLoc = ThisLoc.getChild(*Field); 379 Env.setValue(FieldLoc, *InitExprVal); 380 } 381 382 void VisitCXXConstructExpr(const CXXConstructExpr *S) { 383 const CXXConstructorDecl *ConstructorDecl = S->getConstructor(); 384 assert(ConstructorDecl != nullptr); 385 386 if (ConstructorDecl->isCopyOrMoveConstructor()) { 387 assert(S->getNumArgs() == 1); 388 389 const Expr *Arg = S->getArg(0); 390 assert(Arg != nullptr); 391 392 if (S->isElidable()) { 393 auto *ArgLoc = Env.getStorageLocation(*Arg, SkipPast::Reference); 394 if (ArgLoc == nullptr) 395 return; 396 397 Env.setStorageLocation(*S, *ArgLoc); 398 } else if (auto *ArgVal = Env.getValue(*Arg, SkipPast::Reference)) { 399 auto &Loc = Env.createStorageLocation(*S); 400 Env.setStorageLocation(*S, Loc); 401 Env.setValue(Loc, *ArgVal); 402 } 403 return; 404 } 405 406 auto &Loc = Env.createStorageLocation(*S); 407 Env.setStorageLocation(*S, Loc); 408 if (Value *Val = Env.createValue(S->getType())) 409 Env.setValue(Loc, *Val); 410 } 411 412 void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *S) { 413 if (S->getOperator() == OO_Equal) { 414 assert(S->getNumArgs() == 2); 415 416 const Expr *Arg0 = S->getArg(0); 417 assert(Arg0 != nullptr); 418 419 const Expr *Arg1 = S->getArg(1); 420 assert(Arg1 != nullptr); 421 422 // Evaluate only copy and move assignment operators. 423 auto *Arg0Type = Arg0->getType()->getUnqualifiedDesugaredType(); 424 auto *Arg1Type = Arg1->getType()->getUnqualifiedDesugaredType(); 425 if (Arg0Type != Arg1Type) 426 return; 427 428 auto *ObjectLoc = Env.getStorageLocation(*Arg0, SkipPast::Reference); 429 if (ObjectLoc == nullptr) 430 return; 431 432 auto *Val = Env.getValue(*Arg1, SkipPast::Reference); 433 if (Val == nullptr) 434 return; 435 436 // Assign a value to the storage location of the object. 437 Env.setValue(*ObjectLoc, *Val); 438 439 // FIXME: Add a test for the value of the whole expression. 440 // Assign a storage location for the whole expression. 441 Env.setStorageLocation(*S, *ObjectLoc); 442 } 443 } 444 445 void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *S) { 446 if (S->getCastKind() == CK_ConstructorConversion) { 447 const Expr *SubExpr = S->getSubExpr(); 448 assert(SubExpr != nullptr); 449 450 auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None); 451 if (SubExprLoc == nullptr) 452 return; 453 454 Env.setStorageLocation(*S, *SubExprLoc); 455 } 456 } 457 458 void VisitCXXTemporaryObjectExpr(const CXXTemporaryObjectExpr *S) { 459 auto &Loc = Env.createStorageLocation(*S); 460 Env.setStorageLocation(*S, Loc); 461 if (Value *Val = Env.createValue(S->getType())) 462 Env.setValue(Loc, *Val); 463 } 464 465 void VisitCallExpr(const CallExpr *S) { 466 // Of clang's builtins, only `__builtin_expect` is handled explicitly, since 467 // others (like trap, debugtrap, and unreachable) are handled by CFG 468 // construction. 469 if (S->isCallToStdMove()) { 470 assert(S->getNumArgs() == 1); 471 472 const Expr *Arg = S->getArg(0); 473 assert(Arg != nullptr); 474 475 auto *ArgLoc = Env.getStorageLocation(*Arg, SkipPast::None); 476 if (ArgLoc == nullptr) 477 return; 478 479 Env.setStorageLocation(*S, *ArgLoc); 480 } else if (S->getDirectCallee() != nullptr && 481 S->getDirectCallee()->getBuiltinID() == 482 Builtin::BI__builtin_expect) { 483 assert(S->getNumArgs() > 0); 484 assert(S->getArg(0) != nullptr); 485 // `__builtin_expect` returns by-value, so strip away any potential 486 // references in the argument. 487 auto *ArgLoc = Env.getStorageLocation(*S->getArg(0), SkipPast::Reference); 488 if (ArgLoc == nullptr) 489 return; 490 Env.setStorageLocation(*S, *ArgLoc); 491 } 492 } 493 494 void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *S) { 495 const Expr *SubExpr = S->getSubExpr(); 496 assert(SubExpr != nullptr); 497 498 auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None); 499 if (SubExprLoc == nullptr) 500 return; 501 502 Env.setStorageLocation(*S, *SubExprLoc); 503 } 504 505 void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *S) { 506 const Expr *SubExpr = S->getSubExpr(); 507 assert(SubExpr != nullptr); 508 509 auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None); 510 if (SubExprLoc == nullptr) 511 return; 512 513 Env.setStorageLocation(*S, *SubExprLoc); 514 } 515 516 void VisitCXXStaticCastExpr(const CXXStaticCastExpr *S) { 517 if (S->getCastKind() == CK_NoOp) { 518 const Expr *SubExpr = S->getSubExpr(); 519 assert(SubExpr != nullptr); 520 521 auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None); 522 if (SubExprLoc == nullptr) 523 return; 524 525 Env.setStorageLocation(*S, *SubExprLoc); 526 } 527 } 528 529 void VisitConditionalOperator(const ConditionalOperator *S) { 530 // FIXME: Revisit this once flow conditions are added to the framework. For 531 // `a = b ? c : d` we can add `b => a == c && !b => a == d` to the flow 532 // condition. 533 auto &Loc = Env.createStorageLocation(*S); 534 Env.setStorageLocation(*S, Loc); 535 if (Value *Val = Env.createValue(S->getType())) 536 Env.setValue(Loc, *Val); 537 } 538 539 void VisitInitListExpr(const InitListExpr *S) { 540 QualType Type = S->getType(); 541 542 auto &Loc = Env.createStorageLocation(*S); 543 Env.setStorageLocation(*S, Loc); 544 545 auto *Val = Env.createValue(Type); 546 if (Val == nullptr) 547 return; 548 549 Env.setValue(Loc, *Val); 550 551 if (Type->isStructureOrClassType()) { 552 for (auto IT : llvm::zip(Type->getAsRecordDecl()->fields(), S->inits())) { 553 const FieldDecl *Field = std::get<0>(IT); 554 assert(Field != nullptr); 555 556 const Expr *Init = std::get<1>(IT); 557 assert(Init != nullptr); 558 559 if (Value *InitVal = Env.getValue(*Init, SkipPast::None)) 560 cast<StructValue>(Val)->setChild(*Field, *InitVal); 561 } 562 } 563 // FIXME: Implement array initialization. 564 } 565 566 void VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *S) { 567 auto &Loc = Env.createStorageLocation(*S); 568 Env.setStorageLocation(*S, Loc); 569 Env.setValue(Loc, Env.getBoolLiteralValue(S->getValue())); 570 } 571 572 void VisitParenExpr(const ParenExpr *S) { 573 // The CFG does not contain `ParenExpr` as top-level statements in basic 574 // blocks, however manual traversal to sub-expressions may encounter them. 575 // Redirect to the sub-expression. 576 auto *SubExpr = S->getSubExpr(); 577 assert(SubExpr != nullptr); 578 Visit(SubExpr); 579 } 580 581 void VisitExprWithCleanups(const ExprWithCleanups *S) { 582 // The CFG does not contain `ExprWithCleanups` as top-level statements in 583 // basic blocks, however manual traversal to sub-expressions may encounter 584 // them. Redirect to the sub-expression. 585 auto *SubExpr = S->getSubExpr(); 586 assert(SubExpr != nullptr); 587 Visit(SubExpr); 588 } 589 590 private: 591 BoolValue &getLogicOperatorSubExprValue(const Expr &SubExpr) { 592 // `SubExpr` and its parent logic operator might be part of different basic 593 // blocks. We try to access the value that is assigned to `SubExpr` in the 594 // corresponding environment. 595 if (const Environment *SubExprEnv = StmtToEnv.getEnvironment(SubExpr)) { 596 if (auto *Val = dyn_cast_or_null<BoolValue>( 597 SubExprEnv->getValue(SubExpr, SkipPast::Reference))) 598 return *Val; 599 } 600 601 if (Env.getStorageLocation(SubExpr, SkipPast::None) == nullptr) { 602 // Sub-expressions that are logic operators are not added in basic blocks 603 // (e.g. see CFG for `bool d = a && (b || c);`). If `SubExpr` is a logic 604 // operator, it may not have been evaluated and assigned a value yet. In 605 // that case, we need to first visit `SubExpr` and then try to get the 606 // value that gets assigned to it. 607 Visit(&SubExpr); 608 } 609 610 if (auto *Val = dyn_cast_or_null<BoolValue>( 611 Env.getValue(SubExpr, SkipPast::Reference))) 612 return *Val; 613 614 // If the value of `SubExpr` is still unknown, we create a fresh symbolic 615 // boolean value for it. 616 return Env.makeAtomicBoolValue(); 617 } 618 619 const StmtToEnvMap &StmtToEnv; 620 Environment &Env; 621 }; 622 623 void transfer(const StmtToEnvMap &StmtToEnv, const Stmt &S, Environment &Env) { 624 TransferVisitor(StmtToEnv, Env).Visit(&S); 625 } 626 627 } // namespace dataflow 628 } // namespace clang 629