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