Clean up a large number of C++11 attribute parse issues, including parsing

attributes in more places where we didn't and catching a lot more issues.

This implements nearly every aspect of C++11 attribute parsing, except for:
 - Attributes are permitted on explicit instantiations inside the declarator
   (but not preceding the decl-spec)
 - Attributes are permitted on friend declarations of functions.
 - Multiple instances of the same attribute in an attribute-list (e.g.
   [[noreturn, noreturn]], not [[noreturn]] [[noreturn]] which is conforming)
   are allowed.
The first two are marked as expected-FIXME in the test file and the latter
is probably a defect and is currently untested.

Thanks to Richard Smith for providing the lion's share of the testcases.

llvm-svn: 159072
This commit is contained in:
Alexis Hunt 2012-06-23 05:07:58 +00:00
parent d9c7d0dda4
commit 6aa9beef50
9 changed files with 254 additions and 43 deletions

View File

@ -1045,10 +1045,13 @@ private:
ParsingDeclSpec *DS = 0);
bool isDeclarationAfterDeclarator();
bool isStartOfFunctionDefinition(const ParsingDeclarator &Declarator);
DeclGroupPtrTy ParseDeclarationOrFunctionDefinition(ParsedAttributes &attrs,
AccessSpecifier AS = AS_none);
DeclGroupPtrTy ParseDeclarationOrFunctionDefinition(ParsingDeclSpec &DS,
DeclGroupPtrTy ParseDeclarationOrFunctionDefinition(
ParsedAttributesWithRange &attrs,
ParsingDeclSpec *DS = 0,
AccessSpecifier AS = AS_none);
DeclGroupPtrTy ParseDeclOrFunctionDefInternal(ParsedAttributesWithRange &attrs,
ParsingDeclSpec &DS,
AccessSpecifier AS);
Decl *ParseFunctionDefinition(ParsingDeclarator &D,
const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(),
@ -1496,7 +1499,7 @@ private:
DeclGroupPtrTy ParseSimpleDeclaration(StmtVector &Stmts,
unsigned Context,
SourceLocation &DeclEnd,
ParsedAttributes &attrs,
ParsedAttributesWithRange &attrs,
bool RequireSemi,
ForRangeInit *FRI = 0);
bool MightBeDeclarator(unsigned Context);

View File

@ -1126,10 +1126,12 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(StmtVector &Stmts,
/// simple-declaration: [C99 6.7: declaration] [C++ 7p1: dcl.dcl]
/// declaration-specifiers init-declarator-list[opt] ';'
/// [C++11] attribute-specifier-seq decl-specifier-seq[opt]
/// init-declarator-list ';'
///[C90/C++]init-declarator-list ';' [TODO]
/// [OMP] threadprivate-directive [TODO]
///
/// for-range-declaration: [C++0x 6.5p1: stmt.ranged]
/// for-range-declaration: [C++11 6.5p1: stmt.ranged]
/// attribute-specifier-seq[opt] type-specifier-seq declarator
///
/// If RequireSemi is false, this does not check for a ';' at the end of the
@ -1138,12 +1140,11 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(StmtVector &Stmts,
/// If FRI is non-null, we might be parsing a for-range-declaration instead
/// of a simple-declaration. If we find that we are, we also parse the
/// for-range-initializer, and place it here.
Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(StmtVector &Stmts,
unsigned Context,
SourceLocation &DeclEnd,
ParsedAttributes &attrs,
bool RequireSemi,
ForRangeInit *FRI) {
Parser::DeclGroupPtrTy
Parser::ParseSimpleDeclaration(StmtVector &Stmts, unsigned Context,
SourceLocation &DeclEnd,
ParsedAttributesWithRange &attrs,
bool RequireSemi, ForRangeInit *FRI) {
// Parse the common declaration-specifiers piece.
ParsingDeclSpec DS(*this);
DS.takeAttributesFrom(attrs);
@ -2026,6 +2027,8 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
}
bool EnteringContext = (DSContext == DSC_class || DSContext == DSC_top_level);
bool AttrsLastTime = false;
ParsedAttributesWithRange attrs(AttrFactory);
while (1) {
bool isInvalid = false;
const char *PrevSpec = 0;
@ -2036,14 +2039,32 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
switch (Tok.getKind()) {
default:
DoneWithDeclSpec:
// [C++0x] decl-specifier-seq: decl-specifier attribute-specifier-seq[opt]
MaybeParseCXX0XAttributes(DS.getAttributes());
if (!AttrsLastTime)
ProhibitAttributes(attrs);
else
DS.takeAttributesFrom(attrs);
// If this is not a declaration specifier token, we're done reading decl
// specifiers. First verify that DeclSpec's are consistent.
DS.Finish(Diags, PP);
return;
case tok::l_square:
case tok::kw_alignas:
if (!isCXX11AttributeSpecifier())
goto DoneWithDeclSpec;
ProhibitAttributes(attrs);
// FIXME: It would be good to recover by accepting the attributes,
// but attempting to do that now would cause serious
// madness in terms of diagnostics.
attrs.clear();
attrs.Range = SourceRange();
ParseCXX11Attributes(attrs);
AttrsLastTime = true;
continue;
case tok::code_completion: {
Sema::ParserCompletionContext CCC = Sema::PCC_Namespace;
if (DS.hasTypeSpecifier()) {
@ -2696,6 +2717,8 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
DS.SetRangeEnd(Tok.getLocation());
if (DiagID != diag::err_bool_redeclaration)
ConsumeToken();
AttrsLastTime = false;
}
}
@ -2941,6 +2964,15 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
return cutOffParsing();
}
// If attributes exist after tag, parse them.
ParsedAttributesWithRange attrs(AttrFactory);
MaybeParseGNUAttributes(attrs);
MaybeParseCXX0XAttributes(attrs);
// If declspecs exist after tag, parse them.
while (Tok.is(tok::kw___declspec))
ParseMicrosoftDeclSpec(attrs);
SourceLocation ScopedEnumKWLoc;
bool IsScopedUsingClassTag = false;
@ -2949,6 +2981,10 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
Diag(Tok, diag::warn_cxx98_compat_scoped_enum);
IsScopedUsingClassTag = Tok.is(tok::kw_class);
ScopedEnumKWLoc = ConsumeToken();
ProhibitAttributes(attrs);
// Recovery: assume that the attributes came after the tag.
MaybeParseCXX0XAttributes(attrs);
}
// C++11 [temp.explicit]p12:
@ -2962,14 +2998,6 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization);
SuppressAccessChecks diagsFromTag(*this, shouldDelayDiagsInTag);
// If attributes exist after tag, parse them.
ParsedAttributes attrs(AttrFactory);
MaybeParseGNUAttributes(attrs);
// If declspecs exist after tag, parse them.
while (Tok.is(tok::kw___declspec))
ParseMicrosoftDeclSpec(attrs);
// Enum definitions should not be parsed in a trailing-return-type.
bool AllowDeclaration = DSC != DSC_trailing;
@ -3154,6 +3182,9 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
TParams = MultiTemplateParamsArg(TemplateInfo.TemplateParams->data(),
TemplateInfo.TemplateParams->size());
}
if (TUK == Sema::TUK_Reference)
ProhibitAttributes(attrs);
if (!Name && TUK != Sema::TUK_Definition) {
Diag(Tok, diag::err_enumerator_unnamed_no_def);
@ -3252,8 +3283,10 @@ void Parser::ParseEnumBody(SourceLocation StartLoc, Decl *EnumDecl) {
SourceLocation IdentLoc = ConsumeToken();
// If attributes exist after the enumerator, parse them.
ParsedAttributes attrs(AttrFactory);
ParsedAttributesWithRange attrs(AttrFactory);
MaybeParseGNUAttributes(attrs);
MaybeParseCXX0XAttributes(attrs);
ProhibitAttributes(attrs);
SourceLocation EqualLoc;
ExprResult AssignedVal;

View File

@ -444,6 +444,13 @@ Decl *Parser::ParseUsingDeclaration(unsigned Context,
CXXScopeSpec SS;
SourceLocation TypenameLoc;
bool IsTypeName;
ParsedAttributesWithRange attrs(AttrFactory);
// FIXME: Simply skip the attributes and diagnose, don't bother parsing them.
MaybeParseCXX0XAttributes(attrs);
ProhibitAttributes(attrs);
attrs.clear();
attrs.Range = SourceRange();
// Ignore optional 'typename'.
// FIXME: This is wrong; we should parse this as a typename-specifier.
@ -480,7 +487,7 @@ Decl *Parser::ParseUsingDeclaration(unsigned Context,
return 0;
}
ParsedAttributes attrs(AttrFactory);
MaybeParseCXX0XAttributes(attrs);
// Maybe this is an alias-declaration.
bool IsAliasDecl = Tok.is(tok::equal);
@ -533,9 +540,14 @@ Decl *Parser::ParseUsingDeclaration(unsigned Context,
TypeAlias = ParseTypeName(0, TemplateInfo.Kind ?
Declarator::AliasTemplateContext :
Declarator::AliasDeclContext, AS, OwnedType);
} else
} else {
// C++11 attributes are not allowed on a using-declaration, but GNU ones
// are.
ProhibitAttributes(attrs);
// Parse (optional) attributes (most likely GNU strong-using extension).
MaybeParseGNUAttributes(attrs);
}
// Eat ';'.
DeclEnd = Tok.getLocation();
@ -572,6 +584,7 @@ Decl *Parser::ParseUsingDeclaration(unsigned Context,
MultiTemplateParamsArg TemplateParamsArg(Actions,
TemplateParams ? TemplateParams->data() : 0,
TemplateParams ? TemplateParams->size() : 0);
// FIXME: Propagate attributes.
return Actions.ActOnAliasDeclaration(getCurScope(), AS, TemplateParamsArg,
UsingLoc, Name, TypeAlias);
}
@ -989,7 +1002,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization);
SuppressAccessChecks diagsFromTag(*this, shouldDelayDiagsInTag);
ParsedAttributes attrs(AttrFactory);
ParsedAttributesWithRange attrs(AttrFactory);
// If attributes exist after tag, parse them.
if (Tok.is(tok::kw___attribute))
ParseGNUAttributes(attrs);
@ -1202,6 +1215,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation &&
TUK == Sema::TUK_Declaration) {
// This is an explicit instantiation of a class template.
ProhibitAttributes(attrs);
TagOrTempResult
= Actions.ActOnExplicitInstantiation(getCurScope(),
TemplateInfo.ExternLoc,
@ -1223,6 +1238,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
} else if (TUK == Sema::TUK_Reference ||
(TUK == Sema::TUK_Friend &&
TemplateInfo.Kind == ParsedTemplateInfo::NonTemplate)) {
ProhibitAttributes(attrs);
TypeResult = Actions.ActOnTagTemplateIdType(TUK, TagType, StartLoc,
TemplateId->SS,
TemplateId->TemplateKWLoc,
@ -1287,6 +1303,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
//
// template struct Outer<int>::Inner;
//
ProhibitAttributes(attrs);
TagOrTempResult
= Actions.ActOnExplicitInstantiation(getCurScope(),
TemplateInfo.ExternLoc,
@ -1295,6 +1313,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
NameLoc, attrs.getList());
} else if (TUK == Sema::TUK_Friend &&
TemplateInfo.Kind != ParsedTemplateInfo::NonTemplate) {
ProhibitAttributes(attrs);
TagOrTempResult =
Actions.ActOnTemplatedFriendTag(getCurScope(), DS.getFriendSpecLoc(),
TagType, StartLoc, SS,
@ -1308,6 +1328,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
// FIXME: Diagnose this particular error.
}
if (TUK != Sema::TUK_Declaration && TUK != Sema::TUK_Definition)
ProhibitAttributes(attrs);
bool IsDependent = false;
// Don't pass down template parameter lists if this is just a tag
@ -2951,7 +2974,7 @@ void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs,
SkipUntil(tok::r_square, false);
}
/// ParseCXX11Attributes - Parse a C++0x attribute-specifier-seq.
/// ParseCXX11Attributes - Parse a C++11 attribute-specifier-seq.
///
/// attribute-specifier-seq:
/// attribute-specifier-seq[opt] attribute-specifier

View File

@ -1296,7 +1296,12 @@ bool Parser::ParseCXXCondition(ExprResult &ExprOut,
return true;
}
ParsedAttributesWithRange attrs(AttrFactory);
MaybeParseCXX0XAttributes(attrs);
if (!isCXXConditionDeclaration()) {
ProhibitAttributes(attrs);
// Parse the expression.
ExprOut = ParseExpression(); // expression
DeclOut = 0;

View File

@ -420,7 +420,7 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
// erroneous r_brace would cause an infinite loop if not handled here.
if (Tok.is(tok::r_brace))
break;
ParsedAttributes attrs(AttrFactory);
ParsedAttributesWithRange attrs(AttrFactory);
allTUVariables.push_back(ParseDeclarationOrFunctionDefinition(attrs));
continue;
}

View File

@ -1259,6 +1259,12 @@ StmtResult Parser::ParseDoStatement() {
// Parse the parenthesized condition.
BalancedDelimiterTracker T(*this, tok::l_paren);
T.consumeOpen();
// FIXME: Do not just parse the attribute contents and throw them away
ParsedAttributesWithRange attrs(AttrFactory);
MaybeParseCXX0XAttributes(attrs);
ProhibitAttributes(attrs);
ExprResult Cond = ParseExpression();
T.consumeClose();
DoScope.Exit();
@ -1347,8 +1353,12 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
return StmtError();
}
ParsedAttributesWithRange attrs(AttrFactory);
MaybeParseCXX0XAttributes(attrs);
// Parse the first part of the for specifier.
if (Tok.is(tok::semi)) { // for (;
ProhibitAttributes(attrs);
// no first part, eat the ';'.
ConsumeToken();
} else if (isForInitDeclaration()) { // for (int X = 4;
@ -1393,6 +1403,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
Diag(Tok, diag::err_expected_semi_for);
}
} else {
ProhibitAttributes(attrs);
Value = ParseExpression();
ForEach = isTokIdentifier_in();

View File

@ -219,7 +219,10 @@ Parser::ParseSingleDeclarationAfterTemplate(
ParsingDeclSpec DS(*this, &DiagsFromTParams);
// Move the attributes from the prefix into the DS.
DS.takeAttributesFrom(prefixAttrs);
if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation)
ProhibitAttributes(prefixAttrs);
else
DS.takeAttributesFrom(prefixAttrs);
ParseDeclarationSpecifiers(DS, TemplateInfo, AS,
getDeclSpecContextFromDeclaratorContext(Context));

View File

@ -746,8 +746,7 @@ Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs,
dont_know:
// We can't tell whether this is a function-definition or declaration yet.
if (DS) {
DS->takeAttributesFrom(attrs);
return ParseDeclarationOrFunctionDefinition(*DS);
return ParseDeclarationOrFunctionDefinition(attrs, DS);
} else {
return ParseDeclarationOrFunctionDefinition(attrs);
}
@ -815,20 +814,24 @@ bool Parser::isStartOfFunctionDefinition(const ParsingDeclarator &Declarator) {
/// [OMP] threadprivate-directive [TODO]
///
Parser::DeclGroupPtrTy
Parser::ParseDeclarationOrFunctionDefinition(ParsingDeclSpec &DS,
AccessSpecifier AS) {
Parser::ParseDeclOrFunctionDefInternal(ParsedAttributesWithRange &attrs,
ParsingDeclSpec &DS,
AccessSpecifier AS) {
// Parse the common declaration-specifiers piece.
ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS, DSC_top_level);
// C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };"
// declaration-specifiers init-declarator-list[opt] ';'
if (Tok.is(tok::semi)) {
ProhibitAttributes(attrs);
ConsumeToken();
Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec(getCurScope(), AS, DS);
DS.complete(TheDecl);
return Actions.ConvertDeclToDeclGroup(TheDecl);
}
DS.takeAttributesFrom(attrs);
// ObjC2 allows prefix attributes on class interfaces and protocols.
// FIXME: This still needs better diagnostics. We should only accept
// attributes here, no types, etc.
@ -869,16 +872,20 @@ Parser::ParseDeclarationOrFunctionDefinition(ParsingDeclSpec &DS,
}
Parser::DeclGroupPtrTy
Parser::ParseDeclarationOrFunctionDefinition(ParsedAttributes &attrs,
Parser::ParseDeclarationOrFunctionDefinition(ParsedAttributesWithRange &attrs,
ParsingDeclSpec *DS,
AccessSpecifier AS) {
ParsingDeclSpec DS(*this);
DS.takeAttributesFrom(attrs);
// Must temporarily exit the objective-c container scope for
// parsing c constructs and re-enter objc container scope
// afterwards.
ObjCDeclContextSwitch ObjCDC(*this);
return ParseDeclarationOrFunctionDefinition(DS, AS);
if (DS) {
return ParseDeclOrFunctionDefInternal(attrs, *DS, AS);
} else {
ParsingDeclSpec PDS(*this);
// Must temporarily exit the objective-c container scope for
// parsing c constructs and re-enter objc container scope
// afterwards.
ObjCDeclContextSwitch ObjCDC(*this);
return ParseDeclOrFunctionDefInternal(attrs, PDS, AS);
}
}
/// ParseFunctionDefinition - We parsed and verified that the specified

View File

@ -1,15 +1,50 @@
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fsyntax-only -verify -std=c++11 %s
// Need std::initializer_list
namespace std {
typedef decltype(sizeof(int)) size_t;
// libc++'s implementation
template <class _E>
class initializer_list
{
const _E* __begin_;
size_t __size_;
initializer_list(const _E* __b, size_t __s)
: __begin_(__b),
__size_(__s)
{}
public:
typedef _E value_type;
typedef const _E& reference;
typedef const _E& const_reference;
typedef size_t size_type;
typedef const _E* iterator;
typedef const _E* const_iterator;
initializer_list() : __begin_(nullptr), __size_(0) {}
size_t size() const {return __size_;}
const _E* begin() const {return __begin_;}
const _E* end() const {return __begin_ + __size_;}
};
}
// Declaration syntax checks
[[]] int before_attr;
int [[]] between_attr;
const [[]] int between_attr_2 = 0; // expected-error {{an attribute list cannot appear here}}
int after_attr [[]];
int * [[]] ptr_attr;
int & [[]] ref_attr = after_attr;
int && [[]] rref_attr = 0;
int array_attr [1] [[]];
alignas(8) int aligned_attr;
[[test::valid(for 42 [very] **** '+' symbols went on a trip; the end.)]]
[[test::valid(for 42 [very] **** '+' symbols went on a trip and had a "good"_time; the end.)]]
int garbage_attr;
[[,,,static, class, namespace,, inline, constexpr, mutable,, bi\
tand, bitor::compl(!.*_ Cx.!U^*R),,,]] int more_garbage_attr;
@ -19,7 +54,18 @@ void noexcept_fn_attr () noexcept [[]];
struct MemberFnOrder {
virtual void f() const volatile && noexcept [[]] final = 0;
};
struct [[]] struct_attr;
class [[]] class_attr {};
union [[]] union_attr;
[[]] struct with_init_declarators {} init_declarator;
[[]] struct no_init_declarators; // expected-error {{an attribute list cannot appear here}}
[[]];
struct ctordtor {
[[]] ctordtor();
[[]] ~ctordtor();
};
[[]] ctordtor::ctordtor() {}
[[]] ctordtor::~ctordtor() {}
extern "C++" [[]] int extern_attr;
template <typename T> [[]] void template_attr ();
[[]] [[]] int [[]] [[]] multi_attr [[]] [[]];
@ -27,7 +73,8 @@ template <typename T> [[]] void template_attr ();
int comma_attr [[,]];
int scope_attr [[foo::]]; // expected-error {{expected identifier}}
int (paren_attr) [[]]; // expected-error {{an attribute list cannot appear here}}
unsigned [[]] int attr_in_decl_spec; // expected-error {{expected unqualified-id}}
unsigned [[]] int attr_in_decl_spec; // expected-error {{an attribute list cannot appear here}}
unsigned [[]] int [[]] const double_decl_spec = 0; // expected-error 2{{an attribute list cannot appear here}}
class foo {
void const_after_attr () [[]] const; // expected-error {{expected ';'}}
};
@ -40,6 +87,52 @@ extern "C++" [[]] { } // expected-error {{an attribute list cannot appear here}}
[[]] using ns::i; // expected-error {{an attribute list cannot appear here}}
[[]] using namespace ns;
[[]] using T = int; // expected-error {{an attribute list cannot appear here}}
using T [[]] = int; // ok
template<typename T> using U [[]] = T;
using ns::i [[]]; // expected-error {{an attribute list cannot appear here}}
using [[]] ns::i; // expected-error {{an attribute list cannot appear here}}
auto trailing() -> [[]] const int; // expected-error {{an attribute list cannot appear here}}
auto trailing() -> const [[]] int; // expected-error {{an attribute list cannot appear here}}
auto trailing() -> const int [[]];
auto trailing_2() -> struct struct_attr [[]];
namespace N {
struct S {};
};
template<typename> struct Template {};
// FIXME: Improve this diagnostic
struct [[]] N::S s; // expected-error {{an attribute list cannot appear here}}
struct [[]] Template<int> t; // expected-error {{an attribute list cannot appear here}}
struct [[]] ::template Template<int> u; // expected-error {{an attribute list cannot appear here}}
template struct [[]] Template<char>; // expected-error {{an attribute list cannot appear here}}
template <> struct [[]] Template<void>;
enum [[]] E1 {};
enum [[]] E2; // expected-error {{forbids forward references}}
enum [[]] E1;
enum [[]] E3 : int;
enum [[]] {
k_123 [[]] = 123 // expected-error {{an attribute list cannot appear here}}
};
enum [[]] E1 e; // expected-error {{an attribute list cannot appear here}}
enum [[]] class E4 { }; // expected-error {{an attribute list cannot appear here}}
enum struct [[]] E5;
struct S {
friend int f [[]] (); // expected-FIXME{{an attribute list cannot appear here}}
[[]] friend int g(); // expected-FIXME{{an attribute list cannot appear here}}
[[]] friend int h() {
}
friend class [[]] C; // expected-error{{an attribute list cannot appear here}}
};
template<typename T> void tmpl(T) {}
template void tmpl [[]] (int); // expected-FIXME {{an attribute list cannot appear here}}
template [[]] void tmpl(char); // expected-error {{an attribute list cannot appear here}}
template void [[]] tmpl(short);
// Argument tests
alignas int aligned_no_params; // expected-error {{expected '('}}
alignas(i) int aligned_nonconst; // expected-error {{'aligned' attribute requires integer constant}} expected-note {{read of non-const variable 'i'}}
@ -81,3 +174,36 @@ void foo () {
template<typename...Ts> void variadic() {
void bar [[noreturn...]] (); // expected-error {{attribute 'noreturn' cannot be used as an attribute pack}}
}
// Expression tests
void bar () {
[] () [[noreturn]] { return; } (); // expected-error {{should not return}}
[] () [[noreturn]] { throw; } ();
new int[42][[]][5][[]]{};
}
// Condition tests
void baz () {
if ([[]] bool b = true) {
switch ([[]] int n { 42 }) {
default:
for ([[]] int n = 0; [[]] char b = n < 5; ++b) {
}
}
}
int x;
// An attribute can be applied to an expression-statement, such as the first
// statement in a for. But it can't be applied to a condition which is an
// expression.
for ([[]] x = 0; ; ) {} // expected-error {{an attribute list cannot appear here}}
for (; [[]] x < 5; ) {} // expected-error {{an attribute list cannot appear here}}
while ([[]] bool k { false }) {
}
while ([[]] true) { // expected-error {{an attribute list cannot appear here}}
}
do {
} while ([[]] false); // expected-error {{an attribute list cannot appear here}}
for ([[]] int n : { 1, 2, 3 }) {
}
}