Skip to content

Commit ead1690

Browse files
steffenlarsenAaronBallman
authored andcommitted
Allow parameter pack expansions and initializer lists in annotate attribute
These changes make the Clang parser recognize expression parameter pack expansion and initializer lists in attribute arguments. Because expression parameter pack expansion requires additional handling while creating and instantiating templates, the support for them must be explicitly supported through the AcceptsExprPack flag. Handling expression pack expansions may require a delay to when the arguments of an attribute are correctly populated. To this end, attributes that are set to accept these - through setting the AcceptsExprPack flag - will automatically have an additional variadic expression argument member named DelayedArgs. This member is not exposed the same way other arguments are but is set through the new CreateWithDelayedArgs creator function generated for applicable attributes. To illustrate how to implement support for expression pack expansion support, clang::annotate is made to support pack expansions. This is done by making handleAnnotationAttr delay setting the actual attribute arguments until after template instantiation if it was unable to populate the arguments due to dependencies in the parsed expressions.
1 parent 70ae480 commit ead1690

File tree

17 files changed

+1036
-111
lines changed

17 files changed

+1036
-111
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ New Pragmas in Clang
7575
Attribute Changes in Clang
7676
--------------------------
7777

78+
- Added support for parameter pack expansion in `clang::annotate`.
79+
7880
Windows Support
7981
---------------
8082

@@ -144,6 +146,12 @@ Floating Point Support in Clang
144146
Internal API Changes
145147
--------------------
146148

149+
- Added a new attribute flag `AcceptsExprPack` that when set allows expression
150+
pack expansions in the parsed arguments of the corresponding attribute.
151+
Additionally it introduces delaying of attribute arguments, adding common
152+
handling for creating attributes that cannot be fully initialized prior to
153+
template instantiation.
154+
147155
Build System Changes
148156
--------------------
149157

clang/include/clang/Basic/Attr.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,8 @@ class Attr {
541541
// match rules.
542542
// - It has GNU/CXX11 spelling and doesn't require delayed parsing.
543543
bit PragmaAttributeSupport;
544+
// Set to true if this attribute accepts parameter pack expansion expressions.
545+
bit AcceptsExprPack = 0;
544546
// Lists language options, one of which is required to be true for the
545547
// attribute to be applicable. If empty, no language options are required.
546548
list<LangOpt> LangOpts = [];
@@ -784,6 +786,7 @@ def Annotate : InheritableParamAttr {
784786
}
785787
}];
786788
let PragmaAttributeSupport = 1;
789+
let AcceptsExprPack = 1;
787790
let Documentation = [Undocumented];
788791
}
789792

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,8 @@ def err_attributes_misplaced : Error<"misplaced attributes; expected attributes
719719
def err_l_square_l_square_not_attribute : Error<
720720
"C++11 only allows consecutive left square brackets when "
721721
"introducing an attribute">;
722+
def err_attribute_argument_parm_pack_not_supported : Error<
723+
"attribute %0 does not support argument pack expansion">;
722724
def err_ms_declspec_type : Error<
723725
"__declspec attributes must be an identifier or string literal">;
724726
def err_ms_property_no_getter_or_putter : Error<

clang/include/clang/Parse/Parser.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1814,7 +1814,9 @@ class Parser : public CodeCompletionHandler {
18141814
bool ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
18151815
SmallVectorImpl<SourceLocation> &CommaLocs,
18161816
llvm::function_ref<void()> ExpressionStarts =
1817-
llvm::function_ref<void()>());
1817+
llvm::function_ref<void()>(),
1818+
bool FailImmediatelyOnInvalidExpr = false,
1819+
bool EarlyTypoCorrection = false);
18181820

18191821
/// ParseSimpleExpressionList - A simple comma-separated list of expressions,
18201822
/// used for misc language extensions.

