Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -541,6 +541,9 @@ // match rules. // - It has GNU/CXX11 spelling and doesn't require delayed parsing. bit PragmaAttributeSupport; + // Set to true for attributes that support parameter pack expansion in its + // arguments. + bit ParmExpansionArgsSupport = 0; // Lists language options, one of which is required to be true for the // attribute to be applicable. If empty, no language options are required. list LangOpts = []; @@ -784,6 +787,7 @@ } }]; let PragmaAttributeSupport = 1; + let ParmExpansionArgsSupport = 1; let Documentation = [Undocumented]; } Index: clang/include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticParseKinds.td +++ clang/include/clang/Basic/DiagnosticParseKinds.td @@ -719,6 +719,8 @@ def err_l_square_l_square_not_attribute : Error< "C++11 only allows consecutive left square brackets when " "introducing an attribute">; +def err_attribute_argument_parm_pack_not_supported : Error< + "attribute %0 does not support parameter pack expansion in arguments">; def err_ms_declspec_type : Error< "__declspec attributes must be an identifier or string literal">; def err_ms_property_no_getter_or_putter : Error< Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -319,6 +319,16 @@ #undef CLANG_ATTR_ARG_CONTEXT_LIST } +/// attributeParmPackArgsSupport - Return true if the attribute supports +/// parameter pack expansion in its arguments. +static bool attributeParmPackArgsSupport(const IdentifierInfo &II) { +#define CLANG_ATTR_PARM_EXPANSION_ARGS_SUPPORT_LIST + return llvm::StringSwitch(normalizeAttrName(II.getName())) +#include "clang/Parse/AttrParserStringSwitches.inc" + .Default(false); +#undef CLANG_ATTR_PARM_EXPANSION_ARGS_SUPPORT_LIST +} + IdentifierLoc *Parser::ParseIdentifierLoc() { assert(Tok.is(tok::identifier) && "expected an identifier"); IdentifierLoc *IL = IdentifierLoc::create(Actions.Context, @@ -425,6 +435,18 @@ ExprResult ArgExpr( Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression())); + + if (Tok.is(tok::ellipsis)) { + if (!attributeParmPackArgsSupport(*AttrName)) { + Diag(Tok.getLocation(), + diag::err_attribute_argument_parm_pack_not_supported) + << AttrName; + SkipUntil(tok::r_paren, StopAtSemi); + return 0; + } + ArgExpr = Actions.ActOnPackExpansion(ArgExpr.get(), ConsumeToken()); + } + if (ArgExpr.isInvalid()) { SkipUntil(tok::r_paren, StopAtSemi); return 0; Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -189,13 +189,9 @@ EnterExpressionEvaluationContext Unevaluated( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); SmallVector Args; - Args.reserve(Attr->args_size()); - for (auto *E : Attr->args()) { - ExprResult Result = S.SubstExpr(E, TemplateArgs); - if (!Result.isUsable()) - return; - Args.push_back(Result.get()); - } + if (S.SubstExprs(ArrayRef(Attr->args_begin(), Attr->args_end()), + /*IsCall=*/false, TemplateArgs, Args)) + return; S.AddAnnotationAttr(New, *Attr, Attr->getAnnotation(), Args); } Index: clang/test/Parser/cxx0x-attributes.cpp =================================================================== --- clang/test/Parser/cxx0x-attributes.cpp +++ clang/test/Parser/cxx0x-attributes.cpp @@ -258,8 +258,10 @@ [[]] return; } -template void variadic() { +template void variadic() { void bar [[noreturn...]] (); // expected-error {{attribute 'noreturn' cannot be used as an attribute pack}} + void baz [[clang::no_sanitize(Is...)]] (); // expected-error {{attribute 'no_sanitize' does not support parameter pack expansion in arguments}} + void boo [[unknown::foo(Is...)]] (); // expected-warning {{unknown attribute 'foo' ignored}} } // Expression tests Index: clang/test/SemaTemplate/attributes.cpp =================================================================== --- clang/test/SemaTemplate/attributes.cpp +++ clang/test/SemaTemplate/attributes.cpp @@ -64,6 +64,23 @@ template [[clang::annotate("ANNOTATE_FOO"), clang::annotate("ANNOTATE_BAR")]] void HasAnnotations(); void UseAnnotations() { HasAnnotations(); } +// CHECK: FunctionTemplateDecl {{.*}} HasPackAnnotations +// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BAZ" +// CHECK: FunctionDecl {{.*}} HasPackAnnotations +// CHECK: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} integral 1 +// CHECK-NEXT: TemplateArgument{{.*}} integral 2 +// CHECK-NEXT: TemplateArgument{{.*}} integral 3 +// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BAZ" +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 1 +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 2 +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 3 +template [[clang::annotate("ANNOTATE_BAZ", Is...)]] void HasPackAnnotations(); +void UsePackAnnotations() { HasPackAnnotations<1, 2, 3>(); } + namespace preferred_name { int x [[clang::preferred_name("frank")]]; // expected-error {{expected a type}} int y [[clang::preferred_name(int)]]; // expected-warning {{'preferred_name' attribute only applies to class templates}} Index: clang/utils/TableGen/ClangAttrEmitter.cpp =================================================================== --- clang/utils/TableGen/ClangAttrEmitter.cpp +++ clang/utils/TableGen/ClangAttrEmitter.cpp @@ -1697,6 +1697,22 @@ OS << "#endif // CLANG_ATTR_LATE_PARSED_LIST\n\n"; } +// Emits the ParmExpansionArgsSupport property for attributes. +static void emitClangAttrParmExpansionArgsSupportList(RecordKeeper &Records, + raw_ostream &OS) { + OS << "#if defined(CLANG_ATTR_PARM_EXPANSION_ARGS_SUPPORT_LIST)\n"; + std::vector Attrs = Records.getAllDerivedDefinitions("Attr"); + + for (const auto *Attr : Attrs) { + if (Attr->getValueAsBit("ParmExpansionArgsSupport")) { + std::vector Spellings = GetFlattenedSpellings(*Attr); + for (const auto &I : Spellings) + OS << ".Case(\"" << I.name() << "\", true)\n"; + } + } + OS << "#endif // CLANG_ATTR_PARM_EXPANSION_ARGS_SUPPORT_LIST\n\n"; +} + static bool hasGNUorCXX11Spelling(const Record &Attribute) { std::vector Spellings = GetFlattenedSpellings(Attribute); for (const auto &I : Spellings) { @@ -4221,6 +4237,7 @@ emitClangAttrThisIsaIdentifierArgList(Records, OS); emitClangAttrTypeArgList(Records, OS); emitClangAttrLateParsedList(Records, OS); + emitClangAttrParmExpansionArgsSupportList(Records, OS); } void EmitClangAttrSubjectMatchRulesParserStringSwitches(RecordKeeper &Records,