Disambiguation of '[[':
 * In C++11, '[[' is ill-formed unless it starts an attribute-specifier. Reject
   array sizes and array indexes which begin with a lambda-expression. Recover by
   parsing the lambda as a lambda.
 * In Objective-C++11, either '[' could be the start of a message-send.
   Fully disambiguate this case: it turns out that the grammars of message-sends,
   lambdas and attributes do not actually overlap. Accept any occurrence of '[['
   where either '[' starts a message send, but reject a lambda in an array index
   just like in C++11 mode.

Implement a couple of changes to the attribute wording which occurred after our
attributes implementation landed:
 * In a function-declaration, the attributes go after the exception specification,
   not after the right paren.
 * A reference type can have attributes applied.
 * An 'identifier' in an attribute can also be a keyword. Support for alternative
   tokens (iso646 keywords) in attributes to follow.

And some bug fixes:
 * Parse attributes after declarator-ids, even if they are not simple identifiers.
 * Do not accept attributes after a parenthesized declarator.
 * Accept attributes after an array size in a new-type-id.
 * Partially disamiguate 'delete' followed by a lambda. More work is required
   here for the case where the lambda-introducer is '[]'.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@154369 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index b5ce193..9bafa92 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -906,6 +906,39 @@
     *EndLoc = T.getCloseLocation();
 }
 
