aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/lib/Parse/ParseTentative.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/clang/lib/Parse/ParseTentative.cpp')
-rw-r--r--contrib/llvm-project/clang/lib/Parse/ParseTentative.cpp354
1 files changed, 278 insertions, 76 deletions
diff --git a/contrib/llvm-project/clang/lib/Parse/ParseTentative.cpp b/contrib/llvm-project/clang/lib/Parse/ParseTentative.cpp
index 3bf2bc455bfe..5bfabf55f50c 100644
--- a/contrib/llvm-project/clang/lib/Parse/ParseTentative.cpp
+++ b/contrib/llvm-project/clang/lib/Parse/ParseTentative.cpp
@@ -46,7 +46,10 @@ using namespace clang;
/// 'using' 'namespace' '::'[opt] nested-name-specifier[opt]
/// namespace-name ';'
///
-bool Parser::isCXXDeclarationStatement() {
+bool Parser::isCXXDeclarationStatement(
+ bool DisambiguatingWithExpression /*=false*/) {
+ assert(getLangOpts().CPlusPlus && "Must be called for C++ only.");
+
switch (Tok.getKind()) {
// asm-definition
case tok::kw_asm:
@@ -59,6 +62,51 @@ bool Parser::isCXXDeclarationStatement() {
case tok::kw_static_assert:
case tok::kw__Static_assert:
return true;
+ case tok::coloncolon:
+ case tok::identifier: {
+ if (DisambiguatingWithExpression) {
+ RevertingTentativeParsingAction TPA(*this);
+ // Parse the C++ scope specifier.
+ CXXScopeSpec SS;
+ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+ /*ObjectHasErrors=*/false,
+ /*EnteringContext=*/true);
+
+ switch (Tok.getKind()) {
+ case tok::identifier: {
+ IdentifierInfo *II = Tok.getIdentifierInfo();
+ bool isDeductionGuide = Actions.isDeductionGuideName(
+ getCurScope(), *II, Tok.getLocation(), SS, /*Template=*/nullptr);
+ if (Actions.isCurrentClassName(*II, getCurScope(), &SS) ||
+ isDeductionGuide) {
+ if (isConstructorDeclarator(/*Unqualified=*/SS.isEmpty(),
+ isDeductionGuide,
+ DeclSpec::FriendSpecified::No))
+ return true;
+ } else if (SS.isNotEmpty()) {
+ // If the scope is not empty, it could alternatively be something like
+ // a typedef or using declaration. That declaration might be private
+ // in the global context, which would be diagnosed by calling into
+ // isCXXSimpleDeclaration, but may actually be fine in the context of
+ // member functions and static variable definitions. Check if the next
+ // token is also an identifier and assume a declaration.
+ // We cannot check if the scopes match because the declarations could
+ // involve namespaces and friend declarations.
+ if (NextToken().is(tok::identifier))
+ return true;
+ }
+ break;
+ }
+ case tok::kw_operator:
+ return true;
+ case tok::tilde:
+ return true;
+ default:
+ break;
+ }
+ }
+ }
+ [[fallthrough]];
// simple-declaration
default:
return isCXXSimpleDeclaration(/*AllowForRangeDecl=*/false);
@@ -111,8 +159,8 @@ bool Parser::isCXXSimpleDeclaration(bool AllowForRangeDecl) {
// a case.
bool InvalidAsDeclaration = false;
- TPResult TPR = isCXXDeclarationSpecifier(TPResult::False,
- &InvalidAsDeclaration);
+ TPResult TPR = isCXXDeclarationSpecifier(
+ ImplicitTypenameContext::No, TPResult::False, &InvalidAsDeclaration);
if (TPR != TPResult::Ambiguous)
return TPR != TPResult::False; // Returns true for TPResult::True or
// TPResult::Error.
@@ -158,10 +206,12 @@ Parser::TPResult Parser::TryConsumeDeclarationSpecifier() {
ConsumeToken();
break;
}
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case tok::kw_typeof:
case tok::kw___attribute:
- case tok::kw___underlying_type: {
+#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait:
+#include "clang/Basic/TransformTypeTraits.def"
+ {
ConsumeToken();
if (Tok.isNot(tok::l_paren))
return TPResult::Error;
@@ -203,7 +253,7 @@ Parser::TPResult Parser::TryConsumeDeclarationSpecifier() {
case tok::annot_cxxscope:
ConsumeAnnotationToken();
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
default:
ConsumeAnyToken();
@@ -224,6 +274,7 @@ Parser::TPResult Parser::TryConsumeDeclarationSpecifier() {
/// attribute-specifier-seqopt type-specifier-seq declarator
///
Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) {
+ bool DeclSpecifierIsAuto = Tok.is(tok::kw_auto);
if (TryConsumeDeclarationSpecifier() == TPResult::Error)
return TPResult::Error;
@@ -231,7 +282,7 @@ Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) {
// simple-declaration. Don't bother calling isCXXDeclarationSpecifier in the
// overwhelmingly common case that the next token is a '('.
if (Tok.isNot(tok::l_paren)) {
- TPResult TPR = isCXXDeclarationSpecifier();
+ TPResult TPR = isCXXDeclarationSpecifier(ImplicitTypenameContext::No);
if (TPR == TPResult::Ambiguous)
return TPResult::True;
if (TPR == TPResult::True || TPR == TPResult::Error)
@@ -239,7 +290,8 @@ Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) {
assert(TPR == TPResult::False);
}
- TPResult TPR = TryParseInitDeclaratorList();
+ TPResult TPR = TryParseInitDeclaratorList(
+ /*mayHaveTrailingReturnType=*/DeclSpecifierIsAuto);
if (TPR != TPResult::Ambiguous)
return TPR;
@@ -276,10 +328,15 @@ Parser::TPResult Parser::TryParseSimpleDeclaration(bool AllowForRangeDecl) {
/// '{' initializer-list ','[opt] '}'
/// '{' '}'
///
-Parser::TPResult Parser::TryParseInitDeclaratorList() {
- while (1) {
+Parser::TPResult
+Parser::TryParseInitDeclaratorList(bool MayHaveTrailingReturnType) {
+ while (true) {
// declarator
- TPResult TPR = TryParseDeclarator(false/*mayBeAbstract*/);
+ TPResult TPR = TryParseDeclarator(
+ /*mayBeAbstract=*/false,
+ /*mayHaveIdentifier=*/true,
+ /*mayHaveDirectInit=*/false,
+ /*mayHaveTrailingReturnType=*/MayHaveTrailingReturnType);
if (TPR != TPResult::Ambiguous)
return TPR;
@@ -353,8 +410,8 @@ struct Parser::ConditionDeclarationOrInitStatementState {
if (CanBeForRangeDecl) {
// Skip until we hit a ')', ';', or a ':' with no matching '?'.
// The final case is a for range declaration, the rest are not.
+ unsigned QuestionColonDepth = 0;
while (true) {
- unsigned QuestionColonDepth = 0;
P.SkipUntil({tok::r_paren, tok::semi, tok::question, tok::colon},
StopBeforeMatch);
if (P.Tok.is(tok::question))
@@ -440,7 +497,8 @@ bool Parser::isEnumBase(bool AllowSemi) {
// FIXME: We could disallow non-type decl-specifiers here, but it makes no
// difference: those specifiers are ill-formed regardless of the
// interpretation.
- TPResult R = isCXXDeclarationSpecifier(/*BracedCastResult*/ TPResult::True,
+ TPResult R = isCXXDeclarationSpecifier(ImplicitTypenameContext::No,
+ /*BracedCastResult=*/TPResult::True,
&InvalidAsDeclSpec);
if (R == TPResult::Ambiguous) {
// We either have a decl-specifier followed by '(' or an undeclared
@@ -454,7 +512,8 @@ bool Parser::isEnumBase(bool AllowSemi) {
return true;
// A second decl-specifier unambiguously indicatges an enum-base.
- R = isCXXDeclarationSpecifier(TPResult::True, &InvalidAsDeclSpec);
+ R = isCXXDeclarationSpecifier(ImplicitTypenameContext::No, TPResult::True,
+ &InvalidAsDeclSpec);
}
return R != TPResult::False;
@@ -483,20 +542,27 @@ Parser::isCXXConditionDeclarationOrInitStatement(bool CanBeInitStatement,
ConditionDeclarationOrInitStatementState State(*this, CanBeInitStatement,
CanBeForRangeDecl);
- if (State.update(isCXXDeclarationSpecifier()))
+ if (CanBeInitStatement && Tok.is(tok::kw_using))
+ return ConditionOrInitStatement::InitStmtDecl;
+ if (State.update(isCXXDeclarationSpecifier(ImplicitTypenameContext::No)))
return State.result();
// It might be a declaration; we need tentative parsing.
RevertingTentativeParsingAction PA(*this);
// FIXME: A tag definition unambiguously tells us this is an init-statement.
+ bool MayHaveTrailingReturnType = Tok.is(tok::kw_auto);
if (State.update(TryConsumeDeclarationSpecifier()))
return State.result();
assert(Tok.is(tok::l_paren) && "Expected '('");
while (true) {
// Consume a declarator.
- if (State.update(TryParseDeclarator(false/*mayBeAbstract*/)))
+ if (State.update(TryParseDeclarator(
+ /*mayBeAbstract=*/false,
+ /*mayHaveIdentifier=*/true,
+ /*mayHaveDirectInit=*/false,
+ /*mayHaveTrailingReturnType=*/MayHaveTrailingReturnType)))
return State.result();
// Attributes, asm label, or an initializer imply this is not an expression.
@@ -569,7 +635,7 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) {
// type. The resolution is that any construct that could possibly be a type-id
// in its syntactic context shall be considered a type-id.
- TPResult TPR = isCXXDeclarationSpecifier();
+ TPResult TPR = isCXXDeclarationSpecifier(ImplicitTypenameContext::No);
if (TPR != TPResult::Ambiguous)
return TPR != TPResult::False; // Returns true for TPResult::True or
// TPResult::Error.
@@ -581,13 +647,16 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) {
// We need tentative parsing...
RevertingTentativeParsingAction PA(*this);
+ bool MayHaveTrailingReturnType = Tok.is(tok::kw_auto);
// type-specifier-seq
TryConsumeDeclarationSpecifier();
assert(Tok.is(tok::l_paren) && "Expected '('");
// declarator
- TPR = TryParseDeclarator(true/*mayBeAbstract*/, false/*mayHaveIdentifier*/);
+ TPR = TryParseDeclarator(true /*mayBeAbstract*/, false /*mayHaveIdentifier*/,
+ /*mayHaveDirectInit=*/false,
+ MayHaveTrailingReturnType);
// In case of an error, let the declaration parsing code handle it.
if (TPR == TPResult::Error)
@@ -599,7 +668,12 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) {
if (Context == TypeIdInParens && Tok.is(tok::r_paren)) {
TPR = TPResult::True;
isAmbiguous = true;
-
+ // We are supposed to be inside the first operand to a _Generic selection
+ // expression, so if we find a comma after the declarator, we've found a
+ // type and not an expression.
+ } else if (Context == TypeIdAsGenericSelectionArgument && Tok.is(tok::comma)) {
+ TPR = TPResult::True;
+ isAmbiguous = true;
// We are supposed to be inside a template argument, so if after
// the abstract declarator we encounter a '>', '>>' (in C++0x), or
// ','; or, in C++0x, an ellipsis immediately preceding such, this
@@ -616,6 +690,9 @@ bool Parser::isCXXTypeId(TentativeCXXTypeIdContext Context, bool &isAmbiguous) {
TPR = TPResult::True;
isAmbiguous = true;
+ } else if (Context == TypeIdInTrailingReturnType) {
+ TPR = TPResult::True;
+ isAmbiguous = true;
} else
TPR = TPResult::False;
}
@@ -663,6 +740,9 @@ Parser::isCXX11AttributeSpecifier(bool Disambiguate,
if (Tok.is(tok::kw_alignas))
return CAK_AttributeSpecifier;
+ if (Tok.isRegularKeywordAttribute())
+ return CAK_AttributeSpecifier;
+
if (Tok.isNot(tok::l_square) || NextToken().isNot(tok::l_square))
return CAK_NotAttributeSpecifier;
@@ -802,7 +882,8 @@ Parser::isCXX11AttributeSpecifier(bool Disambiguate,
bool Parser::TrySkipAttributes() {
while (Tok.isOneOf(tok::l_square, tok::kw___attribute, tok::kw___declspec,
- tok::kw_alignas)) {
+ tok::kw_alignas) ||
+ Tok.isRegularKeywordAttribute()) {
if (Tok.is(tok::l_square)) {
ConsumeBracket();
if (Tok.isNot(tok::l_square))
@@ -813,6 +894,9 @@ bool Parser::TrySkipAttributes() {
// Note that explicitly checking for `[[` and `]]` allows to fail as
// expected in the case of the Objective-C message send syntax.
ConsumeBracket();
+ } else if (Tok.isRegularKeywordAttribute() &&
+ !doesKeywordAttributeTakeArgs(Tok.getKind())) {
+ ConsumeToken();
} else {
ConsumeToken();
if (Tok.isNot(tok::l_paren))
@@ -930,7 +1014,7 @@ Parser::TPResult Parser::TryParseOperatorId() {
// Maybe this is a conversion-function-id.
bool AnyDeclSpecifiers = false;
while (true) {
- TPResult TPR = isCXXDeclarationSpecifier();
+ TPResult TPR = isCXXDeclarationSpecifier(ImplicitTypenameContext::No);
if (TPR == TPResult::Error)
return TPR;
if (TPR == TPResult::False) {
@@ -1000,7 +1084,8 @@ Parser::TPResult Parser::TryParseOperatorId() {
///
Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
bool mayHaveIdentifier,
- bool mayHaveDirectInit) {
+ bool mayHaveDirectInit,
+ bool mayHaveTrailingReturnType) {
// declarator:
// direct-declarator
// ptr-operator declarator
@@ -1035,13 +1120,14 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
} else if (Tok.is(tok::l_paren)) {
ConsumeParen();
if (mayBeAbstract &&
- (Tok.is(tok::r_paren) || // 'int()' is a function.
- // 'int(...)' is a function.
+ (Tok.is(tok::r_paren) || // 'int()' is a function.
+ // 'int(...)' is a function.
(Tok.is(tok::ellipsis) && NextToken().is(tok::r_paren)) ||
- isDeclarationSpecifier())) { // 'int(int)' is a function.
+ isDeclarationSpecifier(
+ ImplicitTypenameContext::No))) { // 'int(int)' is a function.
// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
// exception-specification[opt]
- TPResult TPR = TryParseFunctionDeclarator();
+ TPResult TPR = TryParseFunctionDeclarator(mayHaveTrailingReturnType);
if (TPR != TPResult::Ambiguous)
return TPR;
} else {
@@ -1066,7 +1152,7 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
if (mayHaveDirectInit)
return TPResult::Ambiguous;
- while (1) {
+ while (true) {
TPResult TPR(TPResult::Ambiguous);
if (Tok.is(tok::l_paren)) {
@@ -1080,7 +1166,7 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
// direct-declarator '(' parameter-declaration-clause ')'
// cv-qualifier-seq[opt] exception-specification[opt]
ConsumeParen();
- TPR = TryParseFunctionDeclarator();
+ TPR = TryParseFunctionDeclarator(mayHaveTrailingReturnType);
} else if (Tok.is(tok::l_square)) {
// direct-declarator '[' constant-expression[opt] ']'
// direct-abstract-declarator[opt] '[' constant-expression[opt] ']'
@@ -1101,9 +1187,7 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract,
}
bool Parser::isTentativelyDeclared(IdentifierInfo *II) {
- return std::find(TentativelyDeclaredIdentifiers.begin(),
- TentativelyDeclaredIdentifiers.end(), II)
- != TentativelyDeclaredIdentifiers.end();
+ return llvm::is_contained(TentativelyDeclaredIdentifiers, II);
}
namespace {
@@ -1245,19 +1329,37 @@ public:
/// [GNU] restrict
///
Parser::TPResult
-Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
+Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
+ Parser::TPResult BracedCastResult,
bool *InvalidAsDeclSpec) {
- auto IsPlaceholderSpecifier = [&] (TemplateIdAnnotation *TemplateId,
- int Lookahead) {
+ auto IsPlaceholderSpecifier = [&](TemplateIdAnnotation *TemplateId,
+ int Lookahead) {
// We have a placeholder-constraint (we check for 'auto' or 'decltype' to
// distinguish 'C<int>;' from 'C<int> auto c = 1;')
return TemplateId->Kind == TNK_Concept_template &&
- GetLookAheadToken(Lookahead + 1).isOneOf(tok::kw_auto, tok::kw_decltype,
- // If we have an identifier here, the user probably forgot the
- // 'auto' in the placeholder constraint, e.g. 'C<int> x = 2;'
- // This will be diagnosed nicely later, so disambiguate as a
- // declaration.
- tok::identifier);
+ (GetLookAheadToken(Lookahead + 1)
+ .isOneOf(tok::kw_auto, tok::kw_decltype,
+ // If we have an identifier here, the user probably
+ // forgot the 'auto' in the placeholder constraint,
+ // e.g. 'C<int> x = 2;' This will be diagnosed nicely
+ // later, so disambiguate as a declaration.
+ tok::identifier,
+ // CVR qualifierslikely the same situation for the
+ // user, so let this be diagnosed nicely later. We
+ // cannot handle references here, as `C<int> & Other`
+ // and `C<int> && Other` are both legal.
+ tok::kw_const, tok::kw_volatile, tok::kw_restrict) ||
+ // While `C<int> && Other` is legal, doing so while not specifying a
+ // template argument is NOT, so see if we can fix up in that case at
+ // minimum. Concepts require at least 1 template parameter, so we
+ // can count on the argument count.
+ // FIXME: In the future, we migth be able to have SEMA look up the
+ // declaration for this concept, and see how many template
+ // parameters it has. If the concept isn't fully specified, it is
+ // possibly a situation where we want deduction, such as:
+ // `BinaryConcept<int> auto f = bar();`
+ (TemplateId->NumArgs == 0 &&
+ GetLookAheadToken(Lookahead + 1).isOneOf(tok::amp, tok::ampamp)));
};
switch (Tok.getKind()) {
case tok::identifier: {
@@ -1288,7 +1390,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
// template template argument, we'll undo this when checking the
// validity of the argument.
if (getLangOpts().CPlusPlus17) {
- if (TryAnnotateTypeOrScopeToken())
+ if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename))
return TPResult::Error;
if (Tok.isNot(tok::identifier))
break;
@@ -1309,7 +1411,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
// a missing 'typename' keyword. Don't use TryAnnotateName in this case,
// since it will annotate as a primary expression, and we want to use the
// "missing 'typename'" logic.
- if (TryAnnotateTypeOrScopeToken())
+ if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename))
return TPResult::Error;
// If annotation failed, assume it's a non-type.
// FIXME: If this happens due to an undeclared identifier, treat it as
@@ -1319,30 +1421,43 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
}
// We annotated this token as something. Recurse to handle whatever we got.
- return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec);
+ return isCXXDeclarationSpecifier(AllowImplicitTypename, BracedCastResult,
+ InvalidAsDeclSpec);
}
case tok::kw_typename: // typename T::type
// Annotate typenames and C++ scope specifiers. If we get one, just
// recurse to handle whatever we get.
- if (TryAnnotateTypeOrScopeToken())
+ if (TryAnnotateTypeOrScopeToken(ImplicitTypenameContext::Yes))
return TPResult::Error;
- return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec);
+ return isCXXDeclarationSpecifier(ImplicitTypenameContext::Yes,
+ BracedCastResult, InvalidAsDeclSpec);
+
+ case tok::kw_auto: {
+ if (!getLangOpts().CPlusPlus23)
+ return TPResult::True;
+ if (NextToken().is(tok::l_brace))
+ return TPResult::False;
+ if (NextToken().is(tok::l_paren))
+ return TPResult::Ambiguous;
+ return TPResult::True;
+ }
case tok::coloncolon: { // ::foo::bar
const Token &Next = NextToken();
if (Next.isOneOf(tok::kw_new, // ::new
tok::kw_delete)) // ::delete
return TPResult::False;
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
}
case tok::kw___super:
case tok::kw_decltype:
// Annotate typenames and C++ scope specifiers. If we get one, just
// recurse to handle whatever we get.
- if (TryAnnotateTypeOrScopeToken())
+ if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename))
return TPResult::Error;
- return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec);
+ return isCXXDeclarationSpecifier(AllowImplicitTypename, BracedCastResult,
+ InvalidAsDeclSpec);
// decl-specifier:
// storage-class-specifier
@@ -1361,7 +1476,6 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
case tok::kw_static:
case tok::kw_extern:
case tok::kw_mutable:
- case tok::kw_auto:
case tok::kw___thread:
case tok::kw_thread_local:
case tok::kw__Thread_local:
@@ -1401,7 +1515,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
case tok::kw_private:
if (!getLangOpts().OpenCL)
return TPResult::False;
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case tok::kw___private:
case tok::kw___local:
case tok::kw___global:
@@ -1414,6 +1528,12 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
// OpenCL pipe
case tok::kw_pipe:
+ // HLSL address space qualifiers
+ case tok::kw_groupshared:
+ case tok::kw_in:
+ case tok::kw_inout:
+ case tok::kw_out:
+
// GNU
case tok::kw_restrict:
case tok::kw__Complex:
@@ -1443,6 +1563,10 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
case tok::kw___kindof:
return TPResult::True;
+ // WebAssemblyFuncref
+ case tok::kw___funcref:
+ return TPResult::True;
+
// Borland
case tok::kw___pascal:
return TPResult::True;
@@ -1451,6 +1575,17 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
case tok::kw___vector:
return TPResult::True;
+ case tok::kw_this: {
+ // Try to parse a C++23 Explicit Object Parameter
+ // We do that in all language modes to produce a better diagnostic.
+ if (getLangOpts().CPlusPlus) {
+ RevertingTentativeParsingAction PA(*this);
+ ConsumeToken();
+ return isCXXDeclarationSpecifier(AllowImplicitTypename, BracedCastResult,
+ InvalidAsDeclSpec);
+ }
+ return TPResult::False;
+ }
case tok::annot_template_id: {
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
// If lookup for the template-name found nothing, don't assume we have a
@@ -1472,14 +1607,14 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
if (TemplateId->Kind != TNK_Type_template)
return TPResult::False;
CXXScopeSpec SS;
- AnnotateTemplateIdTokenAsType(SS);
+ AnnotateTemplateIdTokenAsType(SS, AllowImplicitTypename);
assert(Tok.is(tok::annot_typename));
goto case_typename;
}
case tok::annot_cxxscope: // foo::bar or ::foo::bar, but already parsed
// We've already annotated a scope; try to annotate a type.
- if (TryAnnotateTypeOrScopeToken())
+ if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename))
return TPResult::Error;
if (!Tok.is(tok::annot_typename)) {
if (Tok.is(tok::annot_cxxscope) &&
@@ -1510,8 +1645,8 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
bool isIdentifier = Tok.is(tok::identifier);
TPResult TPR = TPResult::False;
if (!isIdentifier)
- TPR = isCXXDeclarationSpecifier(BracedCastResult,
- InvalidAsDeclSpec);
+ TPR = isCXXDeclarationSpecifier(
+ AllowImplicitTypename, BracedCastResult, InvalidAsDeclSpec);
if (isIdentifier ||
TPR == TPResult::True || TPR == TPResult::Error)
@@ -1537,7 +1672,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
} else {
// Try to resolve the name. If it doesn't exist, assume it was
// intended to name a type and keep disambiguating.
- switch (TryAnnotateName()) {
+ switch (TryAnnotateName(/*CCC=*/nullptr, AllowImplicitTypename)) {
case ANK_Error:
return TPResult::Error;
case ANK_TentativeDecl:
@@ -1548,7 +1683,10 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
if (getLangOpts().CPlusPlus17) {
if (TryAnnotateTypeOrScopeToken())
return TPResult::Error;
- if (Tok.isNot(tok::identifier))
+ // If we annotated then the current token should not still be ::
+ // FIXME we may want to also check for tok::annot_typename but
+ // currently don't have a test case.
+ if (Tok.isNot(tok::annot_cxxscope))
break;
}
@@ -1567,13 +1705,14 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
// Annotated it, check again.
assert(Tok.isNot(tok::annot_cxxscope) ||
NextToken().isNot(tok::identifier));
- return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec);
+ return isCXXDeclarationSpecifier(AllowImplicitTypename,
+ BracedCastResult, InvalidAsDeclSpec);
}
}
return TPResult::False;
}
// If that succeeded, fallthrough into the generic simple-type-id case.
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
// The ambiguity resides in a simple-type-specifier/typename-specifier
// followed by a '('. The '(' could either be the start of:
@@ -1616,7 +1755,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
return TPResult::True;
}
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case tok::kw_char:
case tok::kw_wchar_t:
@@ -1637,8 +1776,12 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
case tok::kw___bf16:
case tok::kw__Float16:
case tok::kw___float128:
+ case tok::kw___ibm128:
case tok::kw_void:
case tok::annot_decltype:
+ case tok::kw__Accum:
+ case tok::kw__Fract:
+ case tok::kw__Sat:
#define GENERIC_IMAGE_TYPE(ImgType, Id) case tok::kw_##ImgType##_t:
#include "clang/Basic/OpenCLImageTypes.def"
if (NextToken().is(tok::l_paren))
@@ -1681,14 +1824,15 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
return TPResult::True;
}
- // C++0x type traits support
- case tok::kw___underlying_type:
+#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait:
+#include "clang/Basic/TransformTypeTraits.def"
return TPResult::True;
// C11 _Atomic
case tok::kw__Atomic:
return TPResult::True;
+ case tok::kw__BitInt:
case tok::kw__ExtInt: {
if (NextToken().isNot(tok::l_paren))
return TPResult::Error;
@@ -1719,7 +1863,8 @@ bool Parser::isCXXDeclarationSpecifierAType() {
case tok::annot_template_id:
case tok::annot_typename:
case tok::kw_typeof:
- case tok::kw___underlying_type:
+#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait:
+#include "clang/Basic/TransformTypeTraits.def"
return true;
// elaborated-type-specifier
@@ -1740,6 +1885,7 @@ bool Parser::isCXXDeclarationSpecifierAType() {
case tok::kw_short:
case tok::kw_int:
case tok::kw__ExtInt:
+ case tok::kw__BitInt:
case tok::kw_long:
case tok::kw___int64:
case tok::kw___int128:
@@ -1751,9 +1897,13 @@ bool Parser::isCXXDeclarationSpecifierAType() {
case tok::kw___bf16:
case tok::kw__Float16:
case tok::kw___float128:
+ case tok::kw___ibm128:
case tok::kw_void:
case tok::kw___unknown_anytype:
case tok::kw___auto_type:
+ case tok::kw__Accum:
+ case tok::kw__Fract:
+ case tok::kw__Sat:
#define GENERIC_IMAGE_TYPE(ImgType, Id) case tok::kw_##ImgType##_t:
#include "clang/Basic/OpenCLImageTypes.def"
return true;
@@ -1821,7 +1971,8 @@ Parser::TPResult Parser::TryParseProtocolQualifiers() {
/// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt]
/// exception-specification[opt]
///
-bool Parser::isCXXFunctionDeclarator(bool *IsAmbiguous) {
+bool Parser::isCXXFunctionDeclarator(
+ bool *IsAmbiguous, ImplicitTypenameContext AllowImplicitTypename) {
// C++ 8.2p1:
// The ambiguity arising from the similarity between a function-style cast and
@@ -1836,7 +1987,9 @@ bool Parser::isCXXFunctionDeclarator(bool *IsAmbiguous) {
ConsumeParen();
bool InvalidAsDeclaration = false;
- TPResult TPR = TryParseParameterDeclarationClause(&InvalidAsDeclaration);
+ TPResult TPR = TryParseParameterDeclarationClause(
+ &InvalidAsDeclaration, /*VersusTemplateArgument=*/false,
+ AllowImplicitTypename);
if (TPR == TPResult::Ambiguous) {
if (Tok.isNot(tok::r_paren))
TPR = TPResult::False;
@@ -1880,9 +2033,9 @@ bool Parser::isCXXFunctionDeclarator(bool *IsAmbiguous) {
/// attribute-specifier-seq[opt] decl-specifier-seq abstract-declarator[opt]
/// attributes[opt] '=' assignment-expression
///
-Parser::TPResult
-Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration,
- bool VersusTemplateArgument) {
+Parser::TPResult Parser::TryParseParameterDeclarationClause(
+ bool *InvalidAsDeclaration, bool VersusTemplateArgument,
+ ImplicitTypenameContext AllowImplicitTypename) {
if (Tok.is(tok::r_paren))
return TPResult::Ambiguous;
@@ -1894,7 +2047,7 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration,
// parameter-declaration
// parameter-declaration-list ',' parameter-declaration
//
- while (1) {
+ while (true) {
// '...'[opt]
if (Tok.is(tok::ellipsis)) {
ConsumeToken();
@@ -1915,8 +2068,8 @@ 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 = isCXXDeclarationSpecifier(TPResult::False,
- InvalidAsDeclaration);
+ TPResult TPR = isCXXDeclarationSpecifier(
+ AllowImplicitTypename, TPResult::False, InvalidAsDeclaration);
// A declaration-specifier (not followed by '(' or '{') means this can't be
// an expression, but it could still be a template argument.
if (TPR != TPResult::Ambiguous &&
@@ -1924,6 +2077,7 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration,
return TPR;
bool SeenType = false;
+ bool DeclarationSpecifierIsAuto = Tok.is(tok::kw_auto);
do {
SeenType |= isCXXDeclarationSpecifierAType();
if (TryConsumeDeclarationSpecifier() == TPResult::Error)
@@ -1933,7 +2087,7 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration,
if (SeenType && Tok.is(tok::identifier))
return TPResult::True;
- TPR = isCXXDeclarationSpecifier(TPResult::False,
+ TPR = isCXXDeclarationSpecifier(AllowImplicitTypename, TPResult::False,
InvalidAsDeclaration);
if (TPR == TPResult::Error)
return TPR;
@@ -1945,7 +2099,11 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration,
// declarator
// abstract-declarator[opt]
- TPR = TryParseDeclarator(true/*mayBeAbstract*/);
+ TPR = TryParseDeclarator(
+ /*mayBeAbstract=*/true,
+ /*mayHaveIdentifier=*/true,
+ /*mayHaveDirectInit=*/false,
+ /*mayHaveTrailingReturnType=*/DeclarationSpecifierIsAuto);
if (TPR != TPResult::Ambiguous)
return TPR;
@@ -1999,7 +2157,8 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration,
/// exception-specification:
/// 'throw' '(' type-id-list[opt] ')'
///
-Parser::TPResult Parser::TryParseFunctionDeclarator() {
+Parser::TPResult
+Parser::TryParseFunctionDeclarator(bool MayHaveTrailingReturnType) {
// The '(' is already parsed.
TPResult TPR = TryParseParameterDeclarationClause();
@@ -2044,9 +2203,52 @@ Parser::TPResult Parser::TryParseFunctionDeclarator() {
}
}
+ // attribute-specifier-seq
+ if (!TrySkipAttributes())
+ return TPResult::Ambiguous;
+
+ // trailing-return-type
+ if (Tok.is(tok::arrow) && MayHaveTrailingReturnType) {
+ if (TPR == TPResult::True)
+ return TPR;
+ ConsumeToken();
+ if (Tok.is(tok::identifier) && NameAfterArrowIsNonType()) {
+ return TPResult::False;
+ }
+ if (isCXXTypeId(TentativeCXXTypeIdContext::TypeIdInTrailingReturnType))
+ return TPResult::True;
+ }
+
return TPResult::Ambiguous;
}
+// When parsing an identifier after an arrow it may be a member expression,
+// in which case we should not annotate it as an independant expression
+// so we just lookup that name, if it's not a type the construct is not
+// a function declaration.
+bool Parser::NameAfterArrowIsNonType() {
+ assert(Tok.is(tok::identifier));
+ Token Next = NextToken();
+ if (Next.is(tok::coloncolon))
+ return false;
+ IdentifierInfo *Name = Tok.getIdentifierInfo();
+ SourceLocation NameLoc = Tok.getLocation();
+ CXXScopeSpec SS;
+ TentativeParseCCC CCC(Next);
+ Sema::NameClassification Classification =
+ Actions.ClassifyName(getCurScope(), SS, Name, NameLoc, Next, &CCC);
+ switch (Classification.getKind()) {
+ case Sema::NC_OverloadSet:
+ case Sema::NC_NonType:
+ case Sema::NC_VarTemplate:
+ case Sema::NC_FunctionTemplate:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
/// '[' constant-expression[opt] ']'
///
Parser::TPResult Parser::TryParseBracketDeclarator() {
@@ -2095,9 +2297,9 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) {
// but one good distinguishing factor is that a "decl-specifier" not
// followed by '(' or '{' can't appear in an expression.
bool InvalidAsTemplateArgumentList = false;
- if (isCXXDeclarationSpecifier(TPResult::False,
- &InvalidAsTemplateArgumentList) ==
- TPResult::True)
+ if (isCXXDeclarationSpecifier(ImplicitTypenameContext::No, TPResult::False,
+ &InvalidAsTemplateArgumentList) ==
+ TPResult::True)
return TPResult::True;
if (InvalidAsTemplateArgumentList)
return TPResult::False;