Index: clang/include/clang/AST/Attr.h =================================================================== --- clang/include/clang/AST/Attr.h +++ clang/include/clang/AST/Attr.h @@ -48,6 +48,7 @@ unsigned Inherited : 1; unsigned IsPackExpansion : 1; unsigned Implicit : 1; + unsigned ArgsDelayed : 1; // FIXME: These are properties of the attribute kind, not state for this // instance of the attribute. unsigned IsLateParsed : 1; @@ -74,8 +75,8 @@ Attr(ASTContext &Context, const AttributeCommonInfo &CommonInfo, attr::Kind AK, bool IsLateParsed) : AttributeCommonInfo(CommonInfo), AttrKind(AK), Inherited(false), - IsPackExpansion(false), Implicit(false), IsLateParsed(IsLateParsed), - InheritEvenIfAlreadyPresent(false) {} + IsPackExpansion(false), Implicit(false), ArgsDelayed(false), + IsLateParsed(IsLateParsed), InheritEvenIfAlreadyPresent(false) {} public: attr::Kind getKind() const { return static_cast(AttrKind); } @@ -97,6 +98,9 @@ void setPackExpansion(bool PE) { IsPackExpansion = PE; } bool isPackExpansion() const { return IsPackExpansion; } + void setArgsDelayed(bool PE) { ArgsDelayed = PE; } + bool areArgsDelayed() const { return ArgsDelayed; } + // Clone this attribute. Attr *clone(ASTContext &C) const; Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -205,7 +205,7 @@ class TypeArgument : Argument; class UnsignedArgument : Argument; class VariadicUnsignedArgument : Argument; -class VariadicExprArgument : Argument; +class VariadicExprArgument : Argument; class VariadicStringArgument : Argument; class VariadicIdentifierArgument : Argument; @@ -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 = []; @@ -770,7 +772,9 @@ def Annotate : InheritableParamAttr { let Spellings = [Clang<"annotate">]; - let Args = [StringArgument<"Annotation">, VariadicExprArgument<"Args">]; + let Args = [StringArgument<"Annotation">, + VariadicExprArgument<"Args">, + VariadicExprArgument<"DelayedArgs", /*fake=*/1>]; // Ensure that the annotate attribute can be used with // '#pragma clang attribute' even though it has no subject list. let AdditionalMembers = [{ @@ -784,6 +788,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 @@ -4376,6 +4376,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); @@ -10258,9 +10261,19 @@ /// declaration. void AddAlignValueAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E); + /// CheckAnnotationAttrArgExprs - Checks and returns the argument expressions + /// of an annotation attribute. + bool CheckAnnotationAttrArgExprs(const AttributeCommonInfo &CI, + MutableArrayRef ArgExprs, + StringRef &Annot, + SmallVector &Args); /// AddAnnotationAttr - Adds an annotation Annot with Args arguments to D. void AddAnnotationAttr(Decl *D, const AttributeCommonInfo &CI, StringRef Annot, MutableArrayRef Args); + /// AddAnnotationAttrWithDelayedArgs - Adds an annotation with delayed + /// arguments to D. + void AddAnnotationAttrWithDelayedArgs(Decl *D, const AttributeCommonInfo &CI, + MutableArrayRef DelayedArgs); /// 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: Variadic identifier args do not currently support parameter + // packs. + 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 @@ -4118,6 +4126,19 @@ RD->addAttr(::new (S.Context) TransparentUnionAttr(S.Context, AL)); } +bool Sema::CheckAnnotationAttrArgExprs(const AttributeCommonInfo &CI, + MutableArrayRef ArgExprs, + StringRef &Annot, + SmallVector &Args) { + assert(ArgExprs.size()); + if (!checkStringLiteralExpr(CI, ArgExprs[0], Annot)) + return false; + Args.reserve(ArgExprs.size() - 1); + for (unsigned I = 1; I < ArgExprs.size(); ++I) + Args.push_back(ArgExprs[I]); + return true; +} + void Sema::AddAnnotationAttr(Decl *D, const AttributeCommonInfo &CI, StringRef Str, MutableArrayRef Args) { auto *Attr = AnnotateAttr::Create(Context, Str, Args.data(), Args.size(), CI); @@ -4165,19 +4186,36 @@ D->addAttr(Attr); } +void Sema::AddAnnotationAttrWithDelayedArgs( + Decl *D, const AttributeCommonInfo &CI, + MutableArrayRef DelayedArgs) { + auto *Attr = AnnotateAttr::Create(Context, "", nullptr, 0, DelayedArgs.data(), + DelayedArgs.size(), CI); + Attr->setArgsDelayed(true); + 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. - StringRef Str; - if (!S.checkStringLiteralArgumentAttr(AL, 0, Str)) + bool HasValueDependentArg = false; + llvm::SmallVector AllArgs; + AllArgs.reserve(AL.getNumArgs()); + for (unsigned I = 0; I < AL.getNumArgs(); ++I) { + assert(!AL.isArgIdent(I)); + Expr *ArgExpr = AL.getArgAsExpr(I); + AllArgs.push_back(ArgExpr); + if (ArgExpr->isValueDependent()) + HasValueDependentArg = true; + } + + if (HasValueDependentArg) { + S.AddAnnotationAttrWithDelayedArgs(D, AL, AllArgs); return; + } + StringRef Str; 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)); - } + if (!S.CheckAnnotationAttrArgExprs(AL, AllArgs, Str, Args)) + return; S.AddAnnotationAttr(D, AL, Str, Args); } 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); + + StringRef Annotation; SmallVector Args; - Args.reserve(Attr->args_size()); - for (auto *E : Attr->args()) { - ExprResult Result = S.SubstExpr(E, TemplateArgs); - if (!Result.isUsable()) + if (Attr->areArgsDelayed()) { + SmallVector InstantiatedArgs; + if (S.SubstExprs(ArrayRef{Attr->delayedArgs_begin(), + Attr->delayedArgs_end()}, + /*IsCall=*/false, TemplateArgs, InstantiatedArgs)) + return; + if (!S.CheckAnnotationAttrArgExprs(*Attr, InstantiatedArgs, Annotation, + Args)) + return; + } else { + Annotation = Attr->getAnnotation(); + if (S.SubstExprs(ArrayRef{Attr->args_begin(), Attr->args_end()}, + /*IsCall=*/false, TemplateArgs, Args)) return; - Args.push_back(Result.get()); } - S.AddAnnotationAttr(New, *Attr, Attr->getAnnotation(), Args); + S.AddAnnotationAttr(New, *Attr, Annotation, 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/SemaTemplate/attributes.cpp =================================================================== --- clang/test/SemaTemplate/attributes.cpp +++ clang/test/SemaTemplate/attributes.cpp @@ -64,6 +64,24 @@ template [[clang::annotate("ANNOTATE_FOO"), clang::annotate("ANNOTATE_BAR")]] void HasAnnotations(); void UseAnnotations() { HasAnnotations(); } +// CHECK: FunctionTemplateDecl {{.*}} HasPackAnnotations +// CHECK: AnnotateAttr +// CHECK-NEXT: StringLiteral {{.*}} "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 @@ -2138,6 +2138,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 +2154,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 +2184,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 +2193,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 +2269,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 +2345,17 @@ std::vector> Args; Args.reserve(ArgRecords.size()); + bool AttrAcceptsExprPack = Attr->getValueAsBit("AcceptsExprPack"); + assert((!AttrAcceptsExprPack || + std::none_of(ArgRecords.begin(), ArgRecords.end(), + [&](const Record *ArgR) { + return isIdentifierArgument(ArgR) || + isVariadicIdentifierArgument(ArgR) || + isTypeArgument(ArgR); + })) && + "Attributes accepting packs cannot also have identifier or type " + "arguments."); + bool HasOptArg = false; bool HasFakeArg = false; for (const auto *ArgRecord : ArgRecords) { @@ -2611,6 +2647,7 @@ OS << " A->Inherited = Inherited;\n"; OS << " A->IsPackExpansion = IsPackExpansion;\n"; OS << " A->setImplicit(Implicit);\n"; + OS << " A->setArgsDelayed(ArgsDelayed);\n"; OS << " return A;\n}\n\n"; writePrettyPrintFunction(R, Args, OS); @@ -2936,6 +2973,7 @@ OS << " bool isInherited = Record.readInt();\n"; OS << " bool isImplicit = Record.readInt();\n"; OS << " bool isPackExpansion = Record.readInt();\n"; + OS << " bool areArgsDelayed = Record.readInt();\n"; ArgRecords = R.getValueAsListOfDefs("Args"); Args.clear(); for (const auto *Arg : ArgRecords) { @@ -2952,6 +2990,7 @@ OS << " cast(New)->setInherited(isInherited);\n"; OS << " New->setImplicit(isImplicit);\n"; OS << " New->setPackExpansion(isPackExpansion);\n"; + OS << " New->setArgsDelayed(areArgsDelayed);\n"; OS << " break;\n"; OS << " }\n"; } @@ -2979,6 +3018,7 @@ OS << " Record.push_back(SA->isInherited());\n"; OS << " Record.push_back(A->isImplicit());\n"; OS << " Record.push_back(A->isPackExpansion());\n"; + OS << " Record.push_back(A->areArgsDelayed());\n"; for (const auto *Arg : Args) createArgument(*Arg, R.getName())->writePCHWrite(OS); @@ -4219,6 +4259,7 @@ emitClangAttrIdentifierArgList(Records, OS); emitClangAttrVariadicIdentifierArgList(Records, OS); emitClangAttrThisIsaIdentifierArgList(Records, OS); + emitClangAttrAcceptsExprPack(Records, OS); emitClangAttrTypeArgList(Records, OS); emitClangAttrLateParsedList(Records, OS); }