15f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer//===--- AttributeList.cpp --------------------------------------*- C++ -*-===// 25f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// 35f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// The LLVM Compiler Infrastructure 45f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// 50bc735ffcfb223c0186419547abaa5c84482663eChris Lattner// This file is distributed under the University of Illinois Open Source 60bc735ffcfb223c0186419547abaa5c84482663eChris Lattner// License. See LICENSE.TXT for details. 75f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// 85f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer//===----------------------------------------------------------------------===// 95f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// 105f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// This file defines the AttributeList class implementation 115f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// 125f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer//===----------------------------------------------------------------------===// 135f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1419510856727e0e14a3696b2a72c35163bff2a71fJohn McCall#include "clang/Sema/AttributeList.h" 15478851c3ed6bd784e7377dffd8e57b200c1b9ba9Benjamin Kramer#include "clang/AST/ASTContext.h" 16651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines#include "clang/AST/DeclCXX.h" 17651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines#include "clang/AST/DeclTemplate.h" 180b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall#include "clang/AST/Expr.h" 198f823d2d3c557326d22699d66e5d367d0f0e44efChris Lattner#include "clang/Basic/IdentifierTable.h" 20651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines#include "clang/Sema/SemaInternal.h" 21e0d3b4cd2b66f1cef26cacbed5820ab7c22ad5b3Richard Smith#include "llvm/ADT/SmallString.h" 2255fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth#include "llvm/ADT/StringSwitch.h" 235f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerusing namespace clang; 245f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 258edabd95dd4c9099fd124c5e50322730b9200d23Richard SmithIdentifierLoc *IdentifierLoc::create(ASTContext &Ctx, SourceLocation Loc, 268edabd95dd4c9099fd124c5e50322730b9200d23Richard Smith IdentifierInfo *Ident) { 278edabd95dd4c9099fd124c5e50322730b9200d23Richard Smith IdentifierLoc *Result = new (Ctx) IdentifierLoc; 288edabd95dd4c9099fd124c5e50322730b9200d23Richard Smith Result->Loc = Loc; 298edabd95dd4c9099fd124c5e50322730b9200d23Richard Smith Result->Ident = Ident; 308edabd95dd4c9099fd124c5e50322730b9200d23Richard Smith return Result; 318edabd95dd4c9099fd124c5e50322730b9200d23Richard Smith} 328edabd95dd4c9099fd124c5e50322730b9200d23Richard Smith 330b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCallsize_t AttributeList::allocated_size() const { 340b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall if (IsAvailability) return AttributeFactory::AvailabilityAllocSize; 350d5a069f66df09b3308ccfdce84a88170034c657Dmitri Gribenko else if (IsTypeTagForDatatype) 360d5a069f66df09b3308ccfdce84a88170034c657Dmitri Gribenko return AttributeFactory::TypeTagForDatatypeAllocSize; 3776da55d3a49e1805f51b1ced7c5da5bcd7f759d8John McCall else if (IsProperty) 3876da55d3a49e1805f51b1ced7c5da5bcd7f759d8John McCall return AttributeFactory::PropertyAllocSize; 39624421f98d8fcb8ed8ebc406da41217682159aa8Aaron Ballman return (sizeof(AttributeList) + NumArgs * sizeof(ArgsUnion)); 400b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall} 410b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall 420b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCallAttributeFactory::AttributeFactory() { 430b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall // Go ahead and configure all the inline capacity. This is just a memset. 440b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall FreeLists.resize(InlineFreeListsCapacity); 450b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall} 460b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCallAttributeFactory::~AttributeFactory() {} 470b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall 480b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCallstatic size_t getFreeListIndexForSize(size_t size) { 490b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall assert(size >= sizeof(AttributeList)); 500b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall assert((size % sizeof(void*)) == 0); 510b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall return ((size - sizeof(AttributeList)) / sizeof(void*)); 520b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall} 530b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall 540b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCallvoid *AttributeFactory::allocate(size_t size) { 550b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall // Check for a previously reclaimed attribute. 560b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall size_t index = getFreeListIndexForSize(size); 570b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall if (index < FreeLists.size()) { 580b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall if (AttributeList *attr = FreeLists[index]) { 590b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall FreeLists[index] = attr->NextInPool; 600b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall return attr; 610b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall } 62005b235fbc8449142befa81ec30e8e4c654182c5Chris Lattner } 630b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall 640b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall // Otherwise, allocate something new. 650b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall return Alloc.Allocate(size, llvm::AlignOf<AttributeFactory>::Alignment); 660b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall} 670b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall 680b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCallvoid AttributeFactory::reclaimPool(AttributeList *cur) { 690b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall assert(cur && "reclaiming empty pool!"); 700b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall do { 710b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall // Read this here, because we're going to overwrite NextInPool 720b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall // when we toss 'cur' into the appropriate queue. 730b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall AttributeList *next = cur->NextInPool; 740b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall 750b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall size_t size = cur->allocated_size(); 760b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall size_t freeListIndex = getFreeListIndexForSize(size); 770b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall 780b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall // Expand FreeLists to the appropriate size, if required. 790b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall if (freeListIndex >= FreeLists.size()) 800b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall FreeLists.resize(freeListIndex+1); 810b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall 820b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall // Add 'cur' to the appropriate free-list. 830b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall cur->NextInPool = FreeLists[freeListIndex]; 840b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall FreeLists[freeListIndex] = cur; 850b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall 860b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall cur = next; 870b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall } while (cur); 880b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall} 890b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall 900b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCallvoid AttributePool::takePool(AttributeList *pool) { 910b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall assert(pool); 920b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall 930b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall // Fast path: this pool is empty. 940b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall if (!Head) { 950b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall Head = pool; 960b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall return; 970b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall } 980b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall 990b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall // Reverse the pool onto the current head. This optimizes for the 1000b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall // pattern of pulling a lot of pools into a single pool. 1010b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall do { 1020b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall AttributeList *next = pool->NextInPool; 1030b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall pool->NextInPool = Head; 1040b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall Head = pool; 1050b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall pool = next; 1060b7e678a11ece4288dc01aebb5b17e5eef8f8d2dJohn McCall } while (pool); 1075f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer} 1082335191341a58fa3f8abea3c9b391c5bb03d67b3Chris Lattner 1090c19b3c38e356058aeb2424d175eae232bf014d9Douglas Gregor#include "clang/Sema/AttrParsedAttrKinds.inc" 1100c19b3c38e356058aeb2424d175eae232bf014d9Douglas Gregor 111e0d3b4cd2b66f1cef26cacbed5820ab7c22ad5b3Richard SmithAttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name, 11293f95f2a2cbb6bb3d17bfb5fc74ce1cccea751b6Sean Hunt const IdentifierInfo *ScopeName, 11393f95f2a2cbb6bb3d17bfb5fc74ce1cccea751b6Sean Hunt Syntax SyntaxUsed) { 1145f9e272e632e951b1efe824cd16acb4d96077930Chris Lattner StringRef AttrName = Name->getName(); 1152335191341a58fa3f8abea3c9b391c5bb03d67b3Chris Lattner 116651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines SmallString<64> FullName; 117e0d3b4cd2b66f1cef26cacbed5820ab7c22ad5b3Richard Smith if (ScopeName) 118651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines FullName += ScopeName->getName(); 119651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines 120651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines // Normalize the attribute name, __foo__ becomes foo. This is only allowable 121651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines // for GNU attributes. 122651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines bool IsGNU = SyntaxUsed == AS_GNU || (SyntaxUsed == AS_CXX11 && 123651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines FullName == "gnu"); 124651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines if (IsGNU && AttrName.size() >= 4 && AttrName.startswith("__") && 125651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines AttrName.endswith("__")) 126651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines AttrName = AttrName.slice(2, AttrName.size() - 2); 127651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines 12893f95f2a2cbb6bb3d17bfb5fc74ce1cccea751b6Sean Hunt // Ensure that in the case of C++11 attributes, we look for '::foo' if it is 12993f95f2a2cbb6bb3d17bfb5fc74ce1cccea751b6Sean Hunt // unscoped. 13093f95f2a2cbb6bb3d17bfb5fc74ce1cccea751b6Sean Hunt if (ScopeName || SyntaxUsed == AS_CXX11) 131651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines FullName += "::"; 132651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines FullName += AttrName; 13393f95f2a2cbb6bb3d17bfb5fc74ce1cccea751b6Sean Hunt 134651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines return ::getAttrKind(FullName, SyntaxUsed); 1352335191341a58fa3f8abea3c9b391c5bb03d67b3Chris Lattner} 13651d8c52ad36129760eaa586f85176037e2cd0d0eMichael Han 13751d8c52ad36129760eaa586f85176037e2cd0d0eMichael Hanunsigned AttributeList::getAttributeSpellingListIndex() const { 13851d8c52ad36129760eaa586f85176037e2cd0d0eMichael Han // Both variables will be used in tablegen generated 13951d8c52ad36129760eaa586f85176037e2cd0d0eMichael Han // attribute spell list index matching code. 14051d8c52ad36129760eaa586f85176037e2cd0d0eMichael Han StringRef Name = AttrName->getName(); 14151d8c52ad36129760eaa586f85176037e2cd0d0eMichael Han StringRef Scope = ScopeName ? ScopeName->getName() : ""; 14251d8c52ad36129760eaa586f85176037e2cd0d0eMichael Han 14351d8c52ad36129760eaa586f85176037e2cd0d0eMichael Han#include "clang/Sema/AttrSpellingListIndex.inc" 14451d8c52ad36129760eaa586f85176037e2cd0d0eMichael Han 14551d8c52ad36129760eaa586f85176037e2cd0d0eMichael Han} 14651d8c52ad36129760eaa586f85176037e2cd0d0eMichael Han 147bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballmanstruct ParsedAttrInfo { 148bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman unsigned NumArgs : 4; 149bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman unsigned OptArgs : 4; 150bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman unsigned HasCustomParsing : 1; 151651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines unsigned IsTargetSpecific : 1; 152651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines unsigned IsType : 1; 153651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines unsigned IsKnownToGCC : 1; 154651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines 155651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines bool (*DiagAppertainsToDecl)(Sema &S, const AttributeList &Attr, 156651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines const Decl *); 157651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines bool (*DiagLangOpts)(Sema &S, const AttributeList &Attr); 158651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines bool (*ExistsInTarget)(const llvm::Triple &T); 159651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines unsigned (*SpellingIndexToSemanticSpelling)(const AttributeList &Attr); 160bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman}; 161bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman 162bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballmannamespace { 163bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman #include "clang/Sema/AttrParsedAttrImpl.inc" 164bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman} 165bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman 1666ad3cdd401f35217aaa7b00fe765247aaf133cf2Benjamin Kramerstatic const ParsedAttrInfo &getInfo(const AttributeList &A) { 167bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman return AttrInfoMap[A.getKind()]; 168bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman} 169bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman 170bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballmanunsigned AttributeList::getMinArgs() const { 171bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman return getInfo(*this).NumArgs; 172bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman} 173bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman 174bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballmanunsigned AttributeList::getMaxArgs() const { 175bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman return getMinArgs() + getInfo(*this).OptArgs; 176bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman} 177bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman 178bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballmanbool AttributeList::hasCustomParsing() const { 179bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman return getInfo(*this).HasCustomParsing; 180bbb3b3237df2284f6f3c34798944fcfa4b43fd34Aaron Ballman} 181651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines 182651f13cea278ec967336033dd032faef0e9fc2ecStephen Hinesbool AttributeList::diagnoseAppertainsTo(Sema &S, const Decl *D) const { 183651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines return getInfo(*this).DiagAppertainsToDecl(S, *this, D); 184651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines} 185651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines 186651f13cea278ec967336033dd032faef0e9fc2ecStephen Hinesbool AttributeList::diagnoseLangOpts(Sema &S) const { 187651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines return getInfo(*this).DiagLangOpts(S, *this); 188651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines} 189651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines 190651f13cea278ec967336033dd032faef0e9fc2ecStephen Hinesbool AttributeList::isTargetSpecificAttr() const { 191651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines return getInfo(*this).IsTargetSpecific; 192651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines} 193651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines 194651f13cea278ec967336033dd032faef0e9fc2ecStephen Hinesbool AttributeList::isTypeAttr() const { 195651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines return getInfo(*this).IsType; 196651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines} 197651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines 198651f13cea278ec967336033dd032faef0e9fc2ecStephen Hinesbool AttributeList::existsInTarget(const llvm::Triple &T) const { 199651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines return getInfo(*this).ExistsInTarget(T); 200651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines} 201651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines 202651f13cea278ec967336033dd032faef0e9fc2ecStephen Hinesbool AttributeList::isKnownToGCC() const { 203651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines return getInfo(*this).IsKnownToGCC; 204651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines} 205651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines 206651f13cea278ec967336033dd032faef0e9fc2ecStephen Hinesunsigned AttributeList::getSemanticSpelling() const { 207651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines return getInfo(*this).SpellingIndexToSemanticSpelling(*this); 208651f13cea278ec967336033dd032faef0e9fc2ecStephen Hines} 209176edba5311f6eff0cad2631449885ddf4fbc9eaStephen Hines 210176edba5311f6eff0cad2631449885ddf4fbc9eaStephen Hinesbool AttributeList::hasVariadicArg() const { 211176edba5311f6eff0cad2631449885ddf4fbc9eaStephen Hines // If the attribute has the maximum number of optional arguments, we will 212176edba5311f6eff0cad2631449885ddf4fbc9eaStephen Hines // claim that as being variadic. If we someday get an attribute that 213176edba5311f6eff0cad2631449885ddf4fbc9eaStephen Hines // legitimately bumps up against that maximum, we can use another bit to track 214176edba5311f6eff0cad2631449885ddf4fbc9eaStephen Hines // whether it's truly variadic or not. 215176edba5311f6eff0cad2631449885ddf4fbc9eaStephen Hines return getInfo(*this).OptArgs == 15; 216176edba5311f6eff0cad2631449885ddf4fbc9eaStephen Hines} 217