diff options
| -rw-r--r-- | clang/include/clang/Parse/Parser.h | 20 | ||||
| -rw-r--r-- | clang/include/clang/Sema/Sema.h | 26 | ||||
| -rw-r--r-- | clang/lib/Parse/ParseCXXInlineMethods.cpp | 287 | ||||
| -rw-r--r-- | clang/lib/Parse/ParseDecl.cpp | 4 | ||||
| -rw-r--r-- | clang/lib/Parse/ParseTentative.cpp | 395 | ||||
| -rw-r--r-- | clang/lib/Sema/Sema.cpp | 5 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaLookup.cpp | 3 | ||||
| -rw-r--r-- | clang/test/Parser/cxx-ambig-init-templ.cpp | 171 | ||||
| -rw-r--r-- | clang/test/Parser/cxx-default-args.cpp | 17 | ||||
| -rw-r--r-- | clang/test/Parser/cxx0x-member-initializers.cpp | 10 |
10 files changed, 843 insertions, 95 deletions
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index f52ac9c649a..985554636d2 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -608,6 +608,7 @@ private: assert(!isActive && "Forgot to call Commit or Revert!"); } }; + class UnannotatedTentativeParsingAction; /// ObjCDeclContextSwitch - An object used to switch context from /// an objective-c decl context to its enclosing decl context and @@ -1061,6 +1062,11 @@ private: void DeallocateParsedClasses(ParsingClass *Class); void PopParsingClass(Sema::ParsingClassState); + enum CachedInitKind { + CIK_DefaultArgument, + CIK_DefaultInitializer + }; + NamedDecl *ParseCXXInlineMethodDef(AccessSpecifier AS, AttributeList *AccessAttrs, ParsingDeclarator &D, @@ -1082,6 +1088,8 @@ private: void ParseLexedMemberInitializer(LateParsedMemberInitializer &MI); void ParseLexedObjCMethodDefs(LexedMethod &LM, bool parseMethod); bool ConsumeAndStoreFunctionPrologue(CachedTokens &Toks); + bool ConsumeAndStoreInitializer(CachedTokens &Toks, CachedInitKind CIK); + bool ConsumeAndStoreConditional(CachedTokens &Toks); bool ConsumeAndStoreUntil(tok::TokenKind T1, CachedTokens &Toks, bool StopAtSemi = true, @@ -1800,6 +1808,11 @@ private: isCXXDeclarationSpecifier(TPResult BracedCastResult = TPResult::False(), bool *HasMissingTypename = 0); + /// Given that isCXXDeclarationSpecifier returns \c TPResult::True or + /// \c TPResult::Ambiguous, determine whether the decl-specifier would be + /// a type-specifier other than a cv-qualifier. + bool isCXXDeclarationSpecifierAType(); + /// \brief Determine whether an identifier has been tentatively declared as a /// non-type. Such tentative declarations should not be found to name a type /// during a tentative parse, but also should not be annotated as a non-type. @@ -1812,15 +1825,18 @@ private: // that more tentative parsing is necessary for disambiguation. // They all consume tokens, so backtracking should be used after calling them. - TPResult TryParseDeclarationSpecifier(bool *HasMissingTypename = 0); TPResult TryParseSimpleDeclaration(bool AllowForRangeDecl); TPResult TryParseTypeofSpecifier(); TPResult TryParseProtocolQualifiers(); + TPResult TryParsePtrOperatorSeq(); + TPResult TryParseOperatorId(); TPResult TryParseInitDeclaratorList(); TPResult TryParseDeclarator(bool mayBeAbstract, bool mayHaveIdentifier=true); - TPResult TryParseParameterDeclarationClause(bool *InvalidAsDeclaration = 0); + TPResult TryParseParameterDeclarationClause(bool *InvalidAsDeclaration = 0, + bool VersusTemplateArg = false); TPResult TryParseFunctionDeclarator(); TPResult TryParseBracketDeclarator(); + TPResult TryConsumeDeclarationSpecifier(); public: TypeResult ParseTypeName(SourceRange *Range = 0, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index ee1bafb86fd..385bc04f345 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6208,7 +6208,7 @@ public: /// \brief RAII class used to determine whether SFINAE has /// trapped any errors that occur during template argument - /// deduction.` + /// deduction. class SFINAETrap { Sema &SemaRef; unsigned PrevSFINAEErrors; @@ -6240,10 +6240,34 @@ public: } }; + /// \brief RAII class used to indicate that we are performing provisional + /// semantic analysis to determine the validity of a construct, so + /// typo-correction and diagnostics in the immediate context (not within + /// implicitly-instantiated templates) should be suppressed. + class TentativeAnalysisScope { + Sema &SemaRef; + // FIXME: Using a SFINAETrap for this is a hack. + SFINAETrap Trap; + bool PrevDisableTypoCorrection; + public: + explicit TentativeAnalysisScope(Sema &SemaRef) + : SemaRef(SemaRef), Trap(SemaRef, true), + PrevDisableTypoCorrection(SemaRef.DisableTypoCorrection) { + SemaRef.DisableTypoCorrection = true; + } + ~TentativeAnalysisScope() { + SemaRef.DisableTypoCorrection = PrevDisableTypoCorrection; + } + }; + /// \brief The current instantiation scope used to store local /// variables. LocalInstantiationScope *CurrentInstantiationScope; + /// \brief Tracks whether we are in a context where typo correction is + /// disabled. + bool DisableTypoCorrection; + /// \brief The number of typos corrected by CorrectTypo. unsigned TyposCorrected; diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp index ed087e342ae..725d69f77b8 100644 --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -215,8 +215,7 @@ void Parser::ParseCXXNonStaticMemberInitializer(Decl *VarD) { ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/true); } else { // Consume everything up to (but excluding) the comma or semicolon. - ConsumeAndStoreUntil(tok::comma, Toks, /*StopAtSemi=*/true, - /*ConsumeFinalToken=*/false); + ConsumeAndStoreInitializer(Toks, CIK_DefaultInitializer); } // Store an artificial EOF token to ensure that we don't run off the end of @@ -345,8 +344,15 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) { else { if (Tok.is(tok::cxx_defaultarg_end)) ConsumeToken(); - else - Diag(Tok.getLocation(), diag::err_default_arg_unparsed); + else { + // The last two tokens are the terminator and the saved value of + // Tok; the last token in the default argument is the one before + // those. + assert(Toks->size() >= 3 && "expected a token in default arg"); + Diag(Tok.getLocation(), diag::err_default_arg_unparsed) + << SourceRange(Tok.getLocation(), + (*Toks)[Toks->size() - 3].getLocation()); + } Actions.ActOnParamDefaultArgument(LM.DefaultArgs[I].Param, EqualLoc, DefArgResult.take()); } @@ -814,3 +820,276 @@ bool Parser::ConsumeAndStoreFunctionPrologue(CachedTokens &Toks) { } } } + +/// \brief Consume and store tokens from the '?' to the ':' in a conditional +/// expression. +bool Parser::ConsumeAndStoreConditional(CachedTokens &Toks) { + // Consume '?'. + assert(Tok.is(tok::question)); + Toks.push_back(Tok); + ConsumeToken(); + + while (Tok.isNot(tok::colon)) { + if (!ConsumeAndStoreUntil(tok::question, tok::colon, Toks, /*StopAtSemi*/true, + /*ConsumeFinalToken*/false)) + return false; + + // If we found a nested conditional, consume it. + if (Tok.is(tok::question) && !ConsumeAndStoreConditional(Toks)) + return false; + } + + // Consume ':'. + Toks.push_back(Tok); + ConsumeToken(); + return true; +} + +/// \brief A tentative parsing action that can also revert token annotations. +class Parser::UnannotatedTentativeParsingAction : public TentativeParsingAction { +public: + explicit UnannotatedTentativeParsingAction(Parser &Self, + tok::TokenKind EndKind) + : TentativeParsingAction(Self), Self(Self), EndKind(EndKind) { + // Stash away the old token stream, so we can restore it once the + // tentative parse is complete. + TentativeParsingAction Inner(Self); + Self.ConsumeAndStoreUntil(EndKind, Toks, true, /*ConsumeFinalToken*/false); + Inner.Revert(); + } + + void RevertAnnotations() { + Revert(); + + // Put back the original tokens. + Self.SkipUntil(EndKind, true, /*DontConsume*/true); + if (Toks.size()) { + Token *Buffer = new Token[Toks.size()]; + std::copy(Toks.begin() + 1, Toks.end(), Buffer); + Buffer[Toks.size() - 1] = Self.Tok; + Self.PP.EnterTokenStream(Buffer, Toks.size(), true, /*Owned*/true); + + Self.Tok = Toks.front(); + } + } + +private: + Parser &Self; + CachedTokens Toks; + tok::TokenKind EndKind; +}; + +/// ConsumeAndStoreInitializer - Consume and store the token at the passed token +/// container until the end of the current initializer expression (either a +/// default argument or an in-class initializer for a non-static data member). +/// The final token is not consumed. +bool Parser::ConsumeAndStoreInitializer(CachedTokens &Toks, + CachedInitKind CIK) { + // We always want this function to consume at least one token if not at EOF. + bool IsFirstTokenConsumed = true; + + // Number of possible unclosed <s we've seen so far. These might be templates, + // and might not, but if there were none of them (or we know for sure that + // we're within a template), we can avoid a tentative parse. + unsigned AngleCount = 0; + unsigned KnownTemplateCount = 0; + + while (1) { + switch (Tok.getKind()) { + case tok::comma: + // If we might be in a template, perform a tentative parse to check. + if (!AngleCount) + // Not a template argument: this is the end of the initializer. + return true; + if (KnownTemplateCount) + goto consume_token; + + // We hit a comma inside angle brackets. This is the hard case. The + // rule we follow is: + // * For a default argument, if the tokens after the comma form a + // syntactically-valid parameter-declaration-clause, in which each + // parameter has an initializer, then this comma ends the default + // argument. + // * For a default initializer, if the tokens after the comma form a + // syntactically-valid init-declarator-list, then this comma ends + // the default initializer. + { + UnannotatedTentativeParsingAction PA(*this, + CIK == CIK_DefaultInitializer + ? tok::semi : tok::r_paren); + Sema::TentativeAnalysisScope Scope(Actions); + + TPResult Result = TPResult::Error(); + ConsumeToken(); + switch (CIK) { + case CIK_DefaultInitializer: + Result = TryParseInitDeclaratorList(); + // If we parsed a complete, ambiguous init-declarator-list, this + // is only syntactically-valid if it's followed by a semicolon. + if (Result == TPResult::Ambiguous() && Tok.isNot(tok::semi)) + Result = TPResult::False(); + break; + + case CIK_DefaultArgument: + bool InvalidAsDeclaration = false; + Result = TryParseParameterDeclarationClause( + &InvalidAsDeclaration, /*VersusTemplateArgument*/true); + // If this is an expression or a declaration with a missing + // 'typename', assume it's not a declaration. + if (Result == TPResult::Ambiguous() && InvalidAsDeclaration) + Result = TPResult::False(); + break; + } + + // If what follows could be a declaration, it is a declaration. + if (Result != TPResult::False() && Result != TPResult::Error()) { + PA.Revert(); + return true; + } + + // In the uncommon case that we decide the following tokens are part + // of a template argument, revert any annotations we've performed in + // those tokens. We're not going to look them up until we've parsed + // the rest of the class, and that might add more declarations. + PA.RevertAnnotations(); + } + + // Keep going. We know we're inside a template argument list now. + ++KnownTemplateCount; + goto consume_token; + + case tok::eof: + // Ran out of tokens. + return false; + + case tok::less: + // FIXME: A '<' can only start a template-id if it's preceded by an + // identifier, an operator-function-id, or a literal-operator-id. + ++AngleCount; + goto consume_token; + + case tok::question: + // In 'a ? b : c', 'b' can contain an unparenthesized comma. If it does, + // that is *never* the end of the initializer. Skip to the ':'. + if (!ConsumeAndStoreConditional(Toks)) + return false; + break; + + case tok::greatergreatergreater: + if (!getLangOpts().CPlusPlus11) + goto consume_token; + if (AngleCount) --AngleCount; + if (KnownTemplateCount) --KnownTemplateCount; + // Fall through. + case tok::greatergreater: + if (!getLangOpts().CPlusPlus11) + goto consume_token; + if (AngleCount) --AngleCount; + if (KnownTemplateCount) --KnownTemplateCount; + // Fall through. + case tok::greater: + if (AngleCount) --AngleCount; + if (KnownTemplateCount) --KnownTemplateCount; + goto consume_token; + + case tok::kw_template: + // 'template' identifier '<' is known to start a template argument list, + // and can be used to disambiguate the parse. + // FIXME: Support all forms of 'template' unqualified-id '<'. + Toks.push_back(Tok); + ConsumeToken(); + if (Tok.is(tok::identifier)) { + Toks.push_back(Tok); + ConsumeToken(); + if (Tok.is(tok::less)) { + ++KnownTemplateCount; + Toks.push_back(Tok); + ConsumeToken(); + } + } + break; + + case tok::kw_operator: + // If 'operator' precedes other punctuation, that punctuation loses + // its special behavior. + Toks.push_back(Tok); + ConsumeToken(); + switch (Tok.getKind()) { + case tok::comma: + case tok::greatergreatergreater: + case tok::greatergreater: + case tok::greater: + case tok::less: + Toks.push_back(Tok); + ConsumeToken(); + break; + default: + break; + } + break; + + case tok::l_paren: + // Recursively consume properly-nested parens. + Toks.push_back(Tok); + ConsumeParen(); + ConsumeAndStoreUntil(tok::r_paren, Toks, /*StopAtSemi=*/false); + break; + case tok::l_square: + // Recursively consume properly-nested square brackets. + Toks.push_back(Tok); + ConsumeBracket(); + ConsumeAndStoreUntil(tok::r_square, Toks, /*StopAtSemi=*/false); + break; + case tok::l_brace: + // Recursively consume properly-nested braces. + Toks.push_back(Tok); + ConsumeBrace(); + ConsumeAndStoreUntil(tok::r_brace, Toks, /*StopAtSemi=*/false); + break; + + // Okay, we found a ']' or '}' or ')', which we think should be balanced. + // Since the user wasn't looking for this token (if they were, it would + // already be handled), this isn't balanced. If there is a LHS token at a + // higher level, we will assume that this matches the unbalanced token + // and return it. Otherwise, this is a spurious RHS token, which we skip. + case tok::r_paren: + if (CIK == CIK_DefaultArgument) + return true; // End of the default argument. + if (ParenCount && !IsFirstTokenConsumed) + return false; // Matches something. + goto consume_token; + case tok::r_square: + if (BracketCount && !IsFirstTokenConsumed) + return false; // Matches something. + goto consume_token; + case tok::r_brace: + if (BraceCount && !IsFirstTokenConsumed) + return false; // Matches something. + goto consume_token; + + case tok::code_completion: + Toks.push_back(Tok); + ConsumeCodeCompletionToken(); + break; + + case tok::string_literal: + case tok::wide_string_literal: + case tok::utf8_string_literal: + case tok::utf16_string_literal: + case tok::utf32_string_literal: + Toks.push_back(Tok); + ConsumeStringToken(); + break; + case tok::semi: + if (CIK == CIK_DefaultInitializer) + return true; // End of the default initializer. + // FALL THROUGH. + default: + consume_token: + Toks.push_back(Tok); + ConsumeToken(); + break; + } + IsFirstTokenConsumed = false; + } +} diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 6d7ab979b08..74982a3894e 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -5333,9 +5333,7 @@ void Parser::ParseParameterDeclarationClause( // FIXME: Can we use a smart pointer for Toks? DefArgToks = new CachedTokens; - if (!ConsumeAndStoreUntil(tok::comma, tok::r_paren, *DefArgToks, - /*StopAtSemi=*/true, - /*ConsumeFinalToken=*/false)) { + if (!ConsumeAndStoreInitializer(*DefArgToks, CIK_DefaultArgument)) { delete DefArgToks; DefArgToks = 0; Actions.ActOnParamDefaultArgumentError(Param); diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 37e1412be7a..7d1475c3a6b 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -142,6 +142,82 @@ bool Parser::isCXXSimpleDeclaration(bool AllowForRangeDecl) { return TPR == TPResult::True(); } +/// Try to consume a token sequence that we've already identified as +/// (potentially) starting a decl-specifier. +Parser::TPResult Parser::TryConsumeDeclarationSpecifier() { + switch (Tok.getKind()) { + case tok::kw__Atomic: + if (NextToken().isNot(tok::l_paren)) { + ConsumeToken(); + break; + } + // Fall through. + case tok::kw_typeof: + case tok::kw___attribute: + case tok::kw___underlying_type: { + ConsumeToken(); + if (Tok.isNot(tok::l_paren)) + return TPResult::Error(); + ConsumeParen(); + if (!SkipUntil(tok::r_paren, false)) + return TPResult::Error(); + break; + } + + case tok::kw_class: + case tok::kw_struct: + case tok::kw_union: + case tok::kw___interface: + case tok::kw_enum: + // elaborated-type-specifier: + // class-key attribute-specifier-seq[opt] + // nested-name-specifier[opt] identifier + // class-key nested-name-specifier[opt] template[opt] simple-template-id + // enum nested-name-specifier[opt] identifier + // + // FIXME: We don't support class-specifiers nor enum-specifiers here. + ConsumeToken(); + + // Skip attributes. + while (Tok.is(tok::l_square) || Tok.is(tok::kw___attribute) || + Tok.is(tok::kw___declspec) || Tok.is(tok::kw_alignas)) { + if (Tok.is(tok::l_square)) { + ConsumeBracket(); + if (!SkipUntil(tok::r_square, false)) + return TPResult::Error(); + } else { + ConsumeToken(); + if (Tok.isNot(tok::l_paren)) + return TPResult::Error(); + ConsumeParen(); + if (!SkipUntil(tok::r_paren, false)) + return TPResult::Error(); + } + } + + if (TryAnnotateCXXScopeToken()) + return TPResult::Error(); + if (Tok.is(tok::annot_cxxscope)) + ConsumeToken(); + if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id)) + return TPResult::Error(); + ConsumeToken(); + break; + + case tok::annot_cxxscope: + ConsumeToken(); + // Fall through. + default: + ConsumeToken(); + + if (getLangOpts().ObjC1 && Tok.is(tok::less)) + return TryParseProtocolQualifiers(); + break; + } + + return TPResult::Ambiguous(); +} + /// simple-declaration: /// decl-specifier-seq init-declarator-list[opt] ';' /// @@ -151,16 +227,8 @@ bool Parser::isCXXSimpleDeclaration(bool AllowForRangeDecl) { /// attribute-specifier-seqopt type-specifier-seq declarator /// Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) { - if (Tok.is(tok::kw_typeof)) - TryParseTypeofSpecifier(); - else { - if (Tok.is(tok::annot_cxxscope)) - ConsumeToken(); - ConsumeToken(); - - if (getLangOpts().ObjC1 && Tok.is(tok::less)) - TryParseProtocolQualifiers(); - } + if (TryConsumeDeclarationSpecifier() == TPResult::Error()) + return TPResult::Error(); // Two decl-specifiers in a row conclusively disambiguate this as being a // simple-declaration. Don't bother calling isCXXDeclarationSpecifier in the @@ -233,7 +301,7 @@ Parser::TPResult Parser::TryParseInitDeclaratorList() { // expression can never be followed directly by a braced-init-list. return TPResult::True(); } else if (Tok.is(tok::equal) || isTokIdentifier_in()) { - // MSVC and g++ won't examine the rest of declarators if '=' is + // MSVC and g++ won't examine the rest of declarators if '=' is // encountered; they just conclude that we have a declaration. // EDG parses the initializer completely, which is the proper behavior // for this case. @@ -241,12 +309,14 @@ Parser::TPResult Parser::TryParseInitDeclaratorList() { // At present, Clang follows MSVC and g++, since the parser does not have // the ability to parse an expression fully without recording the // results of that parse. - // Also allow 'in' after on objective-c declaration as in: - // for (int (^b)(void) in array). Ideally this should be done in the + // FIXME: Handle this case correctly. + // + // Also allow 'in' after an Objective-C declaration as in: + // for (int (^b)(void) in array). Ideally this should be done in the // context of parsing for-init-statement of a foreach statement only. But, // in any other context 'in' is invalid after a declaration and parser // issues the error regardless of outcome of this decision. - // FIXME. Change if above assumption does not hold. + // FIXME: Change if above assumption does not hold. return TPResult::True(); } @@ -286,14 +356,7 @@ bool Parser::isCXXConditionDeclaration() { TentativeParsingAction PA(*this); // type-specifier-seq - if (Tok.is(tok::kw_typeof)) - TryParseTypeofSpecifier(); - else { - ConsumeToken(); - - if (getLangOpts().ObjC1 && Tok.is(tok::less)) - TryParseProtocolQualifiers(); - } + TryConsumeDeclarationSpecifier(); assert(Tok.is(tok::l_paren) && "Expected '('"); // declarator @@ -363,15 +426,7 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) { TentativeParsingAction PA(*this); // type-specifier-seq - if (Tok.is(tok::kw_typeof)) - TryParseTypeofSpecifier(); - else { - ConsumeToken(); - - if (getLangOpts().ObjC1 && Tok.is(tok::less)) - TryParseProtocolQualifiers(); - } - + TryConsumeDeclarationSpecifier(); assert(Tok.is(tok::l_paren) && "Expected '('"); // declarator @@ -569,6 +624,121 @@ Parser::isCXX11AttributeSpecifier(bool Disambiguate, return CAK_NotAttributeSpecifier; } +Parser::TPResult Parser::TryParsePtrOperatorSeq() { + while (true) { + if (Tok.is(tok::coloncolon) || Tok.is(tok::identifier)) + if (TryAnnotateCXXScopeToken(true)) + return TPResult::Error(); + + if (Tok.is(tok::star) || Tok.is(tok::amp) || Tok.is(tok::caret) || + Tok.is(tok::ampamp) || + (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::star))) { + // ptr-operator + ConsumeToken(); + while (Tok.is(tok::kw_const) || + Tok.is(tok::kw_volatile) || + Tok.is(tok::kw_restrict)) + ConsumeToken(); + } else { + return TPResult::True(); + } + } +} + +/// operator-function-id: +/// 'operator' operator +/// +/// operator: one of +/// new delete new[] delete[] + - * / % ^ [...] +/// +/// conversion-function-id: +/// 'operator' conversion-type-id +/// +/// conversion-type-id: +/// type-specifier-seq conversion-declarator[opt] +/// +/// conversion-declarator: +/// ptr-operator conversion-declarator[opt] +/// +/// literal-operator-id: +/// 'operator' string-literal identifier +/// 'operator' user-defined-string-literal +Parser::TPResult Parser::TryParseOperatorId() { + assert(Tok.is(tok::kw_operator)); + ConsumeToken(); + + // Maybe this is an operator-function-id. + switch (Tok.getKind()) { + case tok::kw_new: case tok::kw_delete: + ConsumeToken(); + if (Tok.is(tok::l_square) && NextToken().is(tok::r_square)) { + ConsumeBracket(); + ConsumeBracket(); + } + return TPResult::True(); + +#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemOnly) \ + case tok::Token: +#define OVERLOADED_OPERATOR_MULTI(Name, Spelling, Unary, Binary, MemOnly) +#include "clang/Basic/OperatorKinds.def" + ConsumeToken(); + return TPResult::True(); + + case tok::l_square: + if (NextToken().is(tok::r_square)) { + ConsumeBracket(); + ConsumeBracket(); + return TPResult::True(); + } + break; + + case tok::l_paren: + if (NextToken().is(tok::r_paren)) { + ConsumeParen(); + ConsumeParen(); + return TPResult::True(); + } + break; + + default: + break; + } + + // Maybe this is a literal-operator-id. + if (getLangOpts().CPlusPlus11 && isTokenStringLiteral()) { + bool FoundUDSuffix = false; + do { + FoundUDSuffix |= Tok.hasUDSuffix(); + ConsumeStringToken(); + } while (isTokenStringLiteral()); + + if (!FoundUDSuffix) { + if (Tok.is(tok::identifier)) + ConsumeToken(); + else + return TPResult::Error(); + } + return TPResult::True(); + } + + // Maybe this is a conversion-function-id. + bool AnyDeclSpecifiers = false; + while (true) { + TPResult TPR = isCXXDeclarationSpecifier(); + if (TPR == TPResult::Error()) + return TPR; + if (TPR == TPResult::False()) { + if (!AnyDeclSpecifiers) + return TPResult::Error(); + break; + } + if (TryConsumeDeclarationSpecifier() == TPResult::Error()) + return TPResult::Error(); + AnyDeclSpecifiers = true; + } + return TryParsePtrOperatorSeq(); +} + /// declarator: /// direct-declarator /// ptr-operator declarator @@ -615,9 +785,11 @@ Parser::isCXX11AttributeSpecifier(bool Disambiguate, /// /// unqualified-id: /// identifier -/// operator-function-id [TODO] -/// conversion-function-id [TODO] +/// operator-function-id +/// conversion-function-id +/// literal-operator-id /// '~' class-name [TODO] +/// '~' decltype-specifier [TODO] /// template-id [TODO] /// Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, @@ -625,40 +797,28 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, // declarator: // direct-declarator // ptr-operator declarator - - while (1) { - if (Tok.is(tok::coloncolon) || Tok.is(tok::identifier)) - if (TryAnnotateCXXScopeToken(true)) - return TPResult::Error(); - - if (Tok.is(tok::star) || Tok.is(tok::amp) || Tok.is(tok::caret) || - Tok.is(tok::ampamp) || - (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::star))) { - // ptr-operator - ConsumeToken(); - while (Tok.is(tok::kw_const) || - Tok.is(tok::kw_volatile) || - Tok.is(tok::kw_restrict)) - ConsumeToken(); - } else { - break; - } - } + if (TryParsePtrOperatorSeq() == TPResult::Error()) + return TPResult::Error(); // direct-declarator: // direct-abstract-declarator: if (Tok.is(tok::ellipsis)) ConsumeToken(); - - if ((Tok.is(tok::identifier) || - (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::identifier))) && + + if ((Tok.is(tok::identifier) || Tok.is(tok::kw_operator) || + (Tok.is(tok::annot_cxxscope) && (NextToken().is(tok::identifier) || + NextToken().is(tok::kw_operator)))) && mayHaveIdentifier) { // declarator-id if (Tok.is(tok::annot_cxxscope)) ConsumeToken(); - else + else if (Tok.is(tok::identifier)) TentativelyDeclaredIdentifiers.push_back(Tok.getIdentifierInfo()); - ConsumeToken(); + if (Tok.is(tok::kw_operator)) { + if (TryParseOperatorId() == TPResult::Error()) + return TPResult::Error(); + } else + ConsumeToken(); } else if (Tok.is(tok::l_paren)) { ConsumeParen(); if (mayBeAbstract && @@ -836,14 +996,15 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) { case tok::kw_wchar_t: case tok::kw_char16_t: case tok::kw_char32_t: - case tok::kw___underlying_type: case tok::kw__Decimal32: case tok::kw__Decimal64: case tok::kw__Decimal128: + case tok::kw___interface: case tok::kw___thread: case tok::kw_thread_local: case tok::kw__Thread_local: case tok::kw_typeof: + case tok::kw___underlying_type: case tok::kw___cdecl: case tok::kw___stdcall: case tok::kw___fastcall: @@ -1103,6 +1264,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, case tok::kw_class: case tok::kw_struct: case tok::kw_union: + case tok::kw___interface: // enum-specifier case tok::kw_enum: // cv-qualifier @@ -1325,6 +1487,56 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, } } +bool Parser::isCXXDeclarationSpecifierAType() { + switch (Tok.getKind()) { + // typename-specifier + case tok::annot_decltype: + case tok::annot_template_id: + case tok::annot_typename: + case tok::kw_typeof: + case tok::kw___underlying_type: + return true; + + // elaborated-type-specifier + case tok::kw_class: + case tok::kw_struct: + case tok::kw_union: + case tok::kw___interface: + case tok::kw_enum: + return true; + + // simple-type-specifier + case tok::kw_char: + case tok::kw_wchar_t: + case tok::kw_char16_t: + case tok::kw_char32_t: + case tok::kw_bool: + case tok::kw_short: + case tok::kw_int: + case tok::kw_long: + case tok::kw___int64: + case tok::kw___int128: + case tok::kw_signed: + case tok::kw_unsigned: + case tok::kw_half: + case tok::kw_float: + case tok::kw_double: + case tok::kw_void: + case tok::kw___unknown_anytype: + return true; + + case tok::kw_auto: + return getLangOpts().CPlusPlus11; + + case tok::kw__Atomic: + // "_Atomic foo" + return NextToken().is(tok::l_paren); + + default: + return false; + } +} + /// [GNU] typeof-specifier: /// 'typeof' '(' expressions ')' /// 'typeof' '(' type-name ')' @@ -1366,27 +1578,6 @@ Parser::TPResult Parser::TryParseProtocolQualifiers() { return TPResult::Error(); } -Parser::TPResult -Parser::TryParseDeclarationSpecifier(bool *HasMissingTypename) { - TPResult TPR = isCXXDeclarationSpecifier(TPResult::False(), - HasMissingTypename); - if (TPR != TPResult::Ambiguous()) - return TPR; - - if (Tok.is(tok::kw_typeof)) - TryParseTypeofSpecifier(); - else { - if (Tok.is(tok::annot_cxxscope)) - ConsumeToken(); - ConsumeToken(); - - if (getLangOpts().ObjC1 && Tok.is(tok::less)) - TryParseProtocolQualifiers(); - } - - return TPResult::Ambiguous(); -} - /// isCXXFunctionDeclarator - Disambiguates between a function declarator or /// a constructor-style initializer, when parsing declaration statements. /// Returns true for function declarator and false for constructor-style @@ -1461,7 +1652,8 @@ bool Parser::isCXXFunctionDeclarator(bool *IsAmbiguous) { /// attributes[opt] '=' assignment-expression /// Parser::TPResult -Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration) { +Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration, + bool VersusTemplateArgument) { if (Tok.is(tok::r_paren)) return TPResult::Ambiguous(); @@ -1494,8 +1686,32 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration) { // decl-specifier-seq // A parameter-declaration's initializer must be preceded by an '=', so // decl-specifier-seq '{' is not a parameter in C++11. - TPResult TPR = TryParseDeclarationSpecifier(InvalidAsDeclaration); - if (TPR != TPResult::Ambiguous()) + TPResult TPR = isCXXDeclarationSpecifier(TPResult::False(), + InvalidAsDeclaration); + + if (VersusTemplateArgument && TPR == TPResult::True()) { + // Consume the decl-specifier-seq. We have to look past it, since a + // type-id might appear here in a template argument. + bool SeenType = false; + do { + SeenType |= isCXXDeclarationSpecifierAType(); + if (TryConsumeDeclarationSpecifier() == TPResult::Error()) + return TPResult::Error(); + + // If we see a parameter name, this can't be a template argument. + if (SeenType && Tok.is(tok::identifier)) + return TPResult::True(); + + TPR = isCXXDeclarationSpecifier(TPResult::False(), + InvalidAsDeclaration); + if (TPR == TPResult::Error()) + return TPR; + } while (TPR != TPResult::False()); + } else if (TPR == TPResult::Ambiguous()) { + // Disambiguate what follows the decl-specifier. + if (TryConsumeDeclarationSpecifier() == TPResult::Error()) + return TPResult::Error(); + } else return TPR; // declarator @@ -1508,9 +1724,24 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration) { if (Tok.is(tok::kw___attribute)) return TPResult::True(); + // If we're disambiguating a template argument in a default argument in + // a class definition versus a parameter declaration, an '=' here + // disambiguates the parse one way or the other. + // If this is a parameter, it must have a default argument because + // (a) the previous parameter did, and + // (b) this must be the first declaration of the function, so we can't + // inherit any default arguments from elsewhere. + // If we see an ')', then we've reached the end of a + // parameter-declaration-clause, and the last param is missing its default + // argument. + if (VersusTemplateArgument) + return (Tok.is(tok::equal) || Tok.is(tok::r_paren)) ? TPResult::True() + : TPResult::False(); + if (Tok.is(tok::equal)) { // '=' assignment-expression // Parse through assignment-expression. + // FIXME: assignment-expression may contain an unparenthesized comma. if (!SkipUntil(tok::comma, tok::r_paren, true/*StopAtSemi*/, true/*DontConsume*/)) return TPResult::Error(); diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index c6f213a7380..72fdc528fd4 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -89,8 +89,9 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, NumSFINAEErrors(0), InFunctionDeclarator(0), AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1), - CurrentInstantiationScope(0), TyposCorrected(0), - AnalysisWarnings(*this), VarDataSharingAttributesStack(0), CurScope(0), + CurrentInstantiationScope(0), DisableTypoCorrection(false), + TyposCorrected(0), AnalysisWarnings(*this), + VarDataSharingAttributesStack(0), CurScope(0), Ident_super(0), Ident___float128(0) { TUScope = 0; diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 5b641d80598..c26960670e2 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -3955,7 +3955,8 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, return Correction; } - if (Diags.hasFatalErrorOccurred() || !getLangOpts().SpellChecking) + if (Diags.hasFatalErrorOccurred() || !getLangOpts().SpellChecking || + DisableTypoCorrection) return TypoCorrection(); // In Microsoft mode, don't perform typo correction in a template member diff --git a/clang/test/Parser/cxx-ambig-init-templ.cpp b/clang/test/Parser/cxx-ambig-init-templ.cpp new file mode 100644 index 00000000000..88ab61c3ab6 --- /dev/null +++ b/clang/test/Parser/cxx-ambig-init-templ.cpp @@ -0,0 +1,171 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s + +template<int> struct c { c(int) = delete; typedef void val; operator int() const; }; + +int val; +int foobar; +struct S { + int k1 = a < b < c, d > ::val, e1; + int k2 = a < b, c < d > ::val, e2; + int k3 = b < a < c, d > ::val, e3; + int k4 = b < c, x, y = d > ::val, e4; + int k5 = T1 < b, &S::operator=(int); // expected-error {{extra qualification}} + int k6 = T2 < b, &S::operator= >::val; + int k7 = T1 < b, &S::operator>(int); // expected-error {{extra qualification}} + int k8 = T2 < b, &S::operator> >::val; + int k9 = T3 < a < b, c >> (d), e5 = 1 > (e4); + int k10 = 0 < T3 < a < b, c >> (d + ) // expected-error {{expected ';' at end of declaration}} + , a > (e4); + int k11 = 0 < 1, c<3>::*ptr; + int k12 = e < 0, int a<b<c>::* >(), e11; + + void f1( + int k1 = a < b < c, d > ::val, + int k2 = b < a < c, d > ::val, + int k3 = b < c, int x = 0 > ::val, + int k4 = a < b, T3 < int > >(), // expected-error {{must be an expression}} + int k5 = a < b, c < d > ::val, + int k6 = a < b, c < d > (n) // expected-error {{undeclared identifier 'n'}} + ); + + void f2a( + // T3<int> here is a parameter type, so must be declared before it is used. + int k1 = c < b, T3 < int > x = 0 // expected-error {{unexpected end of default argument expression}} + ); + + template<typename, int=0> struct T3 { T3(int); operator int(); }; + + void f2b( + int k1 = c < b, T3 < int > x = 0 // ok + ); + + // This is a one-parameter function. Ensure we don't typo-correct it to + // int = a < b, c < foobar > () + // ... which would be a function with two parameters. + int f3(int = a < b, c < goobar > ()); + static constexpr int (S::*f3_test)(int) = &S::f3; + + void f4( + int k1 = a<1,2>::val, + int missing_default // expected-error {{missing default argument on parameter}} + ); + + void f5( + int k1 = b < c, + int missing_default // expected-error {{missing default argument on parameter}} + ); + + void f6( + int k = b < c, + unsigned int (missing_default) // expected-error {{missing default argument on parameter}} + ); + + template<int, int=0> struct a { static const int val = 0; operator int(); }; // expected-note {{here}} + static const int b = 0, c = 1, d = 2, goobar = 3; + template<int, typename> struct e { operator int(); }; + + int mp1 = 0 < 1, + a<b<c,b<c>::*mp2, + mp3 = 0 > a<b<c>::val, + a<b<c,b<c>::*mp4 = 0, + a<b<c,b<c>::*mp5 {0}, + a<b<c,b<c>::*mp6; + + int np1 = e<0, int a<b<c,b<c>::*>(); + + static const int T1 = 4; + template<int, int &(S::*)(int)> struct T2 { static const int val = 0; }; +}; + +namespace NoAnnotationTokens { + template<bool> struct Bool { Bool(int); }; + static const bool in_class = false; + + struct Test { + // Check we don't keep around a Bool<false> annotation token here. + int f(Bool<true> = X<Y, Bool<in_class> >(0)); + + // But it's OK if we do here. + int g(Bool<true> = Z<Y, Bool<in_class> = Bool<false>(0)); + + static const bool in_class = true; + template<int, typename U> using X = U; + static const int Y = 0, Z = 0; + }; +} + +namespace ImplicitInstantiation { + template<typename T> struct HasError { typename T::error error; }; // expected-error {{has no members}} + + struct S { + // This triggers the instantiation of the outer HasError<int> during + // disambiguation, even though it uses the inner HasError<int>. + void f(int a = X<Y, HasError<int>::Z >()); // expected-note {{in instantiation of}} + + template<typename, typename> struct X { operator int(); }; + typedef int Y; + template<typename> struct HasError { typedef int Z; }; + }; + + HasError<int> hei; +} + +namespace CWG325 { + template <int A, typename B> struct T { static int i; operator int(); }; + class C { + int Foo (int i = T<1, int>::i); + }; + + class D { + int Foo (int i = T<1, int>::i); + template <int A, typename B> struct T {static int i;}; + }; + + const int a = 0; + typedef int b; + T<a,b> c; + struct E { + int n = T<a,b>(c); + }; +} + +namespace Operators { + struct Y {}; + constexpr int operator,(const Y&, const Y&) { return 8; } + constexpr int operator>(const Y&, const Y&) { return 8; } + constexpr int operator<(const Y&, const Y&) { return 8; } + constexpr int operator>>(const Y&, const Y&) { return 8; } + + struct X { + typedef int (*Fn)(const Y&, const Y&); + + Fn a = operator,, b = operator<, c = operator>; + void f(Fn a = operator,, Fn b = operator<, Fn c = operator>); + + int k1 = T1<0, operator<, operator>, operator<>::val, l1; + int k2 = T1<0, operator>, operator,, operator,>::val, l2; + int k3 = T2<0, operator,(Y{}, Y{}), operator<(Y{}, Y{})>::val, l3; + int k4 = T2<0, operator>(Y{}, Y{}), operator,(Y{}, Y{})>::val, l4; + int k5 = T3<0, operator>>>::val, l5; + int k6 = T4<0, T3<0, operator>>>>::val, l6; + + template<int, Fn, Fn, Fn> struct T1 { enum { val }; }; + template<int, int, int> struct T2 { enum { val }; }; + template<int, Fn> struct T3 { enum { val }; }; + template<int, typename T> struct T4 : T {}; + }; +} + +namespace ElaboratedTypeSpecifiers { + struct S { + int f(int x = T<a, struct S>()); + int g(int x = T<a, class __declspec() C>()); + int h(int x = T<a, union __attribute__(()) U>()); + int i(int x = T<a, enum E>()); + int j(int x = T<a, struct S::template T<0, enum E>>()); + template <int, typename> struct T { operator int(); }; + static const int a = 0; + enum E {}; + }; +} diff --git a/clang/test/Parser/cxx-default-args.cpp b/clang/test/Parser/cxx-default-args.cpp index 7fe8474142b..36abf0d8cb3 100644 --- a/clang/test/Parser/cxx-default-args.cpp +++ b/clang/test/Parser/cxx-default-args.cpp @@ -14,3 +14,20 @@ typedef struct Inst { struct X { void f(int x = 1:); // expected-error {{unexpected end of default argument expression}} }; + +// PR13657 +struct T { + template <typename A, typename B> struct T1 { enum {V};}; + template <int A, int B> struct T2 { enum {V}; }; + template <int, int> static int func(int); + + + void f1(T1<int, int> = T1<int, int>()); + void f2(T1<int, double> = T1<int, double>(), T2<0, 5> = T2<0, 5>()); + void f3(int a = T2<0, (T1<int, int>::V > 10) ? 5 : 6>::V, bool b = 4<5 ); + void f4(bool a = 1 < 0, bool b = 2 > 0 ); + void f5(bool a = 1 > T2<0, 0>::V, bool b = T1<int,int>::V < 3, int c = 0); + void f6(bool a = T2<0,3>::V < 4, bool b = 4 > T2<0,3>::V); + void f7(bool a = T1<int, bool>::V < 3); + void f8(int = func<0,1<2>(0), int = 1<0, T1<int,int>(int) = 0); +}; diff --git a/clang/test/Parser/cxx0x-member-initializers.cpp b/clang/test/Parser/cxx0x-member-initializers.cpp index a324f974bca..43e99b13364 100644 --- a/clang/test/Parser/cxx0x-member-initializers.cpp +++ b/clang/test/Parser/cxx0x-member-initializers.cpp @@ -27,3 +27,13 @@ struct V1 { int a, b; V1() : a(), b{} {} }; + +template <typename, typename> struct T1 { enum {V};}; +template <int, int> struct T2 { enum {V};}; +struct A { + T1<int, int> a1 = T1<int, int>(), *a2 = new T1<int,int>; + T2<0,0> b1 = T2<0,0>(), b2 = T2<0,0>(), b3; + bool c1 = 1 < 2, c2 = 2 < 1, c3 = false; + bool d1 = T1<int, T1<int, int>>::V < 3, d2; + T1<int, int()> e = T1<int, int()>(); +}; |

