From ceec3063e2dd24fe98a92de352cd174f5282ae18 Mon Sep 17 00:00:00 2001 From: DeLesley Hutchins Date: Fri, 20 Jan 2012 22:37:06 +0000 Subject: [PATCH] Instantiate dependent attributes when instantiating templates. llvm-svn: 148592 --- clang/include/clang/Basic/Attr.td | 15 ++ clang/include/clang/CMakeLists.txt | 1 + clang/include/clang/Makefile | 2 +- clang/include/clang/Sema/CMakeLists.txt | 4 + clang/include/clang/Sema/Makefile | 14 ++ clang/lib/Sema/CMakeLists.txt | 3 +- clang/lib/Sema/SemaDecl.cpp | 3 + clang/lib/Sema/SemaDeclAttr.cpp | 12 +- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 8 +- .../SemaCXX/warn-thread-safety-analysis.cpp | 130 +++++++++++++++- clang/utils/TableGen/ClangAttrEmitter.cpp | 145 +++++++++++++++++- clang/utils/TableGen/ClangAttrEmitter.h | 13 ++ clang/utils/TableGen/TableGen.cpp | 7 + 13 files changed, 339 insertions(+), 18 deletions(-) create mode 100644 clang/include/clang/Sema/CMakeLists.txt create mode 100644 clang/include/clang/Sema/Makefile diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index f414fc623732..292c9a396f58 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -93,6 +93,8 @@ class Attr { list Namespaces = []; // Set to true for attributes with arguments which require delayed parsing. bit LateParsed = 0; + // Set to true for attributes which must be instantiated within templates + bit TemplateDependent = 0; // Any additional text that should be included verbatim in the class. code AdditionalMembers = [{}]; } @@ -601,36 +603,42 @@ def GuardedBy : InheritableAttr { let Spellings = ["guarded_by"]; let Args = [ExprArgument<"Arg">]; let LateParsed = 1; + let TemplateDependent = 1; } def PtGuardedBy : InheritableAttr { let Spellings = ["pt_guarded_by"]; let Args = [ExprArgument<"Arg">]; let LateParsed = 1; + let TemplateDependent = 1; } def AcquiredAfter : InheritableAttr { let Spellings = ["acquired_after"]; let Args = [VariadicExprArgument<"Args">]; let LateParsed = 1; + let TemplateDependent = 1; } def AcquiredBefore : InheritableAttr { let Spellings = ["acquired_before"]; let Args = [VariadicExprArgument<"Args">]; let LateParsed = 1; + let TemplateDependent = 1; } def ExclusiveLockFunction : InheritableAttr { let Spellings = ["exclusive_lock_function"]; let Args = [VariadicExprArgument<"Args">]; let LateParsed = 1; + let TemplateDependent = 1; } def SharedLockFunction : InheritableAttr { let Spellings = ["shared_lock_function"]; let Args = [VariadicExprArgument<"Args">]; let LateParsed = 1; + let TemplateDependent = 1; } // The first argument is an integer or boolean value specifying the return value @@ -639,6 +647,7 @@ def ExclusiveTrylockFunction : InheritableAttr { let Spellings = ["exclusive_trylock_function"]; let Args = [ExprArgument<"SuccessValue">, VariadicExprArgument<"Args">]; let LateParsed = 1; + let TemplateDependent = 1; } // The first argument is an integer or boolean value specifying the return value @@ -647,34 +656,40 @@ def SharedTrylockFunction : InheritableAttr { let Spellings = ["shared_trylock_function"]; let Args = [ExprArgument<"SuccessValue">, VariadicExprArgument<"Args">]; let LateParsed = 1; + let TemplateDependent = 1; } def UnlockFunction : InheritableAttr { let Spellings = ["unlock_function"]; let Args = [VariadicExprArgument<"Args">]; let LateParsed = 1; + let TemplateDependent = 1; } def LockReturned : InheritableAttr { let Spellings = ["lock_returned"]; let Args = [ExprArgument<"Arg">]; let LateParsed = 1; + let TemplateDependent = 1; } def LocksExcluded : InheritableAttr { let Spellings = ["locks_excluded"]; let Args = [VariadicExprArgument<"Args">]; let LateParsed = 1; + let TemplateDependent = 1; } def ExclusiveLocksRequired : InheritableAttr { let Spellings = ["exclusive_locks_required"]; let Args = [VariadicExprArgument<"Args">]; let LateParsed = 1; + let TemplateDependent = 1; } def SharedLocksRequired : InheritableAttr { let Spellings = ["shared_locks_required"]; let Args = [VariadicExprArgument<"Args">]; let LateParsed = 1; + let TemplateDependent = 1; } diff --git a/clang/include/clang/CMakeLists.txt b/clang/include/clang/CMakeLists.txt index fb4e04d55b15..71c37fda7891 100644 --- a/clang/include/clang/CMakeLists.txt +++ b/clang/include/clang/CMakeLists.txt @@ -3,4 +3,5 @@ add_subdirectory(Basic) add_subdirectory(Driver) add_subdirectory(Lex) add_subdirectory(Parse) +add_subdirectory(Sema) add_subdirectory(Serialization) diff --git a/clang/include/clang/Makefile b/clang/include/clang/Makefile index f7ce58bd56a1..4398f3aed575 100644 --- a/clang/include/clang/Makefile +++ b/clang/include/clang/Makefile @@ -1,5 +1,5 @@ CLANG_LEVEL := ../.. -DIRS := AST Basic Driver Lex Parse Serialization +DIRS := AST Basic Driver Lex Parse Sema Serialization include $(CLANG_LEVEL)/Makefile diff --git a/clang/include/clang/Sema/CMakeLists.txt b/clang/include/clang/Sema/CMakeLists.txt new file mode 100644 index 000000000000..110c5f9a4bd1 --- /dev/null +++ b/clang/include/clang/Sema/CMakeLists.txt @@ -0,0 +1,4 @@ +clang_tablegen(AttrTemplateInstantiate.inc -gen-clang-attr-template-instantiate + -I ${CMAKE_CURRENT_SOURCE_DIR}/../../ + SOURCE ../Basic/Attr.td + TARGET ClangAttrTemplateInstantiate) diff --git a/clang/include/clang/Sema/Makefile b/clang/include/clang/Sema/Makefile new file mode 100644 index 000000000000..3ec9c8bf8b9e --- /dev/null +++ b/clang/include/clang/Sema/Makefile @@ -0,0 +1,14 @@ +CLANG_LEVEL := ../../.. +TD_SRC_DIR = $(PROJ_SRC_DIR)/../Basic +BUILT_SOURCES = AttrTemplateInstantiate.inc + +TABLEGEN_INC_FILES_COMMON = 1 + +include $(CLANG_LEVEL)/Makefile + +$(ObjDir)/AttrTemplateInstantiate.inc.tmp : $(TD_SRC_DIR)/Attr.td \ + $(CLANG_TBLGEN) $(ObjDir)/.dir + $(Echo) "Building Clang attribute template instantiate code with tablegen" + $(Verb) $(ClangTableGen) -gen-clang-attr-template-instantiate -o \ + $(call SYSPATH, $@) -I $(PROJ_SRC_DIR)/../../ $< + diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 81cbd9594d05..ae878b06494c 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -44,4 +44,5 @@ add_clang_library(clangSema ) add_dependencies(clangSema ClangARMNeon ClangAttrClasses ClangAttrList - ClangDiagnosticSema ClangDeclNodes ClangStmtNodes) + ClangDiagnosticSema ClangDeclNodes ClangStmtNodes + ClangAttrTemplateInstantiate) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 9dc00c362591..7fd10735b7a4 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -7311,6 +7311,9 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, /// relevant Decl. void Sema::ActOnFinishDelayedAttribute(Scope *S, Decl *D, ParsedAttributes &Attrs) { + // Always attach attributes to the underlying decl. + if (TemplateDecl *TD = dyn_cast(D)) + D = TD->getTemplatedDecl(); ProcessDeclAttributeList(S, D, Attrs.getList()); } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index ea736320f876..e06d637eeb7e 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -393,13 +393,11 @@ static void handleGuardedByAttr(Sema &S, Decl *D, const AttributeList &Attr, if (pointer && !checkIsPointer(S, D, Attr)) return; - if (Arg->isTypeDependent()) - // FIXME: handle attributes with dependent types - return; - - // check that the argument is lockable object - if (!checkForLockableRecord(S, D, Attr, getRecordType(Arg->getType()))) - return; + if (!Arg->isTypeDependent()) { + if (!checkForLockableRecord(S, D, Attr, getRecordType(Arg->getType()))) + return; + // FIXME -- semantic checks for dependent attributes + } if (pointer) D->addAttr(::new (S.Context) PtGuardedByAttr(Attr.getRange(), diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 0d4ad3005b5c..60bcbfb255f8 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -57,7 +57,9 @@ bool TemplateDeclInstantiator::SubstQualifier(const TagDecl *OldDecl, return false; } -// FIXME: Is this still too simple? +// Include attribute instantiation code. +#include "clang/Sema/AttrTemplateInstantiate.inc" + void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, const Decl *Tmpl, Decl *New) { for (AttrVec::const_iterator i = Tmpl->attr_begin(), e = Tmpl->attr_end(); @@ -87,8 +89,8 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, } } - // FIXME: Is cloning correct for all attributes? - Attr *NewAttr = TmplAttr->clone(Context); + Attr *NewAttr = + instantiateTemplateAttribute(TmplAttr, Context, *this, TemplateArgs); New->addAttr(NewAttr); } } diff --git a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp index 497cd5c59932..a3d6a8b0bb87 100644 --- a/clang/test/SemaCXX/warn-thread-safety-analysis.cpp +++ b/clang/test/SemaCXX/warn-thread-safety-analysis.cpp @@ -1051,12 +1051,14 @@ class Foo { public: void func(T x) { mu_.Lock(); - count_ = x; + // count_ = x; mu_.Unlock(); } private: - T count_ GUARDED_BY(mu_); + // FIXME: This test passed earlier only because thread safety was turned + // off for templates. + // T count_ GUARDED_BY(mu_); Bar bar_; Mutex mu_; }; @@ -1605,7 +1607,6 @@ struct TestScopedLockable { } // end namespace test_scoped_lockable - namespace FunctionAttrTest { class Foo { @@ -1727,4 +1728,127 @@ struct TestTryLock { }; // end TestTrylock +namespace TestTemplateAttributeInstantiation { + +class Foo1 { +public: + Mutex mu_; + int a GUARDED_BY(mu_); +}; + +class Foo2 { +public: + int a GUARDED_BY(mu_); + Mutex mu_; +}; + + +class Bar { +public: + // Test non-dependent expressions in attributes on template functions + template + void barND(Foo1 *foo, T *fooT) EXCLUSIVE_LOCKS_REQUIRED(foo->mu_) { + foo->a = 0; + } + + // Test dependent expressions in attributes on template functions + template + void barD(Foo1 *foo, T *fooT) EXCLUSIVE_LOCKS_REQUIRED(fooT->mu_) { + fooT->a = 0; + } +}; + + +template +class BarT { +public: + Foo1 fooBase; + T fooBaseT; + + // Test non-dependent expression in ordinary method on template class + void barND() EXCLUSIVE_LOCKS_REQUIRED(fooBase.mu_) { + fooBase.a = 0; + } + + // Test dependent expressions in ordinary methods on template class + void barD() EXCLUSIVE_LOCKS_REQUIRED(fooBaseT.mu_) { + fooBaseT.a = 0; + } + + // Test dependent expressions in template method in template class + template + void barTD(T2 *fooT) EXCLUSIVE_LOCKS_REQUIRED(fooBaseT.mu_, fooT->mu_) { + fooBaseT.a = 0; + fooT->a = 0; + } +}; + +template +class Cell { +public: + Mutex mu_; + // Test dependent guarded_by + T data GUARDED_BY(mu_); + + void foo() { + mu_.Lock(); + data = 0; + mu_.Unlock(); + } +}; + + +template +class CellDelayed { +public: + // Test dependent guarded_by + T data GUARDED_BY(mu_); + + void foo() { + mu_.Lock(); + data = 0; + mu_.Unlock(); + } + + Mutex mu_; +}; + +void test() { + Bar b; + BarT bt; + Foo1 f1; + Foo2 f2; + + f1.mu_.Lock(); + f2.mu_.Lock(); + bt.fooBase.mu_.Lock(); + bt.fooBaseT.mu_.Lock(); + + b.barND(&f1, &f2); + b.barD(&f1, &f2); + bt.barND(); + bt.barD(); + bt.barTD(&f2); + + f1.mu_.Unlock(); + bt.barTD(&f1); // \ + // expected-warning {{calling function 'barTD' requires exclusive lock on 'mu_'}} + + bt.fooBase.mu_.Unlock(); + bt.fooBaseT.mu_.Unlock(); + f2.mu_.Unlock(); + + Cell cell; + cell.data = 0; // \ + // expected-warning {{writing variable 'data' requires locking 'mu_' exclusively}} + cell.foo(); + + // FIXME: This doesn't work yet + // CellDelayed celld; + // celld.foo(); +} + +}; // end namespace TestTemplateAttributeInstantiation + + diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 16015429bf36..dd3de66fbd85 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -89,6 +89,8 @@ namespace { virtual void writeAccessors(raw_ostream &OS) const = 0; virtual void writeAccessorDefinitions(raw_ostream &OS) const {} virtual void writeCloneArgs(raw_ostream &OS) const = 0; + virtual void writeTemplateInstantiationArgs(raw_ostream &OS) const = 0; + virtual void writeTemplateInstantiation(raw_ostream &OS) const {}; virtual void writeCtorBody(raw_ostream &OS) const {} virtual void writeCtorInitializers(raw_ostream &OS) const = 0; virtual void writeCtorParameters(raw_ostream &OS) const = 0; @@ -107,6 +109,8 @@ namespace { : Argument(Arg, Attr), type(T) {} + std::string getType() const { return type; } + void writeAccessors(raw_ostream &OS) const { OS << " " << type << " get" << getUpperName() << "() const {\n"; OS << " return " << getLowerName() << ";\n"; @@ -115,6 +119,9 @@ namespace { void writeCloneArgs(raw_ostream &OS) const { OS << getLowerName(); } + void writeTemplateInstantiationArgs(raw_ostream &OS) const { + OS << "A->get" << getUpperName() << "()"; + } void writeCtorInitializers(raw_ostream &OS) const { OS << getLowerName() << "(" << getUpperName() << ")"; } @@ -176,6 +183,9 @@ namespace { void writeCloneArgs(raw_ostream &OS) const { OS << "get" << getUpperName() << "()"; } + void writeTemplateInstantiationArgs(raw_ostream &OS) const { + OS << "A->get" << getUpperName() << "()"; + } void writeCtorBody(raw_ostream &OS) const { OS << " std::memcpy(" << getLowerName() << ", " << getUpperName() << ".data(), " << getLowerName() << "Length);"; @@ -266,6 +276,10 @@ namespace { << "Expr) : " << getLowerName() << "Type"; } + void writeTemplateInstantiationArgs(raw_ostream &OS) const { + // FIXME: move the definition in Sema::InstantiateAttrs to here. + // In the meantime, aligned attributes are cloned. + } void writeCtorBody(raw_ostream &OS) const { OS << " if (is" << getLowerName() << "Expr)\n"; OS << " " << getLowerName() << "Expr = reinterpret_cast(" @@ -341,6 +355,11 @@ namespace { void writeCloneArgs(raw_ostream &OS) const { OS << getLowerName() << ", " << getLowerName() << "Size"; } + void writeTemplateInstantiationArgs(raw_ostream &OS) const { + // This isn't elegant, but we have to go through public methods... + OS << "A->" << getLowerName() << "_begin(), " + << "A->" << getLowerName() << "_size()"; + } void writeCtorBody(raw_ostream &OS) const { // FIXME: memcpy is not safe on non-trivial types. OS << " std::memcpy(" << getLowerName() << ", " << getUpperName() @@ -412,6 +431,9 @@ namespace { void writeCloneArgs(raw_ostream &OS) const { OS << getLowerName(); } + void writeTemplateInstantiationArgs(raw_ostream &OS) const { + OS << "A->get" << getUpperName() << "()"; + } void writeCtorInitializers(raw_ostream &OS) const { OS << getLowerName() << "(" << getUpperName() << ")"; } @@ -475,6 +497,9 @@ namespace { void writeCloneArgs(raw_ostream &OS) const { OS << "get" << getUpperName() << "()"; } + void writeTemplateInstantiationArgs(raw_ostream &OS) const { + OS << "A->get" << getUpperName() << "()"; + } void writeCtorBody(raw_ostream &OS) const { } void writeCtorInitializers(raw_ostream &OS) const { @@ -500,6 +525,61 @@ namespace { OS << getLowerName() << "=\" << get" << getUpperName() << "() << \""; } }; + + class ExprArgument : public SimpleArgument { + public: + ExprArgument(Record &Arg, StringRef Attr) + : SimpleArgument(Arg, Attr, "Expr *") + {} + + void writeTemplateInstantiationArgs(raw_ostream &OS) const { + OS << "tempInst" << getUpperName(); + } + + void writeTemplateInstantiation(raw_ostream &OS) const { + OS << " " << getType() << " tempInst" << getUpperName() << ";\n"; + OS << " {\n"; + OS << " EnterExpressionEvaluationContext " + << "Unevaluated(S, Sema::Unevaluated);\n"; + OS << " ExprResult " << "Result = S.SubstExpr(" + << "A->get" << getUpperName() << "(), TemplateArgs);\n"; + OS << " tempInst" << getUpperName() << " = " + << "Result.takeAs();\n"; + OS << " }\n"; + } + }; + + class VariadicExprArgument : public VariadicArgument { + public: + VariadicExprArgument(Record &Arg, StringRef Attr) + : VariadicArgument(Arg, Attr, "Expr *") + {} + + void writeTemplateInstantiationArgs(raw_ostream &OS) const { + OS << "tempInst" << getUpperName() << ", " + << "A->" << getLowerName() << "_size()"; + } + + void writeTemplateInstantiation(raw_ostream &OS) const { + OS << " " << getType() << " *tempInst" << getUpperName() + << " = new (C, 16) " << getType() + << "[A->" << getLowerName() << "_size()];\n"; + OS << " {\n"; + OS << " EnterExpressionEvaluationContext " + << "Unevaluated(S, Sema::Unevaluated);\n"; + OS << " " << getType() << " *TI = tempInst" << getUpperName() + << ";\n"; + OS << " " << getType() << " *I = A->" << getLowerName() + << "_begin();\n"; + OS << " " << getType() << " *E = A->" << getLowerName() + << "_end();\n"; + OS << " for (; I != E; ++I, ++TI) {\n"; + OS << " ExprResult Result = S.SubstExpr(*I, TemplateArgs);\n"; + OS << " *TI = Result.takeAs();\n"; + OS << " }\n"; + OS << " }\n"; + } + }; } static Argument *createArgument(Record &Arg, StringRef Attr, @@ -512,8 +592,7 @@ static Argument *createArgument(Record &Arg, StringRef Attr, if (ArgName == "AlignedArgument") Ptr = new AlignedArgument(Arg, Attr); else if (ArgName == "EnumArgument") Ptr = new EnumArgument(Arg, Attr); - else if (ArgName == "ExprArgument") Ptr = new SimpleArgument(Arg, Attr, - "Expr *"); + else if (ArgName == "ExprArgument") Ptr = new ExprArgument(Arg, Attr); else if (ArgName == "FunctionArgument") Ptr = new SimpleArgument(Arg, Attr, "FunctionDecl *"); else if (ArgName == "IdentifierArgument") @@ -531,7 +610,7 @@ static Argument *createArgument(Record &Arg, StringRef Attr, else if (ArgName == "VariadicUnsignedArgument") Ptr = new VariadicArgument(Arg, Attr, "unsigned"); else if (ArgName == "VariadicExprArgument") - Ptr = new VariadicArgument(Arg, Attr, "Expr *"); + Ptr = new VariadicExprArgument(Arg, Attr); else if (ArgName == "VersionArgument") Ptr = new VersionArgument(Arg, Attr); @@ -851,3 +930,63 @@ void ClangAttrLateParsedListEmitter::run(raw_ostream &OS) { } } } + + +void ClangAttrTemplateInstantiateEmitter::run(raw_ostream &OS) { + OS << "// This file is generated by TableGen. Do not edit.\n\n"; + + std::vector Attrs = Records.getAllDerivedDefinitions("Attr"); + + OS << "Attr* instantiateTemplateAttribute(const Attr *At, ASTContext &C, " + << "Sema &S,\n" + << " const MultiLevelTemplateArgumentList &TemplateArgs) {\n" + << " switch (At->getKind()) {\n" + << " default:\n" + << " break;\n"; + + for (std::vector::iterator I = Attrs.begin(), E = Attrs.end(); + I != E; ++I) { + Record &R = **I; + + OS << " case attr::" << R.getName() << ": {\n"; + OS << " const " << R.getName() << "Attr *A = cast<" + << R.getName() << "Attr>(At);\n"; + bool TDependent = R.getValueAsBit("TemplateDependent"); + + if (!TDependent) { + OS << " return A->clone(C);\n"; + OS << " }\n"; + continue; + } + + std::vector ArgRecords = R.getValueAsListOfDefs("Args"); + std::vector Args; + std::vector::iterator ai, ae; + Args.reserve(ArgRecords.size()); + + for (std::vector::iterator ri = ArgRecords.begin(), + re = ArgRecords.end(); + ri != re; ++ri) { + Record &ArgRecord = **ri; + Argument *Arg = createArgument(ArgRecord, R.getName()); + assert(Arg); + Args.push_back(Arg); + } + ae = Args.end(); + + for (ai = Args.begin(); ai != ae; ++ai) { + (*ai)->writeTemplateInstantiation(OS); + } + OS << " return new (C) " << R.getName() << "Attr(A->getLocation(), C"; + for (ai = Args.begin(); ai != ae; ++ai) { + OS << ", "; + (*ai)->writeTemplateInstantiationArgs(OS); + } + OS << ");\n }\n"; + } + OS << " } // end switch\n" + << " llvm_unreachable(\"Unknown attribute!\");\n" + << " return 0;\n" + << "}\n\n"; +} + diff --git a/clang/utils/TableGen/ClangAttrEmitter.h b/clang/utils/TableGen/ClangAttrEmitter.h index 5acca560f013..91ec754346a1 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.h +++ b/clang/utils/TableGen/ClangAttrEmitter.h @@ -109,6 +109,19 @@ class ClangAttrLateParsedListEmitter : public TableGenBackend { void run(raw_ostream &OS); }; +/// ClangAttrTemplateInstantiateEmitter emits code to instantiate dependent +/// attributes on templates. +class ClangAttrTemplateInstantiateEmitter : public TableGenBackend { + RecordKeeper &Records; + + public: + explicit ClangAttrTemplateInstantiateEmitter(RecordKeeper &R) + : Records(R) + {} + + void run(raw_ostream &OS); +}; + } #endif diff --git a/clang/utils/TableGen/TableGen.cpp b/clang/utils/TableGen/TableGen.cpp index 1d5e28d936ff..6462b3bb7449 100644 --- a/clang/utils/TableGen/TableGen.cpp +++ b/clang/utils/TableGen/TableGen.cpp @@ -36,6 +36,7 @@ enum ActionType { GenClangAttrPCHWrite, GenClangAttrSpellingList, GenClangAttrLateParsedList, + GenClangAttrTemplateInstantiate, GenClangDiagsDefs, GenClangDiagGroups, GenClangDiagsIndexName, @@ -71,6 +72,9 @@ namespace { clEnumValN(GenClangAttrLateParsedList, "gen-clang-attr-late-parsed-list", "Generate a clang attribute LateParsed list"), + clEnumValN(GenClangAttrTemplateInstantiate, + "gen-clang-attr-template-instantiate", + "Generate a clang template instantiate code"), clEnumValN(GenClangDiagsDefs, "gen-clang-diags-defs", "Generate Clang diagnostics definitions"), clEnumValN(GenClangDiagGroups, "gen-clang-diag-groups", @@ -122,6 +126,9 @@ public: case GenClangAttrLateParsedList: ClangAttrLateParsedListEmitter(Records).run(OS); break; + case GenClangAttrTemplateInstantiate: + ClangAttrTemplateInstantiateEmitter(Records).run(OS); + break; case GenClangDiagsDefs: ClangDiagsDefsEmitter(Records, ClangComponent).run(OS); break;