Attribute Changes in Clang
--------------------------
+- Added support for parameter pack expansion in `clang::annotate`.
+
Windows Support
---------------
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
--------------------
// 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<LangOpt> LangOpts = [];
}
}];
let PragmaAttributeSupport = 1;
+ let AcceptsExprPack = 1;
let Documentation = [Undocumented];
}
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<
bool ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
SmallVectorImpl<SourceLocation> &CommaLocs,
llvm::function_ref<void()> ExpressionStarts =
- llvm::function_ref<void()>());
+ llvm::function_ref<void()>(),
+ bool FailImmediatelyOnInvalidExpr = false,
+ bool EarlyTypoCorrection = false);
/// ParseSimpleExpressionList - A simple comma-separated list of expressions,
/// used for misc language extensions.
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.
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<std::pair<attr::SubjectMatchRule, bool>> &Rules,
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;
/// 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
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);
#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<bool>(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
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))
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);
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<void()>(),
+ /*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<PackExpansionExpr>(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();
/// \endverbatim
bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
SmallVectorImpl<SourceLocation> &CommaLocs,
- llvm::function_ref<void()> ExpressionStarts) {
+ llvm::function_ref<void()> ExpressionStarts,
+ bool FailImmediatelyOnInvalidExpr,
+ bool EarlyTypoCorrection) {
bool SawError = false;
while (true) {
if (ExpressionStarts)
} 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)) {
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());
}
return getMinArgs() + getInfo().OptArgs;
}
+unsigned ParsedAttr::getNumArgMembers() const {
+ return getInfo().NumArgMembers;
+}
+
bool ParsedAttr::hasCustomParsing() const {
return getInfo().HasCustomParsing;
}
return getInfo().IsSupportedByPragmaAttribute;
}
+bool ParsedAttr::acceptsExprPack() const { return getInfo().AcceptsExprPack; }
+
unsigned ParsedAttr::getSemanticSpelling() const {
return getInfo().spellingIndexToSemanticSpelling(*this);
}
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();
}
template <typename Ty>
-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.
//
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);
}
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<StringLiteral>(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
// Now check for an actual string literal.
Expr *ArgExpr = AL.getArgAsExpr(ArgNum);
- const auto *Literal = dyn_cast<StringLiteral>(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
// 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<PackExpansionExpr>(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.
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)
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<Expr *> ArgsToInstantiate =
+ HasDelayedArgs
+ ? ArrayRef<Expr *>{Attr->delayedArgs_begin(), Attr->delayedArgs_end()}
+ : ArrayRef<Expr *>{Attr->args_begin(), Attr->args_end()};
+
SmallVector<Expr *, 4> 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<Expr *, 4> 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(
void bar [[noreturn...]] (); // expected-error {{attribute 'noreturn' cannot be used as an attribute pack}}
}
+template <int... Is> 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
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}}
}
[[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 <typename T> [[clang::annotate()]] void f2() {}
+// expected-error@-1 {{'annotate' attribute takes at least 1 argument}}
}
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"
template<typename T> [[clang::annotate("ANNOTATE_FOO"), clang::annotate("ANNOTATE_BAR")]] void HasAnnotations();
void UseAnnotations() { HasAnnotations<int>(); }
+// 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 {{.*}} '<dependent type>'
+// 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 <int... Is> [[clang::annotate("ANNOTATE_BAZ", Is...)]] void HasPackAnnotations();
+void UsePackAnnotations() { HasPackAnnotations<1, 2, 3>(); }
+
+template <int... Is> [[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 {{.*}} '<dependent type>'
+// 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 {{.*}} '<dependent type>'
+// 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 {{.*}} '<dependent type>'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int'
+// CHECK-NEXT: CXXRecordDecl {{.*}} implicit struct AnnotatedPackTemplateStruct
+template <typename T, int... Is> struct [[clang::annotate("ANNOTATE_FOZ", Is...)]] AnnotatedPackTemplateStruct{};
+template <int... Is> struct [[clang::annotate("ANNOTATE_BOO", Is...)]] AnnotatedPackTemplateStruct<int, Is...>{};
+template <int... Is> struct [[clang::annotate("ANNOTATE_FOZ", 4, 5, 6)]] AnnotatedPackTemplateStruct<float, Is...>{};
+template <int... Is> struct [[clang::annotate(Is...)]] AnnotatedPackTemplateStruct<char, Is...>{}; // expected-error {{'annotate' attribute requires a string}} expected-error {{'annotate' attribute takes at least 1 argument}}
+void UseAnnotatedPackTemplateStructSpecializations() {
+ AnnotatedPackTemplateStruct<int, 1, 2, 3> Instance1{};
+ AnnotatedPackTemplateStruct<float, 3, 2, 1> Instance2{};
+ AnnotatedPackTemplateStruct<bool, 7, 8, 9> Instance3{};
+ AnnotatedPackTemplateStruct<char, 1, 2, 3> Instance4{}; // expected-note {{in instantiation of template class 'attribute_annotate::AnnotatedPackTemplateStruct<char, 1, 2, 3>' requested here}}
+ AnnotatedPackTemplateStruct<char> Instance5{}; // expected-note {{in instantiation of template class 'attribute_annotate::AnnotatedPackTemplateStruct<char>' 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 {{.*}} '<dependent type>'
+// 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 {{.*}} '<dependent type>'
+// 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 <typename T, int... Is> struct [[clang::annotate(Is...)]] InvalidAnnotatedPackTemplateStruct{}; // expected-error {{'annotate' attribute requires a string}} expected-error {{'annotate' attribute takes at least 1 argument}}
+template <int... Is> struct [[clang::annotate("ANNOTATE_BIR", Is...)]] InvalidAnnotatedPackTemplateStruct<int, Is...>{};
+template <int... Is> struct InvalidAnnotatedPackTemplateStruct<float, Is...> {};
+template <> struct InvalidAnnotatedPackTemplateStruct<char, 5, 6, 7> {};
+void UseInvalidAnnotatedPackTemplateStruct() {
+ InvalidAnnotatedPackTemplateStruct<int, 1, 2, 3> Instance1{};
+ InvalidAnnotatedPackTemplateStruct<float, 3, 2, 1> Instance2{};
+ InvalidAnnotatedPackTemplateStruct<char, 5, 6, 7> Instance3{};
+ InvalidAnnotatedPackTemplateStruct<bool, 7, 8, 9> Instance4{}; // expected-note {{in instantiation of template class 'attribute_annotate::InvalidAnnotatedPackTemplateStruct<bool, 7, 8, 9>' requested here}}
+ InvalidAnnotatedPackTemplateStruct<bool> Instance5{}; // expected-note {{in instantiation of template class 'attribute_annotate::InvalidAnnotatedPackTemplateStruct<bool>' 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 {{.*}} '<dependent type>'
+// 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 {{.*}} '<dependent type>'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int'
+// CHECK-NEXT: AnnotateAttr {{.*}} "ANNOTATE_BOZ"
+// CHECK-NEXT: PackExpansionExpr {{.*}} '<dependent type>'
+// 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 {{.*}} '<dependent type>'
+// CHECK-NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'Is' 'int'
+// CHECK-NEXT: AnnotateAttr {{.*}} Inherited "ANNOTATE_BOZ"
+// CHECK-NEXT: PackExpansionExpr {{.*}} '<dependent type>'
+// 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 {{.*}} '<dependent type>'
+// 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 <int... Is> [[clang::annotate("ANNOTATE_FAR", Is...)]] void RedeclaredAnnotatedFunc();
+template <int... Is> [[clang::annotate("ANNOTATE_BOZ", Is...)]] void RedeclaredAnnotatedFunc();
+template <int... Is> [[clang::annotate("ANNOTATE_FIZ", 4, 5)]] void RedeclaredAnnotatedFunc();
+template <int... Is> [[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}}
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]);
if (lowerName == "interface")
lowerName = "interface_";
}
+ Argument(const Record &Arg, StringRef Attr)
+ : Argument(Arg.getValueAsString("Name"), Attr) {}
virtual ~Argument() = default;
StringRef getLowerName() const { return lowerName; }
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; }
<< "); }\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;
}
: 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()
}
}
+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";
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.
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<bool>(Arg->getSuperClasses().back().first->getName())
.Case("IdentifierArgument", true)
.Default(false);
}
-static bool isVariadicIdentifierArgument(Record *Arg) {
+static bool isVariadicIdentifierArgument(const Record *Arg) {
return !Arg->getSuperClasses().empty() &&
llvm::StringSwitch<bool>(
Arg->getSuperClasses().back().first->getName())
.Default(false);
}
+static bool isVariadicExprArgument(const Record *Arg) {
+ return !Arg->getSuperClasses().empty() &&
+ llvm::StringSwitch<bool>(
+ 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";
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<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
std::vector<std::unique_ptr<Argument>> 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) {
}
}
+ std::unique_ptr<VariadicExprArgument> DelayedArgs = nullptr;
+ if (AttrAcceptsExprPack) {
+ DelayedArgs =
+ std::make_unique<VariadicExprArgument>("DelayedArgs", R.getName());
+ if (Header) {
+ DelayedArgs->writeDeclarations(OS);
+ OS << "\n\n";
+ }
+ }
+
if (Header)
OS << "public:\n";
});
// 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 *";
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)
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) {
"!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 *";
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) {
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<Argument> &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)
<< "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";
}
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";
// 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<Argument> &arg) {
+ return !arg->isFake() && !arg->isOptional();
+ });
+ if (DelayedArgs && HasRequiredArgs)
+ emitCtor(false, false, true);
if (Header) {
OS << '\n';
}
if (Header) {
+ if (DelayedArgs) {
+ DelayedArgs->writeAccessors(OS);
+ DelayedArgs->writeSetter(OS);
+ }
+
OS << R.getValueAsString("AdditionalMembers");
OS << "\n\n";
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";
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);
std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"),
ArgRecords;
std::vector<std::unique_ptr<Argument>> Args;
+ std::unique_ptr<VariadicExprArgument> DelayedArgs;
OS << " switch (Kind) {\n";
for (const auto *Attr : Attrs) {
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<VariadicExprArgument>("DelayedArgs", R.getName());
+ DelayedArgs->writePCHReadDecls(OS);
+ }
ArgRecords = R.getValueAsListOfDefs("Args");
Args.clear();
for (const auto *Arg : ArgRecords) {
OS << " cast<InheritableAttr>(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";
}
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);
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";
}
// attribute and emit the number of required arguments followed by the
// number of optional arguments.
std::vector<Record *> 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
if (Arg->getValueAsBit("Fake"))
continue;
Arg->getValueAsBit("Optional") ? ++OptCount : ++ArgCount;
+ ++ArgMemberCount;
if (!HasVariadic && isArgVariadic(*Arg, R.getName()))
HasVariadic = true;
}
// 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) {
OS << "}\n\n";
}
+static bool isParamExpr(const Record *Arg) {
+ return !Arg->getSuperClasses().empty() &&
+ llvm::StringSwitch<bool>(
+ 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<Expr *, 4> 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.
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 = ";
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
}
OS << "};\n\n";
+ // Generate function for handling attributes with delayed arguments
+ GenerateHandleAttrWithDelayedArgs(Records, OS);
+
// Generate the attribute match rules.
emitAttributeMatchRules(PragmaAttributeSupport, OS);
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";
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";
emitClangAttrIdentifierArgList(Records, OS);
emitClangAttrVariadicIdentifierArgList(Records, OS);
emitClangAttrThisIsaIdentifierArgList(Records, OS);
+ emitClangAttrAcceptsExprPack(Records, OS);
emitClangAttrTypeArgList(Records, OS);
emitClangAttrLateParsedList(Records, OS);
}