Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -541,6 +541,8 @@ // match rules. // - It has GNU/CXX11 spelling and doesn't require delayed parsing. bit PragmaAttributeSupport; + // Set to true if this attribute accepts parameter pack expansion expressions. + bit AcceptsExprPack = 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 +786,7 @@ } }]; let PragmaAttributeSupport = 1; + let AcceptsExprPack = 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 argument pack expansion">; 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/include/clang/Parse/Parser.h =================================================================== --- clang/include/clang/Parse/Parser.h +++ clang/include/clang/Parse/Parser.h @@ -1814,7 +1814,9 @@ bool ParseExpressionList(SmallVectorImpl &Exprs, SmallVectorImpl &CommaLocs, llvm::function_ref ExpressionStarts = - llvm::function_ref()); + llvm::function_ref(), + bool FailImmediatelyOnInvalidExpr = false, + bool EarlyTypoCorrection = false); /// ParseSimpleExpressionList - A simple comma-separated list of expressions, /// used for misc language extensions. Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -4378,6 +4378,9 @@ const FunctionDecl *FD = nullptr); bool CheckAttrTarget(const ParsedAttr &CurrAttr); bool CheckAttrNoArgs(const ParsedAttr &CurrAttr); + bool checkStringLiteralExpr(const AttributeCommonInfo &CI, const Expr *E, + StringRef &Str, + SourceLocation *ArgLocation = nullptr); bool checkStringLiteralArgumentAttr(const ParsedAttr &Attr, unsigned ArgNum, StringRef &Str, SourceLocation *ArgLocation = nullptr); @@ -10263,6 +10266,10 @@ /// AddAnnotationAttr - Adds an annotation Annot with Args arguments to D. void AddAnnotationAttr(Decl *D, const AttributeCommonInfo &CI, StringRef Annot, MutableArrayRef Args); + /// HandleAnnotationAttr - Verifies the arguments in AllArgs and adds an + /// an annotation with the verified arguments to D. + void HandleAnnotateAttr(Decl *D, const AttributeCommonInfo &CI, + MutableArrayRef AllArgs); /// AddLaunchBoundsAttr - Adds a launch_bounds attribute to a particular /// declaration. Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -300,6 +300,15 @@ #undef CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST } +/// Determine if an attribute accepts parameter packs. +static bool attributeAcceptsExprPack(const IdentifierInfo &II) { +#define CLANG_ATTR_ACCEPTS_EXPR_PACK + return llvm::StringSwitch(normalizeAttrName(II.getName())) +#include "clang/Parse/AttrParserStringSwitches.inc" + .Default(false); +#undef CLANG_ATTR_ACCEPTS_EXPR_PACK +} + /// Determine whether the given attribute parses a type argument. static bool attributeIsTypeArgAttr(const IdentifierInfo &II) { #define CLANG_ATTR_TYPE_ARG_LIST @@ -366,6 +375,8 @@ bool ChangeKWThisToIdent = attributeTreatsKeywordThisAsIdentifier(*AttrName); bool AttributeIsTypeArgAttr = attributeIsTypeArgAttr(*AttrName); + bool AttributeHasVariadicIdentifierArg = + attributeHasVariadicIdentifierArg(*AttrName); // Interpret "kw_this" as an identifier if the attributed requests it. if (ChangeKWThisToIdent && Tok.is(tok::kw_this)) @@ -374,8 +385,8 @@ ArgsVector ArgExprs; if (Tok.is(tok::identifier)) { // If this attribute wants an 'identifier' argument, make it so. - bool IsIdentifierArg = attributeHasIdentifierArg(*AttrName) || - attributeHasVariadicIdentifierArg(*AttrName); + bool IsIdentifierArg = AttributeHasVariadicIdentifierArg || + attributeHasIdentifierArg(*AttrName); ParsedAttr::Kind AttrKind = ParsedAttr::getParsedKind(AttrName, ScopeName, Syntax); @@ -397,42 +408,80 @@ if (!ArgExprs.empty()) ConsumeToken(); - // Parse the non-empty comma-separated list of expressions. - do { - // Interpret "kw_this" as an identifier if the attributed requests it. - if (ChangeKWThisToIdent && Tok.is(tok::kw_this)) - Tok.setKind(tok::identifier); + if (AttributeIsTypeArgAttr) { + // FIXME: Multiple type arguments are not implemented. + TypeResult T = ParseTypeName(); + if (T.isInvalid()) { + SkipUntil(tok::r_paren, StopAtSemi); + return 0; + } + if (T.isUsable()) + TheParsedType = T.get(); + } else if (AttributeHasVariadicIdentifierArg) { + // Parse variadic identifier arg. This can either consume identifiers or + // expressions. + // FIXME: Expression parameter pack expansion is not currently supported + // inside variadic identifier args. + do { + // Interpret "kw_this" as an identifier if the attributed requests it. + if (ChangeKWThisToIdent && Tok.is(tok::kw_this)) + Tok.setKind(tok::identifier); + + ExprResult ArgExpr; + if (Tok.is(tok::identifier)) { + ArgExprs.push_back(ParseIdentifierLoc()); + } else { + bool Uneval = attributeParsedArgsUnevaluated(*AttrName); + EnterExpressionEvaluationContext Unevaluated( + Actions, + Uneval ? Sema::ExpressionEvaluationContext::Unevaluated + : Sema::ExpressionEvaluationContext::ConstantEvaluated); - ExprResult ArgExpr; - if (AttributeIsTypeArgAttr) { - TypeResult T = ParseTypeName(); - if (T.isInvalid()) { - SkipUntil(tok::r_paren, StopAtSemi); - return 0; + ExprResult ArgExpr( + Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression())); + + if (ArgExpr.isInvalid()) { + SkipUntil(tok::r_paren, StopAtSemi); + return 0; + } + ArgExprs.push_back(ArgExpr.get()); } - if (T.isUsable()) - TheParsedType = T.get(); - break; // FIXME: Multiple type arguments are not implemented. - } else if (Tok.is(tok::identifier) && - attributeHasVariadicIdentifierArg(*AttrName)) { - ArgExprs.push_back(ParseIdentifierLoc()); - } else { - bool Uneval = attributeParsedArgsUnevaluated(*AttrName); - EnterExpressionEvaluationContext Unevaluated( - Actions, - Uneval ? Sema::ExpressionEvaluationContext::Unevaluated - : Sema::ExpressionEvaluationContext::ConstantEvaluated); - - ExprResult ArgExpr( - Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression())); - if (ArgExpr.isInvalid()) { + // Eat the comma, move to the next argument + } while (TryConsumeToken(tok::comma)); + } else { + // General case. Parse all available expressions. + bool Uneval = attributeParsedArgsUnevaluated(*AttrName); + EnterExpressionEvaluationContext Unevaluated( + Actions, Uneval + ? Sema::ExpressionEvaluationContext::Unevaluated + : Sema::ExpressionEvaluationContext::ConstantEvaluated); + + CommaLocsTy CommaLocs; + ExprVector ParsedExprs; + if (ParseExpressionList(ParsedExprs, CommaLocs, + llvm::function_ref(), + /*FailImmediatelyOnInvalidExpr=*/true, + /*EarlyTypoCorrection=*/true)) { + SkipUntil(tok::r_paren, StopAtSemi); + return 0; + } + + // Pack expansion must currently be explicitly supported by an attribute. + for (size_t I = 0; I < ParsedExprs.size(); ++I) { + if (!isa(ParsedExprs[I])) + continue; + + if (!attributeAcceptsExprPack(*AttrName)) { + Diag(Tok.getLocation(), + diag::err_attribute_argument_parm_pack_not_supported) + << AttrName; SkipUntil(tok::r_paren, StopAtSemi); return 0; } - ArgExprs.push_back(ArgExpr.get()); } - // Eat the comma, move to the next argument - } while (TryConsumeToken(tok::comma)); + + ArgExprs.insert(ArgExprs.end(), ParsedExprs.begin(), ParsedExprs.end()); + } } SourceLocation RParen = Tok.getLocation(); Index: clang/lib/Parse/ParseExpr.cpp =================================================================== --- clang/lib/Parse/ParseExpr.cpp +++ clang/lib/Parse/ParseExpr.cpp @@ -3356,7 +3356,9 @@ /// \endverbatim bool Parser::ParseExpressionList(SmallVectorImpl &Exprs, SmallVectorImpl &CommaLocs, - llvm::function_ref ExpressionStarts) { + llvm::function_ref ExpressionStarts, + bool FailImmediatelyOnInvalidExpr, + bool EarlyTypoCorrection) { bool SawError = false; while (true) { if (ExpressionStarts) @@ -3369,6 +3371,9 @@ } else Expr = ParseAssignmentExpression(); + if (EarlyTypoCorrection) + Expr = Actions.CorrectDelayedTyposInExpr(Expr); + if (Tok.is(tok::ellipsis)) Expr = Actions.ActOnPackExpansion(Expr.get(), ConsumeToken()); else if (Tok.is(tok::code_completion)) { @@ -3382,8 +3387,10 @@ break; } if (Expr.isInvalid()) { - SkipUntil(tok::comma, tok::r_paren, StopBeforeMatch); SawError = true; + if (FailImmediatelyOnInvalidExpr) + break; + SkipUntil(tok::comma, tok::r_paren, StopBeforeMatch); } else { Exprs.push_back(Expr.get()); } Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -334,6 +334,25 @@ return true; } +/// Check if the argument \p E is a ASCII string literal. If not emit an error +/// and return false, otherwise set \p Str to the value of the string literal +/// and return true. +bool Sema::checkStringLiteralExpr(const AttributeCommonInfo &CI, const Expr *E, + StringRef &Str, SourceLocation *ArgLocation) { + const auto *Literal = dyn_cast(E->IgnoreParenCasts()); + if (ArgLocation) + *ArgLocation = E->getBeginLoc(); + + if (!Literal || !Literal->isAscii()) { + Diag(E->getBeginLoc(), diag::err_attribute_argument_type) + << CI << AANT_ArgumentString; + return false; + } + + Str = Literal->getString(); + return true; +} + /// Check if the argument \p ArgNum of \p Attr is a ASCII string literal. /// If not emit an error and return false. If the argument is an identifier it /// will emit an error with a fixit hint and treat it as if it was a string @@ -356,18 +375,7 @@ // Now check for an actual string literal. Expr *ArgExpr = AL.getArgAsExpr(ArgNum); - const auto *Literal = dyn_cast(ArgExpr->IgnoreParenCasts()); - if (ArgLocation) - *ArgLocation = ArgExpr->getBeginLoc(); - - if (!Literal || !Literal->isAscii()) { - Diag(ArgExpr->getBeginLoc(), diag::err_attribute_argument_type) - << AL << AANT_ArgumentString; - return false; - } - - Str = Literal->getString(); - return true; + return checkStringLiteralExpr(AL, ArgExpr, Str, ArgLocation); } /// Applies the given attribute to the Decl without performing any @@ -4166,21 +4174,42 @@ D->addAttr(Attr); } -static void handleAnnotateAttr(Sema &S, Decl *D, const ParsedAttr &AL) { - // Make sure that there is a string literal as the annotation's first - // argument. +void Sema::HandleAnnotateAttr(Decl *D, const AttributeCommonInfo &CI, + MutableArrayRef AllArgs) { + if (AllArgs.size() < 1) { + Diag(CI.getLoc(), diag::err_attribute_too_few_arguments) << CI << 1; + return; + } + StringRef Str; - if (!S.checkStringLiteralArgumentAttr(AL, 0, Str)) + if (!checkStringLiteralExpr(CI, AllArgs[0], Str)) return; llvm::SmallVector Args; - Args.reserve(AL.getNumArgs() - 1); - for (unsigned Idx = 1; Idx < AL.getNumArgs(); Idx++) { - assert(!AL.isArgIdent(Idx)); - Args.push_back(AL.getArgAsExpr(Idx)); + Args.reserve(AllArgs.size() - 1); + for (unsigned I = 1; I < AllArgs.size(); ++I) + Args.push_back(AllArgs[I]); + + AddAnnotationAttr(D, CI, Str, Args); +} + +static void handleAnnotateAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + llvm::SmallVector AllArgs; + AllArgs.reserve(AL.getNumArgs()); + for (unsigned I = 0; I < AL.getNumArgs(); ++I) { + assert(!AL.isArgIdent(I)); + AllArgs.push_back(AL.getArgAsExpr(I)); + } + + // If the first argument is value dependent we delay setting the arguments. + if (AllArgs.size() && AllArgs[0]->isValueDependent()) { + auto *Attr = AnnotateAttr::CreateWithDelayedArgs( + S.getASTContext(), AllArgs.data(), AllArgs.size(), AL); + D->addAttr(Attr); + return; } - S.AddAnnotationAttr(D, AL, Str, Args); + S.HandleAnnotateAttr(D, AL, AllArgs); } static void handleAlignValueAttr(Sema &S, Decl *D, const ParsedAttr &AL) { Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -188,15 +188,25 @@ const AnnotateAttr *Attr, Decl *New) { EnterExpressionEvaluationContext Unevaluated( S, Sema::ExpressionEvaluationContext::ConstantEvaluated); + + // If the attribute has delayed arguments it will have to instantiate those + // and handle them as new arguments for the attribute. + bool HasDelayedArgs = Attr->delayedArgs_size(); + + ArrayRef ArgsToInstantiate = + HasDelayedArgs + ? ArrayRef{Attr->delayedArgs_begin(), Attr->delayedArgs_end()} + : ArrayRef{Attr->args_begin(), Attr->args_end()}; + 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()); - } - S.AddAnnotationAttr(New, *Attr, Attr->getAnnotation(), Args); + if (S.SubstExprs(ArgsToInstantiate, + /*IsCall=*/false, TemplateArgs, Args)) + return; + + if (HasDelayedArgs) + S.HandleAnnotateAttr(New, *Attr, Args); + else + S.AddAnnotationAttr(New, *Attr, Attr->getAnnotation(), Args); } static Expr *instantiateDependentFunctionAttrCondition( Index: clang/test/Parser/cxx0x-attributes.cpp =================================================================== --- clang/test/Parser/cxx0x-attributes.cpp +++ clang/test/Parser/cxx0x-attributes.cpp @@ -263,6 +263,19 @@ void bar [[noreturn...]] (); // expected-error {{attribute 'noreturn' cannot be used as an attribute pack}} } +template void variadic_nttp() { + 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 argument pack expansion}} + void bor [[clang::annotate("A", "V" ...)]] (); // expected-error {{pack expansion does not contain any unexpanded parameter packs}} + void bir [[clang::annotate("B", {1, 2, 3, 4})]] (); // expected-error {{'annotate' attribute requires parameter 1 to be a constant expression}} expected-note {{subexpression not valid in a constant expression}} + void boo [[unknown::foo(Is...)]] (); // expected-warning {{unknown attribute 'foo' ignored}} + void faz [[clang::annotate("C", (Is + ...))]] (); // expected-warning {{pack fold expression is a C++17 extension}} + void far [[clang::annotate("D", Is...)]] (); + void foz [[clang::annotate("E", 1, 2, 3, Is...)]] (); + void fiz [[clang::annotate("F", Is..., 1, 2, 3)]] (); + void fir [[clang::annotate("G", 1, Is..., 2, 3)]] (); +} + // Expression tests void bar () { // FIXME: GCC accepts [[gnu::noreturn]] on a lambda, even though it appertains Index: clang/test/Sema/annotate.c =================================================================== --- clang/test/Sema/annotate.c +++ clang/test/Sema/annotate.c @@ -12,4 +12,7 @@ int v = __builtin_annotation(z, (char*) L"bar"); // expected-error {{second argument to __builtin_annotation must be a non-wide string constant}} int w = __builtin_annotation(z, "foo"); float b = __builtin_annotation(*a, "foo"); // expected-error {{first argument to __builtin_annotation must be an integer}} + + __attribute__((annotate())) int c; // expected-error {{'annotate' attribute takes at least 1 argument}} + [[clang::annotate()]] int c2; // expected-error {{'annotate' attribute takes at least 1 argument}} } Index: clang/test/SemaCXX/attr-annotate.cpp =================================================================== --- clang/test/SemaCXX/attr-annotate.cpp +++ clang/test/SemaCXX/attr-annotate.cpp @@ -127,4 +127,10 @@ [[clang::annotate("", foldable_but_invalid())]] void f1() {} // expected-error@-1 {{'annotate' attribute requires parameter 1 to be a constant expression}} + +[[clang::annotate()]] void f2() {} +// expected-error@-1 {{'annotate' attribute takes at least 1 argument}} + +template [[clang::annotate()]] void f2() {} +// expected-error@-1 {{'annotate' attribute takes at least 1 argument}} } Index: clang/test/SemaTemplate/attributes.cpp =================================================================== --- clang/test/SemaTemplate/attributes.cpp +++ clang/test/SemaTemplate/attributes.cpp @@ -54,6 +54,7 @@ inline void WBCFRelease(__attribute__((cf_consumed)) T aValue) { if(aValue) CFRelease(aValue); } } +namespace attribute_annotate { // CHECK: FunctionTemplateDecl {{.*}} HasAnnotations // CHECK: AnnotateAttr {{.*}} "ANNOTATE_FOO" // CHECK: AnnotateAttr {{.*}} "ANNOTATE_BAR" @@ -64,6 +65,189 @@ 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>(); } + +template [[clang::annotate(Is...)]] void HasOnlyPackAnnotation() {} // expected-error {{'annotate' attribute takes at least 1 argument}} expected-error {{'annotate' attribute requires a string}} + +void UseOnlyPackAnnotations() { + HasOnlyPackAnnotation<>(); // expected-note {{in instantiation of function template specialization 'attribute_annotate::HasOnlyPackAnnotation<>' requested here}} + HasOnlyPackAnnotation<1>(); // expected-note {{in instantiation of function template specialization 'attribute_annotate::HasOnlyPackAnnotation<1>' requested here}} +} + +// CHECK: ClassTemplateDecl {{.*}} AnnotatedPackTemplateStruct +// CHECK: CXXRecordDecl {{.*}} struct AnnotatedPackTemplateStruct definition +// CHECK: AnnotateAttr {{.*}} "ANNOTATE_FOZ" +// CHECK: ClassTemplateSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition +// CHECK: TemplateArgument{{.*}} type 'int' +// CHECK: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} integral 1 +// CHECK-NEXT: TemplateArgument{{.*}} integral 2 +// CHECK-NEXT: TemplateArgument{{.*}} integral 3 +// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BOO" +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 1 +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 2 +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 3 +// CHECK: ClassTemplateSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition +// CHECK: TemplateArgument{{.*}} type 'float' +// CHECK: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} integral 3 +// CHECK-NEXT: TemplateArgument{{.*}} integral 2 +// CHECK-NEXT: TemplateArgument{{.*}} integral 1 +// CHECK: AnnotateAttr {{.*}} "ANNOTATE_FOZ" +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 4 +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 5 +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 6 +// CHECK: ClassTemplateSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition +// CHECK: TemplateArgument{{.*}} type 'bool' +// CHECK: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} integral 7 +// CHECK-NEXT: TemplateArgument{{.*}} integral 8 +// CHECK-NEXT: TemplateArgument{{.*}} integral 9 +// CHECK: AnnotateAttr {{.*}} "ANNOTATE_FOZ" +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 7 +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 8 +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 9 +// CHECK: ClassTemplatePartialSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition +// CHECK: TemplateArgument{{.*}} type 'int' +// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BOO" +// CHECK: ClassTemplatePartialSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition +// CHECK: TemplateArgument{{.*}} type 'float' +// CHECK: AnnotateAttr {{.*}} "ANNOTATE_FOZ" +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 4 +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 5 +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 6 +template struct [[clang::annotate("ANNOTATE_FOZ", Is...)]] AnnotatedPackTemplateStruct{}; +template struct [[clang::annotate("ANNOTATE_BOO", Is...)]] AnnotatedPackTemplateStruct{}; +template struct [[clang::annotate("ANNOTATE_FOZ", 4, 5, 6)]] AnnotatedPackTemplateStruct{}; +template struct [[clang::annotate(Is...)]] AnnotatedPackTemplateStruct{}; // expected-error {{'annotate' attribute requires a string}} expected-error {{'annotate' attribute takes at least 1 argument}} +void UseAnnotatedPackTemplateStructSpecializations() { + AnnotatedPackTemplateStruct Instance1{}; + AnnotatedPackTemplateStruct Instance2{}; + AnnotatedPackTemplateStruct Instance3{}; + AnnotatedPackTemplateStruct Instance4{}; // expected-note {{in instantiation of template class 'attribute_annotate::AnnotatedPackTemplateStruct' requested here}} + AnnotatedPackTemplateStruct Instance5{}; // expected-note {{in instantiation of template class 'attribute_annotate::AnnotatedPackTemplateStruct' requested here}} +} + +// CHECK: ClassTemplateDecl {{.*}} InvalidAnnotatedPackTemplateStruct +// CHECK: CXXRecordDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition +// CHECK: AnnotateAttr {{.*}} "" +// CHECK: ClassTemplateSpecializationDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition +// CHECK: TemplateArgument{{.*}} type 'int' +// CHECK: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} integral 1 +// CHECK-NEXT: TemplateArgument{{.*}} integral 2 +// CHECK-NEXT: TemplateArgument{{.*}} integral 3 +// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BIR" +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 1 +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 2 +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 3 +// CHECK: ClassTemplateSpecializationDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition +// CHECK: TemplateArgument{{.*}} type 'float' +// CHECK: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} integral 3 +// CHECK-NEXT: TemplateArgument{{.*}} integral 2 +// CHECK-NEXT: TemplateArgument{{.*}} integral 1 +// CHECK: ClassTemplatePartialSpecializationDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition +// CHECK: TemplateArgument{{.*}} type 'int' +// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BIR" +// CHECK: ClassTemplatePartialSpecializationDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition +// CHECK: TemplateArgument{{.*}} type 'float' +// CHECK-NOT: AnnotateAttr {{.*}} "" +template struct [[clang::annotate(Is...)]] InvalidAnnotatedPackTemplateStruct{}; // expected-error {{'annotate' attribute requires a string}} expected-error {{'annotate' attribute takes at least 1 argument}} +template struct [[clang::annotate("ANNOTATE_BIR", Is...)]] InvalidAnnotatedPackTemplateStruct{}; +template struct InvalidAnnotatedPackTemplateStruct {}; +template <> struct InvalidAnnotatedPackTemplateStruct {}; +void UseInvalidAnnotatedPackTemplateStruct() { + InvalidAnnotatedPackTemplateStruct Instance1{}; + InvalidAnnotatedPackTemplateStruct Instance2{}; + InvalidAnnotatedPackTemplateStruct Instance3{}; + InvalidAnnotatedPackTemplateStruct Instance4{}; // expected-note {{in instantiation of template class 'attribute_annotate::InvalidAnnotatedPackTemplateStruct' requested here}} + InvalidAnnotatedPackTemplateStruct Instance5{}; // expected-note {{in instantiation of template class 'attribute_annotate::InvalidAnnotatedPackTemplateStruct' requested here}} +} + +// CHECK: FunctionTemplateDecl {{.*}} RedeclaredAnnotatedFunc +// CHECK: AnnotateAttr {{.*}} "ANNOTATE_FAR" +// CHECK: FunctionDecl {{.*}} RedeclaredAnnotatedFunc +// CHECK: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} integral 1 +// CHECK-NEXT: TemplateArgument{{.*}} integral 2 +// CHECK-NEXT: TemplateArgument{{.*}} integral 3 +// CHECK: AnnotateAttr {{.*}} "ANNOTATE_FAR" +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 1 +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 2 +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 3 +// CHECK: AnnotateAttr {{.*}} "ANNOTATE_FIZ" +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 4 +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 5 +// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BOZ" +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 6 +// CHECK: FunctionTemplateDecl {{.*}} RedeclaredAnnotatedFunc +// CHECK: AnnotateAttr {{.*}} Inherited "ANNOTATE_FAR" +// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BOZ" +// CHECK: FunctionTemplateDecl {{.*}} RedeclaredAnnotatedFunc +// CHECK: AnnotateAttr {{.*}} Inherited "ANNOTATE_FAR" +// CHECK: AnnotateAttr {{.*}} Inherited "ANNOTATE_BOZ" +// CHECK: AnnotateAttr {{.*}} "ANNOTATE_FIZ" +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 4 +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 5 +// CHECK: FunctionTemplateDecl {{.*}} RedeclaredAnnotatedFunc +// CHECK: AnnotateAttr {{.*}} Inherited "ANNOTATE_FAR" +// CHECK: AnnotateAttr {{.*}} Inherited "ANNOTATE_FIZ" +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 4 +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 5 +// CHECK: AnnotateAttr {{.*}} "ANNOTATE_BOZ" +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 6 +template [[clang::annotate("ANNOTATE_FAR", Is...)]] void RedeclaredAnnotatedFunc(); +template [[clang::annotate("ANNOTATE_BOZ", Is...)]] void RedeclaredAnnotatedFunc(); +template [[clang::annotate("ANNOTATE_FIZ", 4, 5)]] void RedeclaredAnnotatedFunc(); +template [[clang::annotate("ANNOTATE_BOZ", 6)]] void RedeclaredAnnotatedFunc(){}; +void UseRedeclaredAnnotatedFunc() { + RedeclaredAnnotatedFunc<1, 2, 3>(); +} + +} // namespace attribute_annotate + 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 @@ -202,9 +202,9 @@ bool Fake; public: - Argument(const Record &Arg, StringRef Attr) - : lowerName(std::string(Arg.getValueAsString("Name"))), - upperName(lowerName), attrName(Attr), isOpt(false), Fake(false) { + Argument(StringRef Arg, StringRef Attr) + : lowerName(std::string(Arg)), upperName(lowerName), attrName(Attr), + isOpt(false), Fake(false) { if (!lowerName.empty()) { lowerName[0] = std::tolower(lowerName[0]); upperName[0] = std::toupper(upperName[0]); @@ -215,6 +215,8 @@ if (lowerName == "interface") lowerName = "interface_"; } + Argument(const Record &Arg, StringRef Attr) + : Argument(Arg.getValueAsString("Name"), Attr) {} virtual ~Argument() = default; StringRef getLowerName() const { return lowerName; } @@ -666,6 +668,11 @@ ArgName(getLowerName().str() + "_"), ArgSizeName(ArgName + "Size"), RangeName(std::string(getLowerName())) {} + VariadicArgument(StringRef Arg, StringRef Attr, std::string T) + : Argument(Arg, Attr), Type(std::move(T)), + ArgName(getLowerName().str() + "_"), ArgSizeName(ArgName + "Size"), + RangeName(std::string(getLowerName())) {} + const std::string &getType() const { return Type; } const std::string &getArgName() const { return ArgName; } const std::string &getArgSizeName() const { return ArgSizeName; } @@ -688,6 +695,18 @@ << "); }\n"; } + void writeSetter(raw_ostream &OS) const { + OS << " void set" << getUpperName() << "(ASTContext &Ctx, "; + writeCtorParameters(OS); + OS << ") {\n"; + OS << " " << ArgSizeName << " = " << getUpperName() << "Size;\n"; + OS << " " << ArgName << " = new (Ctx, 16) " << getType() << "[" + << ArgSizeName << "];\n"; + OS << " "; + writeCtorBody(OS); + OS << " }\n"; + } + void writeCloneArgs(raw_ostream &OS) const override { OS << ArgName << ", " << ArgSizeName; } @@ -1169,6 +1188,9 @@ : VariadicArgument(Arg, Attr, "Expr *") {} + VariadicExprArgument(StringRef ArgName, StringRef Attr) + : VariadicArgument(ArgName, Attr, "Expr *") {} + void writeASTVisitorTraversal(raw_ostream &OS) const override { OS << " {\n"; OS << " " << getType() << " *I = A->" << getLowerName() @@ -2138,6 +2160,11 @@ } } +static bool isTypeArgument(const Record *Arg) { + return !Arg->getSuperClasses().empty() && + Arg->getSuperClasses().back().first->getName() == "TypeArgument"; +} + /// Emits the first-argument-is-type property for attributes. static void emitClangAttrTypeArgList(RecordKeeper &Records, raw_ostream &OS) { OS << "#if defined(CLANG_ATTR_TYPE_ARG_LIST)\n"; @@ -2149,7 +2176,7 @@ if (Args.empty()) continue; - if (Args[0]->getSuperClasses().back().first->getName() != "TypeArgument") + if (!isTypeArgument(Args[0])) continue; // All these spellings take a single type argument. @@ -2179,7 +2206,7 @@ OS << "#endif // CLANG_ATTR_ARG_CONTEXT_LIST\n\n"; } -static bool isIdentifierArgument(Record *Arg) { +static bool isIdentifierArgument(const Record *Arg) { return !Arg->getSuperClasses().empty() && llvm::StringSwitch(Arg->getSuperClasses().back().first->getName()) .Case("IdentifierArgument", true) @@ -2188,7 +2215,7 @@ .Default(false); } -static bool isVariadicIdentifierArgument(Record *Arg) { +static bool isVariadicIdentifierArgument(const Record *Arg) { return !Arg->getSuperClasses().empty() && llvm::StringSwitch( Arg->getSuperClasses().back().first->getName()) @@ -2264,6 +2291,26 @@ OS << "#endif // CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST\n\n"; } +static void emitClangAttrAcceptsExprPack(RecordKeeper &Records, + raw_ostream &OS) { + OS << "#if defined(CLANG_ATTR_ACCEPTS_EXPR_PACK)\n"; + ParsedAttrMap Attrs = getParsedAttrList(Records); + for (const auto &I : Attrs) { + const Record &Attr = *I.second; + + if (!Attr.getValueAsBit("AcceptsExprPack")) + continue; + + // All these spellings take are parsed unevaluated. + forEachUniqueSpelling(Attr, [&](const FlattenedSpelling &S) { + OS << ".Case(\"" << S.name() << "\", " + << "true" + << ")\n"; + }); + } + OS << "#endif // CLANG_ATTR_ACCEPTS_EXPR_PACK\n\n"; +} + static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, bool Header) { std::vector Attrs = Records.getAllDerivedDefinitions("Attr"); @@ -2320,6 +2367,16 @@ std::vector> Args; Args.reserve(ArgRecords.size()); + bool AttrAcceptsExprPack = Attr->getValueAsBit("AcceptsExprPack"); + if (AttrAcceptsExprPack && + std::any_of( + ArgRecords.begin(), ArgRecords.end(), [&](const Record *ArgR) { + return isIdentifierArgument(ArgR) || + isVariadicIdentifierArgument(ArgR) || isTypeArgument(ArgR); + })) + PrintFatalError(Attr->getLoc(), "Attributes accepting packs cannot also " + "have identifier or type arguments."); + bool HasOptArg = false; bool HasFakeArg = false; for (const auto *ArgRecord : ArgRecords) { @@ -2337,6 +2394,16 @@ } } + std::unique_ptr DelayedArgs = nullptr; + if (AttrAcceptsExprPack) { + DelayedArgs = + std::make_unique("DelayedArgs", R.getName()); + if (Header) { + DelayedArgs->writeDeclarations(OS); + OS << "\n\n"; + } + } + if (Header) OS << "public:\n"; @@ -2363,7 +2430,7 @@ }); // Emit CreateImplicit factory methods. - auto emitCreate = [&](bool Implicit, bool emitFake) { + auto emitCreate = [&](bool Implicit, bool DelayedArgsOnly, bool emitFake) { if (Header) OS << " static "; OS << R.getName() << "Attr *"; @@ -2372,12 +2439,20 @@ OS << "Create"; if (Implicit) OS << "Implicit"; + if (DelayedArgsOnly) + OS << "WithDelayedArgs"; OS << "("; OS << "ASTContext &Ctx"; - for (auto const &ai : Args) { - if (ai->isFake() && !emitFake) continue; + if (!DelayedArgsOnly) { + for (auto const &ai : Args) { + if (ai->isFake() && !emitFake) + continue; + OS << ", "; + ai->writeCtorParameters(OS); + } + } else { OS << ", "; - ai->writeCtorParameters(OS); + DelayedArgs->writeCtorParameters(OS); } OS << ", const AttributeCommonInfo &CommonInfo"; if (Header && Implicit) @@ -2391,10 +2466,13 @@ OS << " {\n"; OS << " auto *A = new (Ctx) " << R.getName(); OS << "Attr(Ctx, CommonInfo"; - for (auto const &ai : Args) { - if (ai->isFake() && !emitFake) continue; - OS << ", "; - ai->writeImplicitCtorArgs(OS); + if (!DelayedArgsOnly) { + for (auto const &ai : Args) { + if (ai->isFake() && !emitFake) + continue; + OS << ", "; + ai->writeImplicitCtorArgs(OS); + } } OS << ");\n"; if (Implicit) { @@ -2405,10 +2483,16 @@ "!A->getAttrName())\n"; OS << " A->setAttributeSpellingListIndex(0);\n"; } + if (DelayedArgsOnly) { + OS << " A->setDelayedArgs(Ctx, "; + DelayedArgs->writeImplicitCtorArgs(OS); + OS << ");\n"; + } OS << " return A;\n}\n\n"; }; - auto emitCreateNoCI = [&](bool Implicit, bool emitFake) { + auto emitCreateNoCI = [&](bool Implicit, bool DelayedArgsOnly, + bool emitFake) { if (Header) OS << " static "; OS << R.getName() << "Attr *"; @@ -2417,12 +2501,20 @@ OS << "Create"; if (Implicit) OS << "Implicit"; + if (DelayedArgsOnly) + OS << "WithDelayedArgs"; OS << "("; OS << "ASTContext &Ctx"; - for (auto const &ai : Args) { - if (ai->isFake() && !emitFake) continue; + if (!DelayedArgsOnly) { + for (auto const &ai : Args) { + if (ai->isFake() && !emitFake) + continue; + OS << ", "; + ai->writeCtorParameters(OS); + } + } else { OS << ", "; - ai->writeCtorParameters(OS); + DelayedArgs->writeCtorParameters(OS); } OS << ", SourceRange Range, AttributeCommonInfo::Syntax Syntax"; if (!ElideSpelling) { @@ -2451,38 +2543,55 @@ OS << " return Create"; if (Implicit) OS << "Implicit"; + if (DelayedArgsOnly) + OS << "WithDelayedArgs"; OS << "(Ctx"; - for (auto const &ai : Args) { - if (ai->isFake() && !emitFake) continue; + if (!DelayedArgsOnly) { + for (auto const &ai : Args) { + if (ai->isFake() && !emitFake) + continue; + OS << ", "; + ai->writeImplicitCtorArgs(OS); + } + } else { OS << ", "; - ai->writeImplicitCtorArgs(OS); + DelayedArgs->writeImplicitCtorArgs(OS); } OS << ", I);\n"; OS << "}\n\n"; }; - auto emitCreates = [&](bool emitFake) { - emitCreate(true, emitFake); - emitCreate(false, emitFake); - emitCreateNoCI(true, emitFake); - emitCreateNoCI(false, emitFake); + auto emitCreates = [&](bool DelayedArgsOnly, bool emitFake) { + emitCreate(true, DelayedArgsOnly, emitFake); + emitCreate(false, DelayedArgsOnly, emitFake); + emitCreateNoCI(true, DelayedArgsOnly, emitFake); + emitCreateNoCI(false, DelayedArgsOnly, emitFake); }; if (Header) OS << " // Factory methods\n"; // Emit a CreateImplicit that takes all the arguments. - emitCreates(true); + emitCreates(false, true); // Emit a CreateImplicit that takes all the non-fake arguments. if (HasFakeArg) - emitCreates(false); + emitCreates(false, false); + + // Emit a CreateWithDelayedArgs that takes only the dependent argument + // expressions. + if (DelayedArgs) + emitCreates(true, false); // Emit constructors. - auto emitCtor = [&](bool emitOpt, bool emitFake) { + auto emitCtor = [&](bool emitOpt, bool emitFake, bool emitNoArgs) { auto shouldEmitArg = [=](const std::unique_ptr &arg) { - if (arg->isFake()) return emitFake; - if (arg->isOptional()) return emitOpt; + if (emitNoArgs) + return false; + if (arg->isFake()) + return emitFake; + if (arg->isOptional()) + return emitOpt; return true; }; if (Header) @@ -2493,7 +2602,8 @@ << "Attr(ASTContext &Ctx, const AttributeCommonInfo &CommonInfo"; OS << '\n'; for (auto const &ai : Args) { - if (!shouldEmitArg(ai)) continue; + if (!shouldEmitArg(ai)) + continue; OS << " , "; ai->writeCtorParameters(OS); OS << "\n"; @@ -2523,11 +2633,17 @@ } OS << "\n"; } + if (DelayedArgs) { + OS << " , "; + DelayedArgs->writeCtorDefaultInitializers(OS); + OS << "\n"; + } OS << " {\n"; for (auto const &ai : Args) { - if (!shouldEmitArg(ai)) continue; + if (!shouldEmitArg(ai)) + continue; ai->writeCtorBody(OS); } OS << "}\n\n"; @@ -2538,15 +2654,19 @@ // Emit a constructor that includes all the arguments. // This is necessary for cloning. - emitCtor(true, true); + emitCtor(true, true, false); // Emit a constructor that takes all the non-fake arguments. if (HasFakeArg) - emitCtor(true, false); + emitCtor(true, false, false); // Emit a constructor that takes all the non-fake, non-optional arguments. if (HasOptArg) - emitCtor(false, false); + emitCtor(false, false, false); + + // Emit constructors that takes no arguments. + if (DelayedArgs) + emitCtor(false, false, true); if (Header) { OS << '\n'; @@ -2592,6 +2712,11 @@ } if (Header) { + if (DelayedArgs) { + DelayedArgs->writeAccessors(OS); + DelayedArgs->writeSetter(OS); + } + OS << R.getValueAsString("AdditionalMembers"); OS << "\n\n"; @@ -2600,6 +2725,9 @@ OS << "};\n\n"; } else { + if (DelayedArgs) + DelayedArgs->writeAccessorDefinitions(OS); + OS << R.getName() << "Attr *" << R.getName() << "Attr::clone(ASTContext &C) const {\n"; OS << " auto *A = new (C) " << R.getName() << "Attr(C, *this"; @@ -2611,6 +2739,11 @@ OS << " A->Inherited = Inherited;\n"; OS << " A->IsPackExpansion = IsPackExpansion;\n"; OS << " A->setImplicit(Implicit);\n"; + if (DelayedArgs) { + OS << " A->setDelayedArgs(C, "; + DelayedArgs->writeCloneArgs(OS); + OS << ");\n"; + } OS << " return A;\n}\n\n"; writePrettyPrintFunction(R, Args, OS); @@ -2924,6 +3057,7 @@ std::vector Attrs = Records.getAllDerivedDefinitions("Attr"), ArgRecords; std::vector> Args; + std::unique_ptr DelayedArgs; OS << " switch (Kind) {\n"; for (const auto *Attr : Attrs) { @@ -2936,6 +3070,12 @@ OS << " bool isInherited = Record.readInt();\n"; OS << " bool isImplicit = Record.readInt();\n"; OS << " bool isPackExpansion = Record.readInt();\n"; + DelayedArgs = nullptr; + if (Attr->getValueAsBit("AcceptsExprPack")) { + DelayedArgs = + std::make_unique("DelayedArgs", R.getName()); + DelayedArgs->writePCHReadDecls(OS); + } ArgRecords = R.getValueAsListOfDefs("Args"); Args.clear(); for (const auto *Arg : ArgRecords) { @@ -2952,6 +3092,12 @@ OS << " cast(New)->setInherited(isInherited);\n"; OS << " New->setImplicit(isImplicit);\n"; OS << " New->setPackExpansion(isPackExpansion);\n"; + if (DelayedArgs) { + OS << " cast<" << R.getName() + << "Attr>(New)->setDelayedArgs(Context, "; + DelayedArgs->writePCHReadArgs(OS); + OS << ");\n"; + } OS << " break;\n"; OS << " }\n"; } @@ -2979,6 +3125,8 @@ OS << " Record.push_back(SA->isInherited());\n"; OS << " Record.push_back(A->isImplicit());\n"; OS << " Record.push_back(A->isPackExpansion());\n"; + if (Attr->getValueAsBit("AcceptsExprPack")) + VariadicExprArgument("DelayedArgs", R.getName()).writePCHWrite(OS); for (const auto *Arg : Args) createArgument(*Arg, R.getName())->writePCHWrite(OS); @@ -3266,6 +3414,10 @@ for (const auto *Arg : ArgRecords) createArgument(*Arg, R.getName())->writeASTVisitorTraversal(OS); + if (Attr->getValueAsBit("AcceptsExprPack")) + VariadicExprArgument("DelayedArgs", R.getName()) + .writeASTVisitorTraversal(OS); + OS << " return true;\n"; OS << "}\n\n"; } @@ -4173,6 +4325,9 @@ for (const auto *Arg : Args) createArgument(*Arg, R.getName())->writeDump(SS); + if (Attr->getValueAsBit("AcceptsExprPack")) + VariadicExprArgument("DelayedArgs", R.getName()).writeDump(OS); + if (SS.tell()) { OS << " void Visit" << R.getName() << "Attr(const " << R.getName() << "Attr *A) {\n"; @@ -4200,6 +4355,8 @@ Args = R.getValueAsListOfDefs("Args"); for (const auto *Arg : Args) createArgument(*Arg, R.getName())->writeDumpChildren(SS); + if (Attr->getValueAsBit("AcceptsExprPack")) + VariadicExprArgument("DelayedArgs", R.getName()).writeDumpChildren(SS); if (SS.tell()) { OS << " void Visit" << R.getName() << "Attr(const " << R.getName() << "Attr *A) {\n"; @@ -4219,6 +4376,7 @@ emitClangAttrIdentifierArgList(Records, OS); emitClangAttrVariadicIdentifierArgList(Records, OS); emitClangAttrThisIsaIdentifierArgList(Records, OS); + emitClangAttrAcceptsExprPack(Records, OS); emitClangAttrTypeArgList(Records, OS); emitClangAttrLateParsedList(Records, OS); }