blob: b799935cbecbd0b8f4614bd82033244c26b3c73b [file] [log] [blame]
//===--- Identifier.h - Uniqued Identifier ----------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the Identifier interface.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_AST_IDENTIFIER_H
#define SWIFT_AST_IDENTIFIER_H
#include "swift/Basic/EditorPlaceholder.h"
#include "swift/Basic/LLVM.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/Support/TrailingObjects.h"
namespace llvm {
class raw_ostream;
}
namespace swift {
class ASTContext;
class ParameterList;
/// DeclRefKind - The kind of reference to an identifier.
enum class DeclRefKind {
/// An ordinary reference to an identifier, e.g. 'foo'.
Ordinary,
/// A reference to an identifier as a binary operator, e.g. '+' in 'a+b'.
BinaryOperator,
/// A reference to an identifier as a postfix unary operator, e.g. '++' in
/// 'a++'.
PostfixOperator,
/// A reference to an identifier as a prefix unary operator, e.g. '--' in
/// '--a'.
PrefixOperator
};
/// Identifier - This is an instance of a uniqued identifier created by
/// ASTContext. It just wraps a nul-terminated "const char*".
class Identifier {
friend class ASTContext;
friend class DeclBaseName;
const char *Pointer;
/// Constructor, only accessible by ASTContext, which handles the uniquing.
explicit Identifier(const char *Ptr) : Pointer(Ptr) {}
public:
explicit Identifier() : Pointer(nullptr) {}
const char *get() const { return Pointer; }
StringRef str() const { return Pointer; }
unsigned getLength() const {
assert(Pointer != nullptr && "Tried getting length of empty identifier");
return ::strlen(Pointer);
}
bool empty() const { return Pointer == nullptr; }
bool is(StringRef string) const { return str().equals(string); }
/// isOperator - Return true if this identifier is an operator, false if it is
/// a normal identifier.
/// FIXME: We should maybe cache this.
bool isOperator() const {
if (empty())
return false;
if (isEditorPlaceholder())
return false;
if ((unsigned char)Pointer[0] < 0x80)
return isOperatorStartCodePoint((unsigned char)Pointer[0]);
// Handle the high unicode case out of line.
return isOperatorSlow();
}
/// isOperatorStartCodePoint - Return true if the specified code point is a
/// valid start of an operator.
static bool isOperatorStartCodePoint(uint32_t C) {
// ASCII operator chars.
static const char OpChars[] = "/=-+*%<>!&|^~.?";
if (C < 0x80)
return memchr(OpChars, C, sizeof(OpChars) - 1) != 0;
// Unicode math, symbol, arrow, dingbat, and line/box drawing chars.
return (C >= 0x00A1 && C <= 0x00A7)
|| C == 0x00A9 || C == 0x00AB || C == 0x00AC || C == 0x00AE
|| C == 0x00B0 || C == 0x00B1 || C == 0x00B6 || C == 0x00BB
|| C == 0x00BF || C == 0x00D7 || C == 0x00F7
|| C == 0x2016 || C == 0x2017 || (C >= 0x2020 && C <= 0x2027)
|| (C >= 0x2030 && C <= 0x203E) || (C >= 0x2041 && C <= 0x2053)
|| (C >= 0x2055 && C <= 0x205E) || (C >= 0x2190 && C <= 0x23FF)
|| (C >= 0x2500 && C <= 0x2775) || (C >= 0x2794 && C <= 0x2BFF)
|| (C >= 0x2E00 && C <= 0x2E7F) || (C >= 0x3001 && C <= 0x3003)
|| (C >= 0x3008 && C <= 0x3030);
}
/// isOperatorContinuationCodePoint - Return true if the specified code point
/// is a valid operator code point.
static bool isOperatorContinuationCodePoint(uint32_t C) {
if (isOperatorStartCodePoint(C))
return true;
// Unicode combining characters and variation selectors.
return (C >= 0x0300 && C <= 0x036F)
|| (C >= 0x1DC0 && C <= 0x1DFF)
|| (C >= 0x20D0 && C <= 0x20FF)
|| (C >= 0xFE00 && C <= 0xFE0F)
|| (C >= 0xFE20 && C <= 0xFE2F)
|| (C >= 0xE0100 && C <= 0xE01EF);
}
static bool isEditorPlaceholder(StringRef name) {
return swift::isEditorPlaceholder(name);
}
bool isEditorPlaceholder() const {
return !empty() && isEditorPlaceholder(str());
}
const void *getAsOpaquePointer() const {
return static_cast<const void *>(Pointer);
}
static Identifier getFromOpaquePointer(void *P) {
return Identifier((const char*)P);
}
/// Compare two identifiers, producing -1 if \c *this comes before \c other,
/// 1 if \c *this comes after \c other, and 0 if they are equal.
///
/// Null identifiers come after all other identifiers.
int compare(Identifier other) const;
bool operator==(Identifier RHS) const { return Pointer == RHS.Pointer; }
bool operator!=(Identifier RHS) const { return !(*this==RHS); }
bool operator<(Identifier RHS) const { return Pointer < RHS.Pointer; }
static Identifier getEmptyKey() {
return Identifier((const char*)
llvm::DenseMapInfo<const void*>::getEmptyKey());
}
static Identifier getTombstoneKey() {
return Identifier((const char*)
llvm::DenseMapInfo<const void*>::getTombstoneKey());
}
private:
bool isOperatorSlow() const;
};
class DeclName;
class ObjCSelector;
} // end namespace swift
namespace llvm {
raw_ostream &operator<<(raw_ostream &OS, swift::Identifier I);
raw_ostream &operator<<(raw_ostream &OS, swift::DeclName I);
raw_ostream &operator<<(raw_ostream &OS, swift::ObjCSelector S);
// Identifiers hash just like pointers.
template<> struct DenseMapInfo<swift::Identifier> {
static swift::Identifier getEmptyKey() {
return swift::Identifier::getEmptyKey();
}
static swift::Identifier getTombstoneKey() {
return swift::Identifier::getTombstoneKey();
}
static unsigned getHashValue(swift::Identifier Val) {
return DenseMapInfo<const void*>::getHashValue(Val.get());
}
static bool isEqual(swift::Identifier LHS, swift::Identifier RHS) {
return LHS == RHS;
}
};
// An Identifier is "pointer like".
template<typename T> struct PointerLikeTypeTraits;
template<>
struct PointerLikeTypeTraits<swift::Identifier> {
public:
static inline void *getAsVoidPointer(swift::Identifier I) {
return const_cast<void *>(I.getAsOpaquePointer());
}
static inline swift::Identifier getFromVoidPointer(void *P) {
return swift::Identifier::getFromOpaquePointer(P);
}
enum { NumLowBitsAvailable = 2 };
};
} // end namespace llvm
namespace swift {
/// Wrapper that may either be an Identifier or a special name
/// (e.g. for subscripts)
class DeclBaseName {
public:
enum class Kind: uint8_t {
Normal,
Subscript,
Constructor,
Destructor
};
private:
/// In a special DeclName represenenting a subscript, this opaque pointer
/// is used as the data of the base name identifier.
/// This is an implementation detail that should never leak outside of
/// DeclName.
static void *SubscriptIdentifierData;
/// As above, for special constructor DeclNames.
static void *ConstructorIdentifierData;
/// As above, for special destructor DeclNames.
static void *DestructorIdentifierData;
Identifier Ident;
public:
DeclBaseName() : DeclBaseName(Identifier()) {}
DeclBaseName(Identifier I) : Ident(I) {}
static DeclBaseName createSubscript() {
return DeclBaseName(Identifier((const char *)SubscriptIdentifierData));
}
static DeclBaseName createConstructor() {
return DeclBaseName(Identifier((const char *)ConstructorIdentifierData));
}
static DeclBaseName createDestructor() {
return DeclBaseName(Identifier((const char *)DestructorIdentifierData));
}
Kind getKind() const {
if (Ident.get() == SubscriptIdentifierData) {
return Kind::Subscript;
} else if (Ident.get() == ConstructorIdentifierData) {
return Kind::Constructor;
} else if (Ident.get() == DestructorIdentifierData) {
return Kind::Destructor;
} else {
return Kind::Normal;
}
}
bool isSpecial() const { return getKind() != Kind::Normal; }
/// Return the identifier backing the name. Assumes that the name is not
/// special.
Identifier getIdentifier() const {
assert(!isSpecial() && "Cannot retrieve identifier from special names");
return Ident;
}
bool empty() const { return !isSpecial() && getIdentifier().empty(); }
bool isOperator() const {
return !isSpecial() && getIdentifier().isOperator();
}
bool isEditorPlaceholder() const {
return !isSpecial() && getIdentifier().isEditorPlaceholder();
}
/// A representation of the name to be displayed to users. May be ambiguous
/// between identifiers and special names.
StringRef userFacingName() const {
if (empty())
return "_";
switch (getKind()) {
case Kind::Normal:
return getIdentifier().str();
case Kind::Subscript:
return "subscript";
case Kind::Constructor:
return "init";
case Kind::Destructor:
return "deinit";
}
llvm_unreachable("unhandled kind");
}
int compare(DeclBaseName other) const {
return userFacingName().compare(other.userFacingName());
}
bool operator==(StringRef Str) const {
return !isSpecial() && getIdentifier().is(Str);
}
bool operator!=(StringRef Str) const { return !(*this == Str); }
bool operator==(DeclBaseName RHS) const { return Ident == RHS.Ident; }
bool operator!=(DeclBaseName RHS) const { return !(*this == RHS); }
bool operator<(DeclBaseName RHS) const {
return Ident.get() < RHS.Ident.get();
}
const void *getAsOpaquePointer() const { return Ident.get(); }
static DeclBaseName getFromOpaquePointer(void *P) {
return Identifier::getFromOpaquePointer(P);
}
};
} // end namespace swift
namespace llvm {
raw_ostream &operator<<(raw_ostream &OS, swift::DeclBaseName D);
// DeclBaseNames hash just like pointers.
template<> struct DenseMapInfo<swift::DeclBaseName> {
static swift::DeclBaseName getEmptyKey() {
return swift::Identifier::getEmptyKey();
}
static swift::DeclBaseName getTombstoneKey() {
return swift::Identifier::getTombstoneKey();
}
static unsigned getHashValue(swift::DeclBaseName Val) {
return DenseMapInfo<const void *>::getHashValue(Val.getAsOpaquePointer());
}
static bool isEqual(swift::DeclBaseName LHS, swift::DeclBaseName RHS) {
return LHS == RHS;
}
};
// A DeclBaseName is "pointer like".
template <typename T> struct PointerLikeTypeTraits;
template <> struct PointerLikeTypeTraits<swift::DeclBaseName> {
public:
static inline void *getAsVoidPointer(swift::DeclBaseName D) {
return const_cast<void *>(D.getAsOpaquePointer());
}
static inline swift::DeclBaseName getFromVoidPointer(void *P) {
return swift::DeclBaseName::getFromOpaquePointer(P);
}
enum { NumLowBitsAvailable = PointerLikeTypeTraits<swift::Identifier>::NumLowBitsAvailable };
};
} // end namespace llvm
namespace swift {
/// A declaration name, which may comprise one or more identifier pieces.
class DeclName {
friend class ASTContext;
/// Represents a compound declaration name.
struct alignas(Identifier) CompoundDeclName final : llvm::FoldingSetNode,
private llvm::TrailingObjects<CompoundDeclName, Identifier> {
friend TrailingObjects;
friend class DeclName;
DeclBaseName BaseName;
size_t NumArgs;
explicit CompoundDeclName(DeclBaseName BaseName, size_t NumArgs)
: BaseName(BaseName), NumArgs(NumArgs) {
assert(NumArgs > 0 && "Should use IdentifierAndCompound");
}
ArrayRef<Identifier> getArgumentNames() const {
return {getTrailingObjects<Identifier>(), NumArgs};
}
MutableArrayRef<Identifier> getArgumentNames() {
return {getTrailingObjects<Identifier>(), NumArgs};
}
/// Uniquing for the ASTContext.
static void Profile(llvm::FoldingSetNodeID &id, DeclBaseName baseName,
ArrayRef<Identifier> argumentNames);
void Profile(llvm::FoldingSetNodeID &id) {
Profile(id, BaseName, getArgumentNames());
}
};
// A single stored identifier, along with a bit stating whether it is the
// base name for a zero-argument compound name.
typedef llvm::PointerIntPair<DeclBaseName, 1, bool> BaseNameAndCompound;
// Either a single identifier piece stored inline (with a bit to say whether
// it is simple or compound), or a reference to a compound declaration name.
llvm::PointerUnion<BaseNameAndCompound, CompoundDeclName *> SimpleOrCompound;
DeclName(void *Opaque)
: SimpleOrCompound(decltype(SimpleOrCompound)::getFromOpaqueValue(Opaque))
{}
void initialize(ASTContext &C, DeclBaseName baseName,
ArrayRef<Identifier> argumentNames);
public:
/// Build a null name.
DeclName() : SimpleOrCompound(BaseNameAndCompound()) {}
/// Build a simple value name with one component.
/*implicit*/ DeclName(DeclBaseName simpleName)
: SimpleOrCompound(BaseNameAndCompound(simpleName, false)) {}
/*implicit*/ DeclName(Identifier simpleName)
: DeclName(DeclBaseName(simpleName)) {}
/// Build a compound value name given a base name and a set of argument names.
DeclName(ASTContext &C, DeclBaseName baseName,
ArrayRef<Identifier> argumentNames) {
initialize(C, baseName, argumentNames);
}
/// Build a compound value name given a base name and a set of argument names
/// extracted from a parameter list.
DeclName(ASTContext &C, DeclBaseName baseName, ParameterList *paramList);
/// Retrieve the 'base' name, i.e., the name that follows the introducer,
/// such as the 'foo' in 'func foo(x:Int, y:Int)' or the 'bar' in
/// 'var bar: Int'.
DeclBaseName getBaseName() const {
if (auto compound = SimpleOrCompound.dyn_cast<CompoundDeclName*>())
return compound->BaseName;
return SimpleOrCompound.get<BaseNameAndCompound>().getPointer();
}
/// Assert that the base name is not special and return its identifier.
Identifier getBaseIdentifier() const {
auto baseName = getBaseName();
assert(!baseName.isSpecial() &&
"Can't retrieve the identifier of a special base name");
return baseName.getIdentifier();
}
/// Retrieve the names of the arguments, if there are any.
ArrayRef<Identifier> getArgumentNames() const {
if (auto compound = SimpleOrCompound.dyn_cast<CompoundDeclName*>())
return compound->getArgumentNames();
return { };
}
bool isSpecial() const { return getBaseName().isSpecial(); }
explicit operator bool() const {
if (SimpleOrCompound.dyn_cast<CompoundDeclName*>())
return true;
return !SimpleOrCompound.get<BaseNameAndCompound>().getPointer().empty();
}
/// True if this is a simple one-component name.
bool isSimpleName() const {
if (SimpleOrCompound.dyn_cast<CompoundDeclName*>())
return false;
return !SimpleOrCompound.get<BaseNameAndCompound>().getInt();
}
/// True if this is a compound name.
bool isCompoundName() const {
if (SimpleOrCompound.dyn_cast<CompoundDeclName*>())
return true;
return SimpleOrCompound.get<BaseNameAndCompound>().getInt();
}
/// True if this name is a simple one-component name identical to the
/// given identifier.
bool isSimpleName(DeclBaseName name) const {
return isSimpleName() && getBaseName() == name;
}
/// True if this name is a simple one-component name equal to the
/// given string.
bool isSimpleName(StringRef name) const {
return isSimpleName() && getBaseName() == name;
}
/// True if this name is a compound name equal to the given base name and
/// argument names.
bool isCompoundName(DeclBaseName base, ArrayRef<StringRef> args) const;
/// True if this name is a compound name equal to the given normal
/// base name and argument names.
bool isCompoundName(StringRef base, ArrayRef<StringRef> args) const;
/// True if this name is an operator.
bool isOperator() const {
return getBaseName().isOperator();
}
/// True if this name should be found by a decl ref or member ref under the
/// name specified by 'refName'.
///
/// We currently match compound names either when their first component
/// matches a simple name lookup or when the full compound name matches.
bool matchesRef(DeclName refName) const {
// Identical names always match.
if (SimpleOrCompound == refName.SimpleOrCompound)
return true;
// If the reference is a simple name, try simple name matching.
if (refName.isSimpleName())
return refName.getBaseName() == getBaseName();
// The names don't match.
return false;
}
/// Add a DeclName to a lookup table so that it can be found by its simple
/// name or its compound name.
template<typename LookupTable, typename Element>
void addToLookupTable(LookupTable &table, const Element &elt) {
table[*this].push_back(elt);
if (!isSimpleName()) {
table[getBaseName()].push_back(elt);
}
}
/// Compare two declaration names, producing -1 if \c *this comes before
/// \c other, 1 if \c *this comes after \c other, and 0 if they are equal.
///
/// Null declaration names come after all other declaration names.
int compare(DeclName other) const;
friend bool operator==(DeclName lhs, DeclName rhs) {
return lhs.getOpaqueValue() == rhs.getOpaqueValue();
}
friend bool operator!=(DeclName lhs, DeclName rhs) {
return !(lhs == rhs);
}
friend bool operator<(DeclName lhs, DeclName rhs) {
return lhs.compare(rhs) < 0;
}
friend bool operator<=(DeclName lhs, DeclName rhs) {
return lhs.compare(rhs) <= 0;
}
friend bool operator>(DeclName lhs, DeclName rhs) {
return lhs.compare(rhs) > 0;
}
friend bool operator>=(DeclName lhs, DeclName rhs) {
return lhs.compare(rhs) >= 0;
}
void *getOpaqueValue() const { return SimpleOrCompound.getOpaqueValue(); }
static DeclName getFromOpaqueValue(void *p) { return DeclName(p); }
/// Get a string representation of the name,
///
/// \param scratch Scratch space to use.
StringRef getString(llvm::SmallVectorImpl<char> &scratch,
bool skipEmptyArgumentNames = false) const;
/// Print the representation of this declaration name to the given
/// stream.
///
/// \param skipEmptyArgumentNames When true, don't print the argument labels
/// if they are all empty.
llvm::raw_ostream &print(llvm::raw_ostream &os,
bool skipEmptyArgumentNames = false) const;
/// Print a "pretty" representation of this declaration name to the given
/// stream.
///
/// This is the name used for diagnostics; it is not necessarily the
/// fully-specified name that would be written in the source.
llvm::raw_ostream &printPretty(llvm::raw_ostream &os) const;
/// Dump this name to standard error.
LLVM_ATTRIBUTE_DEPRECATED(void dump() const,
"only for use within the debugger");
};
enum class ObjCSelectorFamily : unsigned {
None,
#define OBJC_SELECTOR_FAMILY(LABEL, PREFIX) LABEL,
#include "swift/AST/ObjCSelectorFamily.def"
};
/// Represents an Objective-C selector.
class ObjCSelector {
/// The storage for an Objective-C selector.
///
/// A zero-argument selector is represented as simple name.
/// A selector with N arguments is represented as a compound name with
/// N arguments, where the simple name is a placeholder.
DeclName Storage;
explicit ObjCSelector(DeclName storage) : Storage(storage) { }
friend struct llvm::DenseMapInfo<ObjCSelector>;
public:
/// Form a selector with the given number of arguments and the given selector
/// pieces.
ObjCSelector(ASTContext &ctx, unsigned numArgs, ArrayRef<Identifier> pieces);
/// Construct an invalid ObjCSelector.
ObjCSelector() : Storage() {}
/// Convert to true if the decl name is valid.
explicit operator bool() const { return (bool)Storage; }
/// Determine the number of arguments in the selector.
///
/// When this is zero, the number of selector pieces will be one. Otherwise,
/// it equals the number of selector pieces.
unsigned getNumArgs() const {
if (Storage.isSimpleName()) {
return 0;
}
return Storage.getArgumentNames().size();
}
/// Determine the number of selector pieces in the selector.
///
/// When this is one, the number of arguments may either be zero or one.
/// Otherwise, it equals the number of arguments.
unsigned getNumSelectorPieces() const {
return getSelectorPieces().size();
}
/// Retrieve the pieces in this selector.
ArrayRef<Identifier> getSelectorPieces() const {
if (Storage.isSimpleName()) {
return { reinterpret_cast<const Identifier*>(&Storage), 1 };
}
return Storage.getArgumentNames();
}
/// Asserts that this is a nullary selector and returns the single identifier.
Identifier getSimpleName() const {
assert(Storage.isSimpleName() && "not a nullary selector");
return Storage.getBaseIdentifier();
}
/// Get a string representation of the selector.
///
/// \param scratch Scratch space to use.
StringRef getString(llvm::SmallVectorImpl<char> &scratch) const;
ObjCSelectorFamily getSelectorFamily() const;
void *getOpaqueValue() const { return Storage.getOpaqueValue(); }
static ObjCSelector getFromOpaqueValue(void *p) {
return ObjCSelector(DeclName::getFromOpaqueValue(p));
}
/// Dump this selector to standard error.
LLVM_ATTRIBUTE_DEPRECATED(void dump() const,
"only for use within the debugger");
/// Compare two Objective-C selectors, producing -1 if \c *this comes before
/// \c other, 1 if \c *this comes after \c other, and 0 if they are equal.
int compare(ObjCSelector other) const {
return Storage.compare(other.Storage);
}
friend bool operator==(ObjCSelector lhs, ObjCSelector rhs) {
return lhs.getOpaqueValue() == rhs.getOpaqueValue();
}
friend bool operator!=(ObjCSelector lhs, ObjCSelector rhs) {
return !(lhs == rhs);
}
friend bool operator<(ObjCSelector lhs, ObjCSelector rhs) {
return lhs.compare(rhs) < 0;
}
friend bool operator<=(ObjCSelector lhs, ObjCSelector rhs) {
return lhs.compare(rhs) <= 0;
}
friend bool operator>(ObjCSelector lhs, ObjCSelector rhs) {
return lhs.compare(rhs) > 0;
}
friend bool operator>=(ObjCSelector lhs, ObjCSelector rhs) {
return lhs.compare(rhs) >= 0;
}
};
} // end namespace swift
namespace llvm {
// A DeclName is "pointer like".
template<typename T> struct PointerLikeTypeTraits;
template<>
struct PointerLikeTypeTraits<swift::DeclName> {
public:
static inline void *getAsVoidPointer(swift::DeclName name) {
return name.getOpaqueValue();
}
static inline swift::DeclName getFromVoidPointer(void *ptr) {
return swift::DeclName::getFromOpaqueValue(ptr);
}
enum { NumLowBitsAvailable = 0 };
};
// DeclNames hash just like pointers.
template<> struct DenseMapInfo<swift::DeclName> {
static swift::DeclName getEmptyKey() {
return swift::Identifier::getEmptyKey();
}
static swift::DeclName getTombstoneKey() {
return swift::Identifier::getTombstoneKey();
}
static unsigned getHashValue(swift::DeclName Val) {
return DenseMapInfo<void*>::getHashValue(Val.getOpaqueValue());
}
static bool isEqual(swift::DeclName LHS, swift::DeclName RHS) {
return LHS.getOpaqueValue() == RHS.getOpaqueValue();
}
};
// An ObjCSelector is "pointer like".
template<typename T> struct PointerLikeTypeTraits;
template<>
struct PointerLikeTypeTraits<swift::ObjCSelector> {
public:
static inline void *getAsVoidPointer(swift::ObjCSelector name) {
return name.getOpaqueValue();
}
static inline swift::ObjCSelector getFromVoidPointer(void *ptr) {
return swift::ObjCSelector::getFromOpaqueValue(ptr);
}
enum { NumLowBitsAvailable = 0 };
};
// ObjCSelectors hash just like pointers.
template<> struct DenseMapInfo<swift::ObjCSelector> {
static swift::ObjCSelector getEmptyKey() {
return swift::ObjCSelector(DenseMapInfo<swift::DeclName>::getEmptyKey());
}
static swift::ObjCSelector getTombstoneKey() {
return swift::ObjCSelector(
DenseMapInfo<swift::DeclName>::getTombstoneKey());
}
static unsigned getHashValue(swift::ObjCSelector Val) {
return DenseMapInfo<void*>::getHashValue(Val.getOpaqueValue());
}
static bool isEqual(swift::ObjCSelector LHS, swift::ObjCSelector RHS) {
return LHS.getOpaqueValue() == RHS.getOpaqueValue();
}
};
} // end namespace llvm
#endif