clang 19.0.0git
Transfer.cpp
Go to the documentation of this file.
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
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"
21#include "clang/AST/Stmt.h"
32#include "llvm/Support/Casting.h"
33#include "llvm/Support/Debug.h"
34#include <assert.h>
35#include <cassert>
36
37#define DEBUG_TYPE "dataflow"
38
39namespace clang {
40namespace dataflow {
41
43 auto BlockIt = ACFG.getStmtToBlock().find(&ignoreCFGOmittedNodes(S));
44 if (BlockIt == ACFG.getStmtToBlock().end()) {
45 assert(false);
46 // Return null to avoid dereferencing the end iterator in non-assert builds.
47 return nullptr;
48 }
49 if (!ACFG.isBlockReachable(*BlockIt->getSecond()))
50 return nullptr;
51 if (BlockIt->getSecond()->getBlockID() == CurBlockID)
52 return &CurState.Env;
53 const auto &State = BlockToState[BlockIt->getSecond()->getBlockID()];
54 if (!(State))
55 return nullptr;
56 return &State->Env;
57}
58
59static BoolValue &evaluateBooleanEquality(const Expr &LHS, const Expr &RHS,
61 Value *LHSValue = Env.getValue(LHS);
62 Value *RHSValue = Env.getValue(RHS);
63
64 if (LHSValue == RHSValue)
65 return Env.getBoolLiteralValue(true);
66
67 if (auto *LHSBool = dyn_cast_or_null<BoolValue>(LHSValue))
68 if (auto *RHSBool = dyn_cast_or_null<BoolValue>(RHSValue))
69 return Env.makeIff(*LHSBool, *RHSBool);
70
71 return Env.makeAtomicBoolValue();
72}
73
75 if (auto *Top = llvm::dyn_cast<TopBoolValue>(&V)) {
77 return A.makeBoolValue(A.makeAtomRef(Top->getAtom()));
78 }
79 return V;
80}
81
82// Unpacks the value (if any) associated with `E` and updates `E` to the new
83// value, if any unpacking occured. Also, does the lvalue-to-rvalue conversion,
84// by skipping past the reference.
86 auto *Loc = Env.getStorageLocation(E);
87 if (Loc == nullptr)
88 return nullptr;
89 auto *Val = Env.getValue(*Loc);
90
91 auto *B = dyn_cast_or_null<BoolValue>(Val);
92 if (B == nullptr)
93 return Val;
94
95 auto &UnpackedVal = unpackValue(*B, Env);
96 if (&UnpackedVal == Val)
97 return Val;
98 Env.setValue(*Loc, UnpackedVal);
99 return &UnpackedVal;
100}
101
102static void propagateValue(const Expr &From, const Expr &To, Environment &Env) {
103 if (From.getType()->isRecordType())
104 return;
105 if (auto *Val = Env.getValue(From))
106 Env.setValue(To, *Val);
107}
108
109static void propagateStorageLocation(const Expr &From, const Expr &To,
110 Environment &Env) {
111 if (auto *Loc = Env.getStorageLocation(From))
112 Env.setStorageLocation(To, *Loc);
113}
114
115// Propagates the value or storage location of `From` to `To` in cases where
116// `From` may be either a glvalue or a prvalue. `To` must be a glvalue iff
117// `From` is a glvalue.
118static void propagateValueOrStorageLocation(const Expr &From, const Expr &To,
119 Environment &Env) {
120 assert(From.isGLValue() == To.isGLValue());
121 if (From.isGLValue())
122 propagateStorageLocation(From, To, Env);
123 else
124 propagateValue(From, To, Env);
125}
126
127namespace {
128
129class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
130public:
131 TransferVisitor(const StmtToEnvMap &StmtToEnv, Environment &Env,
132 Environment::ValueModel &Model)
133 : StmtToEnv(StmtToEnv), Env(Env), Model(Model) {}
134
135 void VisitBinaryOperator(const BinaryOperator *S) {
136 const Expr *LHS = S->getLHS();
137 assert(LHS != nullptr);
138
139 const Expr *RHS = S->getRHS();
140 assert(RHS != nullptr);
141
142 switch (S->getOpcode()) {
143 case BO_Assign: {
144 auto *LHSLoc = Env.getStorageLocation(*LHS);
145 if (LHSLoc == nullptr)
146 break;
147
148 auto *RHSVal = Env.getValue(*RHS);
149 if (RHSVal == nullptr)
150 break;
151
152 // Assign a value to the storage location of the left-hand side.
153 Env.setValue(*LHSLoc, *RHSVal);
154
155 // Assign a storage location for the whole expression.
156 Env.setStorageLocation(*S, *LHSLoc);
157 break;
158 }
159 case BO_LAnd:
160 case BO_LOr: {
161 BoolValue &LHSVal = getLogicOperatorSubExprValue(*LHS);
162 BoolValue &RHSVal = getLogicOperatorSubExprValue(*RHS);
163
164 if (S->getOpcode() == BO_LAnd)
165 Env.setValue(*S, Env.makeAnd(LHSVal, RHSVal));
166 else
167 Env.setValue(*S, Env.makeOr(LHSVal, RHSVal));
168 break;
169 }
170 case BO_NE:
171 case BO_EQ: {
172 auto &LHSEqRHSValue = evaluateBooleanEquality(*LHS, *RHS, Env);
173 Env.setValue(*S, S->getOpcode() == BO_EQ ? LHSEqRHSValue
174 : Env.makeNot(LHSEqRHSValue));
175 break;
176 }
177 case BO_Comma: {
178 propagateValueOrStorageLocation(*RHS, *S, Env);
179 break;
180 }
181 default:
182 break;
183 }
184 }
185
186 void VisitDeclRefExpr(const DeclRefExpr *S) {
187 const ValueDecl *VD = S->getDecl();
188 assert(VD != nullptr);
189
190 // Some `DeclRefExpr`s aren't glvalues, so we can't associate them with a
191 // `StorageLocation`, and there's also no sensible `Value` that we can
192 // assign to them. Examples:
193 // - Non-static member variables
194 // - Non static member functions
195 // Note: Member operators are an exception to this, but apparently only
196 // if the `DeclRefExpr` is used within the callee of a
197 // `CXXOperatorCallExpr`. In other cases, for example when applying the
198 // address-of operator, the `DeclRefExpr` is a prvalue.
199 if (!S->isGLValue())
200 return;
201
202 auto *DeclLoc = Env.getStorageLocation(*VD);
203 if (DeclLoc == nullptr)
204 return;
205
206 Env.setStorageLocation(*S, *DeclLoc);
207 }
208
209 void VisitDeclStmt(const DeclStmt *S) {
210 // Group decls are converted into single decls in the CFG so the cast below
211 // is safe.
212 const auto &D = *cast<VarDecl>(S->getSingleDecl());
213
214 ProcessVarDecl(D);
215 }
216
217 void ProcessVarDecl(const VarDecl &D) {
218 // Static local vars are already initialized in `Environment`.
219 if (D.hasGlobalStorage())
220 return;
221
222 // If this is the holding variable for a `BindingDecl`, we may already
223 // have a storage location set up -- so check. (See also explanation below
224 // where we process the `BindingDecl`.)
225 if (D.getType()->isReferenceType() && Env.getStorageLocation(D) != nullptr)
226 return;
227
228 assert(Env.getStorageLocation(D) == nullptr);
229
230 Env.setStorageLocation(D, Env.createObject(D));
231
232 // `DecompositionDecl` must be handled after we've interpreted the loc
233 // itself, because the binding expression refers back to the
234 // `DecompositionDecl` (even though it has no written name).
235 if (const auto *Decomp = dyn_cast<DecompositionDecl>(&D)) {
236 // If VarDecl is a DecompositionDecl, evaluate each of its bindings. This
237 // needs to be evaluated after initializing the values in the storage for
238 // VarDecl, as the bindings refer to them.
239 // FIXME: Add support for ArraySubscriptExpr.
240 // FIXME: Consider adding AST nodes used in BindingDecls to the CFG.
241 for (const auto *B : Decomp->bindings()) {
242 if (auto *ME = dyn_cast_or_null<MemberExpr>(B->getBinding())) {
243 auto *DE = dyn_cast_or_null<DeclRefExpr>(ME->getBase());
244 if (DE == nullptr)
245 continue;
246
247 // ME and its base haven't been visited because they aren't included
248 // in the statements of the CFG basic block.
249 VisitDeclRefExpr(DE);
250 VisitMemberExpr(ME);
251
252 if (auto *Loc = Env.getStorageLocation(*ME))
253 Env.setStorageLocation(*B, *Loc);
254 } else if (auto *VD = B->getHoldingVar()) {
255 // Holding vars are used to back the `BindingDecl`s of tuple-like
256 // types. The holding var declarations appear after the
257 // `DecompositionDecl`, so we have to explicitly process them here
258 // to know their storage location. They will be processed a second
259 // time when we visit their `VarDecl`s, so we have code that protects
260 // against this above.
261 ProcessVarDecl(*VD);
262 auto *VDLoc = Env.getStorageLocation(*VD);
263 assert(VDLoc != nullptr);
264 Env.setStorageLocation(*B, *VDLoc);
265 }
266 }
267 }
268 }
269
270 void VisitImplicitCastExpr(const ImplicitCastExpr *S) {
271 const Expr *SubExpr = S->getSubExpr();
272 assert(SubExpr != nullptr);
273
274 switch (S->getCastKind()) {
275 case CK_IntegralToBoolean: {
276 // This cast creates a new, boolean value from the integral value. We
277 // model that with a fresh value in the environment, unless it's already a
278 // boolean.
279 if (auto *SubExprVal =
280 dyn_cast_or_null<BoolValue>(Env.getValue(*SubExpr)))
281 Env.setValue(*S, *SubExprVal);
282 else
283 // FIXME: If integer modeling is added, then update this code to create
284 // the boolean based on the integer model.
285 Env.setValue(*S, Env.makeAtomicBoolValue());
286 break;
287 }
288
289 case CK_LValueToRValue: {
290 // When an L-value is used as an R-value, it may result in sharing, so we
291 // need to unpack any nested `Top`s.
292 auto *SubExprVal = maybeUnpackLValueExpr(*SubExpr, Env);
293 if (SubExprVal == nullptr)
294 break;
295
296 Env.setValue(*S, *SubExprVal);
297 break;
298 }
299
300 case CK_IntegralCast:
301 // FIXME: This cast creates a new integral value from the
302 // subexpression. But, because we don't model integers, we don't
303 // distinguish between this new value and the underlying one. If integer
304 // modeling is added, then update this code to create a fresh location and
305 // value.
306 case CK_UncheckedDerivedToBase:
307 case CK_ConstructorConversion:
308 case CK_UserDefinedConversion:
309 // FIXME: Add tests that excercise CK_UncheckedDerivedToBase,
310 // CK_ConstructorConversion, and CK_UserDefinedConversion.
311 case CK_NoOp: {
312 // FIXME: Consider making `Environment::getStorageLocation` skip noop
313 // expressions (this and other similar expressions in the file) instead
314 // of assigning them storage locations.
315 propagateValueOrStorageLocation(*SubExpr, *S, Env);
316 break;
317 }
318 case CK_NullToPointer: {
319 auto &NullPointerVal =
320 Env.getOrCreateNullPointerValue(S->getType()->getPointeeType());
321 Env.setValue(*S, NullPointerVal);
322 break;
323 }
324 case CK_NullToMemberPointer:
325 // FIXME: Implement pointers to members. For now, don't associate a value
326 // with this expression.
327 break;
328 case CK_FunctionToPointerDecay: {
329 StorageLocation *PointeeLoc = Env.getStorageLocation(*SubExpr);
330 if (PointeeLoc == nullptr)
331 break;
332
333 Env.setValue(*S, Env.create<PointerValue>(*PointeeLoc));
334 break;
335 }
336 case CK_BuiltinFnToFnPtr:
337 // Despite its name, the result type of `BuiltinFnToFnPtr` is a function,
338 // not a function pointer. In addition, builtin functions can only be
339 // called directly; it is not legal to take their address. We therefore
340 // don't need to create a value or storage location for them.
341 break;
342 default:
343 break;
344 }
345 }
346
347 void VisitUnaryOperator(const UnaryOperator *S) {
348 const Expr *SubExpr = S->getSubExpr();
349 assert(SubExpr != nullptr);
350
351 switch (S->getOpcode()) {
352 case UO_Deref: {
353 const auto *SubExprVal = Env.get<PointerValue>(*SubExpr);
354 if (SubExprVal == nullptr)
355 break;
356
357 Env.setStorageLocation(*S, SubExprVal->getPointeeLoc());
358 break;
359 }
360 case UO_AddrOf: {
361 // FIXME: Model pointers to members.
362 if (S->getType()->isMemberPointerType())
363 break;
364
365 if (StorageLocation *PointeeLoc = Env.getStorageLocation(*SubExpr))
366 Env.setValue(*S, Env.create<PointerValue>(*PointeeLoc));
367 break;
368 }
369 case UO_LNot: {
370 auto *SubExprVal = dyn_cast_or_null<BoolValue>(Env.getValue(*SubExpr));
371 if (SubExprVal == nullptr)
372 break;
373
374 Env.setValue(*S, Env.makeNot(*SubExprVal));
375 break;
376 }
377 default:
378 break;
379 }
380 }
381
382 void VisitCXXThisExpr(const CXXThisExpr *S) {
383 auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation();
384 if (ThisPointeeLoc == nullptr)
385 // Unions are not supported yet, and will not have a location for the
386 // `this` expression's pointee.
387 return;
388
389 Env.setValue(*S, Env.create<PointerValue>(*ThisPointeeLoc));
390 }
391
392 void VisitCXXNewExpr(const CXXNewExpr *S) {
393 if (Value *Val = Env.createValue(S->getType()))
394 Env.setValue(*S, *Val);
395 }
396
397 void VisitCXXDeleteExpr(const CXXDeleteExpr *S) {
398 // Empty method.
399 // We consciously don't do anything on deletes. Diagnosing double deletes
400 // (for example) should be done by a specific analysis, not by the
401 // framework.
402 }
403
404 void VisitReturnStmt(const ReturnStmt *S) {
405 if (!Env.getDataflowAnalysisContext().getOptions().ContextSensitiveOpts)
406 return;
407
408 auto *Ret = S->getRetValue();
409 if (Ret == nullptr)
410 return;
411
412 if (Ret->isPRValue()) {
413 if (Ret->getType()->isRecordType())
414 return;
415
416 auto *Val = Env.getValue(*Ret);
417 if (Val == nullptr)
418 return;
419
420 // FIXME: Model NRVO.
421 Env.setReturnValue(Val);
422 } else {
423 auto *Loc = Env.getStorageLocation(*Ret);
424 if (Loc == nullptr)
425 return;
426
427 // FIXME: Model NRVO.
428 Env.setReturnStorageLocation(Loc);
429 }
430 }
431
432 void VisitMemberExpr(const MemberExpr *S) {
433 ValueDecl *Member = S->getMemberDecl();
434 assert(Member != nullptr);
435
436 // FIXME: Consider assigning pointer values to function member expressions.
437 if (Member->isFunctionOrFunctionTemplate())
438 return;
439
440 // FIXME: if/when we add support for modeling enums, use that support here.
441 if (isa<EnumConstantDecl>(Member))
442 return;
443
444 if (auto *D = dyn_cast<VarDecl>(Member)) {
445 if (D->hasGlobalStorage()) {
446 auto *VarDeclLoc = Env.getStorageLocation(*D);
447 if (VarDeclLoc == nullptr)
448 return;
449
450 Env.setStorageLocation(*S, *VarDeclLoc);
451 return;
452 }
453 }
454
455 RecordStorageLocation *BaseLoc = getBaseObjectLocation(*S, Env);
456 if (BaseLoc == nullptr)
457 return;
458
459 auto *MemberLoc = BaseLoc->getChild(*Member);
460 if (MemberLoc == nullptr)
461 return;
462 Env.setStorageLocation(*S, *MemberLoc);
463 }
464
465 void VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *S) {
466 const Expr *ArgExpr = S->getExpr();
467 assert(ArgExpr != nullptr);
468 propagateValueOrStorageLocation(*ArgExpr, *S, Env);
469
470 if (S->isPRValue() && S->getType()->isRecordType()) {
471 auto &Loc = Env.getResultObjectLocation(*S);
472 Env.initializeFieldsWithValues(Loc);
473 }
474 }
475
476 void VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *S) {
477 const Expr *InitExpr = S->getExpr();
478 assert(InitExpr != nullptr);
479
480 // If this is a prvalue of record type, the handler for `*InitExpr` (if one
481 // exists) will initialize the result object; there is no value to propgate
482 // here.
483 if (S->getType()->isRecordType() && S->isPRValue())
484 return;
485
486 propagateValueOrStorageLocation(*InitExpr, *S, Env);
487 }
488
489 void VisitCXXConstructExpr(const CXXConstructExpr *S) {
490 const CXXConstructorDecl *ConstructorDecl = S->getConstructor();
491 assert(ConstructorDecl != nullptr);
492
493 // `CXXConstructExpr` can have array type if default-initializing an array
494 // of records. We don't handle this specifically beyond potentially inlining
495 // the call.
496 if (!S->getType()->isRecordType()) {
497 transferInlineCall(S, ConstructorDecl);
498 return;
499 }
500
501 RecordStorageLocation &Loc = Env.getResultObjectLocation(*S);
502
503 if (ConstructorDecl->isCopyOrMoveConstructor()) {
504 // It is permissible for a copy/move constructor to have additional
505 // parameters as long as they have default arguments defined for them.
506 assert(S->getNumArgs() != 0);
507
508 const Expr *Arg = S->getArg(0);
509 assert(Arg != nullptr);
510
511 auto *ArgLoc = Env.get<RecordStorageLocation>(*Arg);
512 if (ArgLoc == nullptr)
513 return;
514
515 // Even if the copy/move constructor call is elidable, we choose to copy
516 // the record in all cases (which isn't wrong, just potentially not
517 // optimal).
518 copyRecord(*ArgLoc, Loc, Env);
519 return;
520 }
521
522 Env.initializeFieldsWithValues(Loc, S->getType());
523
524 transferInlineCall(S, ConstructorDecl);
525 }
526
527 void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *S) {
528 if (S->getOperator() == OO_Equal) {
529 assert(S->getNumArgs() == 2);
530
531 const Expr *Arg0 = S->getArg(0);
532 assert(Arg0 != nullptr);
533
534 const Expr *Arg1 = S->getArg(1);
535 assert(Arg1 != nullptr);
536
537 // Evaluate only copy and move assignment operators.
538 const auto *Method =
539 dyn_cast_or_null<CXXMethodDecl>(S->getDirectCallee());
540 if (!Method)
541 return;
542 if (!Method->isCopyAssignmentOperator() &&
543 !Method->isMoveAssignmentOperator())
544 return;
545
546 RecordStorageLocation *LocSrc = nullptr;
547 if (Arg1->isPRValue()) {
548 LocSrc = &Env.getResultObjectLocation(*Arg1);
549 } else {
550 LocSrc = Env.get<RecordStorageLocation>(*Arg1);
551 }
552 auto *LocDst = Env.get<RecordStorageLocation>(*Arg0);
553
554 if (LocSrc == nullptr || LocDst == nullptr)
555 return;
556
557 copyRecord(*LocSrc, *LocDst, Env);
558
559 // If the expr is a glvalue, we can reasonably assume the operator is
560 // returning T& and thus we can assign it `LocDst`.
561 if (S->isGLValue()) {
562 Env.setStorageLocation(*S, *LocDst);
563 } else if (S->getType()->isRecordType()) {
564 // Assume that the assignment returns the assigned value.
565 copyRecord(*LocDst, Env.getResultObjectLocation(*S), Env);
566 }
567
568 return;
569 }
570
571 // `CXXOperatorCallExpr` can be a prvalue. Call `VisitCallExpr`() to
572 // initialize the prvalue's fields with values.
573 VisitCallExpr(S);
574 }
575
576 void VisitCXXRewrittenBinaryOperator(const CXXRewrittenBinaryOperator *RBO) {
577 propagateValue(*RBO->getSemanticForm(), *RBO, Env);
578 }
579
580 void VisitCallExpr(const CallExpr *S) {
581 // Of clang's builtins, only `__builtin_expect` is handled explicitly, since
582 // others (like trap, debugtrap, and unreachable) are handled by CFG
583 // construction.
584 if (S->isCallToStdMove()) {
585 assert(S->getNumArgs() == 1);
586
587 const Expr *Arg = S->getArg(0);
588 assert(Arg != nullptr);
589
590 auto *ArgLoc = Env.getStorageLocation(*Arg);
591 if (ArgLoc == nullptr)
592 return;
593
594 Env.setStorageLocation(*S, *ArgLoc);
595 } else if (S->getDirectCallee() != nullptr &&
596 S->getDirectCallee()->getBuiltinID() ==
597 Builtin::BI__builtin_expect) {
598 assert(S->getNumArgs() > 0);
599 assert(S->getArg(0) != nullptr);
600 auto *ArgVal = Env.getValue(*S->getArg(0));
601 if (ArgVal == nullptr)
602 return;
603 Env.setValue(*S, *ArgVal);
604 } else if (const FunctionDecl *F = S->getDirectCallee()) {
605 transferInlineCall(S, F);
606
607 // If this call produces a prvalue of record type, initialize its fields
608 // with values.
609 if (S->getType()->isRecordType() && S->isPRValue()) {
610 RecordStorageLocation &Loc = Env.getResultObjectLocation(*S);
611 Env.initializeFieldsWithValues(Loc);
612 }
613 }
614 }
615
616 void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *S) {
617 const Expr *SubExpr = S->getSubExpr();
618 assert(SubExpr != nullptr);
619
620 StorageLocation &Loc = Env.createStorageLocation(*S);
621 Env.setStorageLocation(*S, Loc);
622
623 if (SubExpr->getType()->isRecordType())
624 // Nothing else left to do -- we initialized the record when transferring
625 // `SubExpr`.
626 return;
627
628 if (Value *SubExprVal = Env.getValue(*SubExpr))
629 Env.setValue(Loc, *SubExprVal);
630 }
631
632 void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *S) {
633 const Expr *SubExpr = S->getSubExpr();
634 assert(SubExpr != nullptr);
635
636 propagateValue(*SubExpr, *S, Env);
637 }
638
639 void VisitCXXStaticCastExpr(const CXXStaticCastExpr *S) {
640 if (S->getCastKind() == CK_NoOp) {
641 const Expr *SubExpr = S->getSubExpr();
642 assert(SubExpr != nullptr);
643
644 propagateValueOrStorageLocation(*SubExpr, *S, Env);
645 }
646 }
647
648 void VisitConditionalOperator(const ConditionalOperator *S) {
649 const Environment *TrueEnv = StmtToEnv.getEnvironment(*S->getTrueExpr());
650 const Environment *FalseEnv = StmtToEnv.getEnvironment(*S->getFalseExpr());
651
652 if (TrueEnv == nullptr || FalseEnv == nullptr) {
653 // If the true or false branch is dead, we may not have an environment for
654 // it. We could handle this specifically by forwarding the value or
655 // location of the live branch, but this case is rare enough that this
656 // probably isn't worth the additional complexity.
657 return;
658 }
659
660 if (S->isGLValue()) {
661 StorageLocation *TrueLoc = TrueEnv->getStorageLocation(*S->getTrueExpr());
662 StorageLocation *FalseLoc =
663 FalseEnv->getStorageLocation(*S->getFalseExpr());
664 if (TrueLoc == FalseLoc && TrueLoc != nullptr)
665 Env.setStorageLocation(*S, *TrueLoc);
666 } else if (!S->getType()->isRecordType()) {
667 // The conditional operator can evaluate to either of the values of the
668 // two branches. To model this, join these two values together to yield
669 // the result of the conditional operator.
670 // Note: Most joins happen in `computeBlockInputState()`, but this case is
671 // different:
672 // - `computeBlockInputState()` (which in turn calls `Environment::join()`
673 // joins values associated with the _same_ expression or storage
674 // location, then associates the joined value with that expression or
675 // storage location. This join has nothing to do with transfer --
676 // instead, it joins together the results of performing transfer on two
677 // different blocks.
678 // - Here, we join values associated with _different_ expressions (the
679 // true and false branch), then associate the joined value with a third
680 // expression (the conditional operator itself). This join is what it
681 // means to perform transfer on the conditional operator.
683 S->getType(), TrueEnv->getValue(*S->getTrueExpr()), *TrueEnv,
684 FalseEnv->getValue(*S->getFalseExpr()), *FalseEnv, Env, Model))
685 Env.setValue(*S, *Val);
686 }
687 }
688
689 void VisitInitListExpr(const InitListExpr *S) {
690 QualType Type = S->getType();
691
692 if (!Type->isRecordType()) {
693 // Until array initialization is implemented, we skip arrays and don't
694 // need to care about cases where `getNumInits() > 1`.
695 if (!Type->isArrayType() && S->getNumInits() == 1)
696 propagateValueOrStorageLocation(*S->getInit(0), *S, Env);
697 return;
698 }
699
700 // If the initializer list is transparent, there's nothing to do.
701 if (S->isSemanticForm() && S->isTransparent())
702 return;
703
704 RecordStorageLocation &Loc = Env.getResultObjectLocation(*S);
705
706 // Initialization of base classes and fields of record type happens when we
707 // visit the nested `CXXConstructExpr` or `InitListExpr` for that base class
708 // or field. We therefore only need to deal with fields of non-record type
709 // here.
710
711 RecordInitListHelper InitListHelper(S);
712
713 for (auto [Field, Init] : InitListHelper.field_inits()) {
714 if (Field->getType()->isRecordType())
715 continue;
716 if (Field->getType()->isReferenceType()) {
717 assert(Field->getType().getCanonicalType()->getPointeeType() ==
718 Init->getType().getCanonicalType());
719 Loc.setChild(*Field, &Env.createObject(Field->getType(), Init));
720 continue;
721 }
722 assert(Field->getType().getCanonicalType().getUnqualifiedType() ==
723 Init->getType().getCanonicalType().getUnqualifiedType());
724 StorageLocation *FieldLoc = Loc.getChild(*Field);
725 // Locations for non-reference fields must always be non-null.
726 assert(FieldLoc != nullptr);
727 Value *Val = Env.getValue(*Init);
728 if (Val == nullptr && isa<ImplicitValueInitExpr>(Init) &&
729 Init->getType()->isPointerType())
730 Val =
731 &Env.getOrCreateNullPointerValue(Init->getType()->getPointeeType());
732 if (Val == nullptr)
733 Val = Env.createValue(Field->getType());
734 if (Val != nullptr)
735 Env.setValue(*FieldLoc, *Val);
736 }
737
738 for (const auto &[FieldName, FieldLoc] : Loc.synthetic_fields()) {
739 QualType FieldType = FieldLoc->getType();
740 if (FieldType->isRecordType()) {
741 Env.initializeFieldsWithValues(*cast<RecordStorageLocation>(FieldLoc));
742 } else {
743 if (Value *Val = Env.createValue(FieldType))
744 Env.setValue(*FieldLoc, *Val);
745 }
746 }
747
748 // FIXME: Implement array initialization.
749 }
750
751 void VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *S) {
752 Env.setValue(*S, Env.getBoolLiteralValue(S->getValue()));
753 }
754
755 void VisitIntegerLiteral(const IntegerLiteral *S) {
756 Env.setValue(*S, Env.getIntLiteralValue(S->getValue()));
757 }
758
759 void VisitParenExpr(const ParenExpr *S) {
760 // The CFG does not contain `ParenExpr` as top-level statements in basic
761 // blocks, however manual traversal to sub-expressions may encounter them.
762 // Redirect to the sub-expression.
763 auto *SubExpr = S->getSubExpr();
764 assert(SubExpr != nullptr);
765 Visit(SubExpr);
766 }
767
768 void VisitExprWithCleanups(const ExprWithCleanups *S) {
769 // The CFG does not contain `ExprWithCleanups` as top-level statements in
770 // basic blocks, however manual traversal to sub-expressions may encounter
771 // them. Redirect to the sub-expression.
772 auto *SubExpr = S->getSubExpr();
773 assert(SubExpr != nullptr);
774 Visit(SubExpr);
775 }
776
777private:
778 /// Returns the value for the sub-expression `SubExpr` of a logic operator.
779 BoolValue &getLogicOperatorSubExprValue(const Expr &SubExpr) {
780 // `SubExpr` and its parent logic operator might be part of different basic
781 // blocks. We try to access the value that is assigned to `SubExpr` in the
782 // corresponding environment.
783 if (const Environment *SubExprEnv = StmtToEnv.getEnvironment(SubExpr))
784 if (auto *Val =
785 dyn_cast_or_null<BoolValue>(SubExprEnv->getValue(SubExpr)))
786 return *Val;
787
788 // The sub-expression may lie within a basic block that isn't reachable,
789 // even if we need it to evaluate the current (reachable) expression
790 // (see https://discourse.llvm.org/t/70775). In this case, visit `SubExpr`
791 // within the current environment and then try to get the value that gets
792 // assigned to it.
793 if (Env.getValue(SubExpr) == nullptr)
794 Visit(&SubExpr);
795 if (auto *Val = dyn_cast_or_null<BoolValue>(Env.getValue(SubExpr)))
796 return *Val;
797
798 // If the value of `SubExpr` is still unknown, we create a fresh symbolic
799 // boolean value for it.
800 return Env.makeAtomicBoolValue();
801 }
802
803 // If context sensitivity is enabled, try to analyze the body of the callee
804 // `F` of `S`. The type `E` must be either `CallExpr` or `CXXConstructExpr`.
805 template <typename E>
806 void transferInlineCall(const E *S, const FunctionDecl *F) {
807 const auto &Options = Env.getDataflowAnalysisContext().getOptions();
808 if (!(Options.ContextSensitiveOpts &&
809 Env.canDescend(Options.ContextSensitiveOpts->Depth, F)))
810 return;
811
812 const AdornedCFG *ACFG = Env.getDataflowAnalysisContext().getAdornedCFG(F);
813 if (!ACFG)
814 return;
815
816 // FIXME: We don't support context-sensitive analysis of recursion, so
817 // we should return early here if `F` is the same as the `FunctionDecl`
818 // holding `S` itself.
819
820 auto ExitBlock = ACFG->getCFG().getExit().getBlockID();
821
822 auto CalleeEnv = Env.pushCall(S);
823
824 // FIXME: Use the same analysis as the caller for the callee. Note,
825 // though, that doing so would require support for changing the analysis's
826 // ASTContext.
827 auto Analysis = NoopAnalysis(ACFG->getDecl().getASTContext(),
828 DataflowAnalysisOptions{Options});
829
830 auto BlockToOutputState =
831 dataflow::runDataflowAnalysis(*ACFG, Analysis, CalleeEnv);
832 assert(BlockToOutputState);
833 assert(ExitBlock < BlockToOutputState->size());
834
835 auto &ExitState = (*BlockToOutputState)[ExitBlock];
836 assert(ExitState);
837
838 Env.popCall(S, ExitState->Env);
839 }
840
841 const StmtToEnvMap &StmtToEnv;
842 Environment &Env;
843 Environment::ValueModel &Model;
844};
845
846} // namespace
847
848void transfer(const StmtToEnvMap &StmtToEnv, const Stmt &S, Environment &Env,
850 TransferVisitor(StmtToEnv, Env, Model).Visit(&S);
851}
852
853} // namespace dataflow
854} // namespace clang
#define V(N, I)
Definition: ASTContext.h:3284
MatchType Type
Defines enum values for all the target-independent builtin functions.
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the clang::Expr interface and subclasses for C++ expressions.
const Environment & Env
Definition: HTMLLogger.cpp:148
Defines an enumeration for C++ overloaded operators.
TypeErasedDataflowAnalysis & Analysis
The analysis to be run.
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
Definition: StmtVisitor.h:195
This represents one expression.
Definition: Expr.h:110
bool isGLValue() const
Definition: Expr.h:280
QualType getType() const
Definition: Expr.h:142
Stmt - This represents one statement.
Definition: Stmt.h:84
bool isRecordType() const
Definition: Type.h:7706
bool isBlockReachable(const CFGBlock &B) const
Returns whether B is reachable from the entry block.
Definition: AdornedCFG.h:57
const llvm::DenseMap< const Stmt *, const CFGBlock * > & getStmtToBlock() const
Returns a mapping from statements to basic blocks that contain them.
Definition: AdornedCFG.h:52
BoolValue & makeBoolValue(const Formula &)
Creates a BoolValue wrapping a particular formula.
Definition: Arena.cpp:112
Models a boolean.
Definition: Value.h:94
Supplements Environment with non-standard comparison and join operations.
Holds the state of the program (store and heap) at a given program point.
BoolValue & makeIff(BoolValue &LHS, BoolValue &RHS) const
Returns a boolean value represents LHS <=> RHS.
StorageLocation * getStorageLocation(const ValueDecl &D) const
Returns the storage location assigned to D in the environment, or null if D isn't assigned a storage ...
BoolValue & makeAtomicBoolValue() const
Returns an atomic boolean value.
Value * getValue(const StorageLocation &Loc) const
Returns the value assigned to Loc in the environment or null if Loc isn't assigned a value in the env...
BoolValue & getBoolLiteralValue(bool Value) const
Returns a symbolic boolean value that models a boolean literal equal to Value
DataflowAnalysisContext & getDataflowAnalysisContext() const
Returns the DataflowAnalysisContext used by the environment.
static Value * joinValues(QualType Ty, Value *Val1, const Environment &Env1, Value *Val2, const Environment &Env2, Environment &JoinedEnv, Environment::ValueModel &Model)
Returns a value that approximates both Val1 and Val2, or null if no such value can be produced.
void setStorageLocation(const ValueDecl &D, StorageLocation &Loc)
Assigns Loc as the storage location of D in the environment.
void setValue(const StorageLocation &Loc, Value &Val)
Assigns Val as the value of Loc in the environment.
Maps statements to the environments of basic blocks that contain them.
Definition: Transfer.h:26
const Environment * getEnvironment(const Stmt &S) const
Returns the environment of the basic block that contains S.
Definition: Transfer.cpp:42
Base class for all values computed by abstract interpretation.
Definition: Value.h:33
void transfer(const StmtToEnvMap &StmtToEnv, const Stmt &S, Environment &Env, Environment::ValueModel &Model)
Evaluates S and updates Env accordingly.
Definition: Transfer.cpp:848
static void propagateValueOrStorageLocation(const Expr &From, const Expr &To, Environment &Env)
Definition: Transfer.cpp:118
static void propagateStorageLocation(const Expr &From, const Expr &To, Environment &Env)
Definition: Transfer.cpp:109
llvm::Expected< std::vector< std::optional< DataflowAnalysisState< typename AnalysisT::Lattice > > > > runDataflowAnalysis(const AdornedCFG &ACFG, AnalysisT &Analysis, const Environment &InitEnv, std::function< void(const CFGElement &, const DataflowAnalysisState< typename AnalysisT::Lattice > &)> PostVisitCFG=nullptr, std::int32_t MaxBlockVisits=20 '000)
Performs dataflow analysis and returns a mapping from basic block IDs to dataflow analysis states tha...
static void propagateValue(const Expr &From, const Expr &To, Environment &Env)
Definition: Transfer.cpp:102
static Value * maybeUnpackLValueExpr(const Expr &E, Environment &Env)
Definition: Transfer.cpp:85
void copyRecord(RecordStorageLocation &Src, RecordStorageLocation &Dst, Environment &Env)
Copies a record (struct, class, or union) from Src to Dst.
Definition: RecordOps.cpp:51
static BoolValue & unpackValue(BoolValue &V, Environment &Env)
Definition: Transfer.cpp:74
static BoolValue & evaluateBooleanEquality(const Expr &LHS, const Expr &RHS, Environment &Env)
Definition: Transfer.cpp:59
const Expr & ignoreCFGOmittedNodes(const Expr &E)
Skip past nodes that the CFG does not emit.
Definition: ASTOps.cpp:34
RecordStorageLocation * getBaseObjectLocation(const MemberExpr &ME, const Environment &Env)
Returns the storage location for the base object of a MemberExpr, or null if none is defined in the e...
bool Ret(InterpState &S, CodePtr &PC, APValue &Result)
Definition: Interp.h:217
The JSON file list parser is used to communicate input to InstallAPI.
Environment Env
Model of the state of the program (store and heap).