+/// DiagnoseProhibitedCXX11Attribute - We have found the opening square brackets
+/// of a C++11 attribute-specifier in a location where an attribute is not
+/// permitted. By C++11 [dcl.attr.grammar]p6, this is ill-formed. Diagnose this
+/// situation.
+///
+/// \return \c true if we skipped an attribute-like chunk of tokens, \c false if
+/// this doesn't appear to actually be an attribute-specifier, and the caller
+/// should try to parse it.
+bool Parser::DiagnoseProhibitedCXX11Attribute() {
+  assert(Tok.is(tok::l_square) && NextToken().is(tok::l_square));
+
+  switch (isCXX11AttributeSpecifier(/*Disambiguate*/true)) {
+  case CAK_NotAttributeSpecifier:
+    // No diagnostic: we're in Obj-C++11 and this is not actually an attribute.
+    return false;
+
+  case CAK_InvalidAttributeSpecifier:
+    Diag(Tok.getLocation(), diag::err_l_square_l_square_not_attribute);
+    return false;
+
+  case CAK_AttributeSpecifier:
+    // Parse and discard the attributes.
+    SourceLocation BeginLoc = ConsumeBracket();
+    ConsumeBracket();
+    SkipUntil(tok::r_square, /*StopAtSemi*/ false);
+    assert(Tok.is(tok::r_square) && "isCXX11AttributeSpecifier lied");
+    SourceLocation EndLoc = ConsumeBracket();
+    Diag(BeginLoc, diag::err_attributes_not_allowed)
+      << SourceRange(BeginLoc, EndLoc);
+    return true;
+  }
+}
+
 void Parser::DiagnoseProhibitedAttributes(ParsedAttributesWithRange &attrs) {
   Diag(attrs.Range.getBegin(), diag::err_attributes_not_allowed)
     << attrs.Range;
@@ -3454,14 +3487,11 @@
 void Parser::ParseTypeQualifierListOpt(DeclSpec &DS,
                                        bool VendorAttributesAllowed,
                                        bool CXX0XAttributesAllowed) {
-  if (getLangOpts().CPlusPlus0x && isCXX0XAttributeSpecifier()) {
-    SourceLocation Loc = Tok.getLocation();
+  if (getLangOpts().CPlusPlus0x && CXX0XAttributesAllowed &&
+      isCXX11AttributeSpecifier()) {
     ParsedAttributesWithRange attrs(AttrFactory);
     ParseCXX0XAttributes(attrs);
-    if (CXX0XAttributesAllowed)
-      DS.takeAttributesFrom(attrs);
-    else
-      Diag(Loc, diag::err_attributes_not_allowed);
+    DS.takeAttributesFrom(attrs);
   }
 
   SourceLocation EndLoc;
@@ -3654,6 +3684,7 @@
     // Is a pointer.
     DeclSpec DS(AttrFactory);
 
+    // FIXME: GNU attributes are not allowed here in a new-type-id.
     ParseTypeQualifierListOpt(DS);
     D.ExtendWithDeclSpec(DS);
 
@@ -3684,16 +3715,13 @@
            diag::warn_cxx98_compat_rvalue_reference :
            diag::ext_rvalue_reference);
 
+    // GNU-style and C++11 attributes are allowed here, as is restrict.
+    ParseTypeQualifierListOpt(DS);
+    D.ExtendWithDeclSpec(DS);
+
     // C++ 8.3.2p1: cv-qualified references are ill-formed except when the
     // cv-qualifiers are introduced through the use of a typedef or of a
     // template type argument, in which case the cv-qualifiers are ignored.
-    //
-    // [GNU] Retricted references are allowed.
-    // [GNU] Attributes on references are allowed.
-    // [C++0x] Attributes on references are not allowed.
-    ParseTypeQualifierListOpt(DS, true, false);
-    D.ExtendWithDeclSpec(DS);
-
     if (DS.getTypeQualifiers() != DeclSpec::TQ_unspecified) {
       if (DS.getTypeQualifiers() & DeclSpec::TQ_const)
         Diag(DS.getConstSpecLoc(),
@@ -3754,13 +3782,19 @@
 /// [C99]   direct-declarator '[' 'static' type-qual-list[opt] assign-expr ']'
 /// [C99]   direct-declarator '[' type-qual-list 'static' assignment-expr ']'
 /// [C99]   direct-declarator '[' type-qual-list[opt] '*' ']'
+/// [C++11] direct-declarator '[' constant-expression[opt] ']'
+///                    attribute-specifier-seq[opt]
 ///         direct-declarator '(' parameter-type-list ')'
 ///         direct-declarator '(' identifier-list[opt] ')'
 /// [GNU]   direct-declarator '(' parameter-forward-declarations
 ///                    parameter-type-list[opt] ')'
 /// [C++]   direct-declarator '(' parameter-declaration-clause ')'
 ///                    cv-qualifier-seq[opt] exception-specification[opt]
+/// [C++11] direct-declarator '(' parameter-declaration-clause ')'
+///                    attribute-specifier-seq[opt] cv-qualifier-seq[opt]
+///                    ref-qualifier[opt] exception-specification[opt]
 /// [C++]   declarator-id
+/// [C++11] declarator-id attribute-specifier-seq[opt]
 ///
 ///       declarator-id: [C++ 8]
 ///         '...'[opt] id-expression
@@ -3908,8 +3942,8 @@
   assert(D.isPastIdentifier() &&
          "Haven't past the location of the identifier yet?");
 
-  // Don't parse attributes unless we have an identifier.
-  if (D.getIdentifier())
+  // Don't parse attributes unless we have parsed an unparenthesized name.
+  if (D.hasName() && !D.getNumTypeObjects())
     MaybeParseCXX0XAttributes(D);
 
   while (1) {
@@ -4055,22 +4089,23 @@
 /// declarator D up to a paren, which indicates that we are parsing function
 /// arguments.
 ///
-/// If attrs is non-null, then the caller parsed those arguments immediately
-/// after the open paren - they should be considered to be the first argument of
-/// a parameter.  If RequiresArg is true, then the first argument of the
-/// function is required to be present and required to not be an identifier
-/// list.
+/// If FirstArgAttrs is non-null, then the caller parsed those arguments
+/// immediately after the open paren - they should be considered to be the
+/// first argument of a parameter.
 ///
-/// For C++, after the parameter-list, it also parses cv-qualifier-seq[opt],
-/// (C++0x) ref-qualifier[opt], exception-specification[opt], and
-/// (C++0x) trailing-return-type[opt].
+/// If RequiresArg is true, then the first argument of the function is required
+/// to be present and required to not be an identifier list.
 ///
-/// [C++0x] exception-specification:
+/// For C++, after the parameter-list, it also parses the cv-qualifier-seq[opt],
+/// (C++11) ref-qualifier[opt], exception-specification[opt],
+/// (C++11) attribute-specifier-seq[opt], and (C++11) trailing-return-type[opt].
+///
+/// [C++11] exception-specification:
 ///           dynamic-exception-specification
 ///           noexcept-specification
 ///
 void Parser::ParseFunctionDeclarator(Declarator &D,
-                                     ParsedAttributes &attrs,
+                                     ParsedAttributes &FirstArgAttrs,
                                      BalancedDelimiterTracker &Tracker,
                                      bool RequiresArg) {
   assert(getCurScope()->isFunctionPrototypeScope() && 
@@ -4096,8 +4131,9 @@
   SmallVector<ParsedType, 2> DynamicExceptions;
   SmallVector<SourceRange, 2> DynamicExceptionRanges;
   ExprResult NoexceptExpr;
+  ParsedAttributes FnAttrs(AttrFactory);
   ParsedType TrailingReturnType;
-  
+
   Actions.ActOnStartFunctionDeclarator();
 
   SourceLocation EndLoc;
@@ -4111,7 +4147,7 @@
     EndLoc = Tracker.getCloseLocation();
   } else {
     if (Tok.isNot(tok::r_paren))
-      ParseParameterDeclarationClause(D, attrs, ParamInfo, EllipsisLoc);
+      ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo, EllipsisLoc);
     else if (RequiresArg)
       Diag(Tok, diag::err_argument_required_after_attribute);
 
@@ -4122,22 +4158,24 @@
     EndLoc = Tracker.getCloseLocation();
 
     if (getLangOpts().CPlusPlus) {
-      MaybeParseCXX0XAttributes(attrs);
+      // FIXME: Accept these components in any order, and produce fixits to
+      // correct the order if the user gets it wrong. Ideally we should deal
+      // with the virt-specifier-seq and pure-specifier in the same way.
 
       // Parse cv-qualifier-seq[opt].
-      ParseTypeQualifierListOpt(DS, false /*no attributes*/);
-        if (!DS.getSourceRange().getEnd().isInvalid()) {
-          EndLoc = DS.getSourceRange().getEnd();
-          ConstQualifierLoc = DS.getConstSpecLoc();
-          VolatileQualifierLoc = DS.getVolatileSpecLoc();
-        }
+      ParseTypeQualifierListOpt(DS, false /*no attributes*/, false);
+      if (!DS.getSourceRange().getEnd().isInvalid()) {
+        EndLoc = DS.getSourceRange().getEnd();
+        ConstQualifierLoc = DS.getConstSpecLoc();
+        VolatileQualifierLoc = DS.getVolatileSpecLoc();
+      }
 
       // Parse ref-qualifier[opt].
       if (Tok.is(tok::amp) || Tok.is(tok::ampamp)) {
         Diag(Tok, getLangOpts().CPlusPlus0x ?
              diag::warn_cxx98_compat_ref_qualifier :
              diag::ext_ref_qualifier);
-        
+
         RefQualifierIsLValueRef = Tok.is(tok::amp);
         RefQualifierLoc = ConsumeToken();
         EndLoc = RefQualifierLoc;
@@ -4151,6 +4189,10 @@
       if (ESpecType != EST_None)
         EndLoc = ESpecRange.getEnd();
 
+      // Parse attribute-specifier-seq[opt]. Per DR 979 and DR 1297, this goes
+      // after the exception-specification.
+      MaybeParseCXX0XAttributes(FnAttrs);
+
       // Parse trailing-return-type[opt].
       if (getLangOpts().CPlusPlus0x && Tok.is(tok::arrow)) {
         Diag(Tok, diag::warn_cxx98_compat_trailing_return_type);
@@ -4181,7 +4223,7 @@
                                              Tracker.getOpenLocation(), 
                                              EndLoc, D,
                                              TrailingReturnType),
-                attrs, EndLoc);
+                FnAttrs, EndLoc);
 
   Actions.ActOnEndFunctionDeclarator();
 }
@@ -4455,7 +4497,12 @@
 /// [C99]   direct-declarator '[' 'static' type-qual-list[opt] assign-expr ']'
 /// [C99]   direct-declarator '[' type-qual-list 'static' assignment-expr ']'
 /// [C99]   direct-declarator '[' type-qual-list[opt] '*' ']'
+/// [C++11] direct-declarator '[' constant-expression[opt] ']'
+///                           attribute-specifier-seq[opt]
 void Parser::ParseBracketDeclarator(Declarator &D) {
+  if (CheckProhibitedCXX11Attribute())
+    return;
+
   BalancedDelimiterTracker T(*this, tok::l_square);
   T.consumeOpen();
 
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index 9321c13..10ed3b1 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -2736,38 +2736,38 @@
   Victim->TemplateScope = getCurScope()->getParent()->isTemplateParamScope();
 }
 
-/// ParseCXX0XAttributeSpecifier - Parse a C++0x attribute-specifier. Currently
+/// ParseCXX0XAttributeSpecifier - Parse a C++11 attribute-specifier. Currently
 /// only parses standard attributes.
 ///
-/// [C++0x] attribute-specifier:
+/// [C++11] attribute-specifier:
 ///         '[' '[' attribute-list ']' ']'
 ///         alignment-specifier
 ///
-/// [C++0x] attribute-list:
+/// [C++11] attribute-list:
 ///         attribute[opt]
 ///         attribute-list ',' attribute[opt]
 ///
-/// [C++0x] attribute:
+/// [C++11] attribute:
 ///         attribute-token attribute-argument-clause[opt]
 ///
-/// [C++0x] attribute-token:
+/// [C++11] attribute-token:
 ///         identifier
 ///         attribute-scoped-token
 ///
-/// [C++0x] attribute-scoped-token:
+/// [C++11] attribute-scoped-token:
 ///         attribute-namespace '::' identifier
 ///
-/// [C++0x] attribute-namespace:
+/// [C++11] attribute-namespace:
 ///         identifier
 ///
-/// [C++0x] attribute-argument-clause:
+/// [C++11] attribute-argument-clause:
 ///         '(' balanced-token-seq ')'
 ///
-/// [C++0x] balanced-token-seq:
+/// [C++11] balanced-token-seq:
 ///         balanced-token
 ///         balanced-token-seq balanced-token
 ///
-/// [C++0x] balanced-token:
+/// [C++11] balanced-token:
 ///         '(' balanced-token-seq ')'
 ///         '[' balanced-token-seq ']'
 ///         '{' balanced-token-seq '}'
@@ -2781,7 +2781,7 @@
   }
 
   assert(Tok.is(tok::l_square) && NextToken().is(tok::l_square)
-      && "Not a C++0x attribute list");
+      && "Not a C++11 attribute list");
 
   Diag(Tok.getLocation(), diag::warn_cxx98_compat_attribute);
 
@@ -2793,7 +2793,11 @@
     ConsumeToken();
   }
 
-  while (Tok.is(tok::identifier) || Tok.is(tok::comma)) {
+  // C++11 [dcl.attr.grammar]p3: If a keyword or an alternative token that
+  // satisfies the syntactic requirements of an identifier is contained in an
+  // attribute-token, it is considered an identifier.
+  // FIXME: Support alternative tokens here.
+  while (Tok.getIdentifierInfo() || Tok.is(tok::comma)) {
     // attribute not present
     if (Tok.is(tok::comma)) {
       ConsumeToken();
@@ -2807,7 +2811,7 @@
     if (Tok.is(tok::coloncolon)) {
       ConsumeToken();
 
-      if (!Tok.is(tok::identifier)) {
+      if (!Tok.getIdentifierInfo()) {
         Diag(Tok.getLocation(), diag::err_expected_ident);
         SkipUntil(tok::r_square, tok::comma, true, true);
         continue;
@@ -2824,8 +2828,7 @@
     // No scoped names are supported; ideally we could put all non-standard
     // attributes into namespaces.
     if (!ScopeName) {
-      switch(AttributeList::getKind(AttrName))
-      {
+      switch (AttributeList::getKind(AttrName)) {
       // No arguments
       case AttributeList::AT_carries_dependency:
       case AttributeList::AT_noreturn: {
@@ -2852,6 +2855,9 @@
       // SkipUntil maintains the balancedness of tokens.
       SkipUntil(tok::r_paren, false);
     }
+
+    if (Tok.is(tok::ellipsis))
+      ConsumeToken();
   }
 
   if (ExpectAndConsume(tok::r_square, diag::err_expected_rsquare))
@@ -2874,7 +2880,7 @@
 
   do {
     ParseCXX0XAttributeSpecifier(attrs, endLoc);
-  } while (isCXX0XAttributeSpecifier());
+  } while (isCXX11AttributeSpecifier());
 
   attrs.Range = SourceRange(StartLoc, *endLoc);
 }
@@ -2892,6 +2898,7 @@
   assert(Tok.is(tok::l_square) && "Not a Microsoft attribute list");
 
   while (Tok.is(tok::l_square)) {
+    // FIXME: If this is actually a C++11 attribute, parse it as one.
     ConsumeBracket();
     SkipUntil(tok::r_square, true, true);
     if (endLoc) *endLoc = Tok.getLocation();
diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp
index f68359b..7f3a815 100644
--- a/lib/Parse/ParseExpr.cpp
+++ b/lib/Parse/ParseExpr.cpp
@@ -1287,7 +1287,12 @@
       if (getLangOpts().ObjC1 && Tok.isAtStartOfLine() &&
           isSimpleObjCMessageExpression())
         return move(LHS);
-       
+
+      // Reject array indices starting with a lambda-expression. '[[' is
+      // reserved for attributes.
+      if (CheckProhibitedCXX11Attribute())
+        return ExprError();
+
       BalancedDelimiterTracker T(*this, tok::l_square);
       T.consumeOpen();
       Loc = T.getOpenLocation();
@@ -1756,6 +1761,9 @@
         Comps.back().LocEnd = ConsumeToken();
 
       } else if (Tok.is(tok::l_square)) {
+        if (CheckProhibitedCXX11Attribute())
+          return ExprError();
+
         // offsetof-member-designator: offsetof-member-design '[' expression ']'
         Comps.push_back(Sema::OffsetOfComponent());
         Comps.back().isBrackets = true;
diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
index 0b7b2d9..2af7482 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -1817,7 +1817,9 @@
       bool isNew = Tok.getKind() == tok::kw_new;
       // Consume the 'new' or 'delete'.
       SymbolLocations[SymbolIdx++] = ConsumeToken();
-      if (Tok.is(tok::l_square)) {
+      // Check for array new/delete.
+      if (Tok.is(tok::l_square) &&
+          (!getLangOpts().CPlusPlus0x || NextToken().isNot(tok::l_square))) {
         // Consume the '[' and ']'.
         BalancedDelimiterTracker T(*this, tok::l_square);
         T.consumeOpen();
@@ -2346,6 +2348,10 @@
   // Parse the array dimensions.
   bool first = true;
   while (Tok.is(tok::l_square)) {
+    // An array-size expression can't start with a lambda.
+    if (CheckProhibitedCXX11Attribute())
+      continue;
+
     BalancedDelimiterTracker T(*this, tok::l_square);
     T.consumeOpen();
 
@@ -2360,13 +2366,16 @@
 
     T.consumeClose();
 
-    ParsedAttributes attrs(AttrFactory);
+    // Attributes here appertain to the array type. C++11 [expr.new]p5.
+    ParsedAttributes Attrs(AttrFactory);
+    MaybeParseCXX0XAttributes(Attrs);
+
     D.AddTypeInfo(DeclaratorChunk::getArray(0,
                                             /*static=*/false, /*star=*/false,
                                             Size.release(),
                                             T.getOpenLocation(),
                                             T.getCloseLocation()),
-                  attrs, T.getCloseLocation());
+                  Attrs, T.getCloseLocation());
 
     if (T.getCloseLocation().isInvalid())
       return;
@@ -2418,7 +2427,11 @@
 
   // Array delete?
   bool ArrayDelete = false;
-  if (Tok.is(tok::l_square)) {
+  if (Tok.is(tok::l_square) && NextToken().is(tok::r_square)) {
+    // FIXME: This could be the start of a lambda-expression. We should
+    // disambiguate this, but that will require arbitrary lookahead if
+    // the next token is '(':
+    //   delete [](int*){ /* ... */
     ArrayDelete = true;
     BalancedDelimiterTracker T(*this, tok::l_square);
 
diff --git a/lib/Parse/ParseInit.cpp b/lib/Parse/ParseInit.cpp
index d4fc905..1c349fd 100644
--- a/lib/Parse/ParseInit.cpp
+++ b/lib/Parse/ParseInit.cpp
@@ -211,6 +211,11 @@
     //   [foo ... bar]     -> array designator
     //   [4][foo bar]      -> obsolete GNU designation with objc message send.
     //
+    // We do not need to check for an expression starting with [[ here. If it
+    // contains an Objective-C message send, then it is not an ill-formed
+    // attribute. If it is a lambda-expression within an array-designator, then
+    // it will be rejected because a constant-expression cannot begin with a
+    // lambda-expression.
     InMessageExpressionRAIIObject InMessage(*this, true);
     
     BalancedDelimiterTracker T(*this, tok::l_square);
diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp
index 79b53c3..9f6a3a0 100644
--- a/lib/Parse/ParseStmt.cpp
+++ b/lib/Parse/ParseStmt.cpp
@@ -84,7 +84,7 @@
   ParenBraceBracketBalancer BalancerRAIIObj(*this);
 
   ParsedAttributesWithRange attrs(AttrFactory);
-  MaybeParseCXX0XAttributes(attrs);
+  MaybeParseCXX0XAttributes(attrs, 0, /*MightBeObjCMessageSend*/ true);
 
   // Cases in this switch statement should fall through if the parser expects
   // the token to end in a semicolon (in which case SemiError should be set),
@@ -789,7 +789,7 @@
         ConsumeToken();
 
       ParsedAttributesWithRange attrs(AttrFactory);
-      MaybeParseCXX0XAttributes(attrs);
+      MaybeParseCXX0XAttributes(attrs, 0, /*MightBeObjCMessageSend*/ true);
 
       // If this is the start of a declaration, parse it as such.
       if (isDeclarationStatement()) {
diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp
index b650169..18bc0dd 100644
--- a/lib/Parse/ParseTentative.cpp
+++ b/lib/Parse/ParseTentative.cpp
@@ -374,91 +374,167 @@
   return TPR == TPResult::True();
 }
 
-/// isCXX0XAttributeSpecifier - returns true if this is a C++0x
-/// attribute-specifier. By default, unless in Obj-C++, only a cursory check is
-/// performed that will simply return true if a [[ is seen. Currently C++ has no
-/// syntactical ambiguities from this check, but it may inhibit error recovery.
-/// If CheckClosing is true, a check is made for closing ]] brackets.
+/// \brief Returns true if this is a C++11 attribute-specifier. Per
+/// C++11 [dcl.attr.grammar]p6, two consecutive left square bracket tokens
+/// always introduce an attribute. In Objective-C++11, this rule does not
+/// apply if either '[' begins a message-send.
 ///
-/// If given, After is set to the token after the attribute-specifier so that
-/// appropriate parsing decisions can be made; it is left untouched if false is
-/// returned.
+/// If Disambiguate is true, we try harder to determine whether a '[[' starts
+/// an attribute-specifier, and return CAK_InvalidAttributeSpecifier if not.
 ///
-/// FIXME: If an error is in the closing ]] brackets, the program assumes
-/// the absence of an attribute-specifier, which can cause very yucky errors
-/// to occur.
+/// If OuterMightBeMessageSend is true, we assume the outer '[' is either an
+/// Obj-C message send or the start of an attribute. Otherwise, we assume it
+/// is not an Obj-C message send.
 ///
-/// [C++0x] attribute-specifier:
+/// C++11 [dcl.attr.grammar]:
+///
+///     attribute-specifier:
 ///         '[' '[' attribute-list ']' ']'
 ///         alignment-specifier
 ///
-/// [C++0x] attribute-list:
+///     attribute-list:
 ///         attribute[opt]
 ///         attribute-list ',' attribute[opt]
+///         attribute '...'
+///         attribute-list ',' attribute '...'
 ///
-/// [C++0x] attribute:
+///     attribute:
 ///         attribute-token attribute-argument-clause[opt]
 ///
-/// [C++0x] attribute-token:
+///     attribute-token:
 ///         identifier
-///         attribute-scoped-token
+///         identifier '::' identifier
 ///
-/// [C++0x] attribute-scoped-token:
-///         attribute-namespace '::' identifier
-///
-/// [C++0x] attribute-namespace:
-///         identifier
-///
-/// [C++0x] attribute-argument-clause:
+///     attribute-argument-clause:
 ///         '(' balanced-token-seq ')'
-///
-/// [C++0x] balanced-token-seq:
-///         balanced-token
-///         balanced-token-seq balanced-token
-///
-/// [C++0x] balanced-token:
-///         '(' balanced-token-seq ')'
-///         '[' balanced-token-seq ']'
-///         '{' balanced-token-seq '}'
-///         any token but '(', ')', '[', ']', '{', or '}'
-bool Parser::isCXX0XAttributeSpecifier (bool CheckClosing,
-                                        tok::TokenKind *After) {
+Parser::CXX11AttributeKind
+Parser::isCXX11AttributeSpecifier(bool Disambiguate,
+                                  bool OuterMightBeMessageSend) {
   if (Tok.is(tok::kw_alignas))
-    return true;
+    return CAK_AttributeSpecifier;
 
   if (Tok.isNot(tok::l_square) || NextToken().isNot(tok::l_square))
-    return false;
-  
-  // No tentative parsing if we don't need to look for ]]
-  if (!CheckClosing && !getLangOpts().ObjC1)
-    return true;
-  
-  struct TentativeReverter {
-    TentativeParsingAction PA;
+    return CAK_NotAttributeSpecifier;
 
-    TentativeReverter (Parser& P)
-      : PA(P)
-    {}
-    ~TentativeReverter () {
-      PA.Revert();
-    }
-  } R(*this);
+  // No tentative parsing if we don't need to look for ']]' or a lambda.
+  if (!Disambiguate && !getLangOpts().ObjC1)
+    return CAK_AttributeSpecifier;
+
+  TentativeParsingAction PA(*this);
 
   // Opening brackets were checked for above.
   ConsumeBracket();
+
+  // Outside Obj-C++11, treat anything with a matching ']]' as an attribute.
+  if (!getLangOpts().ObjC1) {
+    ConsumeBracket();
+
+    bool IsAttribute = SkipUntil(tok::r_square, false);
+    IsAttribute &= Tok.is(tok::r_square);
+
+    PA.Revert();
+
+    return IsAttribute ? CAK_AttributeSpecifier : CAK_InvalidAttributeSpecifier;
+  }
+
+  // In Obj-C++11, we need to distinguish four situations:
+  //  1a) int x[[attr]];                     C++11 attribute.
+  //  1b) [[attr]];                          C++11 statement attribute.
+  //   2) int x[[obj](){ return 1; }()];     Lambda in array size/index.
+  //  3a) int x[[obj get]];                  Message send in array size/index.
+  //  3b) [[Class alloc] init];              Message send in message send.
+  //   4) [[obj]{ return self; }() doStuff]; Lambda in message send.
+  // (1) is an attribute, (2) is ill-formed, and (3) and (4) are accepted.
+
+  // If we have a lambda-introducer, then this is definitely not a message send.
+  // FIXME: If this disambiguation is too slow, fold the tentative lambda parse
+  // into the tentative attribute parse below.
+  LambdaIntroducer Intro;
+  if (!TryParseLambdaIntroducer(Intro)) {
+    // A lambda cannot end with ']]', and an attribute must.
+    bool IsAttribute = Tok.is(tok::r_square);
+
+    PA.Revert();
+
+    if (IsAttribute)
+      // Case 1: C++11 attribute.
+      return CAK_AttributeSpecifier;
+
+    if (OuterMightBeMessageSend)
+      // Case 4: Lambda in message send.
+      return CAK_NotAttributeSpecifier;
+
+    // Case 2: Lambda in array size / index.
+    return CAK_InvalidAttributeSpecifier;
+  }
+
   ConsumeBracket();
 
-  // SkipUntil will handle balanced tokens, which are guaranteed in attributes.
-  SkipUntil(tok::r_square, false);
+  // If we don't have a lambda-introducer, then we have an attribute or a
+  // message-send.
+  bool IsAttribute = true;
+  while (Tok.isNot(tok::r_square)) {
+    if (Tok.is(tok::comma)) {
+      // Case 1: Stray commas can only occur in attributes.
+      PA.Revert();
+      return CAK_AttributeSpecifier;
+    }
 
-  if (Tok.isNot(tok::r_square))
-    return false;
-  ConsumeBracket();
+    // Parse the attribute-token, if present.
+    // C++11 [dcl.attr.grammar]:
+    //   If a keyword or an alternative token that satisfies the syntactic
+    //   requirements of an identifier is contained in an attribute-token,
+    //   it is considered an identifier.
+    if (!Tok.getIdentifierInfo()) {
+      IsAttribute = false;
+      break;
+    }
+    ConsumeToken();
+    if (Tok.is(tok::coloncolon)) {
+      ConsumeToken();
+      if (!Tok.getIdentifierInfo()) {
+        IsAttribute = false;
+        break;
+      }
+      ConsumeToken();
+    }
 
-  if (After)
-    *After = Tok.getKind();
+    // Parse the attribute-argument-clause, if present.
+    if (Tok.is(tok::l_paren)) {
+      ConsumeParen();
+      if (!SkipUntil(tok::r_paren, false)) {
+        IsAttribute = false;
+        break;
+      }
+    }
 
-  return true;
+    if (Tok.is(tok::ellipsis))
+      ConsumeToken();
+
+    if (Tok.isNot(tok::comma))
+      break;
+
+    ConsumeToken();
+  }
+
+  // An attribute must end ']]'.
+  if (IsAttribute) {
+    if (Tok.is(tok::r_square)) {
+      ConsumeBracket();
+      IsAttribute = Tok.is(tok::r_square);
+    } else {
+      IsAttribute = false;
+    }
+  }
+
+  PA.Revert();
+
+  if (IsAttribute)
+    // Case 1: C++11 statement attribute.
+    return CAK_AttributeSpecifier;
+
+  // Case 3: Message send.
+  return CAK_NotAttributeSpecifier;
 }
 
 ///         declarator:
@@ -1217,11 +1293,13 @@
 ///   parameter-declaration-list ',' parameter-declaration
 ///
 /// parameter-declaration:
-///   decl-specifier-seq declarator attributes[opt]
-///   decl-specifier-seq declarator attributes[opt] '=' assignment-expression
-///   decl-specifier-seq abstract-declarator[opt] attributes[opt]
-///   decl-specifier-seq abstract-declarator[opt] attributes[opt]
+///   attribute-specifier-seq[opt] decl-specifier-seq declarator attributes[opt]
+///   attribute-specifier-seq[opt] decl-specifier-seq declarator attributes[opt]
 ///     '=' assignment-expression
+///   attribute-specifier-seq[opt] decl-specifier-seq abstract-declarator[opt]
+///     attributes[opt]
+///   attribute-specifier-seq[opt] decl-specifier-seq abstract-declarator[opt]
+///     attributes[opt] '=' assignment-expression
 ///
 Parser::TPResult Parser::TryParseParameterDeclarationClause() {