Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -75,6 +75,8 @@ Attribute Changes in Clang -------------------------- +- Added support for parameter pack expansion in `clang::annotate`. + Windows Support --------------- @@ -144,6 +146,12 @@ Internal API Changes -------------------- +- Added a new attribute flag `AcceptsExprPack` that when set allows expression + pack expansions in the parsed arguments of the corresponding attribute. + Additionally it introduces delaying of attribute arguments, adding common + handling for creating attributes that cannot be fully initialized prior to + template instantiation. + Build System Changes -------------------- 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/ParsedAttr.h =================================================================== --- clang/include/clang/Sema/ParsedAttr.h +++ clang/include/clang/Sema/ParsedAttr.h @@ -49,8 +49,12 @@ unsigned NumArgs : 4; /// The number of optional arguments of this attributes. unsigned OptArgs : 4; + /// The number of non-fake arguments specified in the attribute definition. + unsigned NumArgMembers : 4; /// True if the parsing does not match the semantic content. unsigned HasCustomParsing : 1; + // True if this attribute accepts expression parameter pack expansions. + unsigned AcceptsExprPack : 1; /// True if this attribute is only available for certain targets. unsigned IsTargetSpecific : 1; /// True if this attribute applies to types. @@ -106,6 +110,10 @@ spellingIndexToSemanticSpelling(const ParsedAttr &Attr) const { return UINT_MAX; } + /// Returns true if the specified parameter index for this attribute in + /// Attr.td is an ExprArgument or VariadicExprArgument, or a subclass thereof; + /// returns false otherwise. + virtual bool isParamExpr(size_t N) const { return false; } /// Populate Rules with the match rules of this attribute. virtual void getPragmaAttributeMatchRules( llvm::SmallVectorImpl> &Rules, @@ -601,9 +609,13 @@ bool isStmtAttr() const; bool hasCustomParsing() const; + bool acceptsExprPack() const; + bool isParamExpr(size_t N) const; unsigned getMinArgs() const; unsigned getMaxArgs() const; + unsigned getNumArgMembers() const; bool hasVariadicArg() const; + void handleAttrWithDelayedArgs(Sema &S, Decl *D) const; bool diagnoseAppertainsTo(class Sema &S, const Decl *D) const; bool diagnoseAppertainsTo(class Sema &S, const Stmt *St) const; bool diagnoseMutualExclusion(class Sema &S, const Decl *D) const; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -4366,8 +4366,10 @@ /// such as checking whether a parameter was properly specified, or the /// correct number of arguments were passed, etc. Returns true if the /// attribute has been diagnosed. - bool checkCommonAttributeFeatures(const Decl *D, const ParsedAttr &A); - bool checkCommonAttributeFeatures(const Stmt *S, const ParsedAttr &A); + bool checkCommonAttributeFeatures(const Decl *D, const ParsedAttr &A, + bool SkipArgCountCheck = false); + bool checkCommonAttributeFeatures(const Stmt *S, const ParsedAttr &A, + bool SkipArgCountCheck = false); /// Determine if type T is a valid subject for a nonnull and similar /// attributes. By default, we look through references (the behavior used by @@ -4380,6 +4382,9 @@ const FunctionDecl *FD = nullptr); bool CheckAttrTarget(const ParsedAttr &CurrAttr); bool CheckAttrNoArgs(const ParsedAttr &CurrAttr); + bool checkStringLiteralArgumentAttr(const AttributeCommonInfo &CI, + const Expr *E, StringRef &Str, + SourceLocation *ArgLocation = nullptr); bool checkStringLiteralArgumentAttr(const ParsedAttr &Attr, unsigned ArgNum, StringRef &Str, SourceLocation *ArgLocation = nullptr); 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,81 @@ 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. Variadic identifier args do not support parameter packs + // because those are typically used for attributes with enumeration + // arguments, and those enumerations are not something the user could + // express via a pack. + 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 @@ -3381,7 +3381,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) @@ -3394,6 +3396,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)) { @@ -3407,8 +3412,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/ParsedAttr.cpp =================================================================== --- clang/lib/Sema/ParsedAttr.cpp +++ clang/lib/Sema/ParsedAttr.cpp @@ -155,6 +155,10 @@ return getMinArgs() + getInfo().OptArgs; } +unsigned ParsedAttr::getNumArgMembers() const { + return getInfo().NumArgMembers; +} + bool ParsedAttr::hasCustomParsing() const { return getInfo().HasCustomParsing; } @@ -208,6 +212,8 @@ return getInfo().IsSupportedByPragmaAttribute; } +bool ParsedAttr::acceptsExprPack() const { return getInfo().AcceptsExprPack; } + unsigned ParsedAttr::getSemanticSpelling() const { return getInfo().spellingIndexToSemanticSpelling(*this); } @@ -220,6 +226,14 @@ return getInfo().OptArgs == 15; } +bool ParsedAttr::isParamExpr(size_t N) const { + return getInfo().isParamExpr(N); +} + +void ParsedAttr::handleAttrWithDelayedArgs(Sema &S, Decl *D) const { + ::handleAttrWithDelayedArgs(S, D, *this); +} + static unsigned getNumAttributeArgs(const ParsedAttr &AL) { // FIXME: Include the type in the argument list. return AL.getNumArgs() + AL.hasParsedType(); Index: clang/lib/Sema/SemaAttr.cpp =================================================================== --- clang/lib/Sema/SemaAttr.cpp +++ clang/lib/Sema/SemaAttr.cpp @@ -1213,8 +1213,9 @@ } template -static bool checkCommonAttributeFeatures(Sema& S, const Ty *Node, - const ParsedAttr& A) { +static bool checkCommonAttributeFeatures(Sema &S, const Ty *Node, + const ParsedAttr &A, + bool SkipArgCountCheck) { // Several attributes carry different semantics than the parsing requires, so // those are opted out of the common argument checks. // @@ -1240,26 +1241,30 @@ if (A.hasCustomParsing()) return false; - if (A.getMinArgs() == A.getMaxArgs()) { - // If there are no optional arguments, then checking for the argument count - // is trivial. - if (!A.checkExactlyNumArgs(S, A.getMinArgs())) - return true; - } else { - // There are optional arguments, so checking is slightly more involved. - if (A.getMinArgs() && !A.checkAtLeastNumArgs(S, A.getMinArgs())) - return true; - else if (!A.hasVariadicArg() && A.getMaxArgs() && - !A.checkAtMostNumArgs(S, A.getMaxArgs())) - return true; + if (!SkipArgCountCheck) { + if (A.getMinArgs() == A.getMaxArgs()) { + // If there are no optional arguments, then checking for the argument + // count is trivial. + if (!A.checkExactlyNumArgs(S, A.getMinArgs())) + return true; + } else { + // There are optional arguments, so checking is slightly more involved. + if (A.getMinArgs() && !A.checkAtLeastNumArgs(S, A.getMinArgs())) + return true; + else if (!A.hasVariadicArg() && A.getMaxArgs() && + !A.checkAtMostNumArgs(S, A.getMaxArgs())) + return true; + } } return false; } -bool Sema::checkCommonAttributeFeatures(const Decl *D, const ParsedAttr &A) { - return ::checkCommonAttributeFeatures(*this, D, A); +bool Sema::checkCommonAttributeFeatures(const Decl *D, const ParsedAttr &A, + bool SkipArgCountCheck) { + return ::checkCommonAttributeFeatures(*this, D, A, SkipArgCountCheck); } -bool Sema::checkCommonAttributeFeatures(const Stmt *S, const ParsedAttr &A) { - return ::checkCommonAttributeFeatures(*this, S, A); +bool Sema::checkCommonAttributeFeatures(const Stmt *S, const ParsedAttr &A, + bool SkipArgCountCheck) { + return ::checkCommonAttributeFeatures(*this, S, A, SkipArgCountCheck); } Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -334,6 +334,26 @@ 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::checkStringLiteralArgumentAttr(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 +376,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 checkStringLiteralArgumentAttr(AL, ArgExpr, Str, ArgLocation); } /// Applies the given attribute to the Decl without performing any @@ -8123,6 +8132,37 @@ // Top Level Sema Entry Points //===----------------------------------------------------------------------===// +// Returns true if the attribute must delay setting its arguments until after +// template instantiation, and false otherwise. +static bool MustDelayAttributeArguments(const ParsedAttr &AL) { + // Only attributes that accept expression parameter packs can delay arguments. + if (!AL.acceptsExprPack()) + return false; + + bool AttrHasVariadicArg = AL.hasVariadicArg(); + unsigned AttrNumArgs = AL.getNumArgMembers(); + for (size_t I = 0; I < std::min(AL.getNumArgs(), AttrNumArgs); ++I) { + bool IsLastAttrArg = I == (AttrNumArgs - 1); + // If the argument is the last argument and it is variadic it can contain + // any expression. + if (IsLastAttrArg && AttrHasVariadicArg) + return false; + Expr *E = AL.getArgAsExpr(I); + bool ArgMemberCanHoldExpr = AL.isParamExpr(I); + // If the expression is a pack expansion then arguments must be delayed + // unless the argument is an expression and it is the last argument of the + // attribute. + if (isa(E)) + return !(IsLastAttrArg && ArgMemberCanHoldExpr); + // Last case is if the expression is value dependent then it must delay + // arguments unless the corresponding argument is able to hold the + // expression. + if (E->isValueDependent() && !ArgMemberCanHoldExpr) + return true; + } + return false; +} + /// ProcessDeclAttribute - Apply the specific attribute to the specified decl if /// the attribute applies to decls. If the attribute is a type attribute, just /// silently ignore it if a GNU attribute. @@ -8150,9 +8190,18 @@ return; } - if (S.checkCommonAttributeFeatures(D, AL)) + // Check if argument population must delayed to after template instantiation. + bool MustDelayArgs = MustDelayAttributeArguments(AL); + + // Argument number check must be skipped if arguments are delayed. + if (S.checkCommonAttributeFeatures(D, AL, MustDelayArgs)) return; + if (MustDelayArgs) { + AL.handleAttrWithDelayedArgs(S, D); + return; + } + switch (AL.getKind()) { default: if (AL.getInfo().handleDeclAttribute(S, D, AL) != ParsedAttrInfo::NotHandled) Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -188,15 +188,37 @@ 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()) + if (S.SubstExprs(ArgsToInstantiate, + /*IsCall=*/false, TemplateArgs, Args)) + return; + + StringRef Str = Attr->getAnnotation(); + if (HasDelayedArgs) { + if (Args.size() < 1) { + S.Diag(Attr->getLoc(), diag::err_attribute_too_few_arguments) + << Attr << 1; return; - Args.push_back(Result.get()); + } + + if (!S.checkStringLiteralArgumentAttr(*Attr, Args[0], Str)) + return; + + llvm::SmallVector ActualArgs; + ActualArgs.insert(ActualArgs.begin(), Args.begin() + 1, Args.end()); + std::swap(Args, ActualArgs); } - S.AddAnnotationAttr(New, *Attr, Attr->getAnnotation(), Args); + S.AddAnnotationAttr(New, *Attr, Str, 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,495 @@ template [[clang::annotate("ANNOTATE_FOO"), clang::annotate("ANNOTATE_BAR")]] void HasAnnotations(); void UseAnnotations() { HasAnnotations(); } +// CHECK: FunctionTemplateDecl {{.*}} HasPackAnnotations +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: FunctionDecl {{.*}} HasPackAnnotations 'void ()' +// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_BAZ" +// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' +// CHECK-NEXT: FunctionDecl {{.*}} used HasPackAnnotations 'void ()' +// CHECK-NEXT: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} integral 1 +// CHECK-NEXT: TemplateArgument{{.*}} integral 2 +// CHECK-NEXT: TemplateArgument{{.*}} integral 3 +// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_BAZ" +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 1 +// CHECK-NEXT: SubstNonTypeTemplateParmExpr {{.*}} 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1 +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 2 +// CHECK-NEXT: SubstNonTypeTemplateParmExpr {{.*}} 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 2 +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 3 +// CHECK-NEXT: SubstNonTypeTemplateParmExpr {{.*}} 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: IntegerLiteral {{.*}} '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-NEXT: TemplateTypeParmDecl {{.*}} typename depth 0 index 0 T +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 1 ... Is +// CHECK-NEXT: CXXRecordDecl {{.*}} struct AnnotatedPackTemplateStruct definition +// CHECK-NEXT: DefinitionData +// CHECK-NEXT: DefaultConstructor +// CHECK-NEXT: CopyConstructor +// CHECK-NEXT: MoveConstructor +// CHECK-NEXT: CopyAssignment +// CHECK-NEXT: MoveAssignment +// CHECK-NEXT: Destructor +// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_FOZ" +// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' +// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct AnnotatedPackTemplateStruct +// CHECK-NEXT: ClassTemplateSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition +// CHECK-NEXT: DefinitionData +// CHECK-NEXT: DefaultConstructor +// CHECK-NEXT: CopyConstructor +// CHECK-NEXT: MoveConstructor +// CHECK-NEXT: CopyAssignment +// CHECK-NEXT: MoveAssignment +// CHECK-NEXT: Destructor +// CHECK-NEXT: TemplateArgument{{.*}} type 'int' +// CHECK-NEXT: BuiltinType {{.*}} 'int' +// CHECK-NEXT: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} integral 1 +// CHECK-NEXT: TemplateArgument{{.*}} integral 2 +// CHECK-NEXT: TemplateArgument{{.*}} integral 3 +// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_BOO" +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 1 +// CHECK-NEXT: SubstNonTypeTemplateParmExpr {{.*}} 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1 +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 2 +// CHECK-NEXT: SubstNonTypeTemplateParmExpr {{.*}} 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 2 +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 3 +// CHECK-NEXT: SubstNonTypeTemplateParmExpr {{.*}} 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3 +// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct AnnotatedPackTemplateStruct +// CHECK-NEXT: ClassTemplateSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition +// CHECK-NEXT: DefinitionData +// CHECK-NEXT: DefaultConstructor +// CHECK-NEXT: CopyConstructor +// CHECK-NEXT: MoveConstructor +// CHECK-NEXT: CopyAssignment +// CHECK-NEXT: MoveAssignment +// CHECK-NEXT: Destructor +// CHECK-NEXT: TemplateArgument type 'float' +// CHECK-NEXT: BuiltinType {{.*}} 'float' +// CHECK-NEXT: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} integral 3 +// CHECK-NEXT: TemplateArgument{{.*}} integral 2 +// CHECK-NEXT: TemplateArgument{{.*}} integral 1 +// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_FOZ" +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 4 +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 4 +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 5 +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 5 +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 6 +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 6 +// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct AnnotatedPackTemplateStruct +// CHECK-NEXT: ClassTemplateSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition +// CHECK-NEXT: DefinitionData +// CHECK-NEXT: DefaultConstructor +// CHECK-NEXT: CopyConstructor +// CHECK-NEXT: MoveConstructor +// CHECK-NEXT: CopyAssignment +// CHECK-NEXT: MoveAssignment +// CHECK-NEXT: Destructor +// CHECK-NEXT: TemplateArgument type 'bool' +// CHECK-NEXT: BuiltinType {{.*}} 'bool' +// CHECK-NEXT: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} integral 7 +// CHECK-NEXT: TemplateArgument{{.*}} integral 8 +// CHECK-NEXT: TemplateArgument{{.*}} integral 9 +// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_FOZ" +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 7 +// CHECK-NEXT: SubstNonTypeTemplateParmExpr {{.*}} 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 1 ... Is +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 7 +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 8 +// CHECK-NEXT: SubstNonTypeTemplateParmExpr {{.*}} 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 1 ... Is +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 8 +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 9 +// CHECK-NEXT: SubstNonTypeTemplateParmExpr {{.*}} 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 1 ... Is +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 9 +// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct AnnotatedPackTemplateStruct +// CHECK-NEXT: ClassTemplateSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition +// CHECK-NEXT: DefinitionData +// CHECK-NEXT: DefaultConstructor +// CHECK-NEXT: CopyConstructor +// CHECK-NEXT: MoveConstructor +// CHECK-NEXT: CopyAssignment +// CHECK-NEXT: MoveAssignment +// CHECK-NEXT: Destructor +// CHECK-NEXT: TemplateArgument type 'char' +// CHECK-NEXT: BuiltinType {{.*}} 'char' +// CHECK-NEXT: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} integral 1 +// CHECK-NEXT: TemplateArgument{{.*}} integral 2 +// CHECK-NEXT: TemplateArgument{{.*}} integral 3 +// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct AnnotatedPackTemplateStruct +// CHECK-NEXT: ClassTemplateSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition +// CHECK-NEXT: DefinitionData +// CHECK-NEXT: DefaultConstructor +// CHECK-NEXT: CopyConstructor +// CHECK-NEXT: MoveConstructor +// CHECK-NEXT: CopyAssignment +// CHECK-NEXT: MoveAssignment +// CHECK-NEXT: Destructor +// CHECK-NEXT: TemplateArgument{{.*}} type 'char' +// CHECK-NEXT: BuiltinType {{.*}} 'char' +// CHECK-NEXT: TemplateArgument{{.*}} pack +// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct AnnotatedPackTemplateStruct +// CHECK-NEXT: ClassTemplatePartialSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition +// CHECK-NEXT: DefinitionData +// CHECK-NEXT: DefaultConstructor +// CHECK-NEXT: CopyConstructor +// CHECK-NEXT: MoveConstructor +// CHECK-NEXT: CopyAssignment +// CHECK-NEXT: MoveAssignment +// CHECK-NEXT: Destructor +// CHECK-NEXT: TemplateArgument{{.*}} type 'int' +// CHECK-NEXT: BuiltinType {{.*}} 'int' +// CHECK-NEXT: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} expr +// CHECK-NEXT: PackExpansionExpr {{.*}} 'int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_BOO" +// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' +// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct AnnotatedPackTemplateStruct +// CHECK-NEXT: ClassTemplatePartialSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition +// CHECK-NEXT: DefinitionData +// CHECK-NEXT: DefaultConstructor +// CHECK-NEXT: CopyConstructor +// CHECK-NEXT: MoveConstructor +// CHECK-NEXT: CopyAssignment +// CHECK-NEXT: MoveAssignment +// CHECK-NEXT: Destructor +// CHECK-NEXT: TemplateArgument{{.*}} type 'float' +// CHECK-NEXT: BuiltinType {{.*}} 'float' +// CHECK-NEXT: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} expr +// CHECK-NEXT: PackExpansionExpr {{.*}} 'int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_FOZ" +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 4 +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 4 +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 5 +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 5 +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 6 +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 6 +// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct AnnotatedPackTemplateStruct +// CHECK-NEXT: ClassTemplatePartialSpecializationDecl {{.*}} struct AnnotatedPackTemplateStruct definition +// CHECK-NEXT: DefinitionData +// CHECK-NEXT: DefaultConstructor +// CHECK-NEXT: CopyConstructor +// CHECK-NEXT: MoveConstructor +// CHECK-NEXT: CopyAssignment +// CHECK-NEXT: MoveAssignment +// CHECK-NEXT: Destructor +// CHECK-NEXT: TemplateArgument{{.*}} type 'char' +// CHECK-NEXT: BuiltinType {{.*}} 'char' +// CHECK-NEXT: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} expr +// CHECK-NEXT: PackExpansionExpr {{.*}} 'int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: AnnotateAttr {{.*}} "" +// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' +// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct AnnotatedPackTemplateStruct +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-NEXT: TemplateTypeParmDecl {{.*}} typename depth 0 index 0 T +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 1 ... Is +// CHECK-NEXT: CXXRecordDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition +// CHECK-NEXT: DefinitionData +// CHECK-NEXT: DefaultConstructor +// CHECK-NEXT: CopyConstructor +// CHECK-NEXT: MoveConstructor +// CHECK-NEXT: CopyAssignment +// CHECK-NEXT: MoveAssignment +// CHECK-NEXT: Destructor +// CHECK-NEXT: AnnotateAttr {{.*}} "" +// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' +// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct InvalidAnnotatedPackTemplateStruct +// CHECK-NEXT: ClassTemplateSpecialization {{.*}} 'InvalidAnnotatedPackTemplateStruct' +// CHECK-NEXT: ClassTemplateSpecializationDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition +// CHECK-NEXT: DefinitionData +// CHECK-NEXT: DefaultConstructor +// CHECK-NEXT: CopyConstructor +// CHECK-NEXT: MoveConstructor +// CHECK-NEXT: CopyAssignment +// CHECK-NEXT: MoveAssignment +// CHECK-NEXT: Destructor +// CHECK-NEXT: TemplateArgument{{.*}} type 'int' +// CHECK-NEXT: BuiltinType {{.*}} 'int' +// CHECK-NEXT: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} integral 1 +// CHECK-NEXT: TemplateArgument{{.*}} integral 2 +// CHECK-NEXT: TemplateArgument{{.*}} integral 3 +// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_BIR" +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 1 +// CHECK-NEXT: SubstNonTypeTemplateParmExpr {{.*}} 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1 +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 2 +// CHECK-NEXT: SubstNonTypeTemplateParmExpr {{.*}} 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 2 +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 3 +// CHECK-NEXT: SubstNonTypeTemplateParmExpr {{.*}} 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3 +// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct InvalidAnnotatedPackTemplateStruct +// CHECK-NEXT: ClassTemplateSpecializationDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition +// CHECK-NEXT: DefinitionData +// CHECK-NEXT: DefaultConstructor +// CHECK-NEXT: CopyConstructor +// CHECK-NEXT: MoveConstructor +// CHECK-NEXT: CopyAssignment +// CHECK-NEXT: MoveAssignment +// CHECK-NEXT: Destructor +// CHECK-NEXT: TemplateArgument{{.*}} type 'float' +// CHECK-NEXT: BuiltinType {{.*}} 'float' +// CHECK-NEXT: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} integral 3 +// CHECK-NEXT: TemplateArgument{{.*}} integral 2 +// CHECK-NEXT: TemplateArgument{{.*}} integral 1 +// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct InvalidAnnotatedPackTemplateStruct +// CHECK-NEXT: ClassTemplateSpecializationDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition +// CHECK-NEXT: DefinitionData +// CHECK-NEXT: DefaultConstructor +// CHECK-NEXT: CopyConstructor +// CHECK-NEXT: MoveConstructor +// CHECK-NEXT: CopyAssignment +// CHECK-NEXT: MoveAssignment +// CHECK-NEXT: Destructor +// CHECK-NEXT: TemplateArgument{{.*}} type 'bool' +// CHECK-NEXT: BuiltinType {{.*}} 'bool' +// CHECK-NEXT: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} integral 7 +// CHECK-NEXT: TemplateArgument{{.*}} integral 8 +// CHECK-NEXT: TemplateArgument{{.*}} integral 9 +// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct InvalidAnnotatedPackTemplateStruct +// CHECK-NEXT: ClassTemplateSpecializationDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition +// CHECK-NEXT: DefinitionData +// CHECK-NEXT: DefaultConstructor +// CHECK-NEXT: CopyConstructor +// CHECK-NEXT: MoveConstructor +// CHECK-NEXT: CopyAssignment +// CHECK-NEXT: MoveAssignment +// CHECK-NEXT: Destructor +// CHECK-NEXT: TemplateArgument{{.*}} type 'bool' +// CHECK-NEXT: BuiltinType {{.*}} 'bool' +// CHECK-NEXT: TemplateArgument{{.*}} pack +// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct InvalidAnnotatedPackTemplateStruct +// CHECK-NEXT: ClassTemplatePartialSpecializationDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition +// CHECK-NEXT: DefinitionData +// CHECK-NEXT: DefaultConstructor +// CHECK-NEXT: CopyConstructor +// CHECK-NEXT: MoveConstructor +// CHECK-NEXT: CopyAssignment +// CHECK-NEXT: MoveAssignment +// CHECK-NEXT: Destructor +// CHECK-NEXT: TemplateArgument{{.*}} type 'int' +// CHECK-NEXT: BuiltinType {{.*}} 'int' +// CHECK-NEXT: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} expr +// CHECK-NEXT: PackExpansionExpr {{.*}} 'int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_BIR" +// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' +// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct InvalidAnnotatedPackTemplateStruct +// CHECK-NEXT: ClassTemplatePartialSpecializationDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition +// CHECK-NEXT: DefinitionData +// CHECK-NEXT: DefaultConstructor +// CHECK-NEXT: CopyConstructor +// CHECK-NEXT: MoveConstructor +// CHECK-NEXT: CopyAssignment +// CHECK-NEXT: MoveAssignment +// CHECK-NEXT: Destructor +// CHECK-NEXT: TemplateArgument{{.*}} type 'float' +// CHECK-NEXT: BuiltinType {{.*}} 'float' +// CHECK-NEXT: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} expr +// CHECK-NEXT: PackExpansionExpr {{.*}} 'int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct InvalidAnnotatedPackTemplateStruct +// CHECK-NEXT: ClassTemplateSpecializationDecl {{.*}} struct InvalidAnnotatedPackTemplateStruct definition +// CHECK-NEXT: DefinitionData +// CHECK-NEXT: DefaultConstructor +// CHECK-NEXT: CopyConstructor +// CHECK-NEXT: MoveConstructor +// CHECK-NEXT: CopyAssignment +// CHECK-NEXT: MoveAssignment +// CHECK-NEXT: Destructor +// CHECK-NEXT: TemplateArgument{{.*}} type 'char' +// CHECK-NEXT: BuiltinType {{.*}} 'char' +// CHECK-NEXT: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} integral 5 +// CHECK-NEXT: TemplateArgument{{.*}} integral 6 +// CHECK-NEXT: TemplateArgument{{.*}} integral 7 +// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct InvalidAnnotatedPackTemplateStruct +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-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: FunctionDecl {{.*}} RedeclaredAnnotatedFunc 'void ()' +// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_FAR" +// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' +// CHECK-NEXT: FunctionDecl {{.*}} used RedeclaredAnnotatedFunc 'void ()' +// CHECK-NEXT: TemplateArgument{{.*}} pack +// CHECK-NEXT: TemplateArgument{{.*}} integral 1 +// CHECK-NEXT: TemplateArgument{{.*}} integral 2 +// CHECK-NEXT: TemplateArgument{{.*}} integral 3 +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_FAR" +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 1 +// CHECK-NEXT: SubstNonTypeTemplateParmExpr {{.*}} 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1 +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 2 +// CHECK-NEXT: SubstNonTypeTemplateParmExpr {{.*}} 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 2 +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 3 +// CHECK-NEXT: SubstNonTypeTemplateParmExpr {{.*}} 'int' +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3 +// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_FIZ" +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 4 +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 4 +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 5 +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 5 +// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_BOZ" +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 6 +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 6 +// CHECK-NEXT: FunctionTemplateDecl {{.*}} prev {{.*}} RedeclaredAnnotatedFunc +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} referenced 'int' depth 0 index 0 ... Is +// CHECK-NEXT: FunctionDecl {{.*}} prev {{.*}} RedeclaredAnnotatedFunc 'void ()' +// CHECK-NEXT: AnnotateAttr {{.*}} Inherited "ANNOTATE_FAR" +// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' +// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_BOZ" +// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' +// CHECK-NEXT: Function {{.*}} 'RedeclaredAnnotatedFunc' 'void ()' +// CHECK-NEXT: FunctionTemplateDecl {{.*}} prev {{.*}} RedeclaredAnnotatedFunc +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} 'int' depth 0 index 0 ... Is +// CHECK-NEXT: FunctionDecl {{.*}} prev {{.*}} RedeclaredAnnotatedFunc 'void ()' +// CHECK-NEXT: AnnotateAttr {{.*}} Inherited "ANNOTATE_FAR" +// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' +// CHECK-NEXT: AnnotateAttr {{.*}} Inherited "ANNOTATE_BOZ" +// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' +// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_FIZ" +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 4 +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 4 +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 5 +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 5 +// CHECK-NEXT: Function {{.*}} 'RedeclaredAnnotatedFunc' 'void ()' +// CHECK-NEXT: FunctionTemplateDecl {{.*}} prev {{.*}} RedeclaredAnnotatedFunc +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} 'int' depth 0 index 0 ... Is +// CHECK-NEXT: FunctionDecl {{.*}} prev {{.*}} RedeclaredAnnotatedFunc 'void ()' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: AnnotateAttr {{.*}} Inherited "ANNOTATE_FAR" +// CHECK-NEXT: PackExpansionExpr {{.*}} '' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int' +// CHECK-NEXT: AnnotateAttr {{.*}} Inherited "ANNOTATE_FIZ" +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 4 +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 4 +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 5 +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 5 +// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_BOZ" +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 6 +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 6 +// CHECK-NEXT: Function {{.*}} 'RedeclaredAnnotatedFunc' 'void ()' +// CHECK-NEXT: EmptyDecl +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()) @@ -2197,6 +2224,14 @@ .Default(false); } +static bool isVariadicExprArgument(const Record *Arg) { + return !Arg->getSuperClasses().empty() && + llvm::StringSwitch( + Arg->getSuperClasses().back().first->getName()) + .Case("VariadicExprArgument", true) + .Default(false); +} + static void emitClangAttrVariadicIdentifierArgList(RecordKeeper &Records, raw_ostream &OS) { OS << "#if defined(CLANG_ATTR_VARIADIC_IDENTIFIER_ARG_LIST)\n"; @@ -2264,6 +2299,23 @@ 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; + + 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 +2372,25 @@ std::vector> Args; Args.reserve(ArgRecords.size()); + bool AttrAcceptsExprPack = Attr->getValueAsBit("AcceptsExprPack"); + if (AttrAcceptsExprPack) { + for (size_t I = 0; I < ArgRecords.size(); ++I) { + const Record *ArgR = ArgRecords[I]; + if (isIdentifierArgument(ArgR) || isVariadicIdentifierArgument(ArgR) || + isTypeArgument(ArgR)) + PrintFatalError(Attr->getLoc(), + "Attributes accepting packs cannot also " + "have identifier or type arguments."); + // When trying to determine if value-dependent expressions can populate + // the attribute without prior instantiation, the decision is made based + // on the assumption that only the last argument is ever variadic. + if (I < (ArgRecords.size() - 1) && isVariadicExprArgument(ArgR)) + PrintFatalError(Attr->getLoc(), + "Attributes accepting packs can only have the last " + "argument be variadic."); + } + } + bool HasOptArg = false; bool HasFakeArg = false; for (const auto *ArgRecord : ArgRecords) { @@ -2337,6 +2408,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 +2444,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 +2453,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 +2480,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 +2497,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 +2515,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 +2557,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 +2616,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 +2647,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 +2668,24 @@ // 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 none already exists. + // This is used for delaying arguments. + bool HasRequiredArgs = std::count_if( + Args.begin(), Args.end(), [=](const std::unique_ptr &arg) { + return !arg->isFake() && !arg->isOptional(); + }); + if (DelayedArgs && HasRequiredArgs) + emitCtor(false, false, true); if (Header) { OS << '\n'; @@ -2592,6 +2731,11 @@ } if (Header) { + if (DelayedArgs) { + DelayedArgs->writeAccessors(OS); + DelayedArgs->writeSetter(OS); + } + OS << R.getValueAsString("AdditionalMembers"); OS << "\n\n"; @@ -2600,6 +2744,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 +2758,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 +3076,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 +3089,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 +3111,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 +3144,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 +3433,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"; } @@ -3391,7 +3562,7 @@ // attribute and emit the number of required arguments followed by the // number of optional arguments. std::vector Args = R.getValueAsListOfDefs("Args"); - unsigned ArgCount = 0, OptCount = 0; + unsigned ArgCount = 0, OptCount = 0, ArgMemberCount = 0; bool HasVariadic = false; for (const auto *Arg : Args) { // If the arg is fake, it's the user's job to supply it: general parsing @@ -3399,6 +3570,7 @@ if (Arg->getValueAsBit("Fake")) continue; Arg->getValueAsBit("Optional") ? ++OptCount : ++ArgCount; + ++ArgMemberCount; if (!HasVariadic && isArgVariadic(*Arg, R.getName())) HasVariadic = true; } @@ -3407,6 +3579,7 @@ // to its largest value. Since it's currently a 4-bit number, we set it to 15. OS << " NumArgs = " << ArgCount << ";\n"; OS << " OptArgs = " << (HasVariadic ? 15 : OptCount) << ";\n"; + OS << " NumArgMembers = " << ArgMemberCount << ";\n"; } static std::string GetDiagnosticSpelling(const Record &R) { @@ -3890,6 +4063,55 @@ OS << "}\n\n"; } +static bool isParamExpr(const Record *Arg) { + return !Arg->getSuperClasses().empty() && + llvm::StringSwitch( + Arg->getSuperClasses().back().first->getName()) + .Case("ExprArgument", true) + .Case("VariadicExprArgument", true) + .Default(false); +} + +void GenerateIsParamExpr(const Record &Attr, raw_ostream &OS) { + OS << "bool isParamExpr(size_t N) const override {\n"; + OS << " return "; + auto Args = Attr.getValueAsListOfDefs("Args"); + for (size_t I = 0; I < Args.size(); ++I) + if (isParamExpr(Args[I])) + OS << "(N == " << I << ") || "; + OS << "false;\n"; + OS << "}\n\n"; +} + +void GenerateHandleAttrWithDelayedArgs(RecordKeeper &Records, raw_ostream &OS) { + OS << "static void handleAttrWithDelayedArgs(Sema &S, Decl *D, "; + OS << "const ParsedAttr &Attr) {\n"; + OS << " SmallVector ArgExprs;\n"; + OS << " ArgExprs.reserve(Attr.getNumArgs());\n"; + OS << " for (unsigned I = 0; I < Attr.getNumArgs(); ++I) {\n"; + OS << " assert(!Attr.isArgIdent(I));\n"; + OS << " ArgExprs.push_back(Attr.getArgAsExpr(I));\n"; + OS << " }\n"; + OS << " clang::Attr *CreatedAttr = nullptr;\n"; + OS << " switch (Attr.getKind()) {\n"; + OS << " default:\n"; + OS << " llvm_unreachable(\"Attribute cannot hold delayed arguments.\");\n"; + ParsedAttrMap Attrs = getParsedAttrList(Records); + for (const auto &I : Attrs) { + const Record &R = *I.second; + if (!R.getValueAsBit("AcceptsExprPack")) + continue; + OS << " case ParsedAttr::AT_" << I.first << ": {\n"; + OS << " CreatedAttr = " << R.getName() << "Attr::CreateWithDelayedArgs"; + OS << "(S.Context, ArgExprs.data(), ArgExprs.size(), Attr);\n"; + OS << " break;\n"; + OS << " }\n"; + } + OS << " }\n"; + OS << " D->addAttr(CreatedAttr);\n"; + OS << "}\n\n"; +} + static bool IsKnownToGCC(const Record &Attr) { // Look at the spellings for this subject; if there are any spellings which // claim to be known to GCC, the attribute is known to GCC. @@ -3988,6 +4210,8 @@ emitArgInfo(Attr, OS); OS << " HasCustomParsing = "; OS << Attr.getValueAsBit("HasCustomParsing") << ";\n"; + OS << " AcceptsExprPack = "; + OS << Attr.getValueAsBit("AcceptsExprPack") << ";\n"; OS << " IsTargetSpecific = "; OS << Attr.isSubClassOf("TargetSpecificAttr") << ";\n"; OS << " IsType = "; @@ -4012,6 +4236,7 @@ GenerateSpellingIndexToSemanticSpelling(Attr, OS); PragmaAttributeSupport.generateStrictConformsTo(*I->second, OS); GenerateHandleDeclAttribute(Attr, OS); + GenerateIsParamExpr(Attr, OS); OS << "static const ParsedAttrInfo" << I->first << " Instance;\n"; OS << "};\n"; OS << "const ParsedAttrInfo" << I->first << " ParsedAttrInfo" << I->first @@ -4024,6 +4249,9 @@ } OS << "};\n\n"; + // Generate function for handling attributes with delayed arguments + GenerateHandleAttrWithDelayedArgs(Records, OS); + // Generate the attribute match rules. emitAttributeMatchRules(PragmaAttributeSupport, OS); @@ -4173,6 +4401,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 +4431,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 +4452,7 @@ emitClangAttrIdentifierArgList(Records, OS); emitClangAttrVariadicIdentifierArgList(Records, OS); emitClangAttrThisIsaIdentifierArgList(Records, OS); + emitClangAttrAcceptsExprPack(Records, OS); emitClangAttrTypeArgList(Records, OS); emitClangAttrLateParsedList(Records, OS); }