Bug Summary

File:tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp
Warning:line 102, column 29
Access to field 'CurrentState' results in a dereference of a null pointer (loaded from variable 'Req')

Annotated Source Code

/build/llvm-toolchain-snapshot-6.0~svn318882/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp

1//===-- MPIBugReporter.cpp - bug reporter -----------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9///
10/// \file
11/// This file defines prefabricated reports which are emitted in
12/// case of MPI related bugs, detected by path-sensitive analysis.
13///
14//===----------------------------------------------------------------------===//
15
16#include "MPIBugReporter.h"
17#include "MPIChecker.h"
18#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
19
20namespace clang {
21namespace ento {
22namespace mpi {
23
24void MPIBugReporter::reportDoubleNonblocking(
25 const CallEvent &MPICallEvent, const ento::mpi::Request &Req,
26 const MemRegion *const RequestRegion,
27 const ExplodedNode *const ExplNode,
28 BugReporter &BReporter) const {
29
30 std::string ErrorText;
31 ErrorText = "Double nonblocking on request " +
32 RequestRegion->getDescriptiveName() + ". ";
33
34 auto Report = llvm::make_unique<BugReport>(*DoubleNonblockingBugType,
35 ErrorText, ExplNode);
36
37 Report->addRange(MPICallEvent.getSourceRange());
38 SourceRange Range = RequestRegion->sourceRange();
39
40 if (Range.isValid())
41 Report->addRange(Range);
42
43 Report->addVisitor(llvm::make_unique<RequestNodeVisitor>(
44 RequestRegion, "Request is previously used by nonblocking call here. "));
45 Report->markInteresting(RequestRegion);
46
47 BReporter.emitReport(std::move(Report));
48}
49
50void MPIBugReporter::reportMissingWait(
51 const ento::mpi::Request &Req, const MemRegion *const RequestRegion,
52 const ExplodedNode *const ExplNode,
53 BugReporter &BReporter) const {
54 std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() +
55 " has no matching wait. "};
56
57 auto Report =
58 llvm::make_unique<BugReport>(*MissingWaitBugType, ErrorText, ExplNode);
59
60 SourceRange Range = RequestRegion->sourceRange();
61 if (Range.isValid())
62 Report->addRange(Range);
63 Report->addVisitor(llvm::make_unique<RequestNodeVisitor>(
64 RequestRegion, "Request is previously used by nonblocking call here. "));
65 Report->markInteresting(RequestRegion);
66
67 BReporter.emitReport(std::move(Report));
68}
69
70void MPIBugReporter::reportUnmatchedWait(
71 const CallEvent &CE, const clang::ento::MemRegion *const RequestRegion,
72 const ExplodedNode *const ExplNode,
73 BugReporter &BReporter) const {
74 std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() +
75 " has no matching nonblocking call. "};
76
77 auto Report =
78 llvm::make_unique<BugReport>(*UnmatchedWaitBugType, ErrorText, ExplNode);
79
80 Report->addRange(CE.getSourceRange());
81 SourceRange Range = RequestRegion->sourceRange();
82 if (Range.isValid())
83 Report->addRange(Range);
84
85 BReporter.emitReport(std::move(Report));
86}
87
88std::shared_ptr<PathDiagnosticPiece>
89MPIBugReporter::RequestNodeVisitor::VisitNode(const ExplodedNode *N,
90 const ExplodedNode *PrevN,
91 BugReporterContext &BRC,
92 BugReport &BR) {
93
94 if (IsNodeFound)
1
Assuming the condition is false
2
Taking false branch
95 return nullptr;
96
97 const Request *const Req = N->getState()->get<RequestMap>(RequestRegion);
3
Calling 'ProgramState::get'
12
Returning from 'ProgramState::get'
13
'Req' initialized here
98 const Request *const PrevReq =
99 PrevN->getState()->get<RequestMap>(RequestRegion);
100
101 // Check if request was previously unused or in a different state.
102 if ((Req && !PrevReq) || (Req->CurrentState != PrevReq->CurrentState)) {
14
Assuming 'Req' is null
15
Access to field 'CurrentState' results in a dereference of a null pointer (loaded from variable 'Req')
103 IsNodeFound = true;
104
105 ProgramPoint P = PrevN->getLocation();
106 PathDiagnosticLocation L =
107 PathDiagnosticLocation::create(P, BRC.getSourceManager());
108
109 return std::make_shared<PathDiagnosticEventPiece>(L, ErrorText);
110 }
111
112 return nullptr;
113}
114
115} // end of namespace: mpi
116} // end of namespace: ento
117} // end of namespace: clang

/build/llvm-toolchain-snapshot-6.0~svn318882/tools/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h

