Index: llvm/cmake/modules/TableGen.cmake =================================================================== --- llvm/cmake/modules/TableGen.cmake +++ llvm/cmake/modules/TableGen.cmake @@ -80,6 +80,10 @@ set(tblgen_change_flag "--write-if-changed") endif() + if (LLVM_ENABLE_WARNINGS) + list(APPEND LLVM_TABLEGEN_FLAGS "-warn-on-unused-template-args") + endif() + # We need both _TABLEGEN_TARGET and _TABLEGEN_EXE in the DEPENDS list # (both the target and the file) to have .inc files rebuilt on # a tablegen change, as cmake does not propagate file-level dependencies Index: llvm/include/llvm/TableGen/Record.h =================================================================== --- llvm/include/llvm/TableGen/Record.h +++ llvm/include/llvm/TableGen/Record.h @@ -1414,6 +1414,7 @@ SMLoc Loc; // Source location of definition of name. PointerIntPair TyAndKind; Init *Value; + bool IsUsed = false; public: RecordVal(Init *N, RecTy *T, FieldKind K); @@ -1458,6 +1459,11 @@ /// Set the value and source location of the field. bool setValue(Init *V, SMLoc NewLoc); + /// Whether this value is used. Useful for reporting warnings, for example + /// when a template argument is unused. + void setUsed(bool Used) { IsUsed = Used; } + bool isUsed() const { return IsUsed; } + void dump() const; /// Print the value to an output stream, possibly with a semicolon. @@ -1632,6 +1638,7 @@ } void checkRecordAssertions(); + void checkUnusedTemplateArgs(); bool isSubClassOf(const Record *R) const { for (const auto &SCPair : SuperClasses) Index: llvm/lib/TableGen/Main.cpp =================================================================== --- llvm/lib/TableGen/Main.cpp +++ llvm/lib/TableGen/Main.cpp @@ -55,6 +55,10 @@ static cl::opt TimePhases("time-phases", cl::desc("Time phases of parser and backend")); +static cl::opt WarnOnUnusedTemplateArgs( + "warn-on-unused-template-args", + cl::desc("Emit a warning on unused template arguments.")); + static int reportError(const char *ProgName, Twine Msg) { errs() << ProgName << ": " << Msg; errs().flush(); @@ -107,7 +111,7 @@ // it later. SrcMgr.setIncludeDirs(IncludeDirs); - TGParser Parser(SrcMgr, MacroNames, Records); + TGParser Parser(SrcMgr, MacroNames, Records, WarnOnUnusedTemplateArgs); if (Parser.ParseFile()) return 1; Index: llvm/lib/TableGen/Record.cpp =================================================================== --- llvm/lib/TableGen/Record.cpp +++ llvm/lib/TableGen/Record.cpp @@ -2660,6 +2660,17 @@ } } +// Report a warning if the record has unused template arguments. +void Record::checkUnusedTemplateArgs() { + ArrayRef TArgs = getTemplateArgs(); + for (unsigned I = 0, E = TArgs.size(); I != E; ++I) { + RecordVal *Arg = getValue(TArgs[I]); + if (!Arg->isUsed()) + PrintWarning(Arg->getLoc(), + "unused template argument: " + Twine(Arg->getName())); + } +} + #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_DUMP_METHOD void RecordKeeper::dump() const { errs() << *this; } #endif Index: llvm/lib/TableGen/TGParser.h =================================================================== --- llvm/lib/TableGen/TGParser.h +++ llvm/lib/TableGen/TGParser.h @@ -160,10 +160,13 @@ // exist. }; + bool WarnOnUnusedTemplateArgs = false; + public: - TGParser(SourceMgr &SM, ArrayRef Macros, - RecordKeeper &records) - : Lex(SM, Macros), CurMultiClass(nullptr), Records(records) {} + TGParser(SourceMgr &SM, ArrayRef Macros, RecordKeeper &records, + const bool WarnOnUnusedTemplateArgs = false) + : Lex(SM, Macros), CurMultiClass(nullptr), Records(records), + WarnOnUnusedTemplateArgs(WarnOnUnusedTemplateArgs) {} /// ParseFile - Main entrypoint for parsing a tblgen file. These parser /// routines return true on error, or false on success. Index: llvm/lib/TableGen/TGParser.cpp =================================================================== --- llvm/lib/TableGen/TGParser.cpp +++ llvm/lib/TableGen/TGParser.cpp @@ -874,8 +874,9 @@ Record *TemplateRec = CurMultiClass ? &CurMultiClass->Rec : CurRec; if (TemplateRec->isTemplateArg(TemplateArgName)) { - const RecordVal *RV = TemplateRec->getValue(TemplateArgName); + RecordVal *RV = TemplateRec->getValue(TemplateArgName); assert(RV && "Template arg doesn't exist??"); + RV->setUsed(true); return VarInit::get(TemplateArgName, RV->getType()); } else if (Name->getValue() == "NAME") { return VarInit::get(TemplateArgName, StringRecTy::get()); @@ -3346,7 +3347,12 @@ if (ParseTemplateArgList(CurRec)) return true; - return ParseObjectBody(CurRec); + if (ParseObjectBody(CurRec)) + return true; + + if (WarnOnUnusedTemplateArgs) + CurRec->checkUnusedTemplateArgs(); + return false; } /// ParseLetList - Parse a non-empty list of assignment expressions into a list @@ -3541,6 +3547,9 @@ PopLocalScope(MulticlassScope); } + if (WarnOnUnusedTemplateArgs) + CurMultiClass->Rec.checkUnusedTemplateArgs(); + CurMultiClass = nullptr; return false; } Index: llvm/test/TableGen/warn-unused-template-arg.td =================================================================== --- /dev/null +++ llvm/test/TableGen/warn-unused-template-arg.td @@ -0,0 +1,23 @@ +// RUN: llvm-tblgen --warn-on-unused-template-args %s 2>&1 | FileCheck %s + +class UnusedClassArg {} + +// CHECK: warning: unused template argument: UnusedClassArg:foo +// CHECK-NEXT: class UnusedClassArg {} +// CHECK-NEXT: ^ + +multiclass UnusedMultiClassArg { + def bar; +} + +defm : UnusedMultiClassArg<1>; + +// CHECK: warning: unused template argument: UnusedMultiClassArg::foo +// CHECK-NEXT: multiclass UnusedMultiClassArg { +// CHECK-NEXT: ^ + +class NoWarning { + int a = b; +} + +// CHECK-NOT: warning: unused template argument: NoWarning:b