[c++1z] Implement N3994: a range-based for loop can declare a variable with super-terse notation

for (x : range) { ... }

which is equivalent to

  for (auto &&x : range) { ... }

llvm-svn: 211267
This commit is contained in:
Richard Smith 2014-06-19 11:42:00 +00:00
parent 562fd7534c
commit 955bf016ee
7 changed files with 125 additions and 10 deletions

View File

@ -277,6 +277,13 @@ def ext_for_range : ExtWarn<
def warn_cxx98_compat_for_range : Warning<
"range-based for loop is incompatible with C++98">,
InGroup<CXX98Compat>, DefaultIgnore;
def ext_for_range_identifier : ExtWarn<
"range-based for loop with implicit deduced type is a C++1z extension">,
InGroup<CXX1z>;
def warn_cxx1y_compat_for_range_identifier : Warning<
"range-based for loop with implicit deduced type is incompatible with "
"C++ standards before C++1z">,
InGroup<CXXPre1zCompat>, DefaultIgnore;
def err_for_range_expected_decl : Error<
"for range declaration must declare a variable">;
def err_argument_required_after_attribute : Error<

View File

@ -1827,6 +1827,9 @@ private:
return isDeclarationSpecifier(true);
}
/// \brief Determine whether this is a C++1z for-range-identifier.
bool isForRangeIdentifier();
/// \brief Determine whether we are currently at the start of an Objective-C
/// class message that appears to be missing the open bracket '['.
bool isStartOfObjCClassMessageMissingOpenBracket();
@ -2006,6 +2009,10 @@ private:
// for example, attributes appertain to decl specifiers.
void ProhibitCXX11Attributes(ParsedAttributesWithRange &attrs);
/// \brief Skip C++11 attributes and return the end location of the last one.
/// \returns SourceLocation() if there are no attributes.
SourceLocation SkipCXX11Attributes();
/// \brief Diagnose and skip C++11 attributes that appear in syntactic
/// locations where attributes are not allowed.
void DiagnoseAndSkipCXX11Attributes();

View File

@ -1622,6 +1622,10 @@ public:
void ActOnUninitializedDecl(Decl *dcl, bool TypeMayContainAuto);
void ActOnInitializerError(Decl *Dcl);
void ActOnCXXForRangeDecl(Decl *D);
StmtResult ActOnCXXForRangeIdentifier(Scope *S, SourceLocation IdentLoc,
IdentifierInfo *Ident,
ParsedAttributes &Attrs,
SourceLocation AttrEnd);
void SetDeclDeleted(Decl *dcl, SourceLocation DelLoc);
void SetDeclDefaulted(Decl *dcl, SourceLocation DefaultLoc);
void FinalizeDeclaration(Decl *D);

View File

