From 9d156a7b1b2771e191f2f5a45a7b7a694129463b Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 6 Jan 2011 01:58:22 +0000 Subject: [PATCH] Introduce an AttributedType, but don't actually use it anywhere yet. The initial TreeTransform is a cop-out, but it's more-or-less equivalent to what we were doing before, or rather what we're doing now and might eventually stop doing in favor of using this type. I am simultaneously intrigued by the possibilities of rebuilding a dependent Attri git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@122942 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 5 + include/clang/AST/RecursiveASTVisitor.h | 8 ++ include/clang/AST/Type.h | 95 +++++++++++++++++ include/clang/AST/TypeLoc.h | 124 ++++++++++++++++++++++ include/clang/AST/TypeNodes.def | 1 + include/clang/Serialization/ASTBitCodes.h | 4 +- lib/AST/ASTContext.cpp | 25 +++++ lib/AST/ASTImporter.cpp | 11 ++ lib/AST/TypeLoc.cpp | 1 - lib/AST/TypePrinter.cpp | 78 ++++++++++++++ lib/CodeGen/CGDebugInfo.cpp | 3 + lib/Sema/TreeTransform.h | 38 ++++++- lib/Serialization/ASTReader.cpp | 27 +++++ lib/Serialization/ASTWriter.cpp | 22 ++++ 14 files changed, 439 insertions(+), 3 deletions(-) diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index beba66947e..0abdbb05a5 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -109,6 +109,7 @@ class ASTContext { llvm::FoldingSet PackExpansionTypes; llvm::FoldingSet ObjCObjectTypes; llvm::FoldingSet ObjCObjectPointerTypes; + llvm::FoldingSet AttributedTypes; llvm::FoldingSet QualifiedTemplateNames; llvm::FoldingSet DependentTemplateNames; @@ -648,6 +649,10 @@ public: QualType getInjectedClassNameType(CXXRecordDecl *Decl, QualType TST); + QualType getAttributedType(AttributedType::Kind attrKind, + QualType modifiedType, + QualType equivalentType); + QualType getSubstTemplateTypeParmType(const TemplateTypeParmType *Replaced, QualType Replacement); diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index 7e39a344a6..0a198a8718 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -726,6 +726,10 @@ DEF_TRAVERSE_TYPE(TemplateSpecializationType, { DEF_TRAVERSE_TYPE(InjectedClassNameType, { }) +DEF_TRAVERSE_TYPE(AttributedType, { + TRY_TO(TraverseType(T->getModifiedType())); + }) + DEF_TRAVERSE_TYPE(ParenType, { TRY_TO(TraverseType(T->getInnerType())); }) @@ -936,6 +940,10 @@ DEF_TRAVERSE_TYPELOC(ParenType, { TRY_TO(TraverseTypeLoc(TL.getInnerLoc())); }) +DEF_TRAVERSE_TYPELOC(AttributedType, { + TRY_TO(TraverseTypeLoc(TL.getModifiedLoc())); + }) + // FIXME: use the sourceloc on qualifier? DEF_TRAVERSE_TYPELOC(ElaboratedType, { if (TL.getTypePtr()->getQualifier()) { diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 541cba4e04..14cbb0205b 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -993,9 +993,19 @@ protected: unsigned NumElements : 29 - NumTypeBits; }; + class AttributedTypeBitfields { + friend class AttributedType; + + unsigned : NumTypeBits; + + /// AttrKind - an AttributedType::Kind + unsigned AttrKind : 32 - NumTypeBits; + }; + union { TypeBitfields TypeBits; ArrayTypeBitfields ArrayTypeBits; + AttributedTypeBitfields AttributedTypeBits; BuiltinTypeBitfields BuiltinTypeBits; FunctionTypeBitfields FunctionTypeBits; ObjCObjectTypeBitfields ObjCObjectTypeBits; @@ -2657,6 +2667,91 @@ public: static bool classof(const EnumType *) { return true; } }; +/// AttributedType - An attributed type is a type to which a type +/// attribute has been applied. The "modified type" is the +/// fully-sugared type to which the attributed type was applied; +/// generally it is not canonically equivalent to the attributed type. +/// The "equivalent type" is the minimally-desugared type which the +/// type is canonically equivalent to. +/// +/// For example, in the following attributed type: +/// int32_t __attribute__((vector_size(16))) +/// - the modified type is the TypedefType for int32_t +/// - the equivalent type is VectorType(16, int32_t) +/// - the canonical type is VectorType(16, int) +class AttributedType : public Type, public llvm::FoldingSetNode { +public: + // It is really silly to have yet another attribute-kind enum, but + // clang::attr::Kind doesn't currently cover the pure type attrs. + enum Kind { + // Expression operand. + address_space, + regparm, + vector_size, + neon_vector_type, + neon_polyvector_type, + + FirstExprOperandKind = address_space, + LastExprOperandKind = neon_polyvector_type, + + // Enumerated operand (string or keyword). + objc_gc, + + FirstEnumOperandKind = objc_gc, + LastEnumOperandKind = objc_gc, + + // No operand. + noreturn, + cdecl, + fastcall, + stdcall, + thiscall, + pascal + }; + +private: + QualType ModifiedType; + QualType EquivalentType; + + friend class ASTContext; // creates these + + AttributedType(QualType canon, Kind attrKind, + QualType modified, QualType equivalent) + : Type(Attributed, canon, canon->isDependentType(), + canon->isVariablyModifiedType(), + canon->containsUnexpandedParameterPack()), + ModifiedType(modified), EquivalentType(equivalent) { + AttributedTypeBits.AttrKind = attrKind; + } + +public: + Kind getAttrKind() const { + return static_cast(AttributedTypeBits.AttrKind); + } + + QualType getModifiedType() const { return ModifiedType; } + QualType getEquivalentType() const { return EquivalentType; } + + bool isSugared() const { return true; } + QualType desugar() const { return getEquivalentType(); } + + void Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, getAttrKind(), ModifiedType, EquivalentType); + } + + static void Profile(llvm::FoldingSetNodeID &ID, Kind attrKind, + QualType modified, QualType equivalent) { + ID.AddInteger(attrKind); + ID.AddPointer(modified.getAsOpaquePtr()); + ID.AddPointer(equivalent.getAsOpaquePtr()); + } + + static bool classof(const Type *T) { + return T->getTypeClass() == Attributed; + } + static bool classof(const AttributedType *T) { return true; } +}; + class TemplateTypeParmType : public Type, public llvm::FoldingSetNode { unsigned Depth : 15; unsigned ParameterPack : 1; diff --git a/include/clang/AST/TypeLoc.h b/include/clang/AST/TypeLoc.h index 3480681fe1..c5497b8149 100644 --- a/include/clang/AST/TypeLoc.h +++ b/include/clang/AST/TypeLoc.h @@ -592,6 +592,130 @@ class SubstTemplateTypeParmTypeLoc : SubstTemplateTypeParmType> { }; +struct AttributedLocInfo { + union { + Expr *ExprOperand; + + /// A raw SourceLocation. + unsigned EnumOperandLoc; + }; + + SourceRange OperandParens; + + SourceLocation AttrLoc; +}; + +/// \brief Type source information for an attributed type. +class AttributedTypeLoc : public ConcreteTypeLoc { +public: + AttributedType::Kind getAttrKind() const { + return getTypePtr()->getAttrKind(); + } + + bool hasAttrExprOperand() const { + return (getAttrKind() >= AttributedType::FirstExprOperandKind && + getAttrKind() <= AttributedType::LastExprOperandKind); + } + + bool hasAttrEnumOperand() const { + return (getAttrKind() >= AttributedType::FirstEnumOperandKind && + getAttrKind() <= AttributedType::LastEnumOperandKind); + } + + bool hasAttrOperand() const { + return hasAttrExprOperand() || hasAttrEnumOperand(); + } + + /// The modified type, which is generally canonically different from + /// the attribute type. + /// int main(int, char**) __attribute__((noreturn)) + /// ~~~ ~~~~~~~~~~~~~ + TypeLoc getModifiedLoc() const { + return getInnerTypeLoc(); + } + + /// The location of the attribute name, i.e. + /// __attribute__((regparm(1000))) + /// ^~~~~~~ + SourceLocation getAttrNameLoc() const { + return getLocalData()->AttrLoc; + } + void setAttrNameLoc(SourceLocation loc) { + getLocalData()->AttrLoc = loc; + } + + /// The attribute's expression operand, if it has one. + /// void *cur_thread __attribute__((address_space(21))) + /// ^~ + Expr *getAttrExprOperand() const { + assert(hasAttrExprOperand()); + return getLocalData()->ExprOperand; + } + void setAttrExprOperand(Expr *e) { + assert(hasAttrExprOperand()); + getLocalData()->ExprOperand = e; + } + + /// The location of the attribute's enumerated operand, if it has one. + /// void * __attribute__((objc_gc(weak))) + /// ^~~~ + SourceLocation getAttrEnumOperandLoc() const { + assert(hasAttrEnumOperand()); + return SourceLocation::getFromRawEncoding(getLocalData()->EnumOperandLoc); + } + void setAttrEnumOperandLoc(SourceLocation loc) { + assert(hasAttrEnumOperand()); + getLocalData()->EnumOperandLoc = loc.getRawEncoding(); + } + + /// The location of the parentheses around the operand, if there is + /// an operand. + /// void * __attribute__((objc_gc(weak))) + /// ^ ^ + SourceRange getAttrOperandParensRange() const { + assert(hasAttrOperand()); + return getLocalData()->OperandParens; + } + void setAttrOperandParensRange(SourceRange range) { + assert(hasAttrOperand()); + getLocalData()->OperandParens = range; + } + + SourceRange getLocalSourceRange() const { + // Note that this does *not* include the range of the attribute + // enclosure, e.g.: + // __attribute__((foo(bar))) + // ^~~~~~~~~~~~~~~ ~~ + // or + // [[foo(bar)]] + // ^~ ~~ + // That enclosure doesn't necessarily belong to a single attribute + // anyway. + SourceRange range(getAttrNameLoc()); + if (hasAttrOperand()) + range.setEnd(getAttrOperandParensRange().getEnd()); + return range; + } + + void initializeLocal(SourceLocation loc) { + setAttrNameLoc(loc); + if (hasAttrExprOperand()) { + setAttrOperandParensRange(SourceRange(loc)); + setAttrExprOperand(0); + } else if (hasAttrEnumOperand()) { + setAttrOperandParensRange(SourceRange(loc)); + setAttrEnumOperandLoc(loc); + } + } + + QualType getInnerType() const { + return getTypePtr()->getModifiedType(); + } +}; + struct ObjCProtocolListLocInfo { SourceLocation LAngleLoc; diff --git a/include/clang/AST/TypeNodes.def b/include/clang/AST/TypeNodes.def index 4e25b94c52..52b8f831ac 100644 --- a/include/clang/AST/TypeNodes.def +++ b/include/clang/AST/TypeNodes.def @@ -88,6 +88,7 @@ ABSTRACT_TYPE(Tag, Type) TYPE(Record, TagType) TYPE(Enum, TagType) NON_CANONICAL_TYPE(Elaborated, Type) +NON_CANONICAL_TYPE(Attributed, Type) DEPENDENT_TYPE(TemplateTypeParm, Type) NON_CANONICAL_TYPE(SubstTemplateTypeParm, Type) NON_CANONICAL_UNLESS_DEPENDENT_TYPE(TemplateSpecialization, Type) diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index bbe7c1d2f8..ea93d2e1d6 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -553,7 +553,9 @@ namespace clang { /// \brief A ParenType record. TYPE_PAREN = 34, /// \brief A PackExpansionType record. - TYPE_PACK_EXPANSION = 35 + TYPE_PACK_EXPANSION = 35, + /// \brief An AttributedType record. + TYPE_ATTRIBUTED = 36 }; /// \brief The type IDs for special types constructed by semantic diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 6e7f7c31ea..e76238cb22 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -844,6 +844,10 @@ ASTContext::getTypeInfo(const Type *T) { case Type::Elaborated: return getTypeInfo(cast(T)->getNamedType().getTypePtr()); + case Type::Attributed: + return getTypeInfo( + cast(T)->getEquivalentType().getTypePtr()); + case Type::TemplateSpecialization: assert(getCanonicalType(T) != T && "Cannot request the size of a dependent type"); @@ -1875,6 +1879,27 @@ QualType ASTContext::getEnumType(const EnumDecl *Decl) { return QualType(Decl->TypeForDecl, 0); } +QualType ASTContext::getAttributedType(AttributedType::Kind attrKind, + QualType modifiedType, + QualType equivalentType) { + llvm::FoldingSetNodeID id; + AttributedType::Profile(id, attrKind, modifiedType, equivalentType); + + void *insertPos = 0; + AttributedType *type = AttributedTypes.FindNodeOrInsertPos(id, insertPos); + if (type) return QualType(type, 0); + + QualType canon = getCanonicalType(equivalentType); + type = new (*this, TypeAlignment) + AttributedType(canon, attrKind, modifiedType, equivalentType); + + Types.push_back(type); + AttributedTypes.InsertNode(type, insertPos); + + return QualType(type, 0); +} + + /// \brief Retrieve a substitution-result type. QualType ASTContext::getSubstTemplateTypeParmType(const TemplateTypeParmType *Parm, diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 5a94de9020..60ea7d9081 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -557,6 +557,17 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return false; break; + + case Type::Attributed: + if (!IsStructurallyEquivalent(Context, + cast(T1)->getModifiedType(), + cast(T2)->getModifiedType())) + return false; + if (!IsStructurallyEquivalent(Context, + cast(T1)->getEquivalentType(), + cast(T2)->getEquivalentType())) + return false; + break; case Type::Paren: if (!IsStructurallyEquivalent(Context, diff --git a/lib/AST/TypeLoc.cpp b/lib/AST/TypeLoc.cpp index 6dd5543966..08fe267b1d 100644 --- a/lib/AST/TypeLoc.cpp +++ b/lib/AST/TypeLoc.cpp @@ -229,4 +229,3 @@ TypeLoc TypeLoc::IgnoreParensImpl(TypeLoc TL) { TL = PTL->getInnerLoc(); return TL; } - diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index bd0467d246..7e719be4e9 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -674,6 +674,84 @@ void TypePrinter::printPackExpansion(const PackExpansionType *T, S += "..."; } +void TypePrinter::printAttributed(const AttributedType *T, + std::string &S) { + print(T->getModifiedType(), S); + + // TODO: not all attributes are GCC-style attributes. + S += "__attribute__(("; + switch (T->getAttrKind()) { + case AttributedType::address_space: + S += "address_space("; + S += T->getEquivalentType().getAddressSpace(); + S += ")"; + break; + + case AttributedType::vector_size: { + S += "__vector_size__("; + if (const VectorType *vector =T->getEquivalentType()->getAs()) { + S += vector->getNumElements(); + S += " * sizeof("; + + std::string tmp; + print(vector->getElementType(), tmp); + S += tmp; + S += ")"; + } + S += ")"; + break; + } + + case AttributedType::neon_vector_type: + case AttributedType::neon_polyvector_type: { + if (T->getAttrKind() == AttributedType::neon_vector_type) + S += "neon_vector_type("; + else + S += "neon_polyvector_type("; + const VectorType *vector = T->getEquivalentType()->getAs(); + S += llvm::utostr_32(vector->getNumElements()); + S += ")"; + break; + } + + case AttributedType::regparm: { + S += "regparm("; + QualType t = T->getEquivalentType(); + while (!t->isFunctionType()) + t = t->getPointeeType(); + S += t->getAs()->getRegParmType(); + S += ")"; + break; + } + + case AttributedType::objc_gc: { + S += "objc_gc("; + + QualType tmp = T->getEquivalentType(); + while (tmp.getObjCGCAttr() == Qualifiers::GCNone) { + QualType next = tmp->getPointeeType(); + if (next == tmp) break; + tmp = next; + } + + if (tmp.isObjCGCWeak()) + S += "weak"; + else + S += "strong"; + S += ")"; + break; + } + + case AttributedType::noreturn: S += "noreturn"; break; + case AttributedType::cdecl: S += "cdecl"; break; + case AttributedType::fastcall: S += "fastcall"; break; + case AttributedType::stdcall: S += "stdcall"; break; + case AttributedType::thiscall: S += "thiscall"; break; + case AttributedType::pascal: S += "pascal"; break; + } + S += "))"; +} + void TypePrinter::printObjCInterface(const ObjCInterfaceType *T, std::string &S) { if (!S.empty()) // Prefix the basic type, e.g. 'typedefname X'. diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 615972afbc..37a88efcc4 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -1324,6 +1324,8 @@ static QualType UnwrapTypeForDebugInfo(QualType T) { case Type::Decltype: T = cast(T)->getUnderlyingType(); break; + case Type::Attributed: + T = cast(T)->getEquivalentType(); case Type::Elaborated: T = cast(T)->getNamedType(); break; @@ -1423,6 +1425,7 @@ llvm::DIType CGDebugInfo::CreateTypeNode(QualType Ty, case Type::MemberPointer: return CreateType(cast(Ty), Unit); + case Type::Attributed: case Type::TemplateSpecialization: case Type::Elaborated: case Type::Paren: diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index dc87c5bbd2..25ead7bba5 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -3778,7 +3778,6 @@ QualType TreeTransform::TransformInjectedClassNameType( return T; } - template QualType TreeTransform::TransformTemplateTypeParmType( TypeLocBuilder &TLB, @@ -3945,6 +3944,43 @@ TreeTransform::TransformElaboratedType(TypeLocBuilder &TLB, return Result; } +template +QualType TreeTransform::TransformAttributedType( + TypeLocBuilder &TLB, + AttributedTypeLoc TL) { + const AttributedType *oldType = TL.getTypePtr(); + QualType modifiedType = getDerived().TransformType(TLB, TL.getModifiedLoc()); + if (modifiedType.isNull()) + return QualType(); + + QualType result = TL.getType(); + + // FIXME: dependent operand expressions? + if (getDerived().AlwaysRebuild() || + modifiedType != oldType->getModifiedType()) { + // TODO: this is really lame; we should really be rebuilding the + // equivalent type from first principles. + QualType equivalentType + = getDerived().TransformType(oldType->getEquivalentType()); + if (equivalentType.isNull()) + return QualType(); + result = SemaRef.Context.getAttributedType(oldType->getAttrKind(), + modifiedType, + equivalentType); + } + + AttributedTypeLoc newTL = TLB.push(result); + newTL.setAttrNameLoc(TL.getAttrNameLoc()); + if (TL.hasAttrOperand()) + newTL.setAttrOperandParensRange(TL.getAttrOperandParensRange()); + if (TL.hasAttrExprOperand()) + newTL.setAttrExprOperand(TL.getAttrExprOperand()); + else if (TL.hasAttrEnumOperand()) + newTL.setAttrEnumOperandLoc(TL.getAttrEnumOperandLoc()); + + return result; +} + template QualType TreeTransform::TransformParenType(TypeLocBuilder &TLB, diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 3bf51e6a0e..7c8415a295 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -2924,6 +2924,17 @@ QualType ASTReader::ReadTypeRecord(unsigned Index) { return T; } + case TYPE_ATTRIBUTED: { + if (Record.size() != 3) { + Error("incorrect encoding of attributed type"); + return QualType(); + } + QualType modifiedType = GetType(Record[0]); + QualType equivalentType = GetType(Record[1]); + AttributedType::Kind kind = static_cast(Record[2]); + return Context->getAttributedType(kind, modifiedType, equivalentType); + } + case TYPE_PAREN: { if (Record.size() != 1) { Error("incorrect encoding of paren type"); @@ -3197,6 +3208,22 @@ void TypeLocReader::VisitRecordTypeLoc(RecordTypeLoc TL) { void TypeLocReader::VisitEnumTypeLoc(EnumTypeLoc TL) { TL.setNameLoc(ReadSourceLocation(Record, Idx)); } +void TypeLocReader::VisitAttributedTypeLoc(AttributedTypeLoc TL) { + TL.setAttrNameLoc(ReadSourceLocation(Record, Idx)); + if (TL.hasAttrOperand()) { + SourceRange range; + range.setBegin(ReadSourceLocation(Record, Idx)); + range.setEnd(ReadSourceLocation(Record, Idx)); + TL.setAttrOperandParensRange(range); + } + if (TL.hasAttrExprOperand()) { + if (Record[Idx++]) + TL.setAttrExprOperand(Reader.ReadExpr(F)); + else + TL.setAttrExprOperand(0); + } else if (TL.hasAttrEnumOperand()) + TL.setAttrEnumOperandLoc(ReadSourceLocation(Record, Idx)); +} void TypeLocReader::VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc TL) { TL.setNameLoc(ReadSourceLocation(Record, Idx)); } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 2c0239de17..eb312522c7 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -228,6 +228,13 @@ void ASTTypeWriter::VisitEnumType(const EnumType *T) { Code = TYPE_ENUM; } +void ASTTypeWriter::VisitAttributedType(const AttributedType *T) { + Writer.AddTypeRef(T->getModifiedType(), Record); + Writer.AddTypeRef(T->getEquivalentType(), Record); + Record.push_back(T->getAttrKind()); + Code = TYPE_ATTRIBUTED; +} + void ASTTypeWriter::VisitSubstTemplateTypeParmType( const SubstTemplateTypeParmType *T) { @@ -462,6 +469,21 @@ void TypeLocWriter::VisitRecordTypeLoc(RecordTypeLoc TL) { void TypeLocWriter::VisitEnumTypeLoc(EnumTypeLoc TL) { Writer.AddSourceLocation(TL.getNameLoc(), Record); } +void TypeLocWriter::VisitAttributedTypeLoc(AttributedTypeLoc TL) { + Writer.AddSourceLocation(TL.getAttrNameLoc(), Record); + if (TL.hasAttrOperand()) { + SourceRange range = TL.getAttrOperandParensRange(); + Writer.AddSourceLocation(range.getBegin(), Record); + Writer.AddSourceLocation(range.getEnd(), Record); + } + if (TL.hasAttrExprOperand()) { + Expr *operand = TL.getAttrExprOperand(); + Record.push_back(operand ? 1 : 0); + if (operand) Writer.AddStmt(operand); + } else if (TL.hasAttrEnumOperand()) { + Writer.AddSourceLocation(TL.getAttrEnumOperandLoc(), Record); + } +} void TypeLocWriter::VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc TL) { Writer.AddSourceLocation(TL.getNameLoc(), Record); } -- 2.40.0