clang/include/clang/Sema/ParsedAttr.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,12 @@ struct ParsedAttrInfo {
4949
unsigned NumArgs : 4;
5050
/// The number of optional arguments of this attributes.
5151
unsigned OptArgs : 4;
52+
/// The number of non-fake arguments specified in the attribute definition.
53+
unsigned NumArgMembers : 4;
5254
/// True if the parsing does not match the semantic content.
5355
unsigned HasCustomParsing : 1;
56+
// True if this attribute accepts expression parameter pack expansions.
57+
unsigned AcceptsExprPack : 1;
5458
/// True if this attribute is only available for certain targets.
5559
unsigned IsTargetSpecific : 1;
5660
/// True if this attribute applies to types.
@@ -106,6 +110,10 @@ struct ParsedAttrInfo {
106110
spellingIndexToSemanticSpelling(const ParsedAttr &Attr) const {
107111
return UINT_MAX;
108112
}
113+
/// Returns true if the specified parameter index for this attribute in
114+
/// Attr.td is an ExprArgument or VariadicExprArgument, or a subclass thereof;
115+
/// returns false otherwise.
116+
virtual bool isParamExpr(size_t N) const { return false; }
109117
/// Populate Rules with the match rules of this attribute.
110118
virtual void getPragmaAttributeMatchRules(
111119
llvm::SmallVectorImpl<std::pair<attr::SubjectMatchRule, bool>> &Rules,
@@ -601,9 +609,13 @@ class ParsedAttr final
601609
bool isStmtAttr() const;
602610

603611
bool hasCustomParsing() const;
612+
bool acceptsExprPack() const;
613+
bool isParamExpr(size_t N) const;
604614
unsigned getMinArgs() const;
605615
unsigned getMaxArgs() const;
616+
unsigned getNumArgMembers() const;
606617
bool hasVariadicArg() const;
618+
void handleAttrWithDelayedArgs(Sema &S, Decl *D) const;
607619
bool diagnoseAppertainsTo(class Sema &S, const Decl *D) const;
608620
bool diagnoseAppertainsTo(class Sema &S, const Stmt *St) const;
609621
bool diagnoseMutualExclusion(class Sema &S, const Decl *D) const;

clang/include/clang/Sema/Sema.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4366,8 +4366,10 @@ class Sema final {
43664366
/// such as checking whether a parameter was properly specified, or the
43674367
/// correct number of arguments were passed, etc. Returns true if the
43684368
/// attribute has been diagnosed.
4369-
bool checkCommonAttributeFeatures(const Decl *D, const ParsedAttr &A);
4370-
bool checkCommonAttributeFeatures(const Stmt *S, const ParsedAttr &A);
4369+
bool checkCommonAttributeFeatures(const Decl *D, const ParsedAttr &A,
4370+
bool SkipArgCountCheck = false);
4371+
bool checkCommonAttributeFeatures(const Stmt *S, const ParsedAttr &A,
4372+
bool SkipArgCountCheck = false);
43714373

43724374
/// Determine if type T is a valid subject for a nonnull and similar
43734375
/// attributes. By default, we look through references (the behavior used by
@@ -4380,6 +4382,9 @@ class Sema final {
43804382
const FunctionDecl *FD = nullptr);
43814383
bool CheckAttrTarget(const ParsedAttr &CurrAttr);
43824384
bool CheckAttrNoArgs(const ParsedAttr &CurrAttr);
4385+
bool checkStringLiteralArgumentAttr(const AttributeCommonInfo &CI,
4386+
const Expr *E, StringRef &Str,
4387+
SourceLocation *ArgLocation = nullptr);
43834388
bool checkStringLiteralArgumentAttr(const ParsedAttr &Attr, unsigned ArgNum,
43844389
StringRef &Str,
43854390
SourceLocation *ArgLocation = nullptr);

clang/lib/Parse/ParseDecl.cpp

Lines changed: 82 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,15 @@ static bool attributeTreatsKeywordThisAsIdentifier(const IdentifierInfo &II) {
300300
#undef CLANG_ATTR_THIS_ISA_IDENTIFIER_ARG_LIST
301301
}
302302

303+
/// Determine if an attribute accepts parameter packs.
304+
static bool attributeAcceptsExprPack(const IdentifierInfo &II) {
305+
#define CLANG_ATTR_ACCEPTS_EXPR_PACK
306+
return llvm::StringSwitch<bool>(normalizeAttrName(II.getName()))
307+
#include "clang/Parse/AttrParserStringSwitches.inc"
308+
.Default(false);
309+
#undef CLANG_ATTR_ACCEPTS_EXPR_PACK
310+
}
311+
303312
/// Determine whether the given attribute parses a type argument.
304313
static bool attributeIsTypeArgAttr(const IdentifierInfo &II) {
305314
#define CLANG_ATTR_TYPE_ARG_LIST
@@ -366,6 +375,8 @@ unsigned Parser::ParseAttributeArgsCommon(
366375

367376
bool ChangeKWThisToIdent = attributeTreatsKeywordThisAsIdentifier(*AttrName);
368377
bool AttributeIsTypeArgAttr = attributeIsTypeArgAttr(*AttrName);
378+
bool AttributeHasVariadicIdentifierArg =
379+
attributeHasVariadicIdentifierArg(*AttrName);
369380

370381
// Interpret "kw_this" as an identifier if the attributed requests it.
371382
if (ChangeKWThisToIdent && Tok.is(tok::kw_this))
@@ -374,8 +385,8 @@ unsigned Parser::ParseAttributeArgsCommon(
374385
ArgsVector ArgExprs;
375386
if (Tok.is(tok::identifier)) {
376387
// If this attribute wants an 'identifier' argument, make it so.
377-
bool IsIdentifierArg = attributeHasIdentifierArg(*AttrName) ||
378-
attributeHasVariadicIdentifierArg(*AttrName);
388+
bool IsIdentifierArg = AttributeHasVariadicIdentifierArg ||
389+
attributeHasIdentifierArg(*AttrName);
379390
ParsedAttr::Kind AttrKind =
380391
ParsedAttr::getParsedKind(AttrName, ScopeName, Syntax);
381392

@@ -397,42 +408,81 @@ unsigned Parser::ParseAttributeArgsCommon(
397408
if (!ArgExprs.empty())
398409
ConsumeToken();
399410

400-
// Parse the non-empty comma-separated list of expressions.
401-
do {
402-
// Interpret "kw_this" as an identifier if the attributed requests it.
403-
if (ChangeKWThisToIdent && Tok.is(tok::kw_this))
404-
Tok.setKind(tok::identifier);
411+
if (AttributeIsTypeArgAttr) {
412+
// FIXME: Multiple type arguments are not implemented.
413+
TypeResult T = ParseTypeName();
414+
if (T.isInvalid()) {
415+
SkipUntil(tok::r_paren, StopAtSemi);
416+
return 0;
417+
}
418+
if (T.isUsable())
419+
TheParsedType = T.get();
420+
} else if (AttributeHasVariadicIdentifierArg) {
421+
// Parse variadic identifier arg. This can either consume identifiers or
422+
// expressions. Variadic identifier args do not support parameter packs
423+
// because those are typically used for attributes with enumeration
424+
// arguments, and those enumerations are not something the user could
425+
// express via a pack.
426+
do {
427+
// Interpret "kw_this" as an identifier if the attributed requests it.
428+
if (ChangeKWThisToIdent && Tok.is(tok::kw_this))
429+
Tok.setKind(tok::identifier);
430+
431+
ExprResult ArgExpr;
432+
if (Tok.is(tok::identifier)) {
433+
ArgExprs.push_back(ParseIdentifierLoc());
434+
} else {
435+
bool Uneval = attributeParsedArgsUnevaluated(*AttrName);
436+
EnterExpressionEvaluationContext Unevaluated(
437+
Actions,
438+
Uneval ? Sema::ExpressionEvaluationContext::Unevaluated
439+
: Sema::ExpressionEvaluationContext::ConstantEvaluated);
405440

406-
ExprResult ArgExpr;
407-
if (AttributeIsTypeArgAttr) {
408-
TypeResult T = ParseTypeName();
409-
if (T.isInvalid()) {
410-
SkipUntil(tok::r_paren, StopAtSemi);
411-
return 0;
441+
ExprResult ArgExpr(
442+
Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));
443+
444+
if (ArgExpr.isInvalid()) {
445+
SkipUntil(tok::r_paren, StopAtSemi);
446+
return 0;
447+
}
448+
ArgExprs.push_back(ArgExpr.get());
412449
}
413-
if (T.isUsable())
414-
TheParsedType = T.get();
415-
break; // FIXME: Multiple type arguments are not implemented.
416-
} else if (Tok.is(tok::identifier) &&
417-
attributeHasVariadicIdentifierArg(*AttrName)) {
418-
ArgExprs.push_back(ParseIdentifierLoc());
419-
} else {
420-
bool Uneval = attributeParsedArgsUnevaluated(*AttrName);
421-
EnterExpressionEvaluationContext Unevaluated(
422-
Actions,
423-
Uneval ? Sema::ExpressionEvaluationContext::Unevaluated
424-
: Sema::ExpressionEvaluationContext::ConstantEvaluated);
425-
426-
ExprResult ArgExpr(
427-
Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));
428-
if (ArgExpr.isInvalid()) {
450+
// Eat the comma, move to the next argument
451+
} while (TryConsumeToken(tok::comma));
452+
} else {
453+
// General case. Parse all available expressions.
454+
bool Uneval = attributeParsedArgsUnevaluated(*AttrName);
455+
EnterExpressionEvaluationContext Unevaluated(
456+
Actions, Uneval
457+
? Sema::ExpressionEvaluationContext::Unevaluated
458+
: Sema::ExpressionEvaluationContext::ConstantEvaluated);
459+
460+
CommaLocsTy CommaLocs;
461+
ExprVector ParsedExprs;
462+
if (ParseExpressionList(ParsedExprs, CommaLocs,
463+
llvm::function_ref<void()>(),
464+
/*FailImmediatelyOnInvalidExpr=*/true,
465+
/*EarlyTypoCorrection=*/true)) {
466+
SkipUntil(tok::r_paren, StopAtSemi);
467+
return 0;
468+
}
469+
470+
// Pack expansion must currently be explicitly supported by an attribute.
471+
for (size_t I = 0; I < ParsedExprs.size(); ++I) {
472+
if (!isa<PackExpansionExpr>(ParsedExprs[I]))
473+
continue;
474+
475+
if (!attributeAcceptsExprPack(*AttrName)) {
476+
Diag(Tok.getLocation(),
477+
diag::err_attribute_argument_parm_pack_not_supported)
478+
<< AttrName;
429479
SkipUntil(tok::r_paren, StopAtSemi);
430480
return 0;
431481
}
432-
ArgExprs.push_back(ArgExpr.get());
433482
}
434-
// Eat the comma, move to the next argument
435-
} while (TryConsumeToken(tok::comma));
483+
484+
ArgExprs.insert(ArgExprs.end(), ParsedExprs.begin(), ParsedExprs.end());
485+
}
436486
}
437487

438488
SourceLocation RParen = Tok.getLocation();

clang/lib/Parse/ParseExpr.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3381,7 +3381,9 @@ ExprResult Parser::ParseFoldExpression(ExprResult LHS,
33813381
/// \endverbatim
33823382
bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
33833383
SmallVectorImpl<SourceLocation> &CommaLocs,
3384-
llvm::function_ref<void()> ExpressionStarts) {
3384+
llvm::function_ref<void()> ExpressionStarts,
3385+
bool FailImmediatelyOnInvalidExpr,
3386+
bool EarlyTypoCorrection) {
33853387
bool SawError = false;
33863388
while (true) {
33873389
if (ExpressionStarts)
@@ -3394,6 +3396,9 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
33943396
} else
33953397
Expr = ParseAssignmentExpression();
33963398

3399+
if (EarlyTypoCorrection)
3400+
Expr = Actions.CorrectDelayedTyposInExpr(Expr);
3401+
33973402
if (Tok.is(tok::ellipsis))
33983403
Expr = Actions.ActOnPackExpansion(Expr.get(), ConsumeToken());
33993404
else if (Tok.is(tok::code_completion)) {
@@ -3407,8 +3412,10 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
34073412
break;
34083413
}
34093414
if (Expr.isInvalid()) {
3410-
SkipUntil(tok::comma, tok::r_paren, StopBeforeMatch);
34113415
SawError = true;
3416+
if (FailImmediatelyOnInvalidExpr)
3417+
break;
3418+
SkipUntil(tok::comma, tok::r_paren, StopBeforeMatch);
34123419
} else {
34133420
Exprs.push_back(Expr.get());
34143421
}

clang/lib/Sema/ParsedAttr.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ unsigned ParsedAttr::getMaxArgs() const {
155155
return getMinArgs() + getInfo().OptArgs;
156156
}
157157

158+
unsigned ParsedAttr::getNumArgMembers() const {
159+
return getInfo().NumArgMembers;
160+
}
161+
158162
bool ParsedAttr::hasCustomParsing() const {
159163
return getInfo().HasCustomParsing;
160164
}
@@ -208,6 +212,8 @@ bool ParsedAttr::isSupportedByPragmaAttribute() const {
208212
return getInfo().IsSupportedByPragmaAttribute;
209213
}
210214

215+
bool ParsedAttr::acceptsExprPack() const { return getInfo().AcceptsExprPack; }
216+
211217
unsigned ParsedAttr::getSemanticSpelling() const {
212218
return getInfo().spellingIndexToSemanticSpelling(*this);
213219
}
@@ -220,6 +226,14 @@ bool ParsedAttr::hasVariadicArg() const {
220226
return getInfo().OptArgs == 15;
221227
}
222228

229+
bool ParsedAttr::isParamExpr(size_t N) const {
230+
return getInfo().isParamExpr(N);
231+
}
232+
233+
void ParsedAttr::handleAttrWithDelayedArgs(Sema &S, Decl *D) const {
234+
::handleAttrWithDelayedArgs(S, D, *this);
235+
}
236+
223237
static unsigned getNumAttributeArgs(const ParsedAttr &AL) {
224238
// FIXME: Include the type in the argument list.
225239
return AL.getNumArgs() + AL.hasParsedType();

clang/lib/Sema/SemaAttr.cpp

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,8 +1213,9 @@ void Sema::PopPragmaVisibility(bool IsNamespaceEnd, SourceLocation EndLoc) {
12131213
}
12141214

12151215
template <typename Ty>
1216-
static bool checkCommonAttributeFeatures(Sema& S, const Ty *Node,
1217-
const ParsedAttr& A) {
1216+
static bool checkCommonAttributeFeatures(Sema &S, const Ty *Node,
1217+
const ParsedAttr &A,
1218+
bool SkipArgCountCheck) {
12181219
// Several attributes carry different semantics than the parsing requires, so
12191220
// those are opted out of the common argument checks.
12201221
//
@@ -1240,26 +1241,30 @@ static bool checkCommonAttributeFeatures(Sema& S, const Ty *Node,
12401241
if (A.hasCustomParsing())
12411242
return false;
12421243

1243-
if (A.getMinArgs() == A.getMaxArgs()) {
1244-
// If there are no optional arguments, then checking for the argument count
1245-
// is trivial.
1246-
if (!A.checkExactlyNumArgs(S, A.getMinArgs()))
1247-
return true;
1248-
} else {
1249-
// There are optional arguments, so checking is slightly more involved.
1250-
if (A.getMinArgs() && !A.checkAtLeastNumArgs(S, A.getMinArgs()))
1251-
return true;
1252-
else if (!A.hasVariadicArg() && A.getMaxArgs() &&
1253-
!A.checkAtMostNumArgs(S, A.getMaxArgs()))
1254-
return true;
1244+
if (!SkipArgCountCheck) {
1245+
if (A.getMinArgs() == A.getMaxArgs()) {
1246+
// If there are no optional arguments, then checking for the argument
1247+
// count is trivial.
1248+
if (!A.checkExactlyNumArgs(S, A.getMinArgs()))
1249+
return true;
1250+
} else {
1251+
// There are optional arguments, so checking is slightly more involved.
1252+
if (A.getMinArgs() && !A.checkAtLeastNumArgs(S, A.getMinArgs()))
1253+
return true;
1254+
else if (!A.hasVariadicArg() && A.getMaxArgs() &&
1255+
!A.checkAtMostNumArgs(S, A.getMaxArgs()))
1256+
return true;
1257+
}
12551258
}
12561259

12571260
return false;
12581261
}
12591262

1260-
bool Sema::checkCommonAttributeFeatures(const Decl *D, const ParsedAttr &A) {
1261-
return ::checkCommonAttributeFeatures(*this, D, A);
1263+
bool Sema::checkCommonAttributeFeatures(const Decl *D, const ParsedAttr &A,
1264+
bool SkipArgCountCheck) {
1265+
return ::checkCommonAttributeFeatures(*this, D, A, SkipArgCountCheck);
12621266
}
1263-
bool Sema::checkCommonAttributeFeatures(const Stmt *S, const ParsedAttr &A) {
1264-
return ::checkCommonAttributeFeatures(*this, S, A);
1267+
bool Sema::checkCommonAttributeFeatures(const Stmt *S, const ParsedAttr &A,
1268+
bool SkipArgCountCheck) {
1269+
return ::checkCommonAttributeFeatures(*this, S, A, SkipArgCountCheck);
12651270
}

0 commit comments

Comments
 (0)