xref: /freebsd/contrib/llvm-project/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp (revision 04eeddc0aa8e0a417a16eaf9d7d095207f4a8623)
1*04eeddc0SDimitry Andric //===-- DataflowEnvironment.cpp ---------------------------------*- C++ -*-===//
2*04eeddc0SDimitry Andric //
3*04eeddc0SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*04eeddc0SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*04eeddc0SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*04eeddc0SDimitry Andric //
7*04eeddc0SDimitry Andric //===----------------------------------------------------------------------===//
8*04eeddc0SDimitry Andric //
9*04eeddc0SDimitry Andric //  This file defines an Environment class that is used by dataflow analyses
10*04eeddc0SDimitry Andric //  that run over Control-Flow Graphs (CFGs) to keep track of the state of the
11*04eeddc0SDimitry Andric //  program at given program points.
12*04eeddc0SDimitry Andric //
13*04eeddc0SDimitry Andric //===----------------------------------------------------------------------===//
14*04eeddc0SDimitry Andric 
15*04eeddc0SDimitry Andric #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
16*04eeddc0SDimitry Andric #include "clang/AST/Decl.h"
17*04eeddc0SDimitry Andric #include "clang/AST/DeclCXX.h"
18*04eeddc0SDimitry Andric #include "clang/AST/Type.h"
19*04eeddc0SDimitry Andric #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
20*04eeddc0SDimitry Andric #include "clang/Analysis/FlowSensitive/StorageLocation.h"
21*04eeddc0SDimitry Andric #include "clang/Analysis/FlowSensitive/Value.h"
22*04eeddc0SDimitry Andric #include "llvm/ADT/DenseMap.h"
23*04eeddc0SDimitry Andric #include "llvm/ADT/DenseSet.h"
24*04eeddc0SDimitry Andric #include "llvm/Support/ErrorHandling.h"
25*04eeddc0SDimitry Andric #include <memory>
26*04eeddc0SDimitry Andric #include <utility>
27*04eeddc0SDimitry Andric 
28*04eeddc0SDimitry Andric namespace clang {
29*04eeddc0SDimitry Andric namespace dataflow {
30*04eeddc0SDimitry Andric 
31*04eeddc0SDimitry Andric /// Returns a map consisting of key-value entries that are present in both maps.
32*04eeddc0SDimitry Andric template <typename K, typename V>
33*04eeddc0SDimitry Andric llvm::DenseMap<K, V> intersectDenseMaps(const llvm::DenseMap<K, V> &Map1,
34*04eeddc0SDimitry Andric                                         const llvm::DenseMap<K, V> &Map2) {
35*04eeddc0SDimitry Andric   llvm::DenseMap<K, V> Result;
36*04eeddc0SDimitry Andric   for (auto &Entry : Map1) {
37*04eeddc0SDimitry Andric     auto It = Map2.find(Entry.first);
38*04eeddc0SDimitry Andric     if (It != Map2.end() && Entry.second == It->second)
39*04eeddc0SDimitry Andric       Result.insert({Entry.first, Entry.second});
40*04eeddc0SDimitry Andric   }
41*04eeddc0SDimitry Andric   return Result;
42*04eeddc0SDimitry Andric }
43*04eeddc0SDimitry Andric 
44*04eeddc0SDimitry Andric Environment::Environment(DataflowAnalysisContext &DACtx,
45*04eeddc0SDimitry Andric                          const DeclContext &DeclCtx)
46*04eeddc0SDimitry Andric     : Environment(DACtx) {
47*04eeddc0SDimitry Andric   if (const auto *FuncDecl = dyn_cast<FunctionDecl>(&DeclCtx)) {
48*04eeddc0SDimitry Andric     for (const auto *ParamDecl : FuncDecl->parameters()) {
49*04eeddc0SDimitry Andric       assert(ParamDecl != nullptr);
50*04eeddc0SDimitry Andric       auto &ParamLoc = createStorageLocation(*ParamDecl);
51*04eeddc0SDimitry Andric       setStorageLocation(*ParamDecl, ParamLoc);
52*04eeddc0SDimitry Andric       if (Value *ParamVal = createValue(ParamDecl->getType()))
53*04eeddc0SDimitry Andric         setValue(ParamLoc, *ParamVal);
54*04eeddc0SDimitry Andric     }
55*04eeddc0SDimitry Andric   }
56*04eeddc0SDimitry Andric 
57*04eeddc0SDimitry Andric   if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(&DeclCtx)) {
58*04eeddc0SDimitry Andric     if (!MethodDecl->isStatic()) {
59*04eeddc0SDimitry Andric       QualType ThisPointeeType = MethodDecl->getThisObjectType();
60*04eeddc0SDimitry Andric       // FIXME: Add support for union types.
61*04eeddc0SDimitry Andric       if (!ThisPointeeType->isUnionType()) {
62*04eeddc0SDimitry Andric         auto &ThisPointeeLoc = createStorageLocation(ThisPointeeType);
63*04eeddc0SDimitry Andric         DACtx.setThisPointeeStorageLocation(ThisPointeeLoc);
64*04eeddc0SDimitry Andric         if (Value *ThisPointeeVal = createValue(ThisPointeeType))
65*04eeddc0SDimitry Andric           setValue(ThisPointeeLoc, *ThisPointeeVal);
66*04eeddc0SDimitry Andric       }
67*04eeddc0SDimitry Andric     }
68*04eeddc0SDimitry Andric   }
69*04eeddc0SDimitry Andric }
70*04eeddc0SDimitry Andric 
71*04eeddc0SDimitry Andric bool Environment::operator==(const Environment &Other) const {
72*04eeddc0SDimitry Andric   assert(DACtx == Other.DACtx);
73*04eeddc0SDimitry Andric   return DeclToLoc == Other.DeclToLoc && LocToVal == Other.LocToVal;
74*04eeddc0SDimitry Andric }
75*04eeddc0SDimitry Andric 
76*04eeddc0SDimitry Andric LatticeJoinEffect Environment::join(const Environment &Other,
77*04eeddc0SDimitry Andric                                     Environment::Merger &Merger) {
78*04eeddc0SDimitry Andric   assert(DACtx == Other.DACtx);
79*04eeddc0SDimitry Andric 
80*04eeddc0SDimitry Andric   auto Effect = LatticeJoinEffect::Unchanged;
81*04eeddc0SDimitry Andric 
82*04eeddc0SDimitry Andric   const unsigned DeclToLocSizeBefore = DeclToLoc.size();
83*04eeddc0SDimitry Andric   DeclToLoc = intersectDenseMaps(DeclToLoc, Other.DeclToLoc);
84*04eeddc0SDimitry Andric   if (DeclToLocSizeBefore != DeclToLoc.size())
85*04eeddc0SDimitry Andric     Effect = LatticeJoinEffect::Changed;
86*04eeddc0SDimitry Andric 
87*04eeddc0SDimitry Andric   const unsigned ExprToLocSizeBefore = ExprToLoc.size();
88*04eeddc0SDimitry Andric   ExprToLoc = intersectDenseMaps(ExprToLoc, Other.ExprToLoc);
89*04eeddc0SDimitry Andric   if (ExprToLocSizeBefore != ExprToLoc.size())
90*04eeddc0SDimitry Andric     Effect = LatticeJoinEffect::Changed;
91*04eeddc0SDimitry Andric 
92*04eeddc0SDimitry Andric   llvm::DenseMap<const StorageLocation *, Value *> MergedLocToVal;
93*04eeddc0SDimitry Andric   for (auto &Entry : LocToVal) {
94*04eeddc0SDimitry Andric     const StorageLocation *Loc = Entry.first;
95*04eeddc0SDimitry Andric     assert(Loc != nullptr);
96*04eeddc0SDimitry Andric 
97*04eeddc0SDimitry Andric     Value *Val = Entry.second;
98*04eeddc0SDimitry Andric     assert(Val != nullptr);
99*04eeddc0SDimitry Andric 
100*04eeddc0SDimitry Andric     auto It = Other.LocToVal.find(Loc);
101*04eeddc0SDimitry Andric     if (It == Other.LocToVal.end())
102*04eeddc0SDimitry Andric       continue;
103*04eeddc0SDimitry Andric     assert(It->second != nullptr);
104*04eeddc0SDimitry Andric 
105*04eeddc0SDimitry Andric     if (It->second == Val) {
106*04eeddc0SDimitry Andric       MergedLocToVal.insert({Loc, Val});
107*04eeddc0SDimitry Andric       continue;
108*04eeddc0SDimitry Andric     }
109*04eeddc0SDimitry Andric 
110*04eeddc0SDimitry Andric     // FIXME: Consider destroying `MergedValue` immediately if `Merger::merge`
111*04eeddc0SDimitry Andric     // returns false to avoid storing unneeded values in `DACtx`.
112*04eeddc0SDimitry Andric     if (Value *MergedVal = createValue(Loc->getType()))
113*04eeddc0SDimitry Andric       if (Merger.merge(Loc->getType(), *Val, *It->second, *MergedVal, *this))
114*04eeddc0SDimitry Andric         MergedLocToVal.insert({Loc, MergedVal});
115*04eeddc0SDimitry Andric   }
116*04eeddc0SDimitry Andric   const unsigned LocToValSizeBefore = LocToVal.size();
117*04eeddc0SDimitry Andric   LocToVal = std::move(MergedLocToVal);
118*04eeddc0SDimitry Andric   if (LocToValSizeBefore != LocToVal.size())
119*04eeddc0SDimitry Andric     Effect = LatticeJoinEffect::Changed;
120*04eeddc0SDimitry Andric 
121*04eeddc0SDimitry Andric   return Effect;
122*04eeddc0SDimitry Andric }
123*04eeddc0SDimitry Andric 
124*04eeddc0SDimitry Andric StorageLocation &Environment::createStorageLocation(QualType Type) {
125*04eeddc0SDimitry Andric   assert(!Type.isNull());
126*04eeddc0SDimitry Andric   if (Type->isStructureOrClassType() || Type->isUnionType()) {
127*04eeddc0SDimitry Andric     // FIXME: Explore options to avoid eager initialization of fields as some of
128*04eeddc0SDimitry Andric     // them might not be needed for a particular analysis.
129*04eeddc0SDimitry Andric     llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs;
130*04eeddc0SDimitry Andric     for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) {
131*04eeddc0SDimitry Andric       FieldLocs.insert({Field, &createStorageLocation(Field->getType())});
132*04eeddc0SDimitry Andric     }
133*04eeddc0SDimitry Andric     return takeOwnership(
134*04eeddc0SDimitry Andric         std::make_unique<AggregateStorageLocation>(Type, std::move(FieldLocs)));
135*04eeddc0SDimitry Andric   }
136*04eeddc0SDimitry Andric   return takeOwnership(std::make_unique<ScalarStorageLocation>(Type));
137*04eeddc0SDimitry Andric }
138*04eeddc0SDimitry Andric 
139*04eeddc0SDimitry Andric StorageLocation &Environment::createStorageLocation(const VarDecl &D) {
140*04eeddc0SDimitry Andric   // Evaluated declarations are always assigned the same storage locations to
141*04eeddc0SDimitry Andric   // ensure that the environment stabilizes across loop iterations. Storage
142*04eeddc0SDimitry Andric   // locations for evaluated declarations are stored in the analysis context.
143*04eeddc0SDimitry Andric   if (auto *Loc = DACtx->getStorageLocation(D))
144*04eeddc0SDimitry Andric     return *Loc;
145*04eeddc0SDimitry Andric   auto &Loc = createStorageLocation(D.getType());
146*04eeddc0SDimitry Andric   DACtx->setStorageLocation(D, Loc);
147*04eeddc0SDimitry Andric   return Loc;
148*04eeddc0SDimitry Andric }
149*04eeddc0SDimitry Andric 
150*04eeddc0SDimitry Andric StorageLocation &Environment::createStorageLocation(const Expr &E) {
151*04eeddc0SDimitry Andric   // Evaluated expressions are always assigned the same storage locations to
152*04eeddc0SDimitry Andric   // ensure that the environment stabilizes across loop iterations. Storage
153*04eeddc0SDimitry Andric   // locations for evaluated expressions are stored in the analysis context.
154*04eeddc0SDimitry Andric   if (auto *Loc = DACtx->getStorageLocation(E))
155*04eeddc0SDimitry Andric     return *Loc;
156*04eeddc0SDimitry Andric   auto &Loc = createStorageLocation(E.getType());
157*04eeddc0SDimitry Andric   DACtx->setStorageLocation(E, Loc);
158*04eeddc0SDimitry Andric   return Loc;
159*04eeddc0SDimitry Andric }
160*04eeddc0SDimitry Andric 
161*04eeddc0SDimitry Andric void Environment::setStorageLocation(const ValueDecl &D, StorageLocation &Loc) {
162*04eeddc0SDimitry Andric   assert(DeclToLoc.find(&D) == DeclToLoc.end());
163*04eeddc0SDimitry Andric   DeclToLoc[&D] = &Loc;
164*04eeddc0SDimitry Andric }
165*04eeddc0SDimitry Andric 
166*04eeddc0SDimitry Andric StorageLocation *Environment::getStorageLocation(const ValueDecl &D,
167*04eeddc0SDimitry Andric                                                  SkipPast SP) const {
168*04eeddc0SDimitry Andric   auto It = DeclToLoc.find(&D);
169*04eeddc0SDimitry Andric   return It == DeclToLoc.end() ? nullptr : &skip(*It->second, SP);
170*04eeddc0SDimitry Andric }
171*04eeddc0SDimitry Andric 
172*04eeddc0SDimitry Andric void Environment::setStorageLocation(const Expr &E, StorageLocation &Loc) {
173*04eeddc0SDimitry Andric   assert(ExprToLoc.find(&E) == ExprToLoc.end());
174*04eeddc0SDimitry Andric   ExprToLoc[&E] = &Loc;
175*04eeddc0SDimitry Andric }
176*04eeddc0SDimitry Andric 
177*04eeddc0SDimitry Andric StorageLocation *Environment::getStorageLocation(const Expr &E,
178*04eeddc0SDimitry Andric                                                  SkipPast SP) const {
179*04eeddc0SDimitry Andric   auto It = ExprToLoc.find(&E);
180*04eeddc0SDimitry Andric   return It == ExprToLoc.end() ? nullptr : &skip(*It->second, SP);
181*04eeddc0SDimitry Andric }
182*04eeddc0SDimitry Andric 
183*04eeddc0SDimitry Andric StorageLocation *Environment::getThisPointeeStorageLocation() const {
184*04eeddc0SDimitry Andric   return DACtx->getThisPointeeStorageLocation();
185*04eeddc0SDimitry Andric }
186*04eeddc0SDimitry Andric 
187*04eeddc0SDimitry Andric void Environment::setValue(const StorageLocation &Loc, Value &Val) {
188*04eeddc0SDimitry Andric   LocToVal[&Loc] = &Val;
189*04eeddc0SDimitry Andric 
190*04eeddc0SDimitry Andric   if (auto *StructVal = dyn_cast<StructValue>(&Val)) {
191*04eeddc0SDimitry Andric     auto &AggregateLoc = *cast<AggregateStorageLocation>(&Loc);
192*04eeddc0SDimitry Andric 
193*04eeddc0SDimitry Andric     const QualType Type = AggregateLoc.getType();
194*04eeddc0SDimitry Andric     assert(Type->isStructureOrClassType());
195*04eeddc0SDimitry Andric 
196*04eeddc0SDimitry Andric     for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) {
197*04eeddc0SDimitry Andric       assert(Field != nullptr);
198*04eeddc0SDimitry Andric       setValue(AggregateLoc.getChild(*Field), StructVal->getChild(*Field));
199*04eeddc0SDimitry Andric     }
200*04eeddc0SDimitry Andric   }
201*04eeddc0SDimitry Andric }
202*04eeddc0SDimitry Andric 
203*04eeddc0SDimitry Andric Value *Environment::getValue(const StorageLocation &Loc) const {
204*04eeddc0SDimitry Andric   auto It = LocToVal.find(&Loc);
205*04eeddc0SDimitry Andric   return It == LocToVal.end() ? nullptr : It->second;
206*04eeddc0SDimitry Andric }
207*04eeddc0SDimitry Andric 
208*04eeddc0SDimitry Andric Value *Environment::getValue(const ValueDecl &D, SkipPast SP) const {
209*04eeddc0SDimitry Andric   auto *Loc = getStorageLocation(D, SP);
210*04eeddc0SDimitry Andric   if (Loc == nullptr)
211*04eeddc0SDimitry Andric     return nullptr;
212*04eeddc0SDimitry Andric   return getValue(*Loc);
213*04eeddc0SDimitry Andric }
214*04eeddc0SDimitry Andric 
215*04eeddc0SDimitry Andric Value *Environment::getValue(const Expr &E, SkipPast SP) const {
216*04eeddc0SDimitry Andric   auto *Loc = getStorageLocation(E, SP);
217*04eeddc0SDimitry Andric   if (Loc == nullptr)
218*04eeddc0SDimitry Andric     return nullptr;
219*04eeddc0SDimitry Andric   return getValue(*Loc);
220*04eeddc0SDimitry Andric }
221*04eeddc0SDimitry Andric 
222*04eeddc0SDimitry Andric Value *Environment::createValue(QualType Type) {
223*04eeddc0SDimitry Andric   llvm::DenseSet<QualType> Visited;
224*04eeddc0SDimitry Andric   return createValueUnlessSelfReferential(Type, Visited);
225*04eeddc0SDimitry Andric }
226*04eeddc0SDimitry Andric 
227*04eeddc0SDimitry Andric Value *Environment::createValueUnlessSelfReferential(
228*04eeddc0SDimitry Andric     QualType Type, llvm::DenseSet<QualType> &Visited) {
229*04eeddc0SDimitry Andric   assert(!Type.isNull());
230*04eeddc0SDimitry Andric 
231*04eeddc0SDimitry Andric   if (Type->isIntegerType()) {
232*04eeddc0SDimitry Andric     return &takeOwnership(std::make_unique<IntegerValue>());
233*04eeddc0SDimitry Andric   }
234*04eeddc0SDimitry Andric 
235*04eeddc0SDimitry Andric   if (Type->isReferenceType()) {
236*04eeddc0SDimitry Andric     QualType PointeeType = Type->getAs<ReferenceType>()->getPointeeType();
237*04eeddc0SDimitry Andric     auto &PointeeLoc = createStorageLocation(PointeeType);
238*04eeddc0SDimitry Andric 
239*04eeddc0SDimitry Andric     if (!Visited.contains(PointeeType.getCanonicalType())) {
240*04eeddc0SDimitry Andric       Visited.insert(PointeeType.getCanonicalType());
241*04eeddc0SDimitry Andric       Value *PointeeVal =
242*04eeddc0SDimitry Andric           createValueUnlessSelfReferential(PointeeType, Visited);
243*04eeddc0SDimitry Andric       Visited.erase(PointeeType.getCanonicalType());
244*04eeddc0SDimitry Andric 
245*04eeddc0SDimitry Andric       if (PointeeVal != nullptr)
246*04eeddc0SDimitry Andric         setValue(PointeeLoc, *PointeeVal);
247*04eeddc0SDimitry Andric     }
248*04eeddc0SDimitry Andric 
249*04eeddc0SDimitry Andric     return &takeOwnership(std::make_unique<ReferenceValue>(PointeeLoc));
250*04eeddc0SDimitry Andric   }
251*04eeddc0SDimitry Andric 
252*04eeddc0SDimitry Andric   if (Type->isPointerType()) {
253*04eeddc0SDimitry Andric     QualType PointeeType = Type->getAs<PointerType>()->getPointeeType();
254*04eeddc0SDimitry Andric     auto &PointeeLoc = createStorageLocation(PointeeType);
255*04eeddc0SDimitry Andric 
256*04eeddc0SDimitry Andric     if (!Visited.contains(PointeeType.getCanonicalType())) {
257*04eeddc0SDimitry Andric       Visited.insert(PointeeType.getCanonicalType());
258*04eeddc0SDimitry Andric       Value *PointeeVal =
259*04eeddc0SDimitry Andric           createValueUnlessSelfReferential(PointeeType, Visited);
260*04eeddc0SDimitry Andric       Visited.erase(PointeeType.getCanonicalType());
261*04eeddc0SDimitry Andric 
262*04eeddc0SDimitry Andric       if (PointeeVal != nullptr)
263*04eeddc0SDimitry Andric         setValue(PointeeLoc, *PointeeVal);
264*04eeddc0SDimitry Andric     }
265*04eeddc0SDimitry Andric 
266*04eeddc0SDimitry Andric     return &takeOwnership(std::make_unique<PointerValue>(PointeeLoc));
267*04eeddc0SDimitry Andric   }
268*04eeddc0SDimitry Andric 
269*04eeddc0SDimitry Andric   if (Type->isStructureOrClassType()) {
270*04eeddc0SDimitry Andric     // FIXME: Initialize only fields that are accessed in the context that is
271*04eeddc0SDimitry Andric     // being analyzed.
272*04eeddc0SDimitry Andric     llvm::DenseMap<const ValueDecl *, Value *> FieldValues;
273*04eeddc0SDimitry Andric     for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) {
274*04eeddc0SDimitry Andric       assert(Field != nullptr);
275*04eeddc0SDimitry Andric 
276*04eeddc0SDimitry Andric       QualType FieldType = Field->getType();
277*04eeddc0SDimitry Andric       if (Visited.contains(FieldType.getCanonicalType()))
278*04eeddc0SDimitry Andric         continue;
279*04eeddc0SDimitry Andric 
280*04eeddc0SDimitry Andric       Visited.insert(FieldType.getCanonicalType());
281*04eeddc0SDimitry Andric       FieldValues.insert(
282*04eeddc0SDimitry Andric           {Field, createValueUnlessSelfReferential(FieldType, Visited)});
283*04eeddc0SDimitry Andric       Visited.erase(FieldType.getCanonicalType());
284*04eeddc0SDimitry Andric     }
285*04eeddc0SDimitry Andric 
286*04eeddc0SDimitry Andric     return &takeOwnership(
287*04eeddc0SDimitry Andric         std::make_unique<StructValue>(std::move(FieldValues)));
288*04eeddc0SDimitry Andric   }
289*04eeddc0SDimitry Andric 
290*04eeddc0SDimitry Andric   return nullptr;
291*04eeddc0SDimitry Andric }
292*04eeddc0SDimitry Andric 
293*04eeddc0SDimitry Andric StorageLocation &Environment::skip(StorageLocation &Loc, SkipPast SP) const {
294*04eeddc0SDimitry Andric   switch (SP) {
295*04eeddc0SDimitry Andric   case SkipPast::None:
296*04eeddc0SDimitry Andric     return Loc;
297*04eeddc0SDimitry Andric   case SkipPast::Reference:
298*04eeddc0SDimitry Andric     // References cannot be chained so we only need to skip past one level of
299*04eeddc0SDimitry Andric     // indirection.
300*04eeddc0SDimitry Andric     if (auto *Val = dyn_cast_or_null<ReferenceValue>(getValue(Loc)))
301*04eeddc0SDimitry Andric       return Val->getPointeeLoc();
302*04eeddc0SDimitry Andric     return Loc;
303*04eeddc0SDimitry Andric   case SkipPast::ReferenceThenPointer:
304*04eeddc0SDimitry Andric     StorageLocation &LocPastRef = skip(Loc, SkipPast::Reference);
305*04eeddc0SDimitry Andric     if (auto *Val = dyn_cast_or_null<PointerValue>(getValue(LocPastRef)))
306*04eeddc0SDimitry Andric       return Val->getPointeeLoc();
307*04eeddc0SDimitry Andric     return LocPastRef;
308*04eeddc0SDimitry Andric   }
309*04eeddc0SDimitry Andric   llvm_unreachable("bad SkipPast kind");
310*04eeddc0SDimitry Andric }
311*04eeddc0SDimitry Andric 
312*04eeddc0SDimitry Andric const StorageLocation &Environment::skip(const StorageLocation &Loc,
313*04eeddc0SDimitry Andric                                          SkipPast SP) const {
314*04eeddc0SDimitry Andric   return skip(*const_cast<StorageLocation *>(&Loc), SP);
315*04eeddc0SDimitry Andric }
316*04eeddc0SDimitry Andric 
317*04eeddc0SDimitry Andric } // namespace dataflow
318*04eeddc0SDimitry Andric } // namespace clang
319