diff options
Diffstat (limited to 'clang')
-rw-r--r-- | clang/include/clang/Basic/DiagnosticParseKinds.td | 3 | ||||
-rw-r--r-- | clang/include/clang/Parse/Parser.h | 35 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDecl.cpp | 119 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDeclCXX.cpp | 39 | ||||
-rw-r--r-- | clang/lib/Parse/ParseExpr.cpp | 10 | ||||
-rw-r--r-- | clang/lib/Parse/ParseExprCXX.cpp | 21 | ||||
-rw-r--r-- | clang/lib/Parse/ParseInit.cpp | 5 | ||||
-rw-r--r-- | clang/lib/Parse/ParseStmt.cpp | 4 | ||||
-rw-r--r-- | clang/lib/Parse/ParseTentative.cpp | 204 | ||||
-rw-r--r-- | clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.grammar/p6.cpp | 13 | ||||
-rw-r--r-- | clang/test/Parser/cxx0x-attributes.cpp | 15 | ||||
-rw-r--r-- | clang/test/Parser/objcxx11-attributes.mm | 42 | ||||
-rw-r--r-- | clang/test/SemaCXX/new-delete-0x.cpp | 32 | ||||
-rw-r--r-- | clang/test/SemaCXX/new-delete.cpp | 3 |
14 files changed, 412 insertions, 133 deletions
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 81c38e7da5a..5b53223e466 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -469,6 +469,9 @@ def err_cxx0x_attribute_forbids_arguments : Error< def err_cxx0x_attribute_requires_arguments : Error< "C++11 attribute '%0' must have an argument list">; def err_attributes_not_allowed : Error<"an attribute list cannot appear here">; +def err_l_square_l_square_not_attribute : Error< + "C++11 only allows consecutive left square brackets when " + "introducing an attribute">; def err_alignas_pack_exp_unsupported : Error< "pack expansions in alignment specifiers are not supported yet">; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 0721417f7ba..5e070e4f36c 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1864,6 +1864,16 @@ private: Decl **OwnedType = 0); void ParseBlockId(); + // Check for the start of a C++11 attribute-specifier-seq in a context where + // an attribute is not allowed. + bool CheckProhibitedCXX11Attribute() { + assert(Tok.is(tok::l_square)); + if (!getLangOpts().CPlusPlus0x || NextToken().isNot(tok::l_square)) + return false; + return DiagnoseProhibitedCXX11Attribute(); + } + bool DiagnoseProhibitedCXX11Attribute(); + void ProhibitAttributes(ParsedAttributesWithRange &attrs) { if (!attrs.Range.isValid()) return; DiagnoseProhibitedAttributes(attrs); @@ -1894,7 +1904,7 @@ private: SourceLocation *EndLoc); void MaybeParseCXX0XAttributes(Declarator &D) { - if (getLangOpts().CPlusPlus0x && isCXX0XAttributeSpecifier()) { + if (getLangOpts().CPlusPlus0x && isCXX11AttributeSpecifier()) { ParsedAttributesWithRange attrs(AttrFactory); SourceLocation endLoc; ParseCXX0XAttributes(attrs, &endLoc); @@ -1903,15 +1913,17 @@ private: } void MaybeParseCXX0XAttributes(ParsedAttributes &attrs, SourceLocation *endLoc = 0) { - if (getLangOpts().CPlusPlus0x && isCXX0XAttributeSpecifier()) { + if (getLangOpts().CPlusPlus0x && isCXX11AttributeSpecifier()) { ParsedAttributesWithRange attrsWithRange(AttrFactory); ParseCXX0XAttributes(attrsWithRange, endLoc); attrs.takeAllFrom(attrsWithRange); } } void MaybeParseCXX0XAttributes(ParsedAttributesWithRange &attrs, - SourceLocation *endLoc = 0) { - if (getLangOpts().CPlusPlus0x && isCXX0XAttributeSpecifier()) + SourceLocation *endLoc = 0, + bool OuterMightBeMessageSend = false) { + if (getLangOpts().CPlusPlus0x && + isCXX11AttributeSpecifier(false, OuterMightBeMessageSend)) ParseCXX0XAttributes(attrs, endLoc); } @@ -2029,8 +2041,19 @@ private: //===--------------------------------------------------------------------===// // C++ 7: Declarations [dcl.dcl] - bool isCXX0XAttributeSpecifier(bool FullLookahead = false, - tok::TokenKind *After = 0); + /// The kind of attribute specifier we have found. + enum CXX11AttributeKind { + /// This is not an attribute specifier. + CAK_NotAttributeSpecifier, + /// This should be treated as an attribute-specifier. + CAK_AttributeSpecifier, + /// The next tokens are '[[', but this is not an attribute-specifier. This + /// is ill-formed by C++11 [dcl.attr.grammar]p6. + CAK_InvalidAttributeSpecifier + }; + CXX11AttributeKind + isCXX11AttributeSpecifier(bool Disambiguate = false, + bool OuterMightBeMessageSend = false); Decl *ParseNamespace(unsigned Context, SourceLocation &DeclEnd, SourceLocation InlineLoc = SourceLocation()); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index b5ce193521e..9bafa92d7f5 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -906,6 +906,39 @@ void Parser::ParseThreadSafetyAttribute(IdentifierInfo &AttrName, *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 @@ bool Parser::isConstructorDeclarator() { 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 @@ void Parser::ParseDeclaratorInternal(Declarator &D, // 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 @@ void Parser::ParseDeclaratorInternal(Declarator &D, 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 @@ static void diagnoseMisplacedEllipsis(Parser &P, Declarator &D, /// [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 @@ void Parser::ParseDirectDeclarator(Declarator &D) { 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 @@ void Parser::ParseParenDeclarator(Declarator &D) { /// 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. +/// +/// If RequiresArg is true, then the first argument of the function is required +/// to be present and required to not be an identifier list. /// -/// 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]. +/// 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++0x] exception-specification: +/// [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 @@ void Parser::ParseFunctionDeclarator(Declarator &D, SmallVector<ParsedType, 2> DynamicExceptions; SmallVector<SourceRange, 2> DynamicExceptionRanges; ExprResult NoexceptExpr; + ParsedAttributes FnAttrs(AttrFactory); ParsedType TrailingReturnType; - + Actions.ActOnStartFunctionDeclarator(); SourceLocation EndLoc; @@ -4111,7 +4147,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D, 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 @@ void Parser::ParseFunctionDeclarator(Declarator &D, 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 @@ void Parser::ParseFunctionDeclarator(Declarator &D, 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 @@ void Parser::ParseFunctionDeclarator(Declarator &D, Tracker.getOpenLocation(), EndLoc, D, TrailingReturnType), - attrs, EndLoc); + FnAttrs, EndLoc); Actions.ActOnEndFunctionDeclarator(); } @@ -4455,7 +4497,12 @@ void Parser::ParseParameterDeclarationClause( /// [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/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 9321c137fba..10ed3b1516e 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -2736,38 +2736,38 @@ void Parser::PopParsingClass(Sema::ParsingClassState state) { 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 @@ void Parser::ParseCXX0XAttributeSpecifier(ParsedAttributes &attrs, } 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 @@ void Parser::ParseCXX0XAttributeSpecifier(ParsedAttributes &attrs, 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 @@ void Parser::ParseCXX0XAttributeSpecifier(ParsedAttributes &attrs, 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 @@ void Parser::ParseCXX0XAttributeSpecifier(ParsedAttributes &attrs, // 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 @@ void Parser::ParseCXX0XAttributeSpecifier(ParsedAttributes &attrs, // 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 @@ void Parser::ParseCXX0XAttributes(ParsedAttributesWithRange &attrs, do { ParseCXX0XAttributeSpecifier(attrs, endLoc); - } while (isCXX0XAttributeSpecifier()); + } while (isCXX11AttributeSpecifier()); attrs.Range = SourceRange(StartLoc, *endLoc); } @@ -2892,6 +2898,7 @@ void Parser::ParseMicrosoftAttributes(ParsedAttributes &attrs, 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/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index f68359b8d2f..7f3a815edc0 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1287,7 +1287,12 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { 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 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() { 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/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 0b7b2d9668c..2af74824ebc 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -1817,7 +1817,9 @@ bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext, 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 @@ void Parser::ParseDirectNewDeclarator(Declarator &D) { // 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 @@ void Parser::ParseDirectNewDeclarator(Declarator &D) { 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 @@ Parser::ParseCXXDeleteExpression(bool UseGlobal, SourceLocation Start) { // 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/clang/lib/Parse/ParseInit.cpp b/clang/lib/Parse/ParseInit.cpp index d4fc9056e40..1c349fddbd5 100644 --- a/clang/lib/Parse/ParseInit.cpp +++ b/clang/lib/Parse/ParseInit.cpp @@ -211,6 +211,11 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator() { // [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/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index 79b53c3b245..9f6a3a03daa 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -84,7 +84,7 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts, bool OnlyStatement, 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 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { 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/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index b650169b259..18bc0ddc5b9 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -374,91 +374,167 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) { 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: -/// identifier -/// attribute-scoped-token -/// -/// [C++0x] attribute-scoped-token: -/// attribute-namespace '::' identifier -/// -/// [C++0x] attribute-namespace: +/// attribute-token: /// identifier +/// identifier '::' 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(); - ConsumeBracket(); - // SkipUntil will handle balanced tokens, which are guaranteed in attributes. - SkipUntil(tok::r_square, false); + // 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; + } - if (Tok.isNot(tok::r_square)) - return false; ConsumeBracket(); - if (After) - *After = Tok.getKind(); + // 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; + } + + // 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(); + } + + // Parse the attribute-argument-clause, if present. + if (Tok.is(tok::l_paren)) { + ConsumeParen(); + if (!SkipUntil(tok::r_paren, false)) { + IsAttribute = false; + break; + } + } + + 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; - return true; + // Case 3: Message send. + return CAK_NotAttributeSpecifier; } /// declarator: @@ -1217,11 +1293,13 @@ bool Parser::isCXXFunctionDeclarator(bool warnIfAmbiguous) { /// 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() { diff --git a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.grammar/p6.cpp b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.grammar/p6.cpp new file mode 100644 index 00000000000..f9702ba7ccb --- /dev/null +++ b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.grammar/p6.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s + +namespace std_example { + +int p[10]; +void f() { + int x = 42, y[5]; + // FIXME: Produce a better diagnostic for this case. + int(p[[x] { return x; }()]); // expected-error {{expected ']'}} + y[[] { return 2; }()] = 2; // expected-error {{consecutive left square brackets}} +} + +} diff --git a/clang/test/Parser/cxx0x-attributes.cpp b/clang/test/Parser/cxx0x-attributes.cpp index 198da778cd0..b20873509b6 100644 --- a/clang/test/Parser/cxx0x-attributes.cpp +++ b/clang/test/Parser/cxx0x-attributes.cpp @@ -5,11 +5,17 @@ int [[]] between_attr; 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.)]] int garbage_attr; void fn_attr () [[]]; +void noexcept_fn_attr () noexcept [[]]; +struct MemberFnOrder { + virtual void f() const volatile && noexcept [[]] final = 0; +}; class [[]] class_attr {}; extern "C++" [[]] int extern_attr; template <typename T> [[]] void template_attr (); @@ -17,10 +23,10 @@ template <typename T> [[]] void template_attr (); int comma_attr [[,]]; // expected-error {{expected identifier}} 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}} -int & [[]] ref_attr = after_attr; // expected-error {{an attribute list cannot appear here}} class foo { - void after_const_attr () const [[]]; // expected-error {{expected body of lambda expression}} + void const_after_attr () [[]] const; // expected-error {{expected ';'}} }; extern "C++" [[]] { } // expected-error {{an attribute list cannot appear here}} [[]] template <typename T> void before_template_attr (); // expected-error {{an attribute list cannot appear here}} @@ -58,6 +64,9 @@ void foo () { [[]] try { } [[]] catch (...) { // expected-error {{an attribute list cannot appear here}} } - + struct S { int arr[2]; } s; + (void)s.arr[ [] { return 0; }() ]; // expected-error {{C++11 only allows consecutive left square brackets when introducing an attribute}} + int n = __builtin_offsetof(S, arr[ [] { return 0; }() ]); // expected-error {{C++11 only allows consecutive left square brackets when introducing an attribute}} + [[]] return; } diff --git a/clang/test/Parser/objcxx11-attributes.mm b/clang/test/Parser/objcxx11-attributes.mm new file mode 100644 index 00000000000..719cdcc32f4 --- /dev/null +++ b/clang/test/Parser/objcxx11-attributes.mm @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s + +@interface X {} ++ (X*) alloc; +- (X*) init; +- (int) getSize; +- (void) setSize: (int) size; +- (X*) getSelf; +@end + +void f(X *noreturn) { + // An array size which is computed by a message send is OK. + int a[ [noreturn getSize] ]; + + // ... but is interpreted as an attribute where possible. + int b[ [noreturn] ]; // expected-warning {{'noreturn' only applies to function types}} + + int c[ [noreturn getSize] + 1 ]; + + // An array size which is computed by a lambda is not OK. + int d[ [noreturn] { return 3; } () ]; // expected-error {{expected ']'}} expected-warning {{'noreturn' only applies}} + + // A message send which contains a message send is OK. + [ [ X alloc ] init ]; + [ [ int(), noreturn getSelf ] getSize ]; // expected-warning {{unused}} + + // A message send which contains a lambda is OK. + [ [noreturn] { return noreturn; } () setSize: 4 ]; + [ [bitand] { return noreturn; } () setSize: 5 ]; + [[[[] { return [ X alloc ]; } () init] getSelf] getSize]; + + // An attribute is OK. + [[]]; + [[int(), noreturn]]; + [[class, test(foo 'x' bar),,,]]; +} + +template<typename...Ts> void f(Ts ...x) { + [[test::foo(bar, baz)...]]; + [[used(x)...]]; + [[x...] { return [ X alloc ]; }() init]; +} diff --git a/clang/test/SemaCXX/new-delete-0x.cpp b/clang/test/SemaCXX/new-delete-0x.cpp new file mode 100644 index 00000000000..dcc2e9b8b12 --- /dev/null +++ b/clang/test/SemaCXX/new-delete-0x.cpp @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -triple=i686-pc-linux-gnu -std=c++11 + +using size_t = decltype(sizeof(0)); +struct noreturn_t {} constexpr noreturn = {}; + +void *operator new [[noreturn]] (size_t, noreturn_t); +void operator delete [[noreturn]] (void*, noreturn_t); + +void good_news() +{ + auto p = new int[2][[]]; + auto q = new int[[]][2]; + auto r = new int*[[]][2][[]]; + auto s = new (int(*[[]])[2][[]]); +} + +void bad_news(int *ip) +{ + // attribute-specifiers can go almost anywhere in a new-type-id... + auto r = new int[[]{return 1;}()][2]; // expected-error {{expected ']'}} + auto s = new int*[[]{return 1;}()][2]; // expected-error {{expected ']'}} + // ... but not here: + auto t = new (int(*)[[]]); // expected-error {{an attribute list cannot appear here}} + auto u = new (int(*)[[]{return 1;}()][2]); // expected-error {{C++11 only allows consecutive left square brackets when introducing an attribute}} expected-error {{variably modified type}} +} + +void good_deletes() +{ + delete [&]{ return (int*)0; }(); + // FIXME: This appears to be legal. + delete []{ return (int*)0; }(); // unexpected-error {{expected expression}} +} diff --git a/clang/test/SemaCXX/new-delete.cpp b/clang/test/SemaCXX/new-delete.cpp index 88d85ca792e..e77e3d652f4 100644 --- a/clang/test/SemaCXX/new-delete.cpp +++ b/clang/test/SemaCXX/new-delete.cpp @@ -102,8 +102,7 @@ void good_deletes() void bad_deletes() { delete 0; // expected-error {{cannot delete expression of type 'int'}} - delete [0] (int*)0; // expected-error {{expected ']'}} \ - // expected-note {{to match this '['}} + delete [0] (int*)0; // expected-error {{expected expression}} delete (void*)0; // expected-warning {{cannot delete expression with pointer-to-'void' type 'void *'}} delete (T*)0; // expected-warning {{deleting pointer to incomplete type}} ::S::delete (int*)0; // expected-error {{expected unqualified-id}} |