1//==- ProgramPoint.h - Program Points for Path-Sensitive Analysis --*- 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 interface ProgramPoint, which identifies a
11//  distinct location in a function.
12//
13//===----------------------------------------------------------------------===//
14
15#ifndef LLVM_CLANG_ANALYSIS_PROGRAMPOINT_H
16#define LLVM_CLANG_ANALYSIS_PROGRAMPOINT_H
17
18#include "clang/Analysis/AnalysisDeclContext.h"
19#include "clang/Analysis/CFG.h"
20#include "llvm/ADT/DenseMap.h"
21#include "llvm/ADT/FoldingSet.h"
22#include "llvm/ADT/Optional.h"
23#include "llvm/ADT/PointerIntPair.h"
24#include "llvm/ADT/StringRef.h"
25#include "llvm/Support/Casting.h"
26#include "llvm/Support/DataTypes.h"
27#include <cassert>
28#include <string>
29#include <utility>
30
31namespace clang {
32
33class AnalysisDeclContext;
34class FunctionDecl;
35class LocationContext;
36
37/// ProgramPoints can be "tagged" as representing points specific to a given
38/// analysis entity.  Tags are abstract annotations, with an associated
39/// description and potentially other information.
40class ProgramPointTag {
41public:
42  ProgramPointTag(void *tagKind = nullptr) : TagKind(tagKind) {}
43  virtual ~ProgramPointTag();
44  virtual StringRef getTagDescription() const = 0;
45
46protected:
47  /// Used to implement 'isKind' in subclasses.
48  const void *getTagKind() { return TagKind; }
49
50private:
51  const void *TagKind;
52};
53
54class SimpleProgramPointTag : public ProgramPointTag {
55  std::string Desc;
56public:
57  SimpleProgramPointTag(StringRef MsgProvider, StringRef Msg);
58  StringRef getTagDescription() const override;
59};
60
61class ProgramPoint {
62public:
63  enum Kind { BlockEdgeKind,
64              BlockEntranceKind,
65              BlockExitKind,
66              PreStmtKind,
67              PreStmtPurgeDeadSymbolsKind,
68              PostStmtPurgeDeadSymbolsKind,
69              PostStmtKind,
70              PreLoadKind,
71              PostLoadKind,
72              PreStoreKind,
73              PostStoreKind,
74              PostConditionKind,
75              PostLValueKind,
76              MinPostStmtKind = PostStmtKind,
77              MaxPostStmtKind = PostLValueKind,
78              PostInitializerKind,
79              CallEnterKind,
80              CallExitBeginKind,
81              CallExitEndKind,
82              PreImplicitCallKind,
83              PostImplicitCallKind,
84              MinImplicitCallKind = PreImplicitCallKind,
85              MaxImplicitCallKind = PostImplicitCallKind,
86              LoopExitKind,
87              EpsilonKind};
88
89private:
90  const void *Data1;
91  llvm::PointerIntPair<const void *, 2, unsigned> Data2;
92
93  // The LocationContext could be NULL to allow ProgramPoint to be used in
94  // context insensitive analysis.
95  llvm::PointerIntPair<const LocationContext *, 2, unsigned> L;
96
97  llvm::PointerIntPair<const ProgramPointTag *, 2, unsigned> Tag;
98
99protected:
100  ProgramPoint() {}
101  ProgramPoint(const void *P,
102               Kind k,
103               const LocationContext *l,
104               const ProgramPointTag *tag = nullptr)
105    : Data1(P),
106      Data2(nullptr, (((unsigned) k) >> 0) & 0x3),
107      L(l, (((unsigned) k) >> 2) & 0x3),
108      Tag(tag, (((unsigned) k) >> 4) & 0x3) {
109        assert(getKind() == k);
110        assert(getLocationContext() == l);
111        assert(getData1() == P);
112      }
113
114  ProgramPoint(const void *P1,
115               const void *P2,
116               Kind k,
117               const LocationContext *l,
118               const ProgramPointTag *tag = nullptr)
119    : Data1(P1),
120      Data2(P2, (((unsigned) k) >> 0) & 0x3),
121      L(l, (((unsigned) k) >> 2) & 0x3),
122      Tag(tag, (((unsigned) k) >> 4) & 0x3) {}
123
124protected:
125  const void *getData1() const { return Data1; }
126  const void *getData2() const { return Data2.getPointer(); }
127  void setData2(const void *d) { Data2.setPointer(d); }
128
129public:
130  /// Create a new ProgramPoint object that is the same as the original
131  /// except for using the specified tag value.
132  ProgramPoint withTag(const ProgramPointTag *tag) const {
133    return ProgramPoint(getData1(), getData2(), getKind(),
134                        getLocationContext(), tag);
135  }
136
137  /// \brief Convert to the specified ProgramPoint type, asserting that this
138  /// ProgramPoint is of the desired type.
139  template<typename T>
140  T castAs() const {
141    assert(T::isKind(*this));
142    T t;
143    ProgramPoint& PP = t;
144    PP = *this;
145    return t;
146  }
147
148  /// \brief Convert to the specified ProgramPoint type, returning None if this
149  /// ProgramPoint is not of the desired type.
150  template<typename T>
151  Optional<T> getAs() const {
152    if (!T::isKind(*this))
153      return None;
154    T t;
155    ProgramPoint& PP = t;
156    PP = *this;
157    return t;
158  }
159
160  Kind getKind() const {
161    unsigned x = Tag.getInt();
162    x <<= 2;
163    x |= L.getInt();
164    x <<= 2;
165    x |= Data2.getInt();
166    return (Kind) x;
167  }
168
169  /// \brief Is this a program point corresponding to purge/removal of dead
170  /// symbols and bindings.
171  bool isPurgeKind() {
172    Kind K = getKind();
173    return (K == PostStmtPurgeDeadSymbolsKind ||
174            K == PreStmtPurgeDeadSymbolsKind);
175  }
176
177  const ProgramPointTag *getTag() const { return Tag.getPointer(); }
178
179  const LocationContext *getLocationContext() const {
180    return L.getPointer();
181  }
182
183  // For use with DenseMap.  This hash is probably slow.
184  unsigned getHashValue() const {
185    llvm::FoldingSetNodeID ID;
186    Profile(ID);
187    return ID.ComputeHash();
188  }
189
190  bool operator==(const ProgramPoint & RHS) const {
191    return Data1 == RHS.Data1 &&
192           Data2 == RHS.Data2 &&
193           L == RHS.L &&
194           Tag == RHS.Tag;
195  }
196
197  bool operator!=(const ProgramPoint &RHS) const {
198    return Data1 != RHS.Data1 ||
199           Data2 != RHS.Data2 ||
200           L != RHS.L ||
201           Tag != RHS.Tag;
202  }
203
204  void Profile(llvm::FoldingSetNodeID& ID) const {
205    ID.AddInteger((unsigned) getKind());
206    ID.AddPointer(getData1());
207    ID.AddPointer(getData2());
208    ID.AddPointer(getLocationContext());
209    ID.AddPointer(getTag());
210  }
211
212  static ProgramPoint getProgramPoint(const Stmt *S, ProgramPoint::Kind K,
213                                      const LocationContext *LC,
214                                      const ProgramPointTag *tag);
215};
216
217class BlockEntrance : public ProgramPoint {
218public:
219  BlockEntrance(const CFGBlock *B, const LocationContext *L,
220                const ProgramPointTag *tag = nullptr)
221    : ProgramPoint(B, BlockEntranceKind, L, tag) {
222    assert(B && "BlockEntrance requires non-null block");
223  }
224
225  const CFGBlock *getBlock() const {
226    return reinterpret_cast<const CFGBlock*>(getData1());
227  }
228
229  Optional<CFGElement> getFirstElement() const {
230    const CFGBlock *B = getBlock();
231    return B->empty() ? Optional<CFGElement>() : B->front();
232  }
233
234private:
235  friend class ProgramPoint;
236  BlockEntrance() {}
237  static bool isKind(const ProgramPoint &Location) {
238    return Location.getKind() == BlockEntranceKind;
239  }
240};
241
242class BlockExit : public ProgramPoint {
243public:
244  BlockExit(const CFGBlock *B, const LocationContext *L)
245    : ProgramPoint(B, BlockExitKind, L) {}
246
247  const CFGBlock *getBlock() const {
248    return reinterpret_cast<const CFGBlock*>(getData1());
249  }
250
251  const Stmt *getTerminator() const {
252    return getBlock()->getTerminator();
253  }
254
255private:
256  friend class ProgramPoint;
257  BlockExit() {}
258  static bool isKind(const ProgramPoint &Location) {
259    return Location.getKind() == BlockExitKind;
260  }
261};
262
263class StmtPoint : public ProgramPoint {
264public:
265  StmtPoint(const Stmt *S, const void *p2, Kind k, const LocationContext *L,
266            const ProgramPointTag *tag)
267    : ProgramPoint(S, p2, k, L, tag) {
268    assert(S);
269  }
270
271  const Stmt *getStmt() const { return (const Stmt*) getData1(); }
272
273  template <typename T>
274  const T* getStmtAs() const { return dyn_cast<T>(getStmt()); }
275
276protected:
277  StmtPoint() {}
278private:
279  friend class ProgramPoint;
280  static bool isKind(const ProgramPoint &Location) {
281    unsigned k = Location.getKind();
282    return k >= PreStmtKind && k <= MaxPostStmtKind;
283  }
284};
285
286
287class PreStmt : public StmtPoint {
288public:
289  PreStmt(const Stmt *S, const LocationContext *L, const ProgramPointTag *tag,
290          const Stmt *SubStmt = nullptr)
291    : StmtPoint(S, SubStmt, PreStmtKind, L, tag) {}
292
293  const Stmt *getSubStmt() const { return (const Stmt*) getData2(); }
294
295private:
296  friend class ProgramPoint;
297  PreStmt() {}
298  static bool isKind(const ProgramPoint &Location) {
299    return Location.getKind() == PreStmtKind;
300  }
301};
302
303class PostStmt : public StmtPoint {
304protected:
305  PostStmt() {}
306  PostStmt(const Stmt *S, const void *data, Kind k, const LocationContext *L,
307           const ProgramPointTag *tag = nullptr)
308    : StmtPoint(S, data, k, L, tag) {}
309
310public:
311  explicit PostStmt(const Stmt *S, Kind k, const LocationContext *L,
312                    const ProgramPointTag *tag = nullptr)
313    : StmtPoint(S, nullptr, k, L, tag) {}
314
315  explicit PostStmt(const Stmt *S, const LocationContext *L,
316                    const ProgramPointTag *tag = nullptr)
317    : StmtPoint(S, nullptr, PostStmtKind, L, tag) {}
318
319private:
320  friend class ProgramPoint;
321  static bool isKind(const ProgramPoint &Location) {
322    unsigned k = Location.getKind();
323    return k >= MinPostStmtKind && k <= MaxPostStmtKind;
324  }
325};
326
327// PostCondition represents the post program point of a branch condition.
328class PostCondition : public PostStmt {
329public:
330  PostCondition(const Stmt *S, const LocationContext *L,
331                const ProgramPointTag *tag = nullptr)
332    : PostStmt(S, PostConditionKind, L, tag) {}
333
334private:
335  friend class ProgramPoint;
336  PostCondition() {}
337  static bool isKind(const ProgramPoint &Location) {
338    return Location.getKind() == PostConditionKind;
339  }
340};
341
342class LocationCheck : public StmtPoint {
343protected:
344  LocationCheck() {}
345  LocationCheck(const Stmt *S, const LocationContext *L,
346                ProgramPoint::Kind K, const ProgramPointTag *tag)
347    : StmtPoint(S, nullptr, K, L, tag) {}
348
349private:
350  friend class ProgramPoint;
351  static bool isKind(const ProgramPoint &location) {
352    unsigned k = location.getKind();
353    return k == PreLoadKind || k == PreStoreKind;
354  }
355};
356
357class PreLoad : public LocationCheck {
358public:
359  PreLoad(const Stmt *S, const LocationContext *L,
360          const ProgramPointTag *tag = nullptr)
361    : LocationCheck(S, L, PreLoadKind, tag) {}
362
363private:
364  friend class ProgramPoint;
365  PreLoad() {}
366  static bool isKind(const ProgramPoint &location) {
367    return location.getKind() == PreLoadKind;
368  }
369};
370
371class PreStore : public LocationCheck {
372public:
373  PreStore(const Stmt *S, const LocationContext *L,
374           const ProgramPointTag *tag = nullptr)
375  : LocationCheck(S, L, PreStoreKind, tag) {}
376
377private:
378  friend class ProgramPoint;
379  PreStore() {}
380  static bool isKind(const ProgramPoint &location) {
381    return location.getKind() == PreStoreKind;
382  }
383};
384
385class PostLoad : public PostStmt {
386public:
387  PostLoad(const Stmt *S, const LocationContext *L,
388           const ProgramPointTag *tag = nullptr)
389    : PostStmt(S, PostLoadKind, L, tag) {}
390
391private:
392  friend class ProgramPoint;
393  PostLoad() {}
394  static bool isKind(const ProgramPoint &Location) {
395    return Location.getKind() == PostLoadKind;
396  }
397};
398
399/// \brief Represents a program point after a store evaluation.
400class PostStore : public PostStmt {
401public:
402  /// Construct the post store point.
403  /// \param Loc can be used to store the information about the location
404  /// used in the form it was uttered in the code.
405  PostStore(const Stmt *S, const LocationContext *L, const void *Loc,
406            const ProgramPointTag *tag = nullptr)
407    : PostStmt(S, PostStoreKind, L, tag) {
408    assert(getData2() == nullptr);
409    setData2(Loc);
410  }
411
412  /// \brief Returns the information about the location used in the store,
413  /// how it was uttered in the code.
414  const void *getLocationValue() const {
415    return getData2();
416  }
417
418private:
419  friend class ProgramPoint;
420  PostStore() {}
421  static bool isKind(const ProgramPoint &Location) {
422    return Location.getKind() == PostStoreKind;
423  }
424};
425
426class PostLValue : public PostStmt {
427public:
428  PostLValue(const Stmt *S, const LocationContext *L,
429             const ProgramPointTag *tag = nullptr)
430    : PostStmt(S, PostLValueKind, L, tag) {}
431
432private:
433  friend class ProgramPoint;
434  PostLValue() {}
435  static bool isKind(const ProgramPoint &Location) {
436    return Location.getKind() == PostLValueKind;
437  }
438};
439
440/// Represents a point after we ran remove dead bindings BEFORE
441/// processing the given statement.
442class PreStmtPurgeDeadSymbols : public StmtPoint {
443public:
444  PreStmtPurgeDeadSymbols(const Stmt *S, const LocationContext *L,
445                       const ProgramPointTag *tag = nullptr)
446    : StmtPoint(S, nullptr, PreStmtPurgeDeadSymbolsKind, L, tag) { }
447
448private:
449  friend class ProgramPoint;
450  PreStmtPurgeDeadSymbols() {}
451  static bool isKind(const ProgramPoint &Location) {
452    return Location.getKind() == PreStmtPurgeDeadSymbolsKind;
453  }
454};
455
456/// Represents a point after we ran remove dead bindings AFTER
457/// processing the  given statement.
458class PostStmtPurgeDeadSymbols : public StmtPoint {
459public:
460  PostStmtPurgeDeadSymbols(const Stmt *S, const LocationContext *L,
461                       const ProgramPointTag *tag = nullptr)
462    : StmtPoint(S, nullptr, PostStmtPurgeDeadSymbolsKind, L, tag) { }
463
464private:
465  friend class ProgramPoint;
466  PostStmtPurgeDeadSymbols() {}
467  static bool isKind(const ProgramPoint &Location) {
468    return Location.getKind() == PostStmtPurgeDeadSymbolsKind;
469  }
470};
471
472class BlockEdge : public ProgramPoint {
473public:
474  BlockEdge(const CFGBlock *B1, const CFGBlock *B2, const LocationContext *L)
475    : ProgramPoint(B1, B2, BlockEdgeKind, L) {
476    assert(B1 && "BlockEdge: source block must be non-null");
477    assert(B2 && "BlockEdge: destination block must be non-null");
478  }
479
480  const CFGBlock *getSrc() const {
481    return static_cast<const CFGBlock*>(getData1());
482  }
483
484  const CFGBlock *getDst() const {
485    return static_cast<const CFGBlock*>(getData2());
486  }
487
488private:
489  friend class ProgramPoint;
490  BlockEdge() {}
491  static bool isKind(const ProgramPoint &Location) {
492    return Location.getKind() == BlockEdgeKind;
493  }
494};
495
496class PostInitializer : public ProgramPoint {
497public:
498  /// \brief Construct a PostInitializer point that represents a location after
499  ///   CXXCtorInitializer expression evaluation.
500  ///
501  /// \param I The initializer.
502  /// \param Loc The location of the field being initialized.
503  PostInitializer(const CXXCtorInitializer *I,
504                  const void *Loc,
505                  const LocationContext *L)
506    : ProgramPoint(I, Loc, PostInitializerKind, L) {}
507
508  const CXXCtorInitializer *getInitializer() const {
509    return static_cast<const CXXCtorInitializer *>(getData1());
510  }
511
512  /// \brief Returns the location of the field.
513  const void *getLocationValue() const {
514    return getData2();
515  }
516
517private:
518  friend class ProgramPoint;
519  PostInitializer() {}
520  static bool isKind(const ProgramPoint &Location) {
521    return Location.getKind() == PostInitializerKind;
522  }
523};
524
525/// Represents an implicit call event.
526///
527/// The nearest statement is provided for diagnostic purposes.
528class ImplicitCallPoint : public ProgramPoint {
529public:
530  ImplicitCallPoint(const Decl *D, SourceLocation Loc, Kind K,
531                    const LocationContext *L, const ProgramPointTag *Tag)
532    : ProgramPoint(Loc.getPtrEncoding(), D, K, L, Tag) {}
533
534  const Decl *getDecl() const { return static_cast<const Decl *>(getData2()); }
535  SourceLocation getLocation() const {
536    return SourceLocation::getFromPtrEncoding(getData1());
537  }
538
539protected:
540  ImplicitCallPoint() {}
541private:
542  friend class ProgramPoint;
543  static bool isKind(const ProgramPoint &Location) {
544    return Location.getKind() >= MinImplicitCallKind &&
545           Location.getKind() <= MaxImplicitCallKind;
546  }
547};
548
549/// Represents a program point just before an implicit call event.
550///
551/// Explicit calls will appear as PreStmt program points.
552class PreImplicitCall : public ImplicitCallPoint {
553public:
554  PreImplicitCall(const Decl *D, SourceLocation Loc, const LocationContext *L,
555                  const ProgramPointTag *Tag = nullptr)
556    : ImplicitCallPoint(D, Loc, PreImplicitCallKind, L, Tag) {}
557
558private:
559  friend class ProgramPoint;
560  PreImplicitCall() {}
561  static bool isKind(const ProgramPoint &Location) {
562    return Location.getKind() == PreImplicitCallKind;
563  }
564};
565
566/// Represents a program point just after an implicit call event.
567///
568/// Explicit calls will appear as PostStmt program points.
569class PostImplicitCall : public ImplicitCallPoint {
570public:
571  PostImplicitCall(const Decl *D, SourceLocation Loc, const LocationContext *L,
572                   const ProgramPointTag *Tag = nullptr)
573    : ImplicitCallPoint(D, Loc, PostImplicitCallKind, L, Tag) {}
574
575private:
576  friend class ProgramPoint;
577  PostImplicitCall() {}
578  static bool isKind(const ProgramPoint &Location) {
579    return Location.getKind() == PostImplicitCallKind;
580  }
581};
582
583/// Represents a point when we begin processing an inlined call.
584/// CallEnter uses the caller's location context.
585class CallEnter : public ProgramPoint {
586public:
587  CallEnter(const Stmt *stmt, const StackFrameContext *calleeCtx,
588            const LocationContext *callerCtx)
589    : ProgramPoint(stmt, calleeCtx, CallEnterKind, callerCtx, nullptr) {}
590
591  const Stmt *getCallExpr() const {
592    return static_cast<const Stmt *>(getData1());
593  }
594
595  const StackFrameContext *getCalleeContext() const {
596    return static_cast<const StackFrameContext *>(getData2());
597  }
598
599  /// Returns the entry block in the CFG for the entered function.
600  const CFGBlock *getEntry() const {
601    const StackFrameContext *CalleeCtx = getCalleeContext();
602    const CFG *CalleeCFG = CalleeCtx->getCFG();
603    return &(CalleeCFG->getEntry());
604  }
605
606private:
607  friend class ProgramPoint;
608  CallEnter() {}
609  static bool isKind(const ProgramPoint &Location) {
610    return Location.getKind() == CallEnterKind;
611  }
612};
613
614/// Represents a point when we start the call exit sequence (for inlined call).
615///
616/// The call exit is simulated with a sequence of nodes, which occur between
617/// CallExitBegin and CallExitEnd. The following operations occur between the
618/// two program points:
619/// - CallExitBegin
620/// - Bind the return value
621/// - Run Remove dead bindings (to clean up the dead symbols from the callee).
622/// - CallExitEnd
623class CallExitBegin : public ProgramPoint {
624public:
625  // CallExitBegin uses the callee's location context.
626  CallExitBegin(const StackFrameContext *L, const ReturnStmt *RS)
627    : ProgramPoint(RS, CallExitBeginKind, L, nullptr) { }
628
629private:
630  friend class ProgramPoint;
631  CallExitBegin() {}
632  static bool isKind(const ProgramPoint &Location) {
633    return Location.getKind() == CallExitBeginKind;
634  }
635};
636
637/// Represents a point when we finish the call exit sequence (for inlined call).
638/// \sa CallExitBegin
639class CallExitEnd : public ProgramPoint {
640public:
641  // CallExitEnd uses the caller's location context.
642  CallExitEnd(const StackFrameContext *CalleeCtx,
643              const LocationContext *CallerCtx)
644    : ProgramPoint(CalleeCtx, CallExitEndKind, CallerCtx, nullptr) {}
645
646  const StackFrameContext *getCalleeContext() const {
647    return static_cast<const StackFrameContext *>(getData1());
648  }
649
650private:
651  friend class ProgramPoint;
652  CallExitEnd() {}
653  static bool isKind(const ProgramPoint &Location) {
654    return Location.getKind() == CallExitEndKind;
655  }
656};
657
658/// Represents a point when we exit a loop.
659/// When this ProgramPoint is encountered we can be sure that the symbolic
660/// execution of the corresponding LoopStmt is finished on the given path.
661/// Note: It is possible to encounter a LoopExit element when we haven't even
662/// encountered the loop itself. At the current state not all loop exits will
663/// result in a LoopExit program point.
664class LoopExit : public ProgramPoint {
665public:
666    LoopExit(const Stmt *LoopStmt, const LocationContext *LC)
667            : ProgramPoint(LoopStmt, nullptr, LoopExitKind, LC) {}
668
669    const Stmt *getLoopStmt() const {
670      return static_cast<const Stmt *>(getData1());
671    }
672
673private:
674    friend class ProgramPoint;
675    LoopExit() {}
676    static bool isKind(const ProgramPoint &Location) {
677      return Location.getKind() == LoopExitKind;
678    }
679};
680
681/// This is a meta program point, which should be skipped by all the diagnostic
682/// reasoning etc.
683class EpsilonPoint : public ProgramPoint {
684public:
685  EpsilonPoint(const LocationContext *L, const void *Data1,
686               const void *Data2 = nullptr,
687               const ProgramPointTag *tag = nullptr)
688    : ProgramPoint(Data1, Data2, EpsilonKind, L, tag) {}
689
690  const void *getData() const { return getData1(); }
691
692private:
693  friend class ProgramPoint;
694  EpsilonPoint() {}
695  static bool isKind(const ProgramPoint &Location) {
696    return Location.getKind() == EpsilonKind;
697  }
698};
699
700} // end namespace clang
701
702
703namespace llvm { // Traits specialization for DenseMap
704
705template <> struct DenseMapInfo<clang::ProgramPoint> {
706
707static inline clang::ProgramPoint getEmptyKey() {
708  uintptr_t x =
709   reinterpret_cast<uintptr_t>(DenseMapInfo<void*>::getEmptyKey()) & ~0x7;
710  return clang::BlockEntrance(reinterpret_cast<clang::CFGBlock*>(x), nullptr);
711}
712
713static inline clang::ProgramPoint getTombstoneKey() {
714  uintptr_t x =
715   reinterpret_cast<uintptr_t>(DenseMapInfo<void*>::getTombstoneKey()) & ~0x7;
716  return clang::BlockEntrance(reinterpret_cast<clang::CFGBlock*>(x), nullptr);
717}
718
719static unsigned getHashValue(const clang::ProgramPoint &Loc) {
720  return Loc.getHashValue();
721}
722
723static bool isEqual(const clang::ProgramPoint &L,
724                    const clang::ProgramPoint &R) {
725  return L == R;
726}
727
728};
729
730template <>
731struct isPodLike<clang::ProgramPoint> { static const bool value = true; };
732
733} // end namespace llvm
734
735#endif
736