1//== ProgramState.h - Path-sensitive "State" for tracking values -*- C++ -*--=//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file defines the state of the program along the analysisa path.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_PROGRAMSTATE_H
15#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_PROGRAMSTATE_H
16
17#include "clang/Basic/LLVM.h"
18#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h"
19#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h"
20#include "clang/StaticAnalyzer/Core/PathSensitive/Environment.h"
21#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
22#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
23#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
24#include "clang/StaticAnalyzer/Core/PathSensitive/TaintTag.h"
25#include "llvm/ADT/FoldingSet.h"
26#include "llvm/ADT/ImmutableMap.h"
27#include "llvm/Support/Allocator.h"
28#include <utility>
29
30namespace llvm {
31class APSInt;
32}
33
34namespace clang {
35class ASTContext;
36
37namespace ento {
38
39class CallEvent;
40class CallEventManager;
41
42typedef std::unique_ptr<ConstraintManager>(*ConstraintManagerCreator)(
43 ProgramStateManager &, SubEngine *);
44typedef std::unique_ptr<StoreManager>(*StoreManagerCreator)(
45 ProgramStateManager &);
46typedef llvm::ImmutableMap<const SubRegion*, TaintTagType> TaintedSubRegions;
47
48//===----------------------------------------------------------------------===//
49// ProgramStateTrait - Traits used by the Generic Data Map of a ProgramState.
50//===----------------------------------------------------------------------===//
51
52template <typename T> struct ProgramStatePartialTrait;
53
54template <typename T> struct ProgramStateTrait {
55 typedef typename T::data_type data_type;
56 static inline void *MakeVoidPtr(data_type D) { return (void*) D; }
57 static inline data_type MakeData(void *const* P) {
58 return P ? (data_type) *P : (data_type) 0;
59 }
60};
61
62/// \class ProgramState
63/// ProgramState - This class encapsulates:
64///
65/// 1. A mapping from expressions to values (Environment)
66/// 2. A mapping from locations to values (Store)
67/// 3. Constraints on symbolic values (GenericDataMap)
68///
69/// Together these represent the "abstract state" of a program.
70///
71/// ProgramState is intended to be used as a functional object; that is,
72/// once it is created and made "persistent" in a FoldingSet, its
73/// values will never change.
74class ProgramState : public llvm::FoldingSetNode {
75public:
76 typedef llvm::ImmutableSet<llvm::APSInt*> IntSetTy;
77 typedef llvm::ImmutableMap<void*, void*> GenericDataMap;
78
79private:
80 void operator=(const ProgramState& R) = delete;
81
82 friend class ProgramStateManager;
83 friend class ExplodedGraph;
84 friend class ExplodedNode;
85
86 ProgramStateManager *stateMgr;
87 Environment Env; // Maps a Stmt to its current SVal.
88 Store store; // Maps a location to its current value.
89 GenericDataMap GDM; // Custom data stored by a client of this class.
90 unsigned refCount;
91
92 /// makeWithStore - Return a ProgramState with the same values as the current
93 /// state with the exception of using the specified Store.
94 ProgramStateRef makeWithStore(const StoreRef &store) const;
95
96 void setStore(const StoreRef &storeRef);
97
98public:
99 /// This ctor is used when creating the first ProgramState object.
100 ProgramState(ProgramStateManager *mgr, const Environment& env,
101 StoreRef st, GenericDataMap gdm);
102
103 /// Copy ctor - We must explicitly define this or else the "Next" ptr
104 /// in FoldingSetNode will also get copied.
105 ProgramState(const ProgramState &RHS);
106
107 ~ProgramState();
108
109 /// Return the ProgramStateManager associated with this state.
110 ProgramStateManager &getStateManager() const {
111 return *stateMgr;
112 }
113
114 /// Return the ConstraintManager.
115 ConstraintManager &getConstraintManager() const;
116
117 /// getEnvironment - Return the environment associated with this state.
118 /// The environment is the mapping from expressions to values.
119 const Environment& getEnvironment() const { return Env; }
120
121 /// Return the store associated with this state. The store
122 /// is a mapping from locations to values.
123 Store getStore() const { return store; }
124
125
126 /// getGDM - Return the generic data map associated with this state.
127 GenericDataMap getGDM() const { return GDM; }
128
129 void setGDM(GenericDataMap gdm) { GDM = gdm; }
130
131 /// Profile - Profile the contents of a ProgramState object for use in a
132 /// FoldingSet. Two ProgramState objects are considered equal if they
133 /// have the same Environment, Store, and GenericDataMap.
134 static void Profile(llvm::FoldingSetNodeID& ID, const ProgramState *V) {
135 V->Env.Profile(ID);
136 ID.AddPointer(V->store);
137 V->GDM.Profile(ID);
138 }
139
140 /// Profile - Used to profile the contents of this object for inclusion
141 /// in a FoldingSet.
142 void Profile(llvm::FoldingSetNodeID& ID) const {
143 Profile(ID, this);
144 }
145
146 BasicValueFactory &getBasicVals() const;
147 SymbolManager &getSymbolManager() const;
148
149 //==---------------------------------------------------------------------==//
150 // Constraints on values.
151 //==---------------------------------------------------------------------==//
152 //
153 // Each ProgramState records constraints on symbolic values. These constraints
154 // are managed using the ConstraintManager associated with a ProgramStateManager.
155 // As constraints gradually accrue on symbolic values, added constraints
156 // may conflict and indicate that a state is infeasible (as no real values
157 // could satisfy all the constraints). This is the principal mechanism
158 // for modeling path-sensitivity in ExprEngine/ProgramState.
159 //
160 // Various "assume" methods form the interface for adding constraints to
161 // symbolic values. A call to 'assume' indicates an assumption being placed
162 // on one or symbolic values. 'assume' methods take the following inputs:
163 //
164 // (1) A ProgramState object representing the current state.
165 //
166 // (2) The assumed constraint (which is specific to a given "assume" method).
167 //
168 // (3) A binary value "Assumption" that indicates whether the constraint is
169 // assumed to be true or false.
170 //
171 // The output of "assume*" is a new ProgramState object with the added constraints.
172 // If no new state is feasible, NULL is returned.
173 //
174
175 /// Assumes that the value of \p cond is zero (if \p assumption is "false")
176 /// or non-zero (if \p assumption is "true").
177 ///
178 /// This returns a new state with the added constraint on \p cond.
179 /// If no new state is feasible, NULL is returned.
180 ProgramStateRef assume(DefinedOrUnknownSVal cond, bool assumption) const;
181
182 /// Assumes both "true" and "false" for \p cond, and returns both
183 /// corresponding states (respectively).
184 ///
185 /// This is more efficient than calling assume() twice. Note that one (but not
186 /// both) of the returned states may be NULL.
187 std::pair<ProgramStateRef, ProgramStateRef>
188 assume(DefinedOrUnknownSVal cond) const;
189
190 ProgramStateRef assumeInBound(DefinedOrUnknownSVal idx,
191 DefinedOrUnknownSVal upperBound,
192 bool assumption,
193 QualType IndexType = QualType()) const;
194
195 /// Assumes that the value of \p Val is bounded with [\p From; \p To]
196 /// (if \p assumption is "true") or it is fully out of this range
197 /// (if \p assumption is "false").
198 ///
199 /// This returns a new state with the added constraint on \p cond.
200 /// If no new state is feasible, NULL is returned.
201 ProgramStateRef assumeInclusiveRange(DefinedOrUnknownSVal Val,
202 const llvm::APSInt &From,
203 const llvm::APSInt &To,
204 bool assumption) const;
205
206 /// Assumes given range both "true" and "false" for \p Val, and returns both
207 /// corresponding states (respectively).
208 ///
209 /// This is more efficient than calling assume() twice. Note that one (but not
210 /// both) of the returned states may be NULL.
211 std::pair<ProgramStateRef, ProgramStateRef>
212 assumeInclusiveRange(DefinedOrUnknownSVal Val, const llvm::APSInt &From,
213 const llvm::APSInt &To) const;
214
215 /// \brief Check if the given SVal is constrained to zero or is a zero
216 /// constant.
217 ConditionTruthVal isNull(SVal V) const;
218
219 /// Utility method for getting regions.
220 const VarRegion* getRegion(const VarDecl *D, const LocationContext *LC) const;
221
222 //==---------------------------------------------------------------------==//
223 // Binding and retrieving values to/from the environment and symbolic store.
224 //==---------------------------------------------------------------------==//
225
226 /// Create a new state by binding the value 'V' to the statement 'S' in the
227 /// state's environment.
228 ProgramStateRef BindExpr(const Stmt *S, const LocationContext *LCtx,
229 SVal V, bool Invalidate = true) const;
230
231 ProgramStateRef bindLoc(Loc location,
232 SVal V,
233 const LocationContext *LCtx,
234 bool notifyChanges = true) const;
235
236 ProgramStateRef bindLoc(SVal location, SVal V, const LocationContext *LCtx) const;
237
238 ProgramStateRef bindDefault(SVal loc, SVal V, const LocationContext *LCtx) const;
239
240 ProgramStateRef killBinding(Loc LV) const;
241
242 /// \brief Returns the state with bindings for the given regions
243 /// cleared from the store.
244 ///
245 /// Optionally invalidates global regions as well.
246 ///
247 /// \param Regions the set of regions to be invalidated.
248 /// \param E the expression that caused the invalidation.
249 /// \param BlockCount The number of times the current basic block has been
250 // visited.
251 /// \param CausesPointerEscape the flag is set to true when
252 /// the invalidation entails escape of a symbol (representing a
253 /// pointer). For example, due to it being passed as an argument in a
254 /// call.
255 /// \param IS the set of invalidated symbols.
256 /// \param Call if non-null, the invalidated regions represent parameters to
257 /// the call and should be considered directly invalidated.
258 /// \param ITraits information about special handling for a particular
259 /// region/symbol.
260 ProgramStateRef
261 invalidateRegions(ArrayRef<const MemRegion *> Regions, const Expr *E,
262 unsigned BlockCount, const LocationContext *LCtx,
263 bool CausesPointerEscape, InvalidatedSymbols *IS = nullptr,
264 const CallEvent *Call = nullptr,
265 RegionAndSymbolInvalidationTraits *ITraits = nullptr) const;
266
267 ProgramStateRef
268 invalidateRegions(ArrayRef<SVal> Regions, const Expr *E,
269 unsigned BlockCount, const LocationContext *LCtx,
270 bool CausesPointerEscape, InvalidatedSymbols *IS = nullptr,
271 const CallEvent *Call = nullptr,
272 RegionAndSymbolInvalidationTraits *ITraits = nullptr) const;
273
274 /// enterStackFrame - Returns the state for entry to the given stack frame,
275 /// preserving the current state.
276 ProgramStateRef enterStackFrame(const CallEvent &Call,
277 const StackFrameContext *CalleeCtx) const;
278
279 /// Get the lvalue for a variable reference.
280 Loc getLValue(const VarDecl *D, const LocationContext *LC) const;
281
282 Loc getLValue(const CompoundLiteralExpr *literal,
283 const LocationContext *LC) const;
284
285 /// Get the lvalue for an ivar reference.
286 SVal getLValue(const ObjCIvarDecl *decl, SVal base) const;
287
288 /// Get the lvalue for a field reference.
289 SVal getLValue(const FieldDecl *decl, SVal Base) const;
290
291 /// Get the lvalue for an indirect field reference.
292 SVal getLValue(const IndirectFieldDecl *decl, SVal Base) const;
293
294 /// Get the lvalue for an array index.
295 SVal getLValue(QualType ElementType, SVal Idx, SVal Base) const;
296
297 /// Returns the SVal bound to the statement 'S' in the state's environment.
298 SVal getSVal(const Stmt *S, const LocationContext *LCtx) const;
299
300 SVal getSValAsScalarOrLoc(const Stmt *Ex, const LocationContext *LCtx) const;
301
302 /// \brief Return the value bound to the specified location.
303 /// Returns UnknownVal() if none found.
304 SVal getSVal(Loc LV, QualType T = QualType()) const;
305
306 /// Returns the "raw" SVal bound to LV before any value simplfication.
307 SVal getRawSVal(Loc LV, QualType T= QualType()) const;
308
309 /// \brief Return the value bound to the specified location.
310 /// Returns UnknownVal() if none found.
311 SVal getSVal(const MemRegion* R) const;
312
313 SVal getSValAsScalarOrLoc(const MemRegion *R) const;
314
315 /// \brief Visits the symbols reachable from the given SVal using the provided
316 /// SymbolVisitor.
317 ///
318 /// This is a convenience API. Consider using ScanReachableSymbols class
319 /// directly when making multiple scans on the same state with the same
320 /// visitor to avoid repeated initialization cost.
321 /// \sa ScanReachableSymbols
322 bool scanReachableSymbols(SVal val, SymbolVisitor& visitor) const;
323
324 /// \brief Visits the symbols reachable from the SVals in the given range
325 /// using the provided SymbolVisitor.
326 bool scanReachableSymbols(const SVal *I, const SVal *E,
327 SymbolVisitor &visitor) const;
328
329 /// \brief Visits the symbols reachable from the regions in the given
330 /// MemRegions range using the provided SymbolVisitor.
331 bool scanReachableSymbols(const MemRegion * const *I,
332 const MemRegion * const *E,
333 SymbolVisitor &visitor) const;
334
335 template <typename CB> CB scanReachableSymbols(SVal val) const;
336 template <typename CB> CB scanReachableSymbols(const SVal *beg,
337 const SVal *end) const;
338
339 template <typename CB> CB
340 scanReachableSymbols(const MemRegion * const *beg,
341 const MemRegion * const *end) const;
342
343 /// Create a new state in which the statement is marked as tainted.
344 ProgramStateRef addTaint(const Stmt *S, const LocationContext *LCtx,
345 TaintTagType Kind = TaintTagGeneric) const;
346
347 /// Create a new state in which the value is marked as tainted.
348 ProgramStateRef addTaint(SVal V, TaintTagType Kind = TaintTagGeneric) const;
349
350 /// Create a new state in which the symbol is marked as tainted.
351 ProgramStateRef addTaint(SymbolRef S,
352 TaintTagType Kind = TaintTagGeneric) const;
353
354 /// Create a new state in which the region symbol is marked as tainted.
355 ProgramStateRef addTaint(const MemRegion *R,
356 TaintTagType Kind = TaintTagGeneric) const;
357
358 /// Create a new state in a which a sub-region of a given symbol is tainted.
359 /// This might be necessary when referring to regions that can not have an
360 /// individual symbol, e.g. if they are represented by the default binding of
361 /// a LazyCompoundVal.
362 ProgramStateRef addPartialTaint(SymbolRef ParentSym,
363 const SubRegion *SubRegion,
364 TaintTagType Kind = TaintTagGeneric) const;
365
366 /// Check if the statement is tainted in the current state.
367 bool isTainted(const Stmt *S, const LocationContext *LCtx,
368 TaintTagType Kind = TaintTagGeneric) const;
369 bool isTainted(SVal V, TaintTagType Kind = TaintTagGeneric) const;
370 bool isTainted(SymbolRef Sym, TaintTagType Kind = TaintTagGeneric) const;
371 bool isTainted(const MemRegion *Reg, TaintTagType Kind=TaintTagGeneric) const;
372
373 //==---------------------------------------------------------------------==//
374 // Accessing the Generic Data Map (GDM).
375 //==---------------------------------------------------------------------==//
376
377 void *const* FindGDM(void *K) const;
378
379 template<typename T>
380 ProgramStateRef add(typename ProgramStateTrait<T>::key_type K) const;
381
382 template <typename T>
383 typename ProgramStateTrait<T>::data_type
384 get() const {
385 return ProgramStateTrait<T>::MakeData(FindGDM(ProgramStateTrait<T>::GDMIndex()));
386 }
387
388 template<typename T>
389 typename ProgramStateTrait<T>::lookup_type
390 get(typename ProgramStateTrait<T>::key_type key) const {
391 void *const* d = FindGDM(ProgramStateTrait<T>::GDMIndex());
4
Calling 'ProgramStateTrait::GDMIndex'
5
Returning from 'ProgramStateTrait::GDMIndex'
392 return ProgramStateTrait<T>::Lookup(ProgramStateTrait<T>::MakeData(d), key);
6
Calling 'ProgramStatePartialTrait::MakeData'
9
Returning from 'ProgramStatePartialTrait::MakeData'
10
Calling 'ProgramStatePartialTrait::Lookup'
11
Returning from 'ProgramStatePartialTrait::Lookup'
393 }
394
395 template <typename T>
396 typename ProgramStateTrait<T>::context_type get_context() const;
397
398
399 template<typename T>
400 ProgramStateRef remove(typename ProgramStateTrait<T>::key_type K) const;
401
402 template<typename T>
403 ProgramStateRef remove(typename ProgramStateTrait<T>::key_type K,
404 typename ProgramStateTrait<T>::context_type C) const;
405 template <typename T>
406 ProgramStateRef remove() const;
407
408 template<typename T>
409 ProgramStateRef set(typename ProgramStateTrait<T>::data_type D) const;
410
411 template<typename T>
412 ProgramStateRef set(typename ProgramStateTrait<T>::key_type K,
413 typename ProgramStateTrait<T>::value_type E) const;
414
415 template<typename T>
416 ProgramStateRef set(typename ProgramStateTrait<T>::key_type K,
417 typename ProgramStateTrait<T>::value_type E,
418 typename ProgramStateTrait<T>::context_type C) const;
419
420 template<typename T>
421 bool contains(typename ProgramStateTrait<T>::key_type key) const {
422 void *const* d = FindGDM(ProgramStateTrait<T>::GDMIndex());
423 return ProgramStateTrait<T>::Contains(ProgramStateTrait<T>::MakeData(d), key);
424 }
425
426 // Pretty-printing.
427 void print(raw_ostream &Out, const char *nl = "\n",
428 const char *sep = "") const;
429 void printDOT(raw_ostream &Out) const;
430 void printTaint(raw_ostream &Out, const char *nl = "\n",
431 const char *sep = "") const;
432
433 void dump() const;
434 void dumpTaint() const;
435
436private:
437 friend void ProgramStateRetain(const ProgramState *state);
438 friend void ProgramStateRelease(const ProgramState *state);
439
440 /// \sa invalidateValues()
441 /// \sa invalidateRegions()
442 ProgramStateRef
443 invalidateRegionsImpl(ArrayRef<SVal> Values,
444 const Expr *E, unsigned BlockCount,
445 const LocationContext *LCtx,
446 bool ResultsInSymbolEscape,
447 InvalidatedSymbols *IS,
448 RegionAndSymbolInvalidationTraits *HTraits,
449 const CallEvent *Call) const;
450};
451
452//===----------------------------------------------------------------------===//
453// ProgramStateManager - Factory object for ProgramStates.
454//===----------------------------------------------------------------------===//
455
456class ProgramStateManager {
457 friend class ProgramState;
458 friend void ProgramStateRelease(const ProgramState *state);
459private:
460 /// Eng - The SubEngine that owns this state manager.
461 SubEngine *Eng; /* Can be null. */
462
463 EnvironmentManager EnvMgr;
464 std::unique_ptr<StoreManager> StoreMgr;
465 std::unique_ptr<ConstraintManager> ConstraintMgr;
466
467 ProgramState::GenericDataMap::Factory GDMFactory;
468 TaintedSubRegions::Factory TSRFactory;
469
470 typedef llvm::DenseMap<void*,std::pair<void*,void (*)(void*)> > GDMContextsTy;
471 GDMContextsTy GDMContexts;
472
473 /// StateSet - FoldingSet containing all the states created for analyzing
474 /// a particular function. This is used to unique states.
475 llvm::FoldingSet<ProgramState> StateSet;
476
477 /// Object that manages the data for all created SVals.
478 std::unique_ptr<SValBuilder> svalBuilder;
479
480 /// Manages memory for created CallEvents.
481 std::unique_ptr<CallEventManager> CallEventMgr;
482
483 /// A BumpPtrAllocator to allocate states.
484 llvm::BumpPtrAllocator &Alloc;
485
486 /// A vector of ProgramStates that we can reuse.
487 std::vector<ProgramState *> freeStates;
488
489public:
490 ProgramStateManager(ASTContext &Ctx,
491 StoreManagerCreator CreateStoreManager,
492 ConstraintManagerCreator CreateConstraintManager,
493 llvm::BumpPtrAllocator& alloc,
494 SubEngine *subeng);
495
496 ~ProgramStateManager();
497
498 ProgramStateRef getInitialState(const LocationContext *InitLoc);
499
500 ASTContext &getContext() { return svalBuilder->getContext(); }
501 const ASTContext &getContext() const { return svalBuilder->getContext(); }
502
503 BasicValueFactory &getBasicVals() {
504 return svalBuilder->getBasicValueFactory();
505 }
506
507 SValBuilder &getSValBuilder() {
508 return *svalBuilder;
509 }
510
511 SymbolManager &getSymbolManager() {
512 return svalBuilder->getSymbolManager();
513 }
514 const SymbolManager &getSymbolManager() const {
515 return svalBuilder->getSymbolManager();
516 }
517
518 llvm::BumpPtrAllocator& getAllocator() { return Alloc; }
519
520 MemRegionManager& getRegionManager() {
521 return svalBuilder->getRegionManager();
522 }
523 const MemRegionManager& getRegionManager() const {
524 return svalBuilder->getRegionManager();
525 }
526
527 CallEventManager &getCallEventManager() { return *CallEventMgr; }
528
529 StoreManager& getStoreManager() { return *StoreMgr; }
530 ConstraintManager& getConstraintManager() { return *ConstraintMgr; }
531 SubEngine* getOwningEngine() { return Eng; }
532
533 ProgramStateRef removeDeadBindings(ProgramStateRef St,
534 const StackFrameContext *LCtx,
535 SymbolReaper& SymReaper);
536
537public:
538
539 SVal ArrayToPointer(Loc Array, QualType ElementTy) {
540 return StoreMgr->ArrayToPointer(Array, ElementTy);
541 }
542
543 // Methods that manipulate the GDM.
544 ProgramStateRef addGDM(ProgramStateRef St, void *Key, void *Data);
545 ProgramStateRef removeGDM(ProgramStateRef state, void *Key);
546
547 // Methods that query & manipulate the Store.
548
549 void iterBindings(ProgramStateRef state, StoreManager::BindingsHandler& F) {
550 StoreMgr->iterBindings(state->getStore(), F);
551 }
552
553 ProgramStateRef getPersistentState(ProgramState &Impl);
554 ProgramStateRef getPersistentStateWithGDM(ProgramStateRef FromState,
555 ProgramStateRef GDMState);
556
557 bool haveEqualEnvironments(ProgramStateRef S1, ProgramStateRef S2) {
558 return S1->Env == S2->Env;
559 }
560
561 bool haveEqualStores(ProgramStateRef S1, ProgramStateRef S2) {
562 return S1->store == S2->store;
563 }
564
565 //==---------------------------------------------------------------------==//
566 // Generic Data Map methods.
567 //==---------------------------------------------------------------------==//
568 //
569 // ProgramStateManager and ProgramState support a "generic data map" that allows
570 // different clients of ProgramState objects to embed arbitrary data within a
571 // ProgramState object. The generic data map is essentially an immutable map
572 // from a "tag" (that acts as the "key" for a client) and opaque values.
573 // Tags/keys and values are simply void* values. The typical way that clients
574 // generate unique tags are by taking the address of a static variable.
575 // Clients are responsible for ensuring that data values referred to by a
576 // the data pointer are immutable (and thus are essentially purely functional
577 // data).
578 //
579 // The templated methods below use the ProgramStateTrait<T> class
580 // to resolve keys into the GDM and to return data values to clients.
581 //
582
583 // Trait based GDM dispatch.
584 template <typename T>
585 ProgramStateRef set(ProgramStateRef st, typename ProgramStateTrait<T>::data_type D) {
586 return addGDM(st, ProgramStateTrait<T>::GDMIndex(),
587 ProgramStateTrait<T>::MakeVoidPtr(D));
588 }
589
590 template<typename T>
591 ProgramStateRef set(ProgramStateRef st,
592 typename ProgramStateTrait<T>::key_type K,
593 typename ProgramStateTrait<T>::value_type V,
594 typename ProgramStateTrait<T>::context_type C) {
595
596 return addGDM(st, ProgramStateTrait<T>::GDMIndex(),
597 ProgramStateTrait<T>::MakeVoidPtr(ProgramStateTrait<T>::Set(st->get<T>(), K, V, C)));
598 }
599
600 template <typename T>
601 ProgramStateRef add(ProgramStateRef st,
602 typename ProgramStateTrait<T>::key_type K,
603 typename ProgramStateTrait<T>::context_type C) {
604 return addGDM(st, ProgramStateTrait<T>::GDMIndex(),
605 ProgramStateTrait<T>::MakeVoidPtr(ProgramStateTrait<T>::Add(st->get<T>(), K, C)));
606 }
607
608 template <typename T>
609 ProgramStateRef remove(ProgramStateRef st,
610 typename ProgramStateTrait<T>::key_type K,
611 typename ProgramStateTrait<T>::context_type C) {
612
613 return addGDM(st, ProgramStateTrait<T>::GDMIndex(),
614 ProgramStateTrait<T>::MakeVoidPtr(ProgramStateTrait<T>::Remove(st->get<T>(), K, C)));
615 }
616
617 template <typename T>
618 ProgramStateRef remove(ProgramStateRef st) {
619 return removeGDM(st, ProgramStateTrait<T>::GDMIndex());
620 }
621
622 void *FindGDMContext(void *index,
623 void *(*CreateContext)(llvm::BumpPtrAllocator&),
624 void (*DeleteContext)(void*));
625
626 template <typename T>
627 typename ProgramStateTrait<T>::context_type get_context() {
628 void *p = FindGDMContext(ProgramStateTrait<T>::GDMIndex(),
629 ProgramStateTrait<T>::CreateContext,
630 ProgramStateTrait<T>::DeleteContext);
631
632 return ProgramStateTrait<T>::MakeContext(p);
633 }
634
635 void EndPath(ProgramStateRef St) {
636 ConstraintMgr->EndPath(St);
637 }
638};
639
640
641//===----------------------------------------------------------------------===//
642// Out-of-line method definitions for ProgramState.
643//===----------------------------------------------------------------------===//
644
645inline ConstraintManager &ProgramState::getConstraintManager() const {
646 return stateMgr->getConstraintManager();
647}
648
649inline const VarRegion* ProgramState::getRegion(const VarDecl *D,
650 const LocationContext *LC) const
651{
652 return getStateManager().getRegionManager().getVarRegion(D, LC);
653}
654
655inline ProgramStateRef ProgramState::assume(DefinedOrUnknownSVal Cond,
656 bool Assumption) const {
657 if (Cond.isUnknown())
658 return this;
659
660 return getStateManager().ConstraintMgr
661 ->assume(this, Cond.castAs<DefinedSVal>(), Assumption);
662}
663
664inline std::pair<ProgramStateRef , ProgramStateRef >
665ProgramState::assume(DefinedOrUnknownSVal Cond) const {
666 if (Cond.isUnknown())
667 return std::make_pair(this, this);
668
669 return getStateManager().ConstraintMgr
670 ->assumeDual(this, Cond.castAs<DefinedSVal>());
671}
672
673inline ProgramStateRef ProgramState::assumeInclusiveRange(
674 DefinedOrUnknownSVal Val, const llvm::APSInt &From, const llvm::APSInt &To,
675 bool Assumption) const {
676 if (Val.isUnknown())
677 return this;
678
679 assert(Val.getAs<NonLoc>() && "Only NonLocs are supported!")(static_cast <bool> (Val.getAs<NonLoc>() &&
"Only NonLocs are supported!") ? void (0) : __assert_fail ("Val.getAs<NonLoc>() && \"Only NonLocs are supported!\""
, "/build/llvm-toolchain-snapshot-6.0~svn318882/tools/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
, 679, __extension__ __PRETTY_FUNCTION__))
;
680
681 return getStateManager().ConstraintMgr->assumeInclusiveRange(
682 this, Val.castAs<NonLoc>(), From, To, Assumption);
683}
684
685inline std::pair<ProgramStateRef, ProgramStateRef>
686ProgramState::assumeInclusiveRange(DefinedOrUnknownSVal Val,
687 const llvm::APSInt &From,
688 const llvm::APSInt &To) const {
689 if (Val.isUnknown())
690 return std::make_pair(this, this);
691
692 assert(Val.getAs<NonLoc>() && "Only NonLocs are supported!")(static_cast <bool> (Val.getAs<NonLoc>() &&
"Only NonLocs are supported!") ? void (0) : __assert_fail ("Val.getAs<NonLoc>() && \"Only NonLocs are supported!\""
, "/build/llvm-toolchain-snapshot-6.0~svn318882/tools/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
, 692, __extension__ __PRETTY_FUNCTION__))
;
693
694 return getStateManager().ConstraintMgr->assumeInclusiveRangeDual(
695 this, Val.castAs<NonLoc>(), From, To);
696}
697
698inline ProgramStateRef ProgramState::bindLoc(SVal LV, SVal V, const LocationContext *LCtx) const {
699 if (Optional<Loc> L = LV.getAs<Loc>())
700 return bindLoc(*L, V, LCtx);
701 return this;
702}
703
704inline Loc ProgramState::getLValue(const VarDecl *VD,
705 const LocationContext *LC) const {
706 return getStateManager().StoreMgr->getLValueVar(VD, LC);
707}
708
709inline Loc ProgramState::getLValue(const CompoundLiteralExpr *literal,
710 const LocationContext *LC) const {
711 return getStateManager().StoreMgr->getLValueCompoundLiteral(literal, LC);
712}
713
714inline SVal ProgramState::getLValue(const ObjCIvarDecl *D, SVal Base) const {
715 return getStateManager().StoreMgr->getLValueIvar(D, Base);
716}
717
718inline SVal ProgramState::getLValue(const FieldDecl *D, SVal Base) const {
719 return getStateManager().StoreMgr->getLValueField(D, Base);
720}
721
722inline SVal ProgramState::getLValue(const IndirectFieldDecl *D,
723 SVal Base) const {
724 StoreManager &SM = *getStateManager().StoreMgr;
725 for (const auto *I : D->chain()) {
726 Base = SM.getLValueField(cast<FieldDecl>(I), Base);
727 }
728
729 return Base;
730}
731
732inline SVal ProgramState::getLValue(QualType ElementType, SVal Idx, SVal Base) const{
733 if (Optional<NonLoc> N = Idx.getAs<NonLoc>())
734 return getStateManager().StoreMgr->getLValueElement(ElementType, *N, Base);
735 return UnknownVal();
736}
737
738inline SVal ProgramState::getSVal(const Stmt *Ex,
739 const LocationContext *LCtx) const{
740 return Env.getSVal(EnvironmentEntry(Ex, LCtx),
741 *getStateManager().svalBuilder);
742}
743
744inline SVal
745ProgramState::getSValAsScalarOrLoc(const Stmt *S,
746 const LocationContext *LCtx) const {
747 if (const Expr *Ex = dyn_cast<Expr>(S)) {
748 QualType T = Ex->getType();
749 if (Ex->isGLValue() || Loc::isLocType(T) ||
750 T->isIntegralOrEnumerationType())
751 return getSVal(S, LCtx);
752 }
753
754 return UnknownVal();
755}
756
757inline SVal ProgramState::getRawSVal(Loc LV, QualType T) const {
758 return getStateManager().StoreMgr->getBinding(getStore(), LV, T);
759}
760
761inline SVal ProgramState::getSVal(const MemRegion* R) const {
762 return getStateManager().StoreMgr->getBinding(getStore(),
763 loc::MemRegionVal(R));
764}
765
766inline BasicValueFactory &ProgramState::getBasicVals() const {
767 return getStateManager().getBasicVals();
768}
769
770inline SymbolManager &ProgramState::getSymbolManager() const {
771 return getStateManager().getSymbolManager();
772}
773
774template<typename T>
775ProgramStateRef ProgramState::add(typename ProgramStateTrait<T>::key_type K) const {
776 return getStateManager().add<T>(this, K, get_context<T>());
777}
778
779template <typename T>
780typename ProgramStateTrait<T>::context_type ProgramState::get_context() const {
781 return getStateManager().get_context<T>();
782}
783
784template<typename T>
785ProgramStateRef ProgramState::remove(typename ProgramStateTrait<T>::key_type K) const {
786 return getStateManager().remove<T>(this, K, get_context<T>());
787}
788
789template<typename T>
790ProgramStateRef ProgramState::remove(typename ProgramStateTrait<T>::key_type K,
791 typename ProgramStateTrait<T>::context_type C) const {
792 return getStateManager().remove<T>(this, K, C);
793}
794
795template <typename T>
796ProgramStateRef ProgramState::remove() const {
797 return getStateManager().remove<T>(this);
798}
799
800template<typename T>
801ProgramStateRef ProgramState::set(typename ProgramStateTrait<T>::data_type D) const {
802 return getStateManager().set<T>(this, D);
803}
804
805template<typename T>
806ProgramStateRef ProgramState::set(typename ProgramStateTrait<T>::key_type K,
807 typename ProgramStateTrait<T>::value_type E) const {
808 return getStateManager().set<T>(this, K, E, get_context<T>());
809}
810
811template<typename T>
812ProgramStateRef ProgramState::set(typename ProgramStateTrait<T>::key_type K,
813 typename ProgramStateTrait<T>::value_type E,
814 typename ProgramStateTrait<T>::context_type C) const {
815 return getStateManager().set<T>(this, K, E, C);
816}
817
818template <typename CB>
819CB ProgramState::scanReachableSymbols(SVal val) const {
820 CB cb(this);
821 scanReachableSymbols(val, cb);
822 return cb;
823}
824
825template <typename CB>
826CB ProgramState::scanReachableSymbols(const SVal *beg, const SVal *end) const {
827 CB cb(this);
828 scanReachableSymbols(beg, end, cb);
829 return cb;
830}
831
832template <typename CB>
833CB ProgramState::scanReachableSymbols(const MemRegion * const *beg,
834 const MemRegion * const *end) const {
835 CB cb(this);
836 scanReachableSymbols(beg, end, cb);
837 return cb;
838}
839
840/// \class ScanReachableSymbols
841/// A utility class that visits the reachable symbols using a custom
842/// SymbolVisitor. Terminates recursive traversal when the visitor function
843/// returns false.
844class ScanReachableSymbols {
845 typedef llvm::DenseSet<const void*> VisitedItems;
846
847 VisitedItems visited;
848 ProgramStateRef state;
849 SymbolVisitor &visitor;
850public:
851 ScanReachableSymbols(ProgramStateRef st, SymbolVisitor &v)
852 : state(std::move(st)), visitor(v) {}
853
854 bool scan(nonloc::LazyCompoundVal val);
855 bool scan(nonloc::CompoundVal val);
856 bool scan(SVal val);
857 bool scan(const MemRegion *R);
858 bool scan(const SymExpr *sym);
859};
860
861} // end ento namespace
862
863} // end clang namespace
864
865#endif

/build/llvm-toolchain-snapshot-6.0~svn318882/tools/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h

1//ProgramStateTrait.h - Partial implementations of ProgramStateTrait -*- C++ -*-
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file defines partial implementations of template specializations of
11// the class ProgramStateTrait<>. ProgramStateTrait<> is used by ProgramState
12// to implement set/get methods for manipulating a ProgramState's
13// generic data map.
14//
15//===----------------------------------------------------------------------===//
16
17
18#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_PROGRAMSTATETRAIT_H
19#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_PROGRAMSTATETRAIT_H
20
21#include "llvm/Support/Allocator.h"
22#include "llvm/Support/DataTypes.h"
23
24namespace llvm {
25 template <typename K, typename D, typename I> class ImmutableMap;
26 template <typename K, typename I> class ImmutableSet;
27 template <typename T> class ImmutableList;
28 template <typename T> class ImmutableListImpl;
29}
30
31namespace clang {
32
33namespace ento {
34 template <typename T> struct ProgramStatePartialTrait;
35
36 /// Declares a program state trait for type \p Type called \p Name, and
37 /// introduce a typedef named \c NameTy.
38 /// The macro should not be used inside namespaces, or for traits that must
39 /// be accessible from more than one translation unit.
40 #define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)namespace { class Name {}; typedef Type NameTy; } namespace clang
{ namespace ento { template <> struct ProgramStateTrait
<Name> : public ProgramStatePartialTrait<NameTy> {
static void *GDMIndex() { static int Index; return &Index
; } }; } }
\
41 namespace { \
42 class Name {}; \
43 typedef Type Name ## Ty; \
44 } \
45 namespace clang { \
46 namespace ento { \
47 template <> \
48 struct ProgramStateTrait<Name> \
49 : public ProgramStatePartialTrait<Name ## Ty> { \
50 static void *GDMIndex() { static int Index; return &Index; } \
51 }; \
52 } \
53 }
54
55
56 // Partial-specialization for ImmutableMap.
57
58 template <typename Key, typename Data, typename Info>
59 struct ProgramStatePartialTrait< llvm::ImmutableMap<Key,Data,Info> > {
60 typedef llvm::ImmutableMap<Key,Data,Info> data_type;
61 typedef typename data_type::Factory& context_type;
62 typedef Key key_type;
63 typedef Data value_type;
64 typedef const value_type* lookup_type;
65
66 static inline data_type MakeData(void *const* p) {
67 return p ? data_type((typename data_type::TreeTy*) *p)
7
Assuming 'p' is null
8
'?' condition is false
68 : data_type(nullptr);
69 }
70 static inline void *MakeVoidPtr(data_type B) {
71 return B.getRoot();
72 }
73 static lookup_type Lookup(data_type B, key_type K) {
74 return B.lookup(K);
75 }
76 static data_type Set(data_type B, key_type K, value_type E,context_type F){
77 return F.add(B, K, E);
78 }
79
80 static data_type Remove(data_type B, key_type K, context_type F) {
81 return F.remove(B, K);
82 }
83
84 static bool Contains(data_type B, key_type K) {
85 return B.contains(K);
86 }
87
88 static inline context_type MakeContext(void *p) {
89 return *((typename data_type::Factory*) p);
90 }
91
92 static void *CreateContext(llvm::BumpPtrAllocator& Alloc) {
93 return new typename data_type::Factory(Alloc);
94 }
95
96 static void DeleteContext(void *Ctx) {
97 delete (typename data_type::Factory*) Ctx;
98 }
99 };
100
101 /// Helper for registering a map trait.
102 ///
103 /// If the map type were written directly in the invocation of
104 /// REGISTER_TRAIT_WITH_PROGRAMSTATE, the comma in the template arguments
105 /// would be treated as a macro argument separator, which is wrong.
106 /// This allows the user to specify a map type in a way that the preprocessor
107 /// can deal with.
108 #define CLANG_ENTO_PROGRAMSTATE_MAP(Key, Value)llvm::ImmutableMap<Key, Value> llvm::ImmutableMap<Key, Value>
109
110
111 // Partial-specialization for ImmutableSet.
112
113 template <typename Key, typename Info>
114 struct ProgramStatePartialTrait< llvm::ImmutableSet<Key,Info> > {
115 typedef llvm::ImmutableSet<Key,Info> data_type;
116 typedef typename data_type::Factory& context_type;
117 typedef Key key_type;
118
119 static inline data_type MakeData(void *const* p) {
120 return p ? data_type((typename data_type::TreeTy*) *p)
121 : data_type(nullptr);
122 }
123
124 static inline void *MakeVoidPtr(data_type B) {
125 return B.getRoot();
126 }
127
128 static data_type Add(data_type B, key_type K, context_type F) {
129 return F.add(B, K);
130 }
131
132 static data_type Remove(data_type B, key_type K, context_type F) {
133 return F.remove(B, K);
134 }
135
136 static bool Contains(data_type B, key_type K) {
137 return B.contains(K);
138 }
139
140 static inline context_type MakeContext(void *p) {
141 return *((typename data_type::Factory*) p);
142 }
143
144 static void *CreateContext(llvm::BumpPtrAllocator& Alloc) {
145 return new typename data_type::Factory(Alloc);
146 }
147
148 static void DeleteContext(void *Ctx) {
149 delete (typename data_type::Factory*) Ctx;
150 }
151 };
152
153
154 // Partial-specialization for ImmutableList.
155
156 template <typename T>
157 struct ProgramStatePartialTrait< llvm::ImmutableList<T> > {
158 typedef llvm::ImmutableList<T> data_type;
159 typedef T key_type;
160 typedef typename data_type::Factory& context_type;
161
162 static data_type Add(data_type L, key_type K, context_type F) {
163 return F.add(K, L);
164 }
165
166 static bool Contains(data_type L, key_type K) {
167 return L.contains(K);
168 }
169
170 static inline data_type MakeData(void *const* p) {
171 return p ? data_type((const llvm::ImmutableListImpl<T>*) *p)
172 : data_type(nullptr);
173 }
174
175 static inline void *MakeVoidPtr(data_type D) {
176 return const_cast<llvm::ImmutableListImpl<T> *>(D.getInternalPointer());
177 }
178
179 static inline context_type MakeContext(void *p) {
180 return *((typename data_type::Factory*) p);
181 }
182
183 static void *CreateContext(llvm::BumpPtrAllocator& Alloc) {
184 return new typename data_type::Factory(Alloc);
185 }
186
187 static void DeleteContext(void *Ctx) {
188 delete (typename data_type::Factory*) Ctx;
189 }
190 };
191
192
193 // Partial specialization for bool.
194 template <> struct ProgramStatePartialTrait<bool> {
195 typedef bool data_type;
196
197 static inline data_type MakeData(void *const* p) {
198 return p ? (data_type) (uintptr_t) *p
199 : data_type();
200 }
201 static inline void *MakeVoidPtr(data_type d) {
202 return (void*) (uintptr_t) d;
203 }
204 };
205
206 // Partial specialization for unsigned.
207 template <> struct ProgramStatePartialTrait<unsigned> {
208 typedef unsigned data_type;
209
210 static inline data_type MakeData(void *const* p) {
211 return p ? (data_type) (uintptr_t) *p
212 : data_type();
213 }
214 static inline void *MakeVoidPtr(data_type d) {
215 return (void*) (uintptr_t) d;
216 }
217 };
218
219 // Partial specialization for void*.
220 template <> struct ProgramStatePartialTrait<void*> {
221 typedef void *data_type;
222
223 static inline data_type MakeData(void *const* p) {
224 return p ? *p
225 : data_type();
226 }
227 static inline void *MakeVoidPtr(data_type d) {
228 return d;
229 }
230 };
231
232 // Partial specialization for const void *.
233 template <> struct ProgramStatePartialTrait<const void *> {
234 typedef const void *data_type;
235
236 static inline data_type MakeData(void * const *p) {
237 return p ? *p : data_type();
238 }
239
240 static inline void *MakeVoidPtr(data_type d) {
241 return const_cast<void *>(d);
242 }
243 };
244
245} // end ento namespace
246
247} // end clang namespace
248
249#endif