@ -3390,13 +3390,23 @@ void Parser::ParseCXX11Attributes(ParsedAttributesWithRange &attrs,
}
void Parser::DiagnoseAndSkipCXX11Attributes() {
if (!isCXX11AttributeSpecifier())
return;
// Start and end location of an attribute or an attribute list.
SourceLocation StartLoc = Tok.getLocation();
SourceLocation EndLoc = SkipCXX11Attributes();
if (EndLoc.isValid()) {
SourceRange Range(StartLoc, EndLoc);
Diag(StartLoc, diag::err_attributes_not_allowed)
<< Range;
}
}
SourceLocation Parser::SkipCXX11Attributes() {
SourceLocation EndLoc;
if (!isCXX11AttributeSpecifier())
return EndLoc;
do {
if (Tok.is(tok::l_square)) {
BalancedDelimiterTracker T(*this, tok::l_square);
@ -3413,11 +3423,7 @@ void Parser::DiagnoseAndSkipCXX11Attributes() {
}
} while (isCXX11AttributeSpecifier());
if (EndLoc.isValid()) {
SourceRange Range(StartLoc, EndLoc);
Diag(StartLoc, diag::err_attributes_not_allowed)
<< Range;
}
return EndLoc;
}
/// ParseMicrosoftAttributes - Parse a Microsoft attribute [Attr]

View File

@ -1388,6 +1388,25 @@ StmtResult Parser::ParseDoStatement() {
Cond.get(), T.getCloseLocation());
}
bool Parser::isForRangeIdentifier() {
assert(Tok.is(tok::identifier));
const Token &Next = NextToken();
if (Next.is(tok::colon))
return true;
if (Next.is(tok::l_square) || Next.is(tok::kw_alignas)) {
TentativeParsingAction PA(*this);
ConsumeToken();
SkipCXX11Attributes();
bool Result = Tok.is(tok::colon);
PA.Revert();
return Result;
}
return false;
}
/// ParseForStatement
/// for-statement: [C99 6.8.5.3]
/// 'for' '(' expr[opt] ';' expr[opt] ';' expr[opt] ')' statement
@ -1471,6 +1490,29 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
ProhibitAttributes(attrs);
// no first part, eat the ';'.
ConsumeToken();
} else if (getLangOpts().CPlusPlus && Tok.is(tok::identifier) &&
isForRangeIdentifier()) {
ProhibitAttributes(attrs);
IdentifierInfo *Name = Tok.getIdentifierInfo();
SourceLocation Loc = ConsumeToken();
MaybeParseCXX11Attributes(attrs);
ForRangeInit.ColonLoc = ConsumeToken();
if (Tok.is(tok::l_brace))
ForRangeInit.RangeExpr = ParseBraceInitializer();
else
ForRangeInit.RangeExpr = ParseExpression();
Diag(Loc, getLangOpts().CPlusPlus1z
? diag::warn_cxx1y_compat_for_range_identifier
: diag::ext_for_range_identifier)
<< ((getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus1z)
? FixItHint::CreateInsertion(Loc, "auto &&")
: FixItHint());
FirstPart = Actions.ActOnCXXForRangeIdentifier(getCurScope(), Loc, Name,
attrs, attrs.Range.getEnd());
ForRange = true;
} else if (isForInitDeclaration()) { // for (int X = 4;
// Parse declaration, which eats the ';'.
if (!C99orCXXorObjC) // Use of C99-style for loops in C90 mode?

View File

@ -8941,6 +8941,37 @@ void Sema::ActOnCXXForRangeDecl(Decl *D) {
}
}
StmtResult
Sema::ActOnCXXForRangeIdentifier(Scope *S, SourceLocation IdentLoc,
IdentifierInfo *Ident,
ParsedAttributes &Attrs,
SourceLocation AttrEnd) {
// C++1y [stmt.iter]p1:
// A range-based for statement of the form
// for ( for-range-identifier : for-range-initializer ) statement
// is equivalent to
// for ( auto&& for-range-identifier : for-range-initializer ) statement
DeclSpec DS(Attrs.getPool().getFactory());
const char *PrevSpec;
unsigned DiagID;
DS.SetTypeSpecType(DeclSpec::TST_auto, IdentLoc, PrevSpec, DiagID,
getPrintingPolicy());
Declarator D(DS, Declarator::ForContext);
D.SetIdentifier(Ident, IdentLoc);
D.takeAttributes(Attrs, AttrEnd);
ParsedAttributes EmptyAttrs(Attrs.getPool().getFactory());
D.AddTypeInfo(DeclaratorChunk::getReference(0, IdentLoc, /*lvalue*/false),
EmptyAttrs, IdentLoc);
Decl *Var = ActOnDeclarator(S, D);
cast<VarDecl>(Var)->setCXXForRangeDecl(true);
FinalizeDeclaration(Var);
return ActOnDeclStmt(FinalizeDeclaratorGroup(S, DS, Var), IdentLoc,
AttrEnd.isValid() ? AttrEnd : IdentLoc);
}
void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
if (var->isInvalidDecl()) return;

View File

@ -176,8 +176,9 @@ namespace test4 {
// Make sure these don't crash. Better diagnostics would be nice.
for (: {1, 2, 3}) {} // expected-error {{expected expression}} expected-error {{expected ';'}}
for (x : {1, 2, 3}) {} // expected-error {{undeclared identifier}} expected-error {{expected ';'}}
for (y : {1, 2, 3}) {} // expected-error {{must declare a variable}} expected-warning {{result unused}}
for (1 : {1, 2, 3}) {} // expected-error {{must declare a variable}} expected-warning {{result unused}}
for (+x : {1, 2, 3}) {} // expected-error {{undeclared identifier}} expected-error {{expected ';'}}
for (+y : {1, 2, 3}) {} // expected-error {{must declare a variable}} expected-warning {{result unused}}
}
}
@ -209,3 +210,20 @@ namespace test6 {
// expected-error@-1 {{cannot build range expression with array function parameter 'arr' since parameter with array type 'test6::vector []' is treated as pointer type 'test6::vector *'}}
}
}
namespace test7 {
void f() {
int arr[5], b;
for (a : arr) {} // expected-warning {{extension}}
// FIXME: Give a -Wshadow for this by default?
for (b : arr) {} // expected-warning {{extension}}
for (arr : arr) {} // expected-warning {{extension}}
for (c alignas(8) : arr) { // expected-warning {{extension}}
static_assert(alignof(c) == 8, ""); // expected-warning {{extension}}
}
// FIXME: We should reject this, but don't, because we only check the
// attribute before we deduce the 'auto' type.
for (d alignas(1) : arr) {} // expected-warning {{extension}}
for (e [[deprecated]] : arr) { e = 0; } // expected-warning {{deprecated}} expected-note {{here}} expected-warning {{extension}}
}
}