diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2020-07-26 19:36:28 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2020-07-26 19:36:28 +0000 |
commit | cfca06d7963fa0909f90483b42a6d7d194d01e08 (patch) | |
tree | 209fb2a2d68f8f277793fc8df46c753d31bc853b /clang/lib/Parse | |
parent | 706b4fc47bbc608932d3b491ae19a3b9cde9497b (diff) |
Notes
Diffstat (limited to 'clang/lib/Parse')
-rw-r--r-- | clang/lib/Parse/ParseCXXInlineMethods.cpp | 280 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDecl.cpp | 702 | ||||
-rw-r--r-- | clang/lib/Parse/ParseDeclCXX.cpp | 169 | ||||
-rw-r--r-- | clang/lib/Parse/ParseExpr.cpp | 412 | ||||
-rw-r--r-- | clang/lib/Parse/ParseExprCXX.cpp | 713 | ||||
-rw-r--r-- | clang/lib/Parse/ParseInit.cpp | 25 | ||||
-rw-r--r-- | clang/lib/Parse/ParseObjc.cpp | 66 | ||||
-rw-r--r-- | clang/lib/Parse/ParseOpenMP.cpp | 1503 | ||||
-rw-r--r-- | clang/lib/Parse/ParsePragma.cpp | 330 | ||||
-rw-r--r-- | clang/lib/Parse/ParseStmt.cpp | 53 | ||||
-rw-r--r-- | clang/lib/Parse/ParseStmtAsm.cpp | 166 | ||||
-rw-r--r-- | clang/lib/Parse/ParseTemplate.cpp | 429 | ||||
-rw-r--r-- | clang/lib/Parse/ParseTentative.cpp | 360 | ||||
-rw-r--r-- | clang/lib/Parse/Parser.cpp | 137 |
14 files changed, 3584 insertions, 1761 deletions
diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp index f8b5fec43800..d05332b5ac5a 100644 --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -133,7 +133,6 @@ NamedDecl *Parser::ParseCXXInlineMethodDef( LexedMethod* LM = new LexedMethod(this, FnD); getCurrentClass().LateParsedDeclarations.push_back(LM); - LM->TemplateScope = getCurScope()->isTemplateParamScope(); CachedTokens &Toks = LM->Toks; tok::TokenKind kind = Tok.getKind(); @@ -223,6 +222,7 @@ Parser::LateParsedDeclaration::~LateParsedDeclaration() {} void Parser::LateParsedDeclaration::ParseLexedMethodDeclarations() {} void Parser::LateParsedDeclaration::ParseLexedMemberInitializers() {} void Parser::LateParsedDeclaration::ParseLexedMethodDefs() {} +void Parser::LateParsedDeclaration::ParseLexedAttributes() {} void Parser::LateParsedDeclaration::ParseLexedPragmas() {} Parser::LateParsedClass::LateParsedClass(Parser *P, ParsingClass *C) @@ -244,6 +244,10 @@ void Parser::LateParsedClass::ParseLexedMethodDefs() { Self->ParseLexedMethodDefs(*Class); } +void Parser::LateParsedClass::ParseLexedAttributes() { + Self->ParseLexedAttributes(*Class); +} + void Parser::LateParsedClass::ParseLexedPragmas() { Self->ParseLexedPragmas(*Class); } @@ -260,57 +264,79 @@ void Parser::LateParsedMemberInitializer::ParseLexedMemberInitializers() { Self->ParseLexedMemberInitializer(*this); } +void Parser::LateParsedAttribute::ParseLexedAttributes() { + Self->ParseLexedAttribute(*this, true, false); +} + void Parser::LateParsedPragma::ParseLexedPragmas() { Self->ParseLexedPragma(*this); } +/// Utility to re-enter a possibly-templated scope while parsing its +/// late-parsed components. +struct Parser::ReenterTemplateScopeRAII { + Parser &P; + MultiParseScope Scopes; + TemplateParameterDepthRAII CurTemplateDepthTracker; + + ReenterTemplateScopeRAII(Parser &P, Decl *MaybeTemplated, bool Enter = true) + : P(P), Scopes(P), CurTemplateDepthTracker(P.TemplateParameterDepth) { + if (Enter) { + CurTemplateDepthTracker.addDepth( + P.ReenterTemplateScopes(Scopes, MaybeTemplated)); + } + } +}; + +/// Utility to re-enter a class scope while parsing its late-parsed components. +struct Parser::ReenterClassScopeRAII : ReenterTemplateScopeRAII { + ParsingClass &Class; + + ReenterClassScopeRAII(Parser &P, ParsingClass &Class) + : ReenterTemplateScopeRAII(P, Class.TagOrTemplate, + /*Enter=*/!Class.TopLevelClass), + Class(Class) { + // If this is the top-level class, we're still within its scope. + if (Class.TopLevelClass) + return; + + // Re-enter the class scope itself. + Scopes.Enter(Scope::ClassScope|Scope::DeclScope); + P.Actions.ActOnStartDelayedMemberDeclarations(P.getCurScope(), + Class.TagOrTemplate); + } + ~ReenterClassScopeRAII() { + if (Class.TopLevelClass) + return; + + P.Actions.ActOnFinishDelayedMemberDeclarations(P.getCurScope(), + Class.TagOrTemplate); + } +}; + /// ParseLexedMethodDeclarations - We finished parsing the member /// specification of a top (non-nested) C++ class. Now go over the /// stack of method declarations with some parts for which parsing was /// delayed (such as default arguments) and parse them. void Parser::ParseLexedMethodDeclarations(ParsingClass &Class) { - bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope; - ParseScope ClassTemplateScope(this, Scope::TemplateParamScope, - HasTemplateScope); - TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); - if (HasTemplateScope) { - Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate); - ++CurTemplateDepthTracker; - } - - // The current scope is still active if we're the top-level class. - // Otherwise we'll need to push and enter a new scope. - bool HasClassScope = !Class.TopLevelClass; - ParseScope ClassScope(this, Scope::ClassScope|Scope::DeclScope, - HasClassScope); - if (HasClassScope) - Actions.ActOnStartDelayedMemberDeclarations(getCurScope(), - Class.TagOrTemplate); - - for (size_t i = 0; i < Class.LateParsedDeclarations.size(); ++i) { - Class.LateParsedDeclarations[i]->ParseLexedMethodDeclarations(); - } + ReenterClassScopeRAII InClassScope(*this, Class); - if (HasClassScope) - Actions.ActOnFinishDelayedMemberDeclarations(getCurScope(), - Class.TagOrTemplate); + for (LateParsedDeclaration *LateD : Class.LateParsedDeclarations) + LateD->ParseLexedMethodDeclarations(); } void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) { // If this is a member template, introduce the template parameter scope. - ParseScope TemplateScope(this, Scope::TemplateParamScope, LM.TemplateScope); - TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); - if (LM.TemplateScope) { - Actions.ActOnReenterTemplateScope(getCurScope(), LM.Method); - ++CurTemplateDepthTracker; - } + ReenterTemplateScopeRAII InFunctionTemplateScope(*this, LM.Method); + // Start the delayed C++ method declaration Actions.ActOnStartDelayedCXXMethodDeclaration(getCurScope(), LM.Method); // Introduce the parameters into scope and parse their default // arguments. - ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope | - Scope::FunctionDeclarationScope | Scope::DeclScope); + InFunctionTemplateScope.Scopes.Enter(Scope::FunctionPrototypeScope | + Scope::FunctionDeclarationScope | + Scope::DeclScope); for (unsigned I = 0, N = LM.DefaultArgs.size(); I != N; ++I) { auto Param = cast<ParmVarDecl>(LM.DefaultArgs[I].Param); // Introduce the parameter into scope. @@ -464,7 +490,7 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) { LM.ExceptionSpecTokens = nullptr; } - PrototypeScope.Exit(); + InFunctionTemplateScope.Scopes.Exit(); // Finish the delayed C++ method declaration. Actions.ActOnFinishDelayedCXXMethodDeclaration(getCurScope(), LM.Method); @@ -474,30 +500,15 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) { /// (non-nested) C++ class. Now go over the stack of lexed methods that were /// collected during its parsing and parse them all. void Parser::ParseLexedMethodDefs(ParsingClass &Class) { - bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope; - ParseScope ClassTemplateScope(this, Scope::TemplateParamScope, HasTemplateScope); - TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); - if (HasTemplateScope) { - Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate); - ++CurTemplateDepthTracker; - } - bool HasClassScope = !Class.TopLevelClass; - ParseScope ClassScope(this, Scope::ClassScope|Scope::DeclScope, - HasClassScope); + ReenterClassScopeRAII InClassScope(*this, Class); - for (size_t i = 0; i < Class.LateParsedDeclarations.size(); ++i) { - Class.LateParsedDeclarations[i]->ParseLexedMethodDefs(); - } + for (LateParsedDeclaration *D : Class.LateParsedDeclarations) + D->ParseLexedMethodDefs(); } void Parser::ParseLexedMethodDef(LexedMethod &LM) { // If this is a member template, introduce the template parameter scope. - ParseScope TemplateScope(this, Scope::TemplateParamScope, LM.TemplateScope); - TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); - if (LM.TemplateScope) { - Actions.ActOnReenterTemplateScope(getCurScope(), LM.D); - ++CurTemplateDepthTracker; - } + ReenterTemplateScopeRAII InFunctionTemplateScope(*this, LM.D); ParenBraceBracketBalancer BalancerRAIIObj(*this); @@ -578,23 +589,7 @@ void Parser::ParseLexedMethodDef(LexedMethod &LM) { /// of a top (non-nested) C++ class. Now go over the stack of lexed data member /// initializers that were collected during its parsing and parse them all. void Parser::ParseLexedMemberInitializers(ParsingClass &Class) { - bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope; - ParseScope ClassTemplateScope(this, Scope::TemplateParamScope, - HasTemplateScope); - TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); - if (HasTemplateScope) { - Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate); - ++CurTemplateDepthTracker; - } - // Set or update the scope flags. - bool AlreadyHasClassScope = Class.TopLevelClass; - unsigned ScopeFlags = Scope::ClassScope|Scope::DeclScope; - ParseScope ClassScope(this, ScopeFlags, !AlreadyHasClassScope); - ParseScopeFlags ClassScopeFlags(this, ScopeFlags, AlreadyHasClassScope); - - if (!AlreadyHasClassScope) - Actions.ActOnStartDelayedMemberDeclarations(getCurScope(), - Class.TagOrTemplate); + ReenterClassScopeRAII InClassScope(*this, Class); if (!Class.LateParsedDeclarations.empty()) { // C++11 [expr.prim.general]p4: @@ -602,18 +597,14 @@ void Parser::ParseLexedMemberInitializers(ParsingClass &Class) { // (9.2) of a class X, the expression this is a prvalue of type "pointer // to X" within the optional brace-or-equal-initializer. It shall not // appear elsewhere in the member-declarator. + // FIXME: This should be done in ParseLexedMemberInitializer, not here. Sema::CXXThisScopeRAII ThisScope(Actions, Class.TagOrTemplate, Qualifiers()); - for (size_t i = 0; i < Class.LateParsedDeclarations.size(); ++i) { - Class.LateParsedDeclarations[i]->ParseLexedMemberInitializers(); - } + for (LateParsedDeclaration *D : Class.LateParsedDeclarations) + D->ParseLexedMemberInitializers(); } - if (!AlreadyHasClassScope) - Actions.ActOnFinishDelayedMemberDeclarations(getCurScope(), - Class.TagOrTemplate); - Actions.ActOnFinishDelayedMemberInitializers(Class.TagOrTemplate); } @@ -660,21 +651,115 @@ void Parser::ParseLexedMemberInitializer(LateParsedMemberInitializer &MI) { ConsumeAnyToken(); } -void Parser::ParseLexedPragmas(ParsingClass &Class) { - bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope; - ParseScope ClassTemplateScope(this, Scope::TemplateParamScope, - HasTemplateScope); - TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); - if (HasTemplateScope) { - Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate); - ++CurTemplateDepthTracker; +/// Wrapper class which calls ParseLexedAttribute, after setting up the +/// scope appropriately. +void Parser::ParseLexedAttributes(ParsingClass &Class) { + ReenterClassScopeRAII InClassScope(*this, Class); + + for (LateParsedDeclaration *LateD : Class.LateParsedDeclarations) + LateD->ParseLexedAttributes(); +} + +/// Parse all attributes in LAs, and attach them to Decl D. +void Parser::ParseLexedAttributeList(LateParsedAttrList &LAs, Decl *D, + bool EnterScope, bool OnDefinition) { + assert(LAs.parseSoon() && + "Attribute list should be marked for immediate parsing."); + for (unsigned i = 0, ni = LAs.size(); i < ni; ++i) { + if (D) + LAs[i]->addDecl(D); + ParseLexedAttribute(*LAs[i], EnterScope, OnDefinition); + delete LAs[i]; + } + LAs.clear(); +} + +/// Finish parsing an attribute for which parsing was delayed. +/// This will be called at the end of parsing a class declaration +/// for each LateParsedAttribute. We consume the saved tokens and +/// create an attribute with the arguments filled in. We add this +/// to the Attribute list for the decl. +void Parser::ParseLexedAttribute(LateParsedAttribute &LA, + bool EnterScope, bool OnDefinition) { + // Create a fake EOF so that attribute parsing won't go off the end of the + // attribute. + Token AttrEnd; + AttrEnd.startToken(); + AttrEnd.setKind(tok::eof); + AttrEnd.setLocation(Tok.getLocation()); + AttrEnd.setEofData(LA.Toks.data()); + LA.Toks.push_back(AttrEnd); + + // Append the current token at the end of the new token stream so that it + // doesn't get lost. + LA.Toks.push_back(Tok); + PP.EnterTokenStream(LA.Toks, true, /*IsReinject=*/true); + // Consume the previously pushed token. + ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true); + + ParsedAttributes Attrs(AttrFactory); + SourceLocation endLoc; + + if (LA.Decls.size() > 0) { + Decl *D = LA.Decls[0]; + NamedDecl *ND = dyn_cast<NamedDecl>(D); + RecordDecl *RD = dyn_cast_or_null<RecordDecl>(D->getDeclContext()); + + // Allow 'this' within late-parsed attributes. + Sema::CXXThisScopeRAII ThisScope(Actions, RD, Qualifiers(), + ND && ND->isCXXInstanceMember()); + + if (LA.Decls.size() == 1) { + // If the Decl is templatized, add template parameters to scope. + ReenterTemplateScopeRAII InDeclScope(*this, D, EnterScope); + + // If the Decl is on a function, add function parameters to the scope. + bool HasFunScope = EnterScope && D->isFunctionOrFunctionTemplate(); + if (HasFunScope) { + InDeclScope.Scopes.Enter(Scope::FnScope | Scope::DeclScope | + Scope::CompoundStmtScope); + Actions.ActOnReenterFunctionContext(Actions.CurScope, D); + } + + ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, &endLoc, + nullptr, SourceLocation(), ParsedAttr::AS_GNU, + nullptr); + + if (HasFunScope) + Actions.ActOnExitFunctionContext(); + } else { + // If there are multiple decls, then the decl cannot be within the + // function scope. + ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, &endLoc, + nullptr, SourceLocation(), ParsedAttr::AS_GNU, + nullptr); + } + } else { + Diag(Tok, diag::warn_attribute_no_decl) << LA.AttrName.getName(); } - bool HasClassScope = !Class.TopLevelClass; - ParseScope ClassScope(this, Scope::ClassScope | Scope::DeclScope, - HasClassScope); - for (LateParsedDeclaration *LPD : Class.LateParsedDeclarations) - LPD->ParseLexedPragmas(); + if (OnDefinition && !Attrs.empty() && !Attrs.begin()->isCXX11Attribute() && + Attrs.begin()->isKnownToGCC()) + Diag(Tok, diag::warn_attribute_on_function_definition) + << &LA.AttrName; + + for (unsigned i = 0, ni = LA.Decls.size(); i < ni; ++i) + Actions.ActOnFinishDelayedAttribute(getCurScope(), LA.Decls[i], Attrs); + + // Due to a parsing error, we either went over the cached tokens or + // there are still cached tokens left, so we skip the leftover tokens. + while (Tok.isNot(tok::eof)) + ConsumeAnyToken(); + + if (Tok.is(tok::eof) && Tok.getEofData() == AttrEnd.getEofData()) + ConsumeAnyToken(); +} + +void Parser::ParseLexedPragmas(ParsingClass &Class) { + ReenterClassScopeRAII InClassScope(*this, Class); + + for (LateParsedDeclaration *D : Class.LateParsedDeclarations) + D->ParseLexedPragmas(); } void Parser::ParseLexedPragma(LateParsedPragma &LP) { @@ -1112,17 +1197,14 @@ bool Parser::ConsumeAndStoreInitializer(CachedTokens &Toks, break; } + // Put the token stream back and undo any annotations we performed + // after the comma. They may reflect a different parse than the one + // we will actually perform at the end of the class. + PA.RevertAnnotations(); + // If what follows could be a declaration, it is a declaration. - if (Result != TPResult::False && Result != TPResult::Error) { - PA.Revert(); + if (Result != TPResult::False && Result != TPResult::Error) 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. diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 69a3ed9cbad7..c87d240a8206 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -1409,154 +1409,6 @@ void Parser::ParseObjCBridgeRelatedAttribute(IdentifierInfo &ObjCBridgeRelated, Syntax); } -// Late Parsed Attributes: -// See other examples of late parsing in lib/Parse/ParseCXXInlineMethods - -void Parser::LateParsedDeclaration::ParseLexedAttributes() {} - -void Parser::LateParsedClass::ParseLexedAttributes() { - Self->ParseLexedAttributes(*Class); -} - -void Parser::LateParsedAttribute::ParseLexedAttributes() { - Self->ParseLexedAttribute(*this, true, false); -} - -/// Wrapper class which calls ParseLexedAttribute, after setting up the -/// scope appropriately. -void Parser::ParseLexedAttributes(ParsingClass &Class) { - // Deal with templates - // FIXME: Test cases to make sure this does the right thing for templates. - bool HasTemplateScope = !Class.TopLevelClass && Class.TemplateScope; - ParseScope ClassTemplateScope(this, Scope::TemplateParamScope, - HasTemplateScope); - if (HasTemplateScope) - Actions.ActOnReenterTemplateScope(getCurScope(), Class.TagOrTemplate); - - // Set or update the scope flags. - bool AlreadyHasClassScope = Class.TopLevelClass; - unsigned ScopeFlags = Scope::ClassScope|Scope::DeclScope; - ParseScope ClassScope(this, ScopeFlags, !AlreadyHasClassScope); - ParseScopeFlags ClassScopeFlags(this, ScopeFlags, AlreadyHasClassScope); - - // Enter the scope of nested classes - if (!AlreadyHasClassScope) - Actions.ActOnStartDelayedMemberDeclarations(getCurScope(), - Class.TagOrTemplate); - if (!Class.LateParsedDeclarations.empty()) { - for (unsigned i = 0, ni = Class.LateParsedDeclarations.size(); i < ni; ++i){ - Class.LateParsedDeclarations[i]->ParseLexedAttributes(); - } - } - - if (!AlreadyHasClassScope) - Actions.ActOnFinishDelayedMemberDeclarations(getCurScope(), - Class.TagOrTemplate); -} - -/// Parse all attributes in LAs, and attach them to Decl D. -void Parser::ParseLexedAttributeList(LateParsedAttrList &LAs, Decl *D, - bool EnterScope, bool OnDefinition) { - assert(LAs.parseSoon() && - "Attribute list should be marked for immediate parsing."); - for (unsigned i = 0, ni = LAs.size(); i < ni; ++i) { - if (D) - LAs[i]->addDecl(D); - ParseLexedAttribute(*LAs[i], EnterScope, OnDefinition); - delete LAs[i]; - } - LAs.clear(); -} - -/// Finish parsing an attribute for which parsing was delayed. -/// This will be called at the end of parsing a class declaration -/// for each LateParsedAttribute. We consume the saved tokens and -/// create an attribute with the arguments filled in. We add this -/// to the Attribute list for the decl. -void Parser::ParseLexedAttribute(LateParsedAttribute &LA, - bool EnterScope, bool OnDefinition) { - // Create a fake EOF so that attribute parsing won't go off the end of the - // attribute. - Token AttrEnd; - AttrEnd.startToken(); - AttrEnd.setKind(tok::eof); - AttrEnd.setLocation(Tok.getLocation()); - AttrEnd.setEofData(LA.Toks.data()); - LA.Toks.push_back(AttrEnd); - - // Append the current token at the end of the new token stream so that it - // doesn't get lost. - LA.Toks.push_back(Tok); - PP.EnterTokenStream(LA.Toks, true, /*IsReinject=*/true); - // Consume the previously pushed token. - ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true); - - ParsedAttributes Attrs(AttrFactory); - SourceLocation endLoc; - - if (LA.Decls.size() > 0) { - Decl *D = LA.Decls[0]; - NamedDecl *ND = dyn_cast<NamedDecl>(D); - RecordDecl *RD = dyn_cast_or_null<RecordDecl>(D->getDeclContext()); - - // Allow 'this' within late-parsed attributes. - Sema::CXXThisScopeRAII ThisScope(Actions, RD, Qualifiers(), - ND && ND->isCXXInstanceMember()); - - if (LA.Decls.size() == 1) { - // If the Decl is templatized, add template parameters to scope. - bool HasTemplateScope = EnterScope && D->isTemplateDecl(); - ParseScope TempScope(this, Scope::TemplateParamScope, HasTemplateScope); - if (HasTemplateScope) - Actions.ActOnReenterTemplateScope(Actions.CurScope, D); - - // If the Decl is on a function, add function parameters to the scope. - bool HasFunScope = EnterScope && D->isFunctionOrFunctionTemplate(); - ParseScope FnScope( - this, Scope::FnScope | Scope::DeclScope | Scope::CompoundStmtScope, - HasFunScope); - if (HasFunScope) - Actions.ActOnReenterFunctionContext(Actions.CurScope, D); - - ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, &endLoc, - nullptr, SourceLocation(), ParsedAttr::AS_GNU, - nullptr); - - if (HasFunScope) { - Actions.ActOnExitFunctionContext(); - FnScope.Exit(); // Pop scope, and remove Decls from IdResolver - } - if (HasTemplateScope) { - TempScope.Exit(); - } - } else { - // If there are multiple decls, then the decl cannot be within the - // function scope. - ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, &endLoc, - nullptr, SourceLocation(), ParsedAttr::AS_GNU, - nullptr); - } - } else { - Diag(Tok, diag::warn_attribute_no_decl) << LA.AttrName.getName(); - } - - if (OnDefinition && !Attrs.empty() && !Attrs.begin()->isCXX11Attribute() && - Attrs.begin()->isKnownToGCC()) - Diag(Tok, diag::warn_attribute_on_function_definition) - << &LA.AttrName; - - for (unsigned i = 0, ni = LA.Decls.size(); i < ni; ++i) - Actions.ActOnFinishDelayedAttribute(getCurScope(), LA.Decls[i], Attrs); - - // Due to a parsing error, we either went over the cached tokens or - // there are still cached tokens left, so we skip the leftover tokens. - while (Tok.isNot(tok::eof)) - ConsumeAnyToken(); - - if (Tok.is(tok::eof) && Tok.getEofData() == AttrEnd.getEofData()) - ConsumeAnyToken(); -} - void Parser::ParseTypeTagForDatatypeAttribute(IdentifierInfo &AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, @@ -2046,46 +1898,52 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, } // Check to see if we have a function *definition* which must have a body. - if (D.isFunctionDeclarator() && - // Look at the next token to make sure that this isn't a function - // declaration. We have to check this because __attribute__ might be the - // start of a function definition in GCC-extended K&R C. - !isDeclarationAfterDeclarator()) { - - // Function definitions are only allowed at file scope and in C++ classes. - // The C++ inline method definition case is handled elsewhere, so we only - // need to handle the file scope definition case. - if (Context == DeclaratorContext::FileContext) { - if (isStartOfFunctionDefinition(D)) { - if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) { - Diag(Tok, diag::err_function_declared_typedef); - - // Recover by treating the 'typedef' as spurious. - DS.ClearStorageClassSpecs(); - } + if (D.isFunctionDeclarator()) { + if (Tok.is(tok::equal) && NextToken().is(tok::code_completion)) { + Actions.CodeCompleteAfterFunctionEquals(D); + cutOffParsing(); + return nullptr; + } + // Look at the next token to make sure that this isn't a function + // declaration. We have to check this because __attribute__ might be the + // start of a function definition in GCC-extended K&R C. + if (!isDeclarationAfterDeclarator()) { + + // Function definitions are only allowed at file scope and in C++ classes. + // The C++ inline method definition case is handled elsewhere, so we only + // need to handle the file scope definition case. + if (Context == DeclaratorContext::FileContext) { + if (isStartOfFunctionDefinition(D)) { + if (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) { + Diag(Tok, diag::err_function_declared_typedef); + + // Recover by treating the 'typedef' as spurious. + DS.ClearStorageClassSpecs(); + } - Decl *TheDecl = - ParseFunctionDefinition(D, ParsedTemplateInfo(), &LateParsedAttrs); - return Actions.ConvertDeclToDeclGroup(TheDecl); - } + Decl *TheDecl = ParseFunctionDefinition(D, ParsedTemplateInfo(), + &LateParsedAttrs); + return Actions.ConvertDeclToDeclGroup(TheDecl); + } - if (isDeclarationSpecifier()) { - // If there is an invalid declaration specifier right after the - // function prototype, then we must be in a missing semicolon case - // where this isn't actually a body. Just fall through into the code - // that handles it as a prototype, and let the top-level code handle - // the erroneous declspec where it would otherwise expect a comma or - // semicolon. + if (isDeclarationSpecifier()) { + // If there is an invalid declaration specifier right after the + // function prototype, then we must be in a missing semicolon case + // where this isn't actually a body. Just fall through into the code + // that handles it as a prototype, and let the top-level code handle + // the erroneous declspec where it would otherwise expect a comma or + // semicolon. + } else { + Diag(Tok, diag::err_expected_fn_body); + SkipUntil(tok::semi); + return nullptr; + } } else { - Diag(Tok, diag::err_expected_fn_body); - SkipUntil(tok::semi); - return nullptr; - } - } else { - if (Tok.is(tok::l_brace)) { - Diag(Tok, diag::err_function_definition_not_allowed); - SkipMalformedDecl(); - return nullptr; + if (Tok.is(tok::l_brace)) { + Diag(Tok, diag::err_function_definition_not_allowed); + SkipMalformedDecl(); + return nullptr; + } } } } @@ -2359,7 +2217,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( << 0 /* default */; else Diag(ConsumeToken(), diag::err_default_special_members) - << getLangOpts().CPlusPlus2a; + << getLangOpts().CPlusPlus20; } else { InitializerScopeRAII InitScope(*this, D, ThisDecl); @@ -2460,6 +2318,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( InitializerScopeRAII InitScope(*this, D, ThisDecl); + PreferredType.enterVariableInit(Tok.getLocation(), ThisDecl); ExprResult Init(ParseBraceInitializer()); InitScope.pop(); @@ -2741,7 +2600,7 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, default: // This is probably supposed to be a type. This includes cases like: // int f(itn); - // struct S { unsinged : 4; }; + // struct S { unsigned : 4; }; break; } } @@ -2879,6 +2738,25 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs, ParsedAttr::AS_Keyword, EllipsisLoc); } +ExprResult Parser::ParseExtIntegerArgument() { + assert(Tok.is(tok::kw__ExtInt) && "Not an extended int type"); + ConsumeToken(); + + BalancedDelimiterTracker T(*this, tok::l_paren); + if (T.expectAndConsume()) + return ExprError(); + + ExprResult ER = ParseConstantExpression(); + if (ER.isInvalid()) { + T.skipToEnd(); + return ExprError(); + } + + if(T.consumeClose()) + return ExprError(); + return ER; +} + /// Determine whether we're looking at something that might be a declarator /// in a simple-declaration. If it can't possibly be a declarator, maybe /// diagnose a missing semicolon after a prior tag definition in the decl @@ -2962,6 +2840,7 @@ Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS, case Sema::NC_ContextIndependentExpr: case Sema::NC_VarTemplate: case Sema::NC_FunctionTemplate: + case Sema::NC_Concept: // Might be a redeclaration of a prior entity. break; } @@ -3160,9 +3039,19 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // We are looking for a qualified typename. Token Next = NextToken(); - if (Next.is(tok::annot_template_id) && - static_cast<TemplateIdAnnotation *>(Next.getAnnotationValue()) - ->Kind == TNK_Type_template) { + + TemplateIdAnnotation *TemplateId = Next.is(tok::annot_template_id) + ? takeTemplateIdAnnotation(Next) + : nullptr; + if (TemplateId && TemplateId->hasInvalidName()) { + // We found something like 'T::U<Args> x', but U is not a template. + // Assume it was supposed to be a type. + DS.SetTypeSpecError(); + ConsumeAnnotationToken(); + break; + } + + if (TemplateId && TemplateId->Kind == TNK_Type_template) { // We have a qualified template-id, e.g., N::A<int> // If this would be a valid constructor declaration with template @@ -3172,12 +3061,11 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // // To improve diagnostics for this case, parse the declaration as a // constructor (and reject the extra template arguments later). - TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Next); if ((DSContext == DeclSpecContext::DSC_top_level || DSContext == DeclSpecContext::DSC_class) && TemplateId->Name && Actions.isCurrentClassName(*TemplateId->Name, getCurScope(), &SS) && - isConstructorDeclarator(/*Unqualified*/ false)) { + isConstructorDeclarator(/*Unqualified=*/false)) { // The user meant this to be an out-of-line constructor // definition, but template arguments are not allowed // there. Just allow this as a constructor; we'll @@ -3189,23 +3077,29 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, ConsumeAnnotationToken(); // The C++ scope. assert(Tok.is(tok::annot_template_id) && "ParseOptionalCXXScopeSpecifier not working"); - AnnotateTemplateIdTokenAsType(); + AnnotateTemplateIdTokenAsType(SS); + continue; + } + + if (TemplateId && TemplateId->Kind == TNK_Concept_template && + GetLookAheadToken(2).isOneOf(tok::kw_auto, tok::kw_decltype)) { + DS.getTypeSpecScope() = SS; + // This is a qualified placeholder-specifier, e.g., ::C<int> auto ... + // Consume the scope annotation and continue to consume the template-id + // as a placeholder-specifier. + ConsumeAnnotationToken(); continue; } if (Next.is(tok::annot_typename)) { DS.getTypeSpecScope() = SS; ConsumeAnnotationToken(); // The C++ scope. - if (Tok.getAnnotationValue()) { - ParsedType T = getTypeAnnotation(Tok); - isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, - Tok.getAnnotationEndLoc(), - PrevSpec, DiagID, T, Policy); - if (isInvalid) - break; - } - else - DS.SetTypeSpecError(); + TypeResult T = getTypeAnnotation(Tok); + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, + Tok.getAnnotationEndLoc(), + PrevSpec, DiagID, T, Policy); + if (isInvalid) + break; DS.SetRangeEnd(Tok.getAnnotationEndLoc()); ConsumeAnnotationToken(); // The typename } @@ -3235,6 +3129,11 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // C++ doesn't have implicit int. Diagnose it as a typo w.r.t. to the // typename. if (!TypeRep) { + if (TryAnnotateTypeConstraint()) + goto DoneWithDeclSpec; + if (Tok.isNot(tok::annot_cxxscope) || + NextToken().isNot(tok::identifier)) + continue; // Eat the scope spec so the identifier is current. ConsumeAnnotationToken(); ParsedAttributesWithRange Attrs(AttrFactory); @@ -3268,13 +3167,9 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, if (DS.hasTypeSpecifier() && DS.hasTagDefinition()) goto DoneWithDeclSpec; - if (Tok.getAnnotationValue()) { - ParsedType T = getTypeAnnotation(Tok); - isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, - DiagID, T, Policy); - } else - DS.SetTypeSpecError(); - + TypeResult T = getTypeAnnotation(Tok); + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, + DiagID, T, Policy); if (isInvalid) break; @@ -3384,6 +3279,10 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // If this is not a typedef name, don't parse it as part of the declspec, // it must be an implicit int or an error. if (!TypeRep) { + if (TryAnnotateTypeConstraint()) + goto DoneWithDeclSpec; + if (Tok.isNot(tok::identifier)) + continue; ParsedAttributesWithRange Attrs(AttrFactory); if (ParseImplicitInt(DS, nullptr, TemplateInfo, AS, DSContext, Attrs)) { if (!Attrs.empty()) { @@ -3433,9 +3332,62 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, continue; } - // type-name + // type-name or placeholder-specifier case tok::annot_template_id: { TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); + + if (TemplateId->hasInvalidName()) { + DS.SetTypeSpecError(); + break; + } + + if (TemplateId->Kind == TNK_Concept_template) { + // If we've already diagnosed that this type-constraint has invalid + // arguemnts, drop it and just form 'auto' or 'decltype(auto)'. + if (TemplateId->hasInvalidArgs()) + TemplateId = nullptr; + + if (NextToken().is(tok::identifier)) { + Diag(Loc, diag::err_placeholder_expected_auto_or_decltype_auto) + << FixItHint::CreateInsertion(NextToken().getLocation(), "auto"); + // Attempt to continue as if 'auto' was placed here. + isInvalid = DS.SetTypeSpecType(TST_auto, Loc, PrevSpec, DiagID, + TemplateId, Policy); + break; + } + if (!NextToken().isOneOf(tok::kw_auto, tok::kw_decltype)) + goto DoneWithDeclSpec; + ConsumeAnnotationToken(); + SourceLocation AutoLoc = Tok.getLocation(); + if (TryConsumeToken(tok::kw_decltype)) { + BalancedDelimiterTracker Tracker(*this, tok::l_paren); + if (Tracker.consumeOpen()) { + // Something like `void foo(Iterator decltype i)` + Diag(Tok, diag::err_expected) << tok::l_paren; + } else { + if (!TryConsumeToken(tok::kw_auto)) { + // Something like `void foo(Iterator decltype(int) i)` + Tracker.skipToEnd(); + Diag(Tok, diag::err_placeholder_expected_auto_or_decltype_auto) + << FixItHint::CreateReplacement(SourceRange(AutoLoc, + Tok.getLocation()), + "auto"); + } else { + Tracker.consumeClose(); + } + } + ConsumedEnd = Tok.getLocation(); + // Even if something went wrong above, continue as if we've seen + // `decltype(auto)`. + isInvalid = DS.SetTypeSpecType(TST_decltype_auto, Loc, PrevSpec, + DiagID, TemplateId, Policy); + } else { + isInvalid = DS.SetTypeSpecType(TST_auto, Loc, PrevSpec, DiagID, + TemplateId, Policy); + } + break; + } + if (TemplateId->Kind != TNK_Type_template && TemplateId->Kind != TNK_Undeclared_template) { // This template-id does not refer to a type name, so we're @@ -3448,12 +3400,13 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // constructor declaration. if (getLangOpts().CPlusPlus && DSContext == DeclSpecContext::DSC_class && Actions.isCurrentClassName(*TemplateId->Name, getCurScope()) && - isConstructorDeclarator(TemplateId->SS.isEmpty())) + isConstructorDeclarator(/*Unqualified=*/true)) goto DoneWithDeclSpec; // Turn the template-id annotation token into a type annotation // token, then try again to parse it as a type-specifier. - AnnotateTemplateIdTokenAsType(); + CXXScopeSpec SS; + AnnotateTemplateIdTokenAsType(SS); continue; } @@ -3617,7 +3570,11 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, ConsumedEnd = ExplicitLoc; ConsumeToken(); // kw_explicit if (Tok.is(tok::l_paren)) { - if (getLangOpts().CPlusPlus2a) { + if (getLangOpts().CPlusPlus20 || isExplicitBool() == TPResult::True) { + Diag(Tok.getLocation(), getLangOpts().CPlusPlus20 + ? diag::warn_cxx17_compat_explicit_bool + : diag::ext_explicit_bool); + ExprResult ExplicitExpr(static_cast<Expr *>(nullptr)); BalancedDelimiterTracker Tracker(*this, tok::l_paren); Tracker.consumeOpen(); @@ -3630,8 +3587,9 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, Actions.ActOnExplicitBoolSpecifier(ExplicitExpr.get()); } else Tracker.skipToEnd(); - } else - Diag(Tok.getLocation(), diag::warn_cxx2a_compat_explicit_bool); + } else { + Diag(Tok.getLocation(), diag::warn_cxx20_compat_explicit_bool); + } } isInvalid = DS.setFunctionSpecExplicit(ExplicitLoc, PrevSpec, DiagID, ExplicitSpec, CloseParenLoc); @@ -3726,6 +3684,14 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, isInvalid = DS.SetTypeSpecType(DeclSpec::TST_int, Loc, PrevSpec, DiagID, Policy); break; + case tok::kw__ExtInt: { + ExprResult ER = ParseExtIntegerArgument(); + if (ER.isInvalid()) + continue; + isInvalid = DS.SetExtIntType(Loc, ER.get(), PrevSpec, DiagID, Policy); + ConsumedEnd = PrevTokLocation; + break; + } case tok::kw___int128: isInvalid = DS.SetTypeSpecType(DeclSpec::TST_int128, Loc, PrevSpec, DiagID, Policy); @@ -3734,6 +3700,10 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, isInvalid = DS.SetTypeSpecType(DeclSpec::TST_half, Loc, PrevSpec, DiagID, Policy); break; + case tok::kw___bf16: + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_BFloat16, Loc, PrevSpec, + DiagID, Policy); + break; case tok::kw_float: isInvalid = DS.SetTypeSpecType(DeclSpec::TST_float, Loc, PrevSpec, DiagID, Policy); @@ -4137,7 +4107,7 @@ void Parser::ParseStructDeclaration( /// [OBC] '@' 'defs' '(' class-name ')' /// void Parser::ParseStructUnionBody(SourceLocation RecordLoc, - DeclSpec::TST TagType, Decl *TagDecl) { + DeclSpec::TST TagType, RecordDecl *TagDecl) { PrettyDeclStackTraceEntry CrashInfo(Actions.Context, TagDecl, RecordLoc, "parsing struct/union body"); assert(!getLangOpts().CPlusPlus && "C++ declarations not supported"); @@ -4149,8 +4119,6 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, ParseScope StructScope(this, Scope::ClassScope|Scope::DeclScope); Actions.ActOnTagStartDefinition(getCurScope(), TagDecl); - SmallVector<Decl *, 32> FieldDecls; - // While we still have something to read, read the declarations in the struct. while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) { @@ -4202,7 +4170,6 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, Actions.ActOnField(getCurScope(), TagDecl, FD.D.getDeclSpec().getSourceRange().getBegin(), FD.D, FD.BitfieldSize); - FieldDecls.push_back(Field); FD.complete(Field); }; @@ -4226,7 +4193,6 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, SmallVector<Decl *, 16> Fields; Actions.ActOnDefs(getCurScope(), TagDecl, Tok.getLocation(), Tok.getIdentifierInfo(), Fields); - FieldDecls.insert(FieldDecls.end(), Fields.begin(), Fields.end()); ConsumeToken(); ExpectAndConsume(tok::r_paren); } @@ -4252,6 +4218,9 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, // If attributes exist after struct contents, parse them. MaybeParseGNUAttributes(attrs); + SmallVector<Decl *, 32> FieldDecls(TagDecl->field_begin(), + TagDecl->field_end()); + Actions.ActOnFields(getCurScope(), RecordLoc, TagDecl, FieldDecls, T.getOpenLocation(), T.getCloseLocation(), attrs); StructScope.Exit(); @@ -4286,7 +4255,7 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, /// ':' type-specifier-seq /// /// [C++] elaborated-type-specifier: -/// [C++] 'enum' '::'[opt] nested-name-specifier[opt] identifier +/// [C++] 'enum' nested-name-specifier[opt] identifier /// void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, const ParsedTemplateInfo &TemplateInfo, @@ -4335,17 +4304,24 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, TemplateInfo.Kind == ParsedTemplateInfo::ExplicitSpecialization); SuppressAccessChecks diagsFromTag(*this, shouldDelayDiagsInTag); - // Enum definitions should not be parsed in a trailing-return-type. - bool AllowDeclaration = DSC != DeclSpecContext::DSC_trailing; + // Determine whether this declaration is permitted to have an enum-base. + AllowDefiningTypeSpec AllowEnumSpecifier = + isDefiningTypeSpecifierContext(DSC); + bool CanBeOpaqueEnumDeclaration = + DS.isEmpty() && isOpaqueEnumDeclarationContext(DSC); + bool CanHaveEnumBase = (getLangOpts().CPlusPlus11 || getLangOpts().ObjC || + getLangOpts().MicrosoftExt) && + (AllowEnumSpecifier == AllowDefiningTypeSpec::Yes || + CanBeOpaqueEnumDeclaration); CXXScopeSpec &SS = DS.getTypeSpecScope(); if (getLangOpts().CPlusPlus) { - // "enum foo : bar;" is not a potential typo for "enum foo::bar;" - // if a fixed underlying type is allowed. - ColonProtectionRAIIObject X(*this, AllowDeclaration); + // "enum foo : bar;" is not a potential typo for "enum foo::bar;". + ColonProtectionRAIIObject X(*this); CXXScopeSpec Spec; - if (ParseOptionalCXXScopeSpecifier(Spec, nullptr, + if (ParseOptionalCXXScopeSpecifier(Spec, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, /*EnteringContext=*/true)) return; @@ -4362,9 +4338,9 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, SS = Spec; } - // Must have either 'enum name' or 'enum {...}'. + // Must have either 'enum name' or 'enum {...}' or (rarely) 'enum : T { ... }'. if (Tok.isNot(tok::identifier) && Tok.isNot(tok::l_brace) && - !(AllowDeclaration && Tok.is(tok::colon))) { + Tok.isNot(tok::colon)) { Diag(Tok, diag::err_expected_either) << tok::identifier << tok::l_brace; // Skip the rest of this declarator, up until the comma or semicolon. @@ -4394,78 +4370,69 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, diagsFromTag.done(); TypeResult BaseType; + SourceRange BaseRange; - // Parse the fixed underlying type. - bool CanBeBitfield = getCurScope()->getFlags() & Scope::ClassScope; - if (AllowDeclaration && Tok.is(tok::colon)) { - bool PossibleBitfield = false; - if (CanBeBitfield) { - // If we're in class scope, this can either be an enum declaration with - // an underlying type, or a declaration of a bitfield member. We try to - // use a simple disambiguation scheme first to catch the common cases - // (integer literal, sizeof); if it's still ambiguous, we then consider - // anything that's a simple-type-specifier followed by '(' as an - // expression. This suffices because function types are not valid - // underlying types anyway. - EnterExpressionEvaluationContext Unevaluated( - Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); - TPResult TPR = isExpressionOrTypeSpecifierSimple(NextToken().getKind()); - // If the next token starts an expression, we know we're parsing a - // bit-field. This is the common case. - if (TPR == TPResult::True) - PossibleBitfield = true; - // If the next token starts a type-specifier-seq, it may be either a - // a fixed underlying type or the start of a function-style cast in C++; - // lookahead one more token to see if it's obvious that we have a - // fixed underlying type. - else if (TPR == TPResult::False && - GetLookAheadToken(2).getKind() == tok::semi) { - // Consume the ':'. - ConsumeToken(); - } else { - // We have the start of a type-specifier-seq, so we have to perform - // tentative parsing to determine whether we have an expression or a - // type. - TentativeParsingAction TPA(*this); - - // Consume the ':'. - ConsumeToken(); + bool CanBeBitfield = (getCurScope()->getFlags() & Scope::ClassScope) && + ScopedEnumKWLoc.isInvalid() && Name; - // If we see a type specifier followed by an open-brace, we have an - // ambiguity between an underlying type and a C++11 braced - // function-style cast. Resolve this by always treating it as an - // underlying type. - // FIXME: The standard is not entirely clear on how to disambiguate in - // this case. - if ((getLangOpts().CPlusPlus && - isCXXDeclarationSpecifier(TPResult::True) != TPResult::True) || - (!getLangOpts().CPlusPlus && !isDeclarationSpecifier(true))) { - // We'll parse this as a bitfield later. - PossibleBitfield = true; - TPA.Revert(); - } else { - // We have a type-specifier-seq. - TPA.Commit(); - } - } - } else { - // Consume the ':'. - ConsumeToken(); - } + // Parse the fixed underlying type. + if (Tok.is(tok::colon)) { + // This might be an enum-base or part of some unrelated enclosing context. + // + // 'enum E : base' is permitted in two circumstances: + // + // 1) As a defining-type-specifier, when followed by '{'. + // 2) As the sole constituent of a complete declaration -- when DS is empty + // and the next token is ';'. + // + // The restriction to defining-type-specifiers is important to allow parsing + // a ? new enum E : int{} + // _Generic(a, enum E : int{}) + // properly. + // + // One additional consideration applies: + // + // C++ [dcl.enum]p1: + // A ':' following "enum nested-name-specifier[opt] identifier" within + // the decl-specifier-seq of a member-declaration is parsed as part of + // an enum-base. + // + // Other language modes supporting enumerations with fixed underlying types + // do not have clear rules on this, so we disambiguate to determine whether + // the tokens form a bit-field width or an enum-base. + + if (CanBeBitfield && !isEnumBase(CanBeOpaqueEnumDeclaration)) { + // Outside C++11, do not interpret the tokens as an enum-base if they do + // not make sense as one. In C++11, it's an error if this happens. + if (getLangOpts().CPlusPlus11) + Diag(Tok.getLocation(), diag::err_anonymous_enum_bitfield); + } else if (CanHaveEnumBase || !ColonIsSacred) { + SourceLocation ColonLoc = ConsumeToken(); + + // Parse a type-specifier-seq as a type. We can't just ParseTypeName here, + // because under -fms-extensions, + // enum E : int *p; + // declares 'enum E : int; E *p;' not 'enum E : int*; E p;'. + DeclSpec DS(AttrFactory); + ParseSpecifierQualifierList(DS, AS, DeclSpecContext::DSC_type_specifier); + Declarator DeclaratorInfo(DS, DeclaratorContext::TypeNameContext); + BaseType = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo); - if (!PossibleBitfield) { - SourceRange Range; - BaseType = ParseTypeName(&Range); + BaseRange = SourceRange(ColonLoc, DeclaratorInfo.getSourceRange().getEnd()); if (!getLangOpts().ObjC) { if (getLangOpts().CPlusPlus11) - Diag(StartLoc, diag::warn_cxx98_compat_enum_fixed_underlying_type); + Diag(ColonLoc, diag::warn_cxx98_compat_enum_fixed_underlying_type) + << BaseRange; else if (getLangOpts().CPlusPlus) - Diag(StartLoc, diag::ext_cxx11_enum_fixed_underlying_type); + Diag(ColonLoc, diag::ext_cxx11_enum_fixed_underlying_type) + << BaseRange; else if (getLangOpts().MicrosoftExt) - Diag(StartLoc, diag::ext_ms_c_enum_fixed_underlying_type); + Diag(ColonLoc, diag::ext_ms_c_enum_fixed_underlying_type) + << BaseRange; else - Diag(StartLoc, diag::ext_clang_c_enum_fixed_underlying_type); + Diag(ColonLoc, diag::ext_clang_c_enum_fixed_underlying_type) + << BaseRange; } } } @@ -4481,14 +4448,19 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, // enum foo {..}; void bar() { enum foo x; } <- use of old foo. // Sema::TagUseKind TUK; - if (!AllowDeclaration) { + if (AllowEnumSpecifier == AllowDefiningTypeSpec::No) TUK = Sema::TUK_Reference; - } else if (Tok.is(tok::l_brace)) { + else if (Tok.is(tok::l_brace)) { if (DS.isFriendSpecified()) { Diag(Tok.getLocation(), diag::err_friend_decl_defines_type) << SourceRange(DS.getFriendSpecLoc()); ConsumeBrace(); SkipUntil(tok::r_brace, StopAtSemi); + // Discard any other definition-only pieces. + attrs.clear(); + ScopedEnumKWLoc = SourceLocation(); + IsScopedUsingClassTag = false; + BaseType = TypeResult(); TUK = Sema::TUK_Friend; } else { TUK = Sema::TUK_Definition; @@ -4497,6 +4469,9 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, (Tok.is(tok::semi) || (Tok.isAtStartOfLine() && !isValidAfterTypeSpecifier(CanBeBitfield)))) { + // An opaque-enum-declaration is required to be standalone (no preceding or + // following tokens in the declaration). Sema enforces this separately by + // diagnosing anything else in the DeclSpec. TUK = DS.isFriendSpecified() ? Sema::TUK_Friend : Sema::TUK_Declaration; if (Tok.isNot(tok::semi)) { // A semicolon was missing after this declaration. Diagnose and recover. @@ -4508,8 +4483,11 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, TUK = Sema::TUK_Reference; } - // If this is an elaborated type specifier, and we delayed - // diagnostics before, just merge them into the current pool. + bool IsElaboratedTypeSpecifier = + TUK == Sema::TUK_Reference || TUK == Sema::TUK_Friend; + + // If this is an elaborated type specifier nested in a larger declaration, + // and we delayed diagnostics before, just merge them into the current pool. if (TUK == Sema::TUK_Reference && shouldDelayDiagsInTag) { diagsFromTag.redelay(); } @@ -4536,9 +4514,6 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, TemplateInfo.TemplateParams->size()); } - if (TUK == Sema::TUK_Reference) - ProhibitAttributes(attrs); - if (!Name && TUK != Sema::TUK_Definition) { Diag(Tok, diag::err_enumerator_unnamed_no_def); @@ -4547,6 +4522,25 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, return; } + // An elaborated-type-specifier has a much more constrained grammar: + // + // 'enum' nested-name-specifier[opt] identifier + // + // If we parsed any other bits, reject them now. + // + // MSVC and (for now at least) Objective-C permit a full enum-specifier + // or opaque-enum-declaration anywhere. + if (IsElaboratedTypeSpecifier && !getLangOpts().MicrosoftExt && + !getLangOpts().ObjC) { + ProhibitAttributes(attrs); + if (BaseType.isUsable()) + Diag(BaseRange.getBegin(), diag::ext_enum_base_in_type_specifier) + << (AllowEnumSpecifier == AllowDefiningTypeSpec::Yes) << BaseRange; + else if (ScopedEnumKWLoc.isValid()) + Diag(ScopedEnumKWLoc, diag::ext_elaborated_enum_class) + << FixItHint::CreateRemoval(ScopedEnumKWLoc) << IsScopedUsingClassTag; + } + stripTypeAttributesOffDeclSpec(attrs, DS, TUK); Sema::SkipBodyInfo SkipBody; @@ -4621,7 +4615,7 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, return; } - if (Tok.is(tok::l_brace) && TUK != Sema::TUK_Reference) { + if (Tok.is(tok::l_brace) && TUK == Sema::TUK_Definition) { Decl *D = SkipBody.CheckSameAsPrevious ? SkipBody.New : TagDecl; ParseEnumBody(StartLoc, D); if (SkipBody.CheckSameAsPrevious && @@ -4808,6 +4802,8 @@ bool Parser::isKnownToBeTypeSpecifier(const Token &Tok) const { case tok::kw_char16_t: case tok::kw_char32_t: case tok::kw_int: + case tok::kw__ExtInt: + case tok::kw___bf16: case tok::kw_half: case tok::kw_float: case tok::kw_double: @@ -4887,7 +4883,9 @@ bool Parser::isTypeSpecifierQualifier() { case tok::kw_char16_t: case tok::kw_char32_t: case tok::kw_int: + case tok::kw__ExtInt: case tok::kw_half: + case tok::kw___bf16: case tok::kw_float: case tok::kw_double: case tok::kw__Accum: @@ -4991,6 +4989,8 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { // recurse to handle whatever we get. if (TryAnnotateTypeOrScopeToken()) return true; + if (TryAnnotateTypeConstraint()) + return true; if (Tok.is(tok::identifier)) return false; @@ -5051,7 +5051,9 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { case tok::kw_char32_t: case tok::kw_int: + case tok::kw__ExtInt: case tok::kw_half: + case tok::kw___bf16: case tok::kw_float: case tok::kw_double: case tok::kw__Accum: @@ -5124,8 +5126,27 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { // placeholder-type-specifier case tok::annot_template_id: { TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); - return TemplateId->Kind == TNK_Concept_template && - (NextToken().is(tok::kw_auto) || NextToken().is(tok::kw_decltype)); + if (TemplateId->hasInvalidName()) + return true; + // FIXME: What about type templates that have only been annotated as + // annot_template_id, not as annot_typename? + return isTypeConstraintAnnotation() && + (NextToken().is(tok::kw_auto) || NextToken().is(tok::kw_decltype)); + } + + case tok::annot_cxxscope: { + TemplateIdAnnotation *TemplateId = + NextToken().is(tok::annot_template_id) + ? takeTemplateIdAnnotation(NextToken()) + : nullptr; + if (TemplateId && TemplateId->hasInvalidName()) + return true; + // FIXME: What about type templates that have only been annotated as + // annot_template_id, not as annot_typename? + if (NextToken().is(tok::identifier) && TryAnnotateTypeConstraint()) + return true; + return isTypeConstraintAnnotation() && + GetLookAheadToken(2).isOneOf(tok::kw_auto, tok::kw_decltype); } case tok::kw___declspec: @@ -5173,7 +5194,8 @@ bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide) { // Parse the C++ scope specifier. CXXScopeSpec SS; - if (ParseOptionalCXXScopeSpecifier(SS, nullptr, + if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, /*EnteringContext=*/true)) { TPA.Revert(); return false; @@ -5553,7 +5575,8 @@ void Parser::ParseDeclaratorInternal(Declarator &D, D.getContext() == DeclaratorContext::FileContext || D.getContext() == DeclaratorContext::MemberContext; CXXScopeSpec SS; - ParseOptionalCXXScopeSpecifier(SS, nullptr, EnteringContext); + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, EnteringContext); if (SS.isNotEmpty()) { if (Tok.isNot(tok::star)) { @@ -5568,8 +5591,8 @@ void Parser::ParseDeclaratorInternal(Declarator &D, return; } - SourceLocation Loc = ConsumeToken(); - D.SetRangeEnd(Loc); + SourceLocation StarLoc = ConsumeToken(); + D.SetRangeEnd(StarLoc); DeclSpec DS(AttrFactory); ParseTypeQualifierListOpt(DS); D.ExtendWithDeclSpec(DS); @@ -5580,7 +5603,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D, // Sema will have to catch (syntactically invalid) pointers into global // scope. It has to catch pointers into namespace scope anyway. D.AddTypeInfo(DeclaratorChunk::getMemberPointer( - SS, DS.getTypeQualifiers(), DS.getEndLoc()), + SS, DS.getTypeQualifiers(), StarLoc, DS.getEndLoc()), std::move(DS.getAttributes()), /* Don't replace range end. */ SourceLocation()); return; @@ -5776,8 +5799,9 @@ void Parser::ParseDirectDeclarator(Declarator &D) { bool EnteringContext = D.getContext() == DeclaratorContext::FileContext || D.getContext() == DeclaratorContext::MemberContext; - ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec(), nullptr, - EnteringContext); + ParseOptionalCXXScopeSpecifier( + D.getCXXScopeSpec(), /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, EnteringContext); } if (D.getCXXScopeSpec().isValid()) { @@ -5851,10 +5875,11 @@ void Parser::ParseDirectDeclarator(Declarator &D) { bool HadScope = D.getCXXScopeSpec().isValid(); if (ParseUnqualifiedId(D.getCXXScopeSpec(), + /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, /*EnteringContext=*/true, /*AllowDestructorName=*/true, AllowConstructorName, - AllowDeductionGuide, nullptr, nullptr, - D.getName()) || + AllowDeductionGuide, nullptr, D.getName()) || // Once we're past the identifier, if the scope was bad, mark the // whole declarator bad. D.getCXXScopeSpec().isInvalid()) { @@ -6021,11 +6046,12 @@ void Parser::ParseDirectDeclarator(Declarator &D) { while (1) { if (Tok.is(tok::l_paren)) { + bool IsFunctionDeclaration = D.isFunctionDeclaratorAFunctionDeclaration(); // Enter function-declaration scope, limiting any declarators to the // function prototype scope, including parameter declarators. ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope|Scope::DeclScope| - (D.isFunctionDeclaratorAFunctionDeclaration() + (IsFunctionDeclaration ? Scope::FunctionDeclarationScope : 0)); // The paren may be part of a C++ direct initializer, eg. "int x(1);". @@ -6044,7 +6070,12 @@ void Parser::ParseDirectDeclarator(Declarator &D) { ParsedAttributes attrs(AttrFactory); BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); + if (IsFunctionDeclaration) + Actions.ActOnStartFunctionDeclarationDeclarator(D, + TemplateParameterDepth); ParseFunctionDeclarator(D, attrs, T, IsAmbiguous); + if (IsFunctionDeclaration) + Actions.ActOnFinishFunctionDeclarationDeclarator(D); PrototypeScope.Exit(); } else if (Tok.is(tok::l_square)) { ParseBracketDeclarator(D); @@ -6360,7 +6391,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D, ProhibitAttributes(FnAttrs); } else { if (Tok.isNot(tok::r_paren)) - ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo, + ParseParameterDeclarationClause(D.getContext(), FirstArgAttrs, ParamInfo, EllipsisLoc); else if (RequiresArg) Diag(Tok, diag::err_argument_required_after_attribute); @@ -6578,9 +6609,9 @@ void Parser::ParseFunctionDeclaratorIdentifierList( /// after the opening parenthesis. This function will not parse a K&R-style /// identifier list. /// -/// D is the declarator being parsed. If FirstArgAttrs is non-null, then the -/// caller parsed those arguments immediately after the open paren - they should -/// be considered to be part of the first parameter. +/// DeclContext is the context of the declarator being parsed. If FirstArgAttrs +/// is non-null, then the caller parsed those attributes immediately after the +/// open paren - they should be considered to be part of the first parameter. /// /// After returning, ParamInfo will hold the parsed parameters. EllipsisLoc will /// be the location of the ellipsis, if any was parsed. @@ -6606,7 +6637,7 @@ void Parser::ParseFunctionDeclaratorIdentifierList( /// [C++11] attribute-specifier-seq parameter-declaration /// void Parser::ParseParameterDeclarationClause( - Declarator &D, + DeclaratorContext DeclaratorCtx, ParsedAttributes &FirstArgAttrs, SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo, SourceLocation &EllipsisLoc) { @@ -6655,9 +6686,11 @@ void Parser::ParseParameterDeclarationClause( // "LambdaExprParameterContext", because we must accept either // 'declarator' or 'abstract-declarator' here. Declarator ParmDeclarator( - DS, D.getContext() == DeclaratorContext::LambdaExprContext - ? DeclaratorContext::LambdaExprParameterContext - : DeclaratorContext::PrototypeContext); + DS, DeclaratorCtx == DeclaratorContext::RequiresExprContext + ? DeclaratorContext::RequiresExprContext + : DeclaratorCtx == DeclaratorContext::LambdaExprContext + ? DeclaratorContext::LambdaExprParameterContext + : DeclaratorContext::PrototypeContext); ParseDeclarator(ParmDeclarator); // Parse GNU attributes, if present. @@ -6700,6 +6733,31 @@ void Parser::ParseParameterDeclarationClause( Actions.containsUnexpandedParameterPacks(ParmDeclarator)) DiagnoseMisplacedEllipsisInDeclarator(ConsumeToken(), ParmDeclarator); + // Now we are at the point where declarator parsing is finished. + // + // Try to catch keywords in place of the identifier in a declarator, and + // in particular the common case where: + // 1 identifier comes at the end of the declarator + // 2 if the identifier is dropped, the declarator is valid but anonymous + // (no identifier) + // 3 declarator parsing succeeds, and then we have a trailing keyword, + // which is never valid in a param list (e.g. missing a ',') + // And we can't handle this in ParseDeclarator because in general keywords + // may be allowed to follow the declarator. (And in some cases there'd be + // better recovery like inserting punctuation). ParseDeclarator is just + // treating this as an anonymous parameter, and fortunately at this point + // we've already almost done that. + // + // We care about case 1) where the declarator type should be known, and + // the identifier should be null. + if (!ParmDeclarator.isInvalidType() && !ParmDeclarator.hasName()) { + if (Tok.getIdentifierInfo() && + Tok.getIdentifierInfo()->isKeyword(getLangOpts())) { + Diag(Tok, diag::err_keyword_as_parameter) << PP.getSpelling(Tok); + // Consume the keyword. + ConsumeToken(); + } + } // Inform the actions module about the parameter declarator, so it gets // added to the current scope. Decl *Param = Actions.ActOnParamDeclarator(getCurScope(), ParmDeclarator); @@ -6711,7 +6769,7 @@ void Parser::ParseParameterDeclarationClause( SourceLocation EqualLoc = Tok.getLocation(); // Parse the default argument - if (D.getContext() == DeclaratorContext::MemberContext) { + if (DeclaratorCtx == DeclaratorContext::MemberContext) { // If we're inside a class definition, cache the tokens // corresponding to the default argument. We'll actually parse // them when we see the end of the class definition. diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 081d4d8b1209..ddcbb5615fee 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -155,7 +155,7 @@ Parser::DeclGroupPtrTy Parser::ParseNamespace(DeclaratorContext Context, // Normal namespace definition, not a nested-namespace-definition. } else if (InlineLoc.isValid()) { Diag(InlineLoc, diag::err_inline_nested_namespace_definition); - } else if (getLangOpts().CPlusPlus2a) { + } else if (getLangOpts().CPlusPlus20) { Diag(ExtraNSs[0].NamespaceLoc, diag::warn_cxx14_compat_nested_namespace_definition); if (FirstNestedInlineLoc.isValid()) @@ -290,7 +290,9 @@ Decl *Parser::ParseNamespaceAlias(SourceLocation NamespaceLoc, CXXScopeSpec SS; // Parse (optional) nested-name-specifier. - ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false, + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, + /*EnteringContext=*/false, /*MayBePseudoDestructor=*/nullptr, /*IsTypename=*/false, /*LastII=*/nullptr, @@ -530,7 +532,9 @@ Decl *Parser::ParseUsingDirective(DeclaratorContext Context, CXXScopeSpec SS; // Parse (optional) nested-name-specifier. - ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false, + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, + /*EnteringContext=*/false, /*MayBePseudoDestructor=*/nullptr, /*IsTypename=*/false, /*LastII=*/nullptr, @@ -597,7 +601,9 @@ bool Parser::ParseUsingDeclarator(DeclaratorContext Context, // Parse nested-name-specifier. IdentifierInfo *LastII = nullptr; - if (ParseOptionalCXXScopeSpecifier(D.SS, nullptr, /*EnteringContext=*/false, + if (ParseOptionalCXXScopeSpecifier(D.SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, + /*EnteringContext=*/false, /*MayBePseudoDtor=*/nullptr, /*IsTypename=*/false, /*LastII=*/&LastII, @@ -632,12 +638,12 @@ bool Parser::ParseUsingDeclarator(DeclaratorContext Context, D.Name.setConstructorName(Type, IdLoc, IdLoc); } else { if (ParseUnqualifiedId( - D.SS, /*EnteringContext=*/false, + D.SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, /*EnteringContext=*/false, /*AllowDestructorName=*/true, - /*AllowConstructorName=*/!(Tok.is(tok::identifier) && - NextToken().is(tok::equal)), - /*AllowDeductionGuide=*/false, - nullptr, nullptr, D.Name)) + /*AllowConstructorName=*/ + !(Tok.is(tok::identifier) && NextToken().is(tok::equal)), + /*AllowDeductionGuide=*/false, nullptr, D.Name)) return true; } @@ -978,10 +984,10 @@ SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) { EnterExpressionEvaluationContext Unevaluated( Actions, Sema::ExpressionEvaluationContext::Unevaluated, nullptr, Sema::ExpressionEvaluationContextRecord::EK_Decltype); - Result = - Actions.CorrectDelayedTyposInExpr(ParseExpression(), [](Expr *E) { - return E->hasPlaceholderType() ? ExprError() : E; - }); + Result = Actions.CorrectDelayedTyposInExpr( + ParseExpression(), /*InitDecl=*/nullptr, + /*RecoverUncorrectedTypos=*/false, + [](Expr *E) { return E->hasPlaceholderType() ? ExprError() : E; }); if (Result.isInvalid()) { DS.SetTypeSpecError(); if (SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch)) { @@ -1115,7 +1121,9 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc, // Parse optional nested-name-specifier CXXScopeSpec SS; - if (ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false)) + if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, + /*EnteringContext=*/false)) return true; BaseLoc = Tok.getLocation(); @@ -1139,19 +1147,14 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc, // Check whether we have a template-id that names a type. if (Tok.is(tok::annot_template_id)) { TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); - if (TemplateId->Kind == TNK_Type_template || - TemplateId->Kind == TNK_Dependent_template_name || - TemplateId->Kind == TNK_Undeclared_template) { - AnnotateTemplateIdTokenAsType(/*IsClassName*/true); + if (TemplateId->mightBeType()) { + AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/true); assert(Tok.is(tok::annot_typename) && "template-id -> type failed"); - ParsedType Type = getTypeAnnotation(Tok); + TypeResult Type = getTypeAnnotation(Tok); EndLocation = Tok.getAnnotationEndLoc(); ConsumeAnnotationToken(); - - if (Type) - return Type; - return true; + return Type; } // Fall through to produce an error below. @@ -1168,7 +1171,9 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc, if (Tok.is(tok::less)) { // It looks the user intended to write a template-id here, but the // template-name was wrong. Try to fix that. - TemplateNameKind TNK = TNK_Type_template; + // FIXME: Invoke ParseOptionalCXXScopeSpecifier in a "'template' is neither + // required nor permitted" mode, and do this there. + TemplateNameKind TNK = TNK_Non_template; TemplateTy Template; if (!Actions.DiagnoseUnknownTemplateName(*Id, IdLoc, getCurScope(), &SS, Template, TNK)) { @@ -1176,14 +1181,6 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc, << Id; } - if (!Template) { - TemplateArgList TemplateArgs; - SourceLocation LAngleLoc, RAngleLoc; - ParseTemplateIdAfterTemplateName(true, LAngleLoc, TemplateArgs, - RAngleLoc); - return true; - } - // Form the template name UnqualifiedId TemplateName; TemplateName.setIdentifier(Id, IdLoc); @@ -1192,8 +1189,9 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc, if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(), TemplateName)) return true; - if (TNK == TNK_Type_template || TNK == TNK_Dependent_template_name) - AnnotateTemplateIdTokenAsType(/*IsClassName*/true); + if (Tok.is(tok::annot_template_id) && + takeTemplateIdAnnotation(Tok)->mightBeType()) + AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/true); // If we didn't end up with a typename token, there's nothing more we // can do. @@ -1203,7 +1201,7 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc, // Retrieve the type from the annotation token, consume that token, and // return. EndLocation = Tok.getAnnotationEndLoc(); - ParsedType Type = getTypeAnnotation(Tok); + TypeResult Type = getTypeAnnotation(Tok); ConsumeAnnotationToken(); return Type; } @@ -1285,7 +1283,8 @@ bool Parser::isValidAfterTypeSpecifier(bool CouldBeBitfield) { case tok::annot_pragma_ms_pointers_to_members: return true; case tok::colon: - return CouldBeBitfield; // enum E { ... } : 2; + return CouldBeBitfield || // enum E { ... } : 2; + ColonIsSacred; // _Generic(..., enum E : 2); // Microsoft compatibility case tok::kw___cdecl: // struct foo {...} __cdecl x; case tok::kw___fastcall: // struct foo {...} __fastcall x; @@ -1547,7 +1546,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, CXXScopeSpec Spec; bool HasValidSpec = true; - if (ParseOptionalCXXScopeSpecifier(Spec, nullptr, EnteringContext)) { + if (ParseOptionalCXXScopeSpecifier(Spec, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, + EnteringContext)) { DS.SetTypeSpecError(); HasValidSpec = false; } @@ -1620,9 +1621,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, NameLoc = ConsumeAnnotationToken(); if (TemplateId->Kind == TNK_Undeclared_template) { - // Try to resolve the template name to a type template. - Actions.ActOnUndeclaredTypeTemplateName(getCurScope(), TemplateId->Template, - TemplateId->Kind, NameLoc, Name); + // Try to resolve the template name to a type template. May update Kind. + Actions.ActOnUndeclaredTypeTemplateName( + getCurScope(), TemplateId->Template, TemplateId->Kind, NameLoc, Name); if (TemplateId->Kind == TNK_Undeclared_template) { RecoverFromUndeclaredTemplateName( Name, NameLoc, @@ -1631,10 +1632,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, } } - if (TemplateId && TemplateId->Kind != TNK_Type_template && - TemplateId->Kind != TNK_Dependent_template_name) { + if (TemplateId && !TemplateId->mightBeType()) { // The template-name in the simple-template-id refers to - // something other than a class template. Give an appropriate + // something other than a type template. Give an appropriate // error message and skip to the ';'. SourceRange Range(NameLoc); if (SS.isNotEmpty()) @@ -1681,7 +1681,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, const PrintingPolicy &Policy = Actions.getASTContext().getPrintingPolicy(); Sema::TagUseKind TUK; - if (DSC == DeclSpecContext::DSC_trailing) + if (isDefiningTypeSpecifierContext(DSC) == AllowDefiningTypeSpec::No || + (getLangOpts().OpenMP && OpenMPDirectiveParsing)) TUK = Sema::TUK_Reference; else if (Tok.is(tok::l_brace) || (getLangOpts().CPlusPlus && Tok.is(tok::colon)) || @@ -1806,7 +1807,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // or explicit instantiation. ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), TemplateId->NumArgs); - if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation && + if (TemplateId->isInvalid()) { + // Can't build the declaration. + } else if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation && TUK == Sema::TUK_Declaration) { // This is an explicit instantiation of a class template. ProhibitAttributes(attrs); @@ -1826,7 +1829,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, TemplateInfo.Kind == ParsedTemplateInfo::NonTemplate)) { ProhibitAttributes(attrs); TypeResult = Actions.ActOnTagTemplateIdType(TUK, TagType, StartLoc, - TemplateId->SS, + SS, TemplateId->TemplateKWLoc, TemplateId->Template, TemplateId->TemplateNameLoc, @@ -1876,7 +1879,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // Build the class template specialization. TagOrTempResult = Actions.ActOnClassTemplateSpecialization( getCurScope(), TagType, TUK, StartLoc, DS.getModulePrivateSpecLoc(), - *TemplateId, attrs, + SS, *TemplateId, attrs, MultiTemplateParamsArg(TemplateParams ? &(*TemplateParams)[0] : nullptr, TemplateParams ? TemplateParams->size() : 0), @@ -1962,7 +1965,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, Decl *D = SkipBody.CheckSameAsPrevious ? SkipBody.New : TagOrTempResult.get(); // Parse the definition body. - ParseStructUnionBody(StartLoc, TagType, D); + ParseStructUnionBody(StartLoc, TagType, cast<RecordDecl>(D)); if (SkipBody.CheckSameAsPrevious && !Actions.ActOnDuplicateDefinition(DS, TagOrTempResult.get(), SkipBody)) { @@ -2182,7 +2185,6 @@ void Parser::HandleMemberFunctionDeclDelays(Declarator& DeclaratorInfo, // declarations. auto LateMethod = new LateParsedMethodDeclaration(this, ThisDecl); getCurrentClass().LateParsedDeclarations.push_back(LateMethod); - LateMethod->TemplateScope = getCurScope()->isTemplateParamScope(); // Stash the exception-specification tokens in the late-pased method. LateMethod->ExceptionSpecTokens = FTI.ExceptionSpecTokens; @@ -2501,7 +2503,8 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, if (isAccessDecl) { // Collect the scope specifier token we annotated earlier. CXXScopeSpec SS; - ParseOptionalCXXScopeSpecifier(SS, nullptr, + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, /*EnteringContext=*/false); if (SS.isInvalid()) { @@ -2512,8 +2515,9 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, // Try to parse an unqualified-id. SourceLocation TemplateKWLoc; UnqualifiedId Name; - if (ParseUnqualifiedId(SS, false, true, true, false, nullptr, - &TemplateKWLoc, Name)) { + if (ParseUnqualifiedId(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, false, true, true, + false, &TemplateKWLoc, Name)) { SkipUntil(tok::semi); return nullptr; } @@ -2642,6 +2646,8 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, } ParsingDeclarator DeclaratorInfo(*this, DS, DeclaratorContext::MemberContext); + if (TemplateInfo.TemplateParams) + DeclaratorInfo.setTemplateParameterLists(TemplateParams); VirtSpecifiers VS; // Hold late-parsed attributes so we can attach a Decl to them later. @@ -2656,7 +2662,7 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, auto &Zero = NextToken(); SmallString<8> Buffer; - if (Zero.isNot(tok::numeric_constant) || Zero.getLength() != 1 || + if (Zero.isNot(tok::numeric_constant) || PP.getSpelling(Zero, Buffer) != "0") return false; @@ -2707,6 +2713,11 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, DefinitionKind = FDK_Defaulted; else if (KW.is(tok::kw_delete)) DefinitionKind = FDK_Deleted; + else if (KW.is(tok::code_completion)) { + Actions.CodeCompleteAfterFunctionEquals(DeclaratorInfo); + cutOffParsing(); + return nullptr; + } } } DeclaratorInfo.setFunctionDefinitionKind(DefinitionKind); @@ -2714,7 +2725,7 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, // C++11 [dcl.attr.grammar] p4: If an attribute-specifier-seq appertains // to a friend declaration, that declaration shall be a definition. if (DeclaratorInfo.isFunctionDeclarator() && - DefinitionKind != FDK_Definition && DS.isFriendSpecified()) { + DefinitionKind == FDK_Declaration && DS.isFriendSpecified()) { // Diagnose attributes that appear before decl specifier: // [[]] friend int foo(); ProhibitAttributes(FnAttrs); @@ -2782,7 +2793,7 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, !DS.isFriendSpecified()) { // It's a default member initializer. if (BitfieldSize.get()) - Diag(Tok, getLangOpts().CPlusPlus2a + Diag(Tok, getLangOpts().CPlusPlus20 ? diag::warn_cxx17_compat_bitfield_member_init : diag::ext_bitfield_member_init); HasInClassInit = Tok.is(tok::equal) ? ICIS_CopyInit : ICIS_ListInit; @@ -2987,7 +2998,7 @@ ExprResult Parser::ParseCXXMemberInitializer(Decl *D, bool IsFunction, << 0 /* default */; else Diag(ConsumeToken(), diag::err_default_special_members) - << getLangOpts().CPlusPlus2a; + << getLangOpts().CPlusPlus20; return ExprError(); } } @@ -3334,6 +3345,7 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, // Each iteration of this loop reads one member-declaration. ParseCXXClassMemberDeclarationWithPragmas( CurAS, AccessAttrs, static_cast<DeclSpec::TST>(TagType), TagDecl); + MaybeDestroyTemplateIds(); } T.consumeClose(); } else { @@ -3359,6 +3371,16 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, // are complete and we can parse the delayed portions of method // declarations and the lexed inline method definitions, along with any // delayed attributes. + + // Save the state of Sema.FPFeatures, and change the setting + // to the levels specified on the command line. Previous level + // will be restored when the RAII object is destroyed. + Sema::FPFeaturesStateRAII SaveFPFeaturesState(Actions); + FPOptionsOverride NewOverrides; + Actions.CurFPFeatures = NewOverrides.applyOverrides(getLangOpts()); + Actions.FpPragmaStack.Act(Tok.getLocation(), Sema::PSK_Reset, StringRef(), + 0 /*unused*/); + SourceLocation SavedPrevTokLocation = PrevTokLocation; ParseLexedPragmas(getCurrentClass()); ParseLexedAttributes(getCurrentClass()); @@ -3491,7 +3513,9 @@ void Parser::ParseConstructorInitializer(Decl *ConstructorDecl) { MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) { // parse '::'[opt] nested-name-specifier[opt] CXXScopeSpec SS; - if (ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false)) + if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, + /*EnteringContext=*/false)) return true; // : identifier @@ -3500,7 +3524,7 @@ MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) { // : declype(...) DeclSpec DS(AttrFactory); // : template_name<...> - ParsedType TemplateTypeTy; + TypeResult TemplateTypeTy; if (Tok.is(tok::identifier)) { // Get the identifier. This may be a member name or a class name, @@ -3517,15 +3541,11 @@ MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) { TemplateIdAnnotation *TemplateId = Tok.is(tok::annot_template_id) ? takeTemplateIdAnnotation(Tok) : nullptr; - if (TemplateId && (TemplateId->Kind == TNK_Type_template || - TemplateId->Kind == TNK_Dependent_template_name || - TemplateId->Kind == TNK_Undeclared_template)) { - AnnotateTemplateIdTokenAsType(/*IsClassName*/true); + if (TemplateId && TemplateId->mightBeType()) { + AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/true); assert(Tok.is(tok::annot_typename) && "template-id -> type failed"); TemplateTypeTy = getTypeAnnotation(Tok); ConsumeAnnotationToken(); - if (!TemplateTypeTy) - return true; } else { Diag(Tok, diag::err_expected_member_or_base_name); return true; @@ -3544,8 +3564,10 @@ MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) { SourceLocation EllipsisLoc; TryConsumeToken(tok::ellipsis, EllipsisLoc); + if (TemplateTypeTy.isInvalid()) + return true; return Actions.ActOnMemInitializer(ConstructorDecl, getCurScope(), SS, II, - TemplateTypeTy, DS, IdLoc, + TemplateTypeTy.get(), DS, IdLoc, InitList.get(), EllipsisLoc); } else if(Tok.is(tok::l_paren)) { BalancedDelimiterTracker T(*this, tok::l_paren); @@ -3555,8 +3577,10 @@ MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) { ExprVector ArgExprs; CommaLocsTy CommaLocs; auto RunSignatureHelp = [&] { + if (TemplateTypeTy.isInvalid()) + return QualType(); QualType PreferredType = Actions.ProduceCtorInitMemberSignatureHelp( - getCurScope(), ConstructorDecl, SS, TemplateTypeTy, ArgExprs, II, + getCurScope(), ConstructorDecl, SS, TemplateTypeTy.get(), ArgExprs, II, T.getOpenLocation()); CalledSignatureHelp = true; return PreferredType; @@ -3577,12 +3601,17 @@ MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) { SourceLocation EllipsisLoc; TryConsumeToken(tok::ellipsis, EllipsisLoc); + if (TemplateTypeTy.isInvalid()) + return true; return Actions.ActOnMemInitializer(ConstructorDecl, getCurScope(), SS, II, - TemplateTypeTy, DS, IdLoc, + TemplateTypeTy.get(), DS, IdLoc, T.getOpenLocation(), ArgExprs, T.getCloseLocation(), EllipsisLoc); } + if (TemplateTypeTy.isInvalid()) + return true; + if (getLangOpts().CPlusPlus11) return Diag(Tok, diag::err_expected_either) << tok::l_paren << tok::l_brace; else @@ -3908,8 +3937,8 @@ void Parser::PopParsingClass(Sema::ParsingClassState state) { // after the top-level class is completely defined. Therefore, add // it to the list of nested classes within its parent. assert(getCurScope()->isClassScope() && "Nested class outside of class scope?"); - ClassStack.top()->LateParsedDeclarations.push_back(new LateParsedClass(this, Victim)); - Victim->TemplateScope = getCurScope()->getParent()->isTemplateParamScope(); + ClassStack.top()->LateParsedDeclarations.push_back( + new LateParsedClass(this, Victim)); } /// Try to parse an 'identifier' which appears within an attribute-token. @@ -4364,7 +4393,7 @@ void Parser::ParseMicrosoftAttributes(ParsedAttributes &attrs, BalancedDelimiterTracker T(*this, tok::l_square); T.consumeOpen(); - // Skip most ms attributes except for a whitelist. + // Skip most ms attributes except for a specific list. while (true) { SkipUntil(tok::r_square, tok::identifier, StopAtSemi | StopBeforeMatch); if (Tok.isNot(tok::identifier)) // ']', but also eof diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 1442df046bb9..81e87582c6ad 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -234,7 +234,7 @@ ExprResult Parser::ParseCaseExpression(SourceLocation CaseLoc) { /// \endverbatim ExprResult Parser::ParseConstraintExpression() { EnterExpressionEvaluationContext ConstantEvaluated( - Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); + Actions, Sema::ExpressionEvaluationContext::Unevaluated); ExprResult LHS(ParseCastExpression(AnyCastExpr)); ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::LogicalOr)); if (Res.isUsable() && !Actions.CheckConstraintExpression(Res.get())) { @@ -256,7 +256,7 @@ ExprResult Parser::ParseConstraintExpression() { ExprResult Parser::ParseConstraintLogicalAndExpression(bool IsTrailingRequiresClause) { EnterExpressionEvaluationContext ConstantEvaluated( - Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated); + Actions, Sema::ExpressionEvaluationContext::Unevaluated); bool NotPrimaryExpression = false; auto ParsePrimary = [&] () { ExprResult E = ParseCastExpression(PrimaryExprOnly, @@ -625,13 +625,31 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) { SourceRange(Actions.getExprRange(LHS.get()).getBegin(), Actions.getExprRange(RHS.get()).getEnd())); - LHS = Actions.ActOnBinOp(getCurScope(), OpToken.getLocation(), - OpToken.getKind(), LHS.get(), RHS.get()); + ExprResult BinOp = + Actions.ActOnBinOp(getCurScope(), OpToken.getLocation(), + OpToken.getKind(), LHS.get(), RHS.get()); + if (BinOp.isInvalid()) + BinOp = Actions.CreateRecoveryExpr(LHS.get()->getBeginLoc(), + RHS.get()->getEndLoc(), + {LHS.get(), RHS.get()}); + LHS = BinOp; } else { - LHS = Actions.ActOnConditionalOp(OpToken.getLocation(), ColonLoc, - LHS.get(), TernaryMiddle.get(), - RHS.get()); + ExprResult CondOp = Actions.ActOnConditionalOp( + OpToken.getLocation(), ColonLoc, LHS.get(), TernaryMiddle.get(), + RHS.get()); + if (CondOp.isInvalid()) { + std::vector<clang::Expr *> Args; + // TernaryMiddle can be null for the GNU conditional expr extension. + if (TernaryMiddle.get()) + Args = {LHS.get(), TernaryMiddle.get(), RHS.get()}; + else + Args = {LHS.get(), RHS.get()}; + CondOp = Actions.CreateRecoveryExpr(LHS.get()->getBeginLoc(), + RHS.get()->getEndLoc(), Args); + } + + LHS = CondOp; } // In this case, ActOnBinOp or ActOnConditionalOp performed the // CorrectDelayedTyposInExpr check. @@ -756,6 +774,7 @@ class CastExpressionIdValidator final : public CorrectionCandidateCallback { /// [C++11] user-defined-literal /// '(' expression ')' /// [C11] generic-selection +/// [C++2a] requires-expression /// '__func__' [C99 6.4.2.2] /// [GNU] '__FUNCTION__' /// [MS] '__FUNCDNAME__' @@ -901,6 +920,11 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, auto SavedType = PreferredType; NotCastExpr = false; + // Are postfix-expression suffix operators permitted after this + // cast-expression? If not, and we find some, we'll parse them anyway and + // diagnose them. + bool AllowSuffix = true; + // This handles all of cast-expression, unary-expression, postfix-expression, // and primary-expression. We handle them together like this for efficiency // and to simplify handling of an expression starting with a '(' token: which @@ -910,8 +934,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, // If the parsed tokens consist of a primary-expression, the cases below // break out of the switch; at the end we call ParsePostfixExpressionSuffix // to handle the postfix expression suffixes. Cases that cannot be followed - // by postfix exprs should return without invoking - // ParsePostfixExpressionSuffix. + // by postfix exprs should set AllowSuffix to false. switch (SavedKind) { case tok::l_paren: { // If this expression is limited to being a unary-expression, the paren can @@ -934,8 +957,11 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, Res = ParseParenExpression(ParenExprType, false/*stopIfCastExr*/, isTypeCast == IsTypeCast, CastTy, RParenLoc); + // FIXME: What should we do if a vector literal is followed by a + // postfix-expression suffix? Usually postfix operators are permitted on + // literals. if (isVectorLiteral) - return Res; + return Res; switch (ParenExprType) { case SimpleExpr: break; // Nothing else to do. @@ -973,14 +999,31 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, case tok::kw___objc_yes: case tok::kw___objc_no: - return ParseObjCBoolLiteral(); + Res = ParseObjCBoolLiteral(); + break; case tok::kw_nullptr: Diag(Tok, diag::warn_cxx98_compat_nullptr); - return Actions.ActOnCXXNullPtrLiteral(ConsumeToken()); + Res = Actions.ActOnCXXNullPtrLiteral(ConsumeToken()); + break; + case tok::annot_uneval_primary_expr: case tok::annot_primary_expr: Res = getExprAnnotation(Tok); + if (SavedKind == tok::annot_uneval_primary_expr) { + if (Expr *E = Res.get()) { + if (!E->isTypeDependent() && !E->containsErrors()) { + // TransformToPotentiallyEvaluated expects that it will still be in a + // (temporary) unevaluated context and then looks through that context + // to build it in the surrounding context. So we need to push an + // unevaluated context to balance things out. + EnterExpressionEvaluationContext Unevaluated( + Actions, Sema::ExpressionEvaluationContext::Unevaluated, + Sema::ReuseLambdaContextDecl); + Res = Actions.TransformToPotentiallyEvaluated(Res.get()); + } + } + } ConsumeAnnotationToken(); if (!Res.isInvalid() && Tok.is(tok::less)) checkPotentialAngleBracket(Res); @@ -1005,7 +1048,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, assert(Tok.isNot(tok::kw_decltype) && Tok.isNot(tok::kw___super)); return ParseCastExpression(ParseKind, isAddressOfOperand, isTypeCast, isVectorLiteral, NotPrimaryExpression); - + case tok::identifier: { // primary-expression: identifier // unqualified-id: identifier // constant: enumeration-constant @@ -1261,7 +1304,8 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, Res = ParseGenericSelectionExpression(); break; case tok::kw___builtin_available: - return ParseAvailabilityCheckExpr(Tok.getLocation()); + Res = ParseAvailabilityCheckExpr(Tok.getLocation()); + break; case tok::kw___builtin_va_arg: case tok::kw___builtin_offsetof: case tok::kw___builtin_choose_expr: @@ -1273,9 +1317,11 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, case tok::kw___builtin_LINE: if (NotPrimaryExpression) *NotPrimaryExpression = true; + // This parses the complete suffix; we can return early. return ParseBuiltinPrimaryExpression(); case tok::kw___null: - return Actions.ActOnGNUNullExpr(ConsumeToken()); + Res = Actions.ActOnGNUNullExpr(ConsumeToken()); + break; case tok::plusplus: // unary-expression: '++' unary-expression [C99] case tok::minusminus: { // unary-expression: '--' unary-expression [C99] @@ -1304,9 +1350,14 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, UnconsumeToken(SavedTok); return ExprError(); } - if (!Res.isInvalid()) + if (!Res.isInvalid()) { + Expr *Arg = Res.get(); Res = Actions.ActOnUnaryOp(getCurScope(), SavedTok.getLocation(), - SavedKind, Res.get()); + SavedKind, Arg); + if (Res.isInvalid()) + Res = Actions.CreateRecoveryExpr(SavedTok.getLocation(), + Arg->getEndLoc(), Arg); + } return Res; } case tok::amp: { // unary-expression: '&' cast-expression @@ -1316,8 +1367,13 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, SourceLocation SavedLoc = ConsumeToken(); PreferredType.enterUnary(Actions, Tok.getLocation(), tok::amp, SavedLoc); Res = ParseCastExpression(AnyCastExpr, true); - if (!Res.isInvalid()) - Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Res.get()); + if (!Res.isInvalid()) { + Expr *Arg = Res.get(); + Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Arg); + if (Res.isInvalid()) + Res = Actions.CreateRecoveryExpr(Tok.getLocation(), Arg->getEndLoc(), + Arg); + } return Res; } @@ -1333,8 +1389,12 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, SourceLocation SavedLoc = ConsumeToken(); PreferredType.enterUnary(Actions, Tok.getLocation(), SavedKind, SavedLoc); Res = ParseCastExpression(AnyCastExpr); - if (!Res.isInvalid()) - Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Res.get()); + if (!Res.isInvalid()) { + Expr *Arg = Res.get(); + Res = Actions.ActOnUnaryOp(getCurScope(), SavedLoc, SavedKind, Arg); + if (Res.isInvalid()) + Res = Actions.CreateRecoveryExpr(SavedLoc, Arg->getEndLoc(), Arg); + } return Res; } @@ -1373,7 +1433,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, case tok::kw___builtin_omp_required_simd_align: if (NotPrimaryExpression) *NotPrimaryExpression = true; - return ParseUnaryExprOrTypeTraitExpression(); + AllowSuffix = false; + Res = ParseUnaryExprOrTypeTraitExpression(); + break; case tok::ampamp: { // unary-expression: '&&' identifier if (NotPrimaryExpression) *NotPrimaryExpression = true; @@ -1389,12 +1451,14 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, Tok.getLocation()); Res = Actions.ActOnAddrLabel(AmpAmpLoc, Tok.getLocation(), LD); ConsumeToken(); - return Res; + AllowSuffix = false; + break; } case tok::kw_const_cast: case tok::kw_dynamic_cast: case tok::kw_reinterpret_cast: case tok::kw_static_cast: + case tok::kw_addrspace_cast: if (NotPrimaryExpression) *NotPrimaryExpression = true; Res = ParseCXXCasts(); @@ -1417,10 +1481,12 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, case tok::kw_this: Res = ParseCXXThis(); break; - + case tok::kw___builtin_unique_stable_name: + Res = ParseUniqueStableNameExpression(); + break; case tok::annot_typename: if (isStartOfObjCClassMessageMissingOpenBracket()) { - ParsedType Type = getTypeAnnotation(Tok); + TypeResult Type = getTypeAnnotation(Tok); // Fake up a Declarator to use with ActOnTypeName. DeclSpec DS(AttrFactory); @@ -1457,11 +1523,13 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, case tok::kw_long: case tok::kw___int64: case tok::kw___int128: + case tok::kw__ExtInt: case tok::kw_signed: case tok::kw_unsigned: case tok::kw_half: case tok::kw_float: case tok::kw_double: + case tok::kw___bf16: case tok::kw__Float16: case tok::kw___float128: case tok::kw_void: @@ -1528,9 +1596,10 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, // type, translate it into a type and continue parsing as a // cast expression. CXXScopeSpec SS; - ParseOptionalCXXScopeSpecifier(SS, nullptr, + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, /*EnteringContext=*/false); - AnnotateTemplateIdTokenAsType(); + AnnotateTemplateIdTokenAsType(SS); return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, isTypeCast, isVectorLiteral, NotPrimaryExpression); @@ -1548,7 +1617,8 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, // We have a template-id that we know refers to a type, // translate it into a type and continue parsing as a cast // expression. - AnnotateTemplateIdTokenAsType(); + CXXScopeSpec SS; + AnnotateTemplateIdTokenAsType(SS); return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, isTypeCast, isVectorLiteral, NotPrimaryExpression); @@ -1577,12 +1647,16 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, if (Tok.is(tok::kw_new)) { if (NotPrimaryExpression) *NotPrimaryExpression = true; - return ParseCXXNewExpression(true, CCLoc); + Res = ParseCXXNewExpression(true, CCLoc); + AllowSuffix = false; + break; } if (Tok.is(tok::kw_delete)) { if (NotPrimaryExpression) *NotPrimaryExpression = true; - return ParseCXXDeleteExpression(true, CCLoc); + Res = ParseCXXDeleteExpression(true, CCLoc); + AllowSuffix = false; + break; } // This is not a type name or scope specifier, it is an invalid expression. @@ -1593,12 +1667,21 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, case tok::kw_new: // [C++] new-expression if (NotPrimaryExpression) *NotPrimaryExpression = true; - return ParseCXXNewExpression(false, Tok.getLocation()); + Res = ParseCXXNewExpression(false, Tok.getLocation()); + AllowSuffix = false; + break; case tok::kw_delete: // [C++] delete-expression if (NotPrimaryExpression) *NotPrimaryExpression = true; - return ParseCXXDeleteExpression(false, Tok.getLocation()); + Res = ParseCXXDeleteExpression(false, Tok.getLocation()); + AllowSuffix = false; + break; + + case tok::kw_requires: // [C++2a] requires-expression + Res = ParseRequiresExpression(); + AllowSuffix = false; + break; case tok::kw_noexcept: { // [C++0x] 'noexcept' '(' expression ')' if (NotPrimaryExpression) @@ -1614,32 +1697,36 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, // which is an unevaluated operand, can throw an exception. EnterExpressionEvaluationContext Unevaluated( Actions, Sema::ExpressionEvaluationContext::Unevaluated); - ExprResult Result = ParseExpression(); + Res = ParseExpression(); T.consumeClose(); - if (!Result.isInvalid()) - Result = Actions.ActOnNoexceptExpr(KeyLoc, T.getOpenLocation(), - Result.get(), T.getCloseLocation()); - return Result; + if (!Res.isInvalid()) + Res = Actions.ActOnNoexceptExpr(KeyLoc, T.getOpenLocation(), Res.get(), + T.getCloseLocation()); + AllowSuffix = false; + break; } #define TYPE_TRAIT(N,Spelling,K) \ case tok::kw_##Spelling: #include "clang/Basic/TokenKinds.def" - return ParseTypeTrait(); + Res = ParseTypeTrait(); + break; case tok::kw___array_rank: case tok::kw___array_extent: if (NotPrimaryExpression) *NotPrimaryExpression = true; - return ParseArrayTypeTrait(); + Res = ParseArrayTypeTrait(); + break; case tok::kw___is_lvalue_expr: case tok::kw___is_rvalue_expr: if (NotPrimaryExpression) *NotPrimaryExpression = true; - return ParseExpressionTrait(); + Res = ParseExpressionTrait(); + break; case tok::at: { if (NotPrimaryExpression) @@ -1696,6 +1783,41 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, // parsed. return Res; + if (!AllowSuffix) { + // FIXME: Don't parse a primary-expression suffix if we encountered a parse + // error already. + if (Res.isInvalid()) + return Res; + + switch (Tok.getKind()) { + case tok::l_square: + case tok::l_paren: + case tok::plusplus: + case tok::minusminus: + // "expected ';'" or similar is probably the right diagnostic here. Let + // the caller decide what to do. + if (Tok.isAtStartOfLine()) + return Res; + + LLVM_FALLTHROUGH; + case tok::period: + case tok::arrow: + break; + + default: + return Res; + } + + // This was a unary-expression for which a postfix-expression suffix is + // not permitted by the grammar (eg, a sizeof expression or + // new-expression or similar). Diagnose but parse the suffix anyway. + Diag(Tok.getLocation(), diag::err_postfix_after_unary_requires_parens) + << Tok.getKind() << Res.get()->getSourceRange() + << FixItHint::CreateInsertion(Res.get()->getBeginLoc(), "(") + << FixItHint::CreateInsertion(PP.getLocForEndOfToken(PrevTokLocation), + ")"); + } + // These can be followed by postfix-expr pieces. PreferredType = SavedType; Res = ParsePostfixExpressionSuffix(Res); @@ -1787,8 +1909,8 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { BalancedDelimiterTracker T(*this, tok::l_square); T.consumeOpen(); Loc = T.getOpenLocation(); - ExprResult Idx, Length; - SourceLocation ColonLoc; + ExprResult Idx, Length, Stride; + SourceLocation ColonLocFirst, ColonLocSecond; PreferredType.enterSubscript(Actions, Tok.getLocation(), LHS.get()); if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) { Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists); @@ -1802,10 +1924,22 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { } if (Tok.is(tok::colon)) { // Consume ':' - ColonLoc = ConsumeToken(); - if (Tok.isNot(tok::r_square)) + ColonLocFirst = ConsumeToken(); + if (Tok.isNot(tok::r_square) && + (getLangOpts().OpenMP < 50 || + ((Tok.isNot(tok::colon) && getLangOpts().OpenMP >= 50)))) Length = ParseExpression(); } + if (getLangOpts().OpenMP >= 50 && + (OMPClauseKind == llvm::omp::Clause::OMPC_to || + OMPClauseKind == llvm::omp::Clause::OMPC_from) && + Tok.is(tok::colon)) { + // Consume ':' + ColonLocSecond = ConsumeToken(); + if (Tok.isNot(tok::r_square)) { + Stride = ParseExpression(); + } + } } else Idx = ParseExpression(); @@ -1815,10 +1949,11 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { Idx = Actions.CorrectDelayedTyposInExpr(Idx); Length = Actions.CorrectDelayedTyposInExpr(Length); if (!LHS.isInvalid() && !Idx.isInvalid() && !Length.isInvalid() && - Tok.is(tok::r_square)) { - if (ColonLoc.isValid()) { - LHS = Actions.ActOnOMPArraySectionExpr(LHS.get(), Loc, Idx.get(), - ColonLoc, Length.get(), RLoc); + !Stride.isInvalid() && Tok.is(tok::r_square)) { + if (ColonLocFirst.isValid() || ColonLocSecond.isValid()) { + LHS = Actions.ActOnOMPArraySectionExpr( + LHS.get(), Loc, Idx.get(), ColonLocFirst, ColonLocSecond, + Length.get(), Stride.get(), RLoc); } else { LHS = Actions.ActOnArraySubscriptExpr(getCurScope(), LHS.get(), Loc, Idx.get(), RLoc); @@ -1935,12 +2070,18 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { PT.consumeClose(); LHS = ExprError(); } else { - assert((ArgExprs.size() == 0 || - ArgExprs.size()-1 == CommaLocs.size())&& - "Unexpected number of commas!"); - LHS = Actions.ActOnCallExpr(getCurScope(), LHS.get(), Loc, - ArgExprs, Tok.getLocation(), + assert( + (ArgExprs.size() == 0 || ArgExprs.size() - 1 == CommaLocs.size()) && + "Unexpected number of commas!"); + Expr *Fn = LHS.get(); + SourceLocation RParLoc = Tok.getLocation(); + LHS = Actions.ActOnCallExpr(getCurScope(), Fn, Loc, ArgExprs, RParLoc, ExecConfig); + if (LHS.isInvalid()) { + ArgExprs.insert(ArgExprs.begin(), Fn); + LHS = + Actions.CreateRecoveryExpr(Fn->getBeginLoc(), RParLoc, ArgExprs); + } PT.consumeClose(); } @@ -1972,15 +2113,22 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { return ParsePostfixExpressionSuffix(Base); } - LHS = Actions.ActOnStartCXXMemberReference(getCurScope(), Base, - OpLoc, OpKind, ObjectType, + LHS = Actions.ActOnStartCXXMemberReference(getCurScope(), Base, OpLoc, + OpKind, ObjectType, MayBePseudoDestructor); - if (LHS.isInvalid()) + if (LHS.isInvalid()) { + // Clang will try to perform expression based completion as a + // fallback, which is confusing in case of member references. So we + // stop here without any completions. + if (Tok.is(tok::code_completion)) { + cutOffParsing(); + return ExprError(); + } break; - - ParseOptionalCXXScopeSpecifier(SS, ObjectType, - /*EnteringContext=*/false, - &MayBePseudoDestructor); + } + ParseOptionalCXXScopeSpecifier( + SS, ObjectType, LHS.get() && LHS.get()->containsErrors(), + /*EnteringContext=*/false, &MayBePseudoDestructor); if (SS.isNotEmpty()) ObjectType = nullptr; } @@ -2040,14 +2188,13 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { IdentifierInfo *Id = Tok.getIdentifierInfo(); SourceLocation Loc = ConsumeToken(); Name.setIdentifier(Id, Loc); - } else if (ParseUnqualifiedId(SS, - /*EnteringContext=*/false, - /*AllowDestructorName=*/true, - /*AllowConstructorName=*/ - getLangOpts().MicrosoftExt && - SS.isNotEmpty(), - /*AllowDeductionGuide=*/false, - ObjectType, &TemplateKWLoc, Name)) { + } else if (ParseUnqualifiedId( + SS, ObjectType, LHS.get() && LHS.get()->containsErrors(), + /*EnteringContext=*/false, + /*AllowDestructorName=*/true, + /*AllowConstructorName=*/ + getLangOpts().MicrosoftExt && SS.isNotEmpty(), + /*AllowDeductionGuide=*/false, &TemplateKWLoc, Name)) { (void)Actions.CorrectDelayedTyposInExpr(LHS); LHS = ExprError(); } @@ -2057,15 +2204,25 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { OpKind, SS, TemplateKWLoc, Name, CurParsedObjCImpl ? CurParsedObjCImpl->Dcl : nullptr); - if (!LHS.isInvalid() && Tok.is(tok::less)) - checkPotentialAngleBracket(LHS); + if (!LHS.isInvalid()) { + if (Tok.is(tok::less)) + checkPotentialAngleBracket(LHS); + } else if (OrigLHS && Name.isValid()) { + // Preserve the LHS if the RHS is an invalid member. + LHS = Actions.CreateRecoveryExpr(OrigLHS->getBeginLoc(), + Name.getEndLoc(), {OrigLHS}); + } break; } case tok::plusplus: // postfix-expression: postfix-expression '++' case tok::minusminus: // postfix-expression: postfix-expression '--' if (!LHS.isInvalid()) { + Expr *Arg = LHS.get(); LHS = Actions.ActOnPostfixUnaryOp(getCurScope(), Tok.getLocation(), - Tok.getKind(), LHS.get()); + Tok.getKind(), Arg); + if (LHS.isInvalid()) + LHS = Actions.CreateRecoveryExpr(Arg->getBeginLoc(), + Tok.getLocation(), Arg); } ConsumeToken(); break; @@ -2175,6 +2332,43 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, } +ExprResult Parser::ParseUniqueStableNameExpression() { + assert(Tok.is(tok::kw___builtin_unique_stable_name) && + "Not __bulitin_unique_stable_name"); + + SourceLocation OpLoc = ConsumeToken(); + BalancedDelimiterTracker T(*this, tok::l_paren); + + // typeid expressions are always parenthesized. + if (T.expectAndConsume(diag::err_expected_lparen_after, + "__builtin_unique_stable_name")) + return ExprError(); + + if (isTypeIdInParens()) { + TypeResult Ty = ParseTypeName(); + T.consumeClose(); + + if (Ty.isInvalid()) + return ExprError(); + + return Actions.ActOnUniqueStableNameExpr(OpLoc, T.getOpenLocation(), + T.getCloseLocation(), Ty.get()); + } + + EnterExpressionEvaluationContext Unevaluated( + Actions, Sema::ExpressionEvaluationContext::Unevaluated); + ExprResult Result = ParseExpression(); + + if (Result.isInvalid()) { + SkipUntil(tok::r_paren, StopAtSemi); + return Result; + } + + T.consumeClose(); + return Actions.ActOnUniqueStableNameExpr(OpLoc, T.getOpenLocation(), + T.getCloseLocation(), Result.get()); +} + /// Parse a sizeof or alignof expression. /// /// \verbatim @@ -2556,6 +2750,33 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() { return ParsePostfixExpressionSuffix(Res.get()); } +bool Parser::tryParseOpenMPArrayShapingCastPart() { + assert(Tok.is(tok::l_square) && "Expected open bracket"); + bool ErrorFound = true; + TentativeParsingAction TPA(*this); + do { + if (Tok.isNot(tok::l_square)) + break; + // Consume '[' + ConsumeBracket(); + // Skip inner expression. + while (!SkipUntil(tok::r_square, tok::annot_pragma_openmp_end, + StopAtSemi | StopBeforeMatch)) + ; + if (Tok.isNot(tok::r_square)) + break; + // Consume ']' + ConsumeBracket(); + // Found ')' - done. + if (Tok.is(tok::r_paren)) { + ErrorFound = false; + break; + } + } while (Tok.isNot(tok::annot_pragma_openmp_end)); + TPA.Revert(); + return !ErrorFound; +} + /// ParseParenExpression - This parses the unit that starts with a '(' token, /// based on what is allowed by ExprType. The actual thing parsed is returned /// in ExprType. If stopIfCastExpr is true, it will only return the parsed type, @@ -2580,6 +2801,8 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() { /// '(' '...' fold-operator cast-expression ')' /// '(' cast-expression fold-operator '...' /// fold-operator cast-expression ')' +/// [OPENMP] Array shaping operation +/// '(' '[' expression ']' { '[' expression ']' } cast-expression /// \endverbatim ExprResult Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, @@ -2650,7 +2873,8 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, // If the substmt parsed correctly, build the AST node. if (!Stmt.isInvalid()) { - Result = Actions.ActOnStmtExpr(OpenLoc, Stmt.get(), Tok.getLocation()); + Result = Actions.ActOnStmtExpr(getCurScope(), OpenLoc, Stmt.get(), + Tok.getLocation()); } else { Actions.ActOnStmtExprError(); } @@ -2685,7 +2909,7 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, PreferredType.enterTypeCast(Tok.getLocation(), Ty.get().get()); ExprResult SubExpr = ParseCastExpression(AnyCastExpr); - + if (Ty.isInvalid() || SubExpr.isInvalid()) return ExprError(); @@ -2855,6 +3079,38 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, Result = Actions.ActOnParenListExpr(OpenLoc, Tok.getLocation(), ArgExprs); } + } else if (getLangOpts().OpenMP >= 50 && OpenMPDirectiveParsing && + ExprType == CastExpr && Tok.is(tok::l_square) && + tryParseOpenMPArrayShapingCastPart()) { + bool ErrorFound = false; + SmallVector<Expr *, 4> OMPDimensions; + SmallVector<SourceRange, 4> OMPBracketsRanges; + do { + BalancedDelimiterTracker TS(*this, tok::l_square); + TS.consumeOpen(); + ExprResult NumElements = + Actions.CorrectDelayedTyposInExpr(ParseExpression()); + if (!NumElements.isUsable()) { + ErrorFound = true; + while (!SkipUntil(tok::r_square, tok::r_paren, + StopAtSemi | StopBeforeMatch)) + ; + } + TS.consumeClose(); + OMPDimensions.push_back(NumElements.get()); + OMPBracketsRanges.push_back(TS.getRange()); + } while (Tok.isNot(tok::r_paren)); + // Match the ')'. + T.consumeClose(); + RParenLoc = T.getCloseLocation(); + Result = Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()); + if (ErrorFound) { + Result = ExprError(); + } else if (!Result.isInvalid()) { + Result = Actions.ActOnOMPArrayShapingExpr( + Result.get(), OpenLoc, RParenLoc, OMPDimensions, OMPBracketsRanges); + } + return Result; } else { InMessageExpressionRAIIObject InMessage(*this, false); @@ -3123,6 +3379,16 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs, if (Tok.is(tok::ellipsis)) Expr = Actions.ActOnPackExpansion(Expr.get(), ConsumeToken()); + else if (Tok.is(tok::code_completion)) { + // There's nothing to suggest in here as we parsed a full expression. + // Instead fail and propogate the error since caller might have something + // the suggest, e.g. signature help in function call. Note that this is + // performed before pushing the \p Expr, so that signature help can report + // current argument correctly. + SawError = true; + cutOffParsing(); + break; + } if (Expr.isInvalid()) { SkipUntil(tok::comma, tok::r_paren, StopBeforeMatch); SawError = true; diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index e685d5ea8a9c..aa35200c33b6 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -11,7 +11,9 @@ //===----------------------------------------------------------------------===// #include "clang/Parse/Parser.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/ExprCXX.h" #include "clang/Basic/PrettyStackTrace.h" #include "clang/Lex/LiteralSupport.h" #include "clang/Parse/ParseDiagnostic.h" @@ -29,10 +31,11 @@ static int SelectDigraphErrorMessage(tok::TokenKind Kind) { // template name case tok::unknown: return 0; // casts - case tok::kw_const_cast: return 1; - case tok::kw_dynamic_cast: return 2; - case tok::kw_reinterpret_cast: return 3; - case tok::kw_static_cast: return 4; + case tok::kw_addrspace_cast: return 1; + case tok::kw_const_cast: return 2; + case tok::kw_dynamic_cast: return 3; + case tok::kw_reinterpret_cast: return 4; + case tok::kw_static_cast: return 5; default: llvm_unreachable("Unknown type for digraph error message."); } @@ -122,13 +125,17 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType, /// the "." or "->" of a member access expression, this parameter provides the /// type of the object whose members are being accessed. /// +/// \param ObjectHadErrors if this unqualified-id occurs within a member access +/// expression, indicates whether the original subexpressions had any errors. +/// When true, diagnostics for missing 'template' keyword will be supressed. +/// /// \param EnteringContext whether we will be entering into the context of /// the nested-name-specifier after parsing it. /// /// \param MayBePseudoDestructor When non-NULL, points to a flag that /// indicates whether this nested-name-specifier may be part of a /// pseudo-destructor name. In this case, the flag will be set false -/// if we don't actually end up parsing a destructor name. Moreorover, +/// if we don't actually end up parsing a destructor name. Moreover, /// if we do end up determining that we are parsing a destructor name, /// the last component of the nested-name-specifier is not parsed as /// part of the scope specifier. @@ -144,14 +151,10 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType, /// /// /// \returns true if there was an error parsing a scope specifier -bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, - ParsedType ObjectType, - bool EnteringContext, - bool *MayBePseudoDestructor, - bool IsTypename, - IdentifierInfo **LastII, - bool OnlyNamespace, - bool InUsingDeclaration) { +bool Parser::ParseOptionalCXXScopeSpecifier( + CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors, + bool EnteringContext, bool *MayBePseudoDestructor, bool IsTypename, + IdentifierInfo **LastII, bool OnlyNamespace, bool InUsingDeclaration) { assert(getLangOpts().CPlusPlus && "Call sites of this function should be guarded by checking for C++"); @@ -165,13 +168,6 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, return false; } - if (Tok.is(tok::annot_template_id)) { - // If the current token is an annotated template id, it may already have - // a scope specifier. Restore it. - TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); - SS = TemplateId->SS; - } - // Has to happen before any "return false"s in this function. bool CheckForDestructor = false; if (MayBePseudoDestructor && *MayBePseudoDestructor) { @@ -321,13 +317,11 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, // Commit to parsing the template-id. TPA.Commit(); TemplateTy Template; - if (TemplateNameKind TNK = Actions.ActOnDependentTemplateName( - getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType, - EnteringContext, Template, /*AllowInjectedClassName*/ true)) { - if (AnnotateTemplateIdToken(Template, TNK, SS, TemplateKWLoc, - TemplateName, false)) - return true; - } else + TemplateNameKind TNK = Actions.ActOnTemplateName( + getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType, + EnteringContext, Template, /*AllowInjectedClassName*/ true); + if (AnnotateTemplateIdToken(Template, TNK, SS, TemplateKWLoc, + TemplateName, false)) return true; continue; @@ -361,7 +355,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), TemplateId->NumArgs); - if (Actions.ActOnCXXNestedNameSpecifier(getCurScope(), + if (TemplateId->isInvalid() || + Actions.ActOnCXXNestedNameSpecifier(getCurScope(), SS, TemplateId->TemplateKWLoc, TemplateId->Template, @@ -423,8 +418,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, } if (Next.is(tok::coloncolon)) { - if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde) && - !Actions.isNonTypeNestedNameSpecifier(getCurScope(), SS, IdInfo)) { + if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde)) { *MayBePseudoDestructor = true; return false; } @@ -517,28 +511,29 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, if (MemberOfUnknownSpecialization && (ObjectType || SS.isSet()) && (IsTypename || isTemplateArgumentList(1) == TPResult::True)) { - // We have something like t::getAs<T>, where getAs is a - // member of an unknown specialization. However, this will only - // parse correctly as a template, so suggest the keyword 'template' - // before 'getAs' and treat this as a dependent template name. - unsigned DiagID = diag::err_missing_dependent_template_keyword; - if (getLangOpts().MicrosoftExt) - DiagID = diag::warn_missing_dependent_template_keyword; - - Diag(Tok.getLocation(), DiagID) - << II.getName() - << FixItHint::CreateInsertion(Tok.getLocation(), "template "); - - if (TemplateNameKind TNK = Actions.ActOnDependentTemplateName( - getCurScope(), SS, Tok.getLocation(), TemplateName, ObjectType, - EnteringContext, Template, /*AllowInjectedClassName*/ true)) { - // Consume the identifier. - ConsumeToken(); - if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(), - TemplateName, false)) - return true; + // If we had errors before, ObjectType can be dependent even without any + // templates. Do not report missing template keyword in that case. + if (!ObjectHadErrors) { + // We have something like t::getAs<T>, where getAs is a + // member of an unknown specialization. However, this will only + // parse correctly as a template, so suggest the keyword 'template' + // before 'getAs' and treat this as a dependent template name. + unsigned DiagID = diag::err_missing_dependent_template_keyword; + if (getLangOpts().MicrosoftExt) + DiagID = diag::warn_missing_dependent_template_keyword; + + Diag(Tok.getLocation(), DiagID) + << II.getName() + << FixItHint::CreateInsertion(Tok.getLocation(), "template "); } - else + + SourceLocation TemplateNameLoc = ConsumeToken(); + + TemplateNameKind TNK = Actions.ActOnTemplateName( + getCurScope(), SS, TemplateNameLoc, TemplateName, ObjectType, + EnteringContext, Template, /*AllowInjectedClassName*/ true); + if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(), + TemplateName, false)) return true; continue; @@ -553,7 +548,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, // Even if we didn't see any pieces of a nested-name-specifier, we // still check whether there is a tilde in this position, which // indicates a potential pseudo-destructor. - if (CheckForDestructor && Tok.is(tok::tilde)) + if (CheckForDestructor && !HasScopeSpecifier && Tok.is(tok::tilde)) *MayBePseudoDestructor = true; return false; @@ -599,12 +594,12 @@ ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS, default: SourceLocation TemplateKWLoc; UnqualifiedId Name; - if (ParseUnqualifiedId(SS, + if (ParseUnqualifiedId(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, /*EnteringContext=*/false, /*AllowDestructorName=*/false, /*AllowConstructorName=*/false, - /*AllowDeductionGuide=*/false, - /*ObjectType=*/nullptr, &TemplateKWLoc, Name)) + /*AllowDeductionGuide=*/false, &TemplateKWLoc, Name)) return ExprError(); // This is only the direct operand of an & operator if it is not @@ -672,7 +667,9 @@ ExprResult Parser::ParseCXXIdExpression(bool isAddressOfOperand) { // '::' unqualified-id // CXXScopeSpec SS; - ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false); + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, + /*EnteringContext=*/false); Token Replacement; ExprResult Result = @@ -1261,17 +1258,16 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( }; // FIXME: Consider allowing this as an extension for GCC compatibiblity. - const bool HasExplicitTemplateParams = Tok.is(tok::less); - ParseScope TemplateParamScope(this, Scope::TemplateParamScope, - /*EnteredScope=*/HasExplicitTemplateParams); - if (HasExplicitTemplateParams) { - Diag(Tok, getLangOpts().CPlusPlus2a + MultiParseScope TemplateParamScope(*this); + if (Tok.is(tok::less)) { + Diag(Tok, getLangOpts().CPlusPlus20 ? diag::warn_cxx17_compat_lambda_template_parameter_list : diag::ext_lambda_template_parameter_list); SmallVector<NamedDecl*, 4> TemplateParams; SourceLocation LAngleLoc, RAngleLoc; - if (ParseTemplateParameters(CurTemplateDepthTracker.getDepth(), + if (ParseTemplateParameters(TemplateParamScope, + CurTemplateDepthTracker.getDepth(), TemplateParams, LAngleLoc, RAngleLoc)) { Actions.ActOnLambdaError(LambdaBeginLoc, getCurScope()); return ExprError(); @@ -1306,8 +1302,8 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( Actions.RecordParsingTemplateParameterDepth( CurTemplateDepthTracker.getOriginalDepth()); - ParseParameterDeclarationClause(D, Attr, ParamInfo, EllipsisLoc); - + ParseParameterDeclarationClause(D.getContext(), Attr, ParamInfo, + EllipsisLoc); // For a generic lambda, each 'auto' within the parameter declaration // clause creates a template type parameter, so increment the depth. // If we've parsed any explicit template parameters, then the depth will @@ -1516,12 +1512,15 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( /// 'reinterpret_cast' '<' type-name '>' '(' expression ')' /// 'const_cast' '<' type-name '>' '(' expression ')' /// +/// C++ for OpenCL s2.3.1 adds: +/// 'addrspace_cast' '<' type-name '>' '(' expression ')' ExprResult Parser::ParseCXXCasts() { tok::TokenKind Kind = Tok.getKind(); const char *CastName = nullptr; // For error messages switch (Kind) { default: llvm_unreachable("Unknown C++ cast!"); + case tok::kw_addrspace_cast: CastName = "addrspace_cast"; break; case tok::kw_const_cast: CastName = "const_cast"; break; case tok::kw_dynamic_cast: CastName = "dynamic_cast"; break; case tok::kw_reinterpret_cast: CastName = "reinterpret_cast"; break; @@ -1694,31 +1693,42 @@ ExprResult Parser::ParseCXXUuidof() { /// Parse a C++ pseudo-destructor expression after the base, /// . or -> operator, and nested-name-specifier have already been -/// parsed. +/// parsed. We're handling this fragment of the grammar: /// -/// postfix-expression: [C++ 5.2] -/// postfix-expression . pseudo-destructor-name -/// postfix-expression -> pseudo-destructor-name +/// postfix-expression: [C++2a expr.post] +/// postfix-expression . template[opt] id-expression +/// postfix-expression -> template[opt] id-expression /// -/// pseudo-destructor-name: -/// ::[opt] nested-name-specifier[opt] type-name :: ~type-name -/// ::[opt] nested-name-specifier template simple-template-id :: -/// ~type-name -/// ::[opt] nested-name-specifier[opt] ~type-name +/// id-expression: +/// qualified-id +/// unqualified-id /// +/// qualified-id: +/// nested-name-specifier template[opt] unqualified-id +/// +/// nested-name-specifier: +/// type-name :: +/// decltype-specifier :: FIXME: not implemented, but probably only +/// allowed in C++ grammar by accident +/// nested-name-specifier identifier :: +/// nested-name-specifier template[opt] simple-template-id :: +/// [...] +/// +/// unqualified-id: +/// ~ type-name +/// ~ decltype-specifier +/// [...] +/// +/// ... where the all but the last component of the nested-name-specifier +/// has already been parsed, and the base expression is not of a non-dependent +/// class type. ExprResult Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc, tok::TokenKind OpKind, CXXScopeSpec &SS, ParsedType ObjectType) { - // We're parsing either a pseudo-destructor-name or a dependent - // member access that has the same form as a - // pseudo-destructor-name. We parse both in the same way and let - // the action model sort them out. - // - // Note that the ::[opt] nested-name-specifier[opt] has already - // been parsed, and if there was a simple-template-id, it has - // been coalesced into a template-id annotation token. + // If the last component of the (optional) nested-name-specifier is + // template[opt] simple-template-id, it has already been annotated. UnqualifiedId FirstTypeName; SourceLocation CCLoc; if (Tok.is(tok::identifier)) { @@ -1727,14 +1737,16 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc, assert(Tok.is(tok::coloncolon) &&"ParseOptionalCXXScopeSpecifier fail"); CCLoc = ConsumeToken(); } else if (Tok.is(tok::annot_template_id)) { - // FIXME: retrieve TemplateKWLoc from template-id annotation and - // store it in the pseudo-dtor node (to be used when instantiating it). - FirstTypeName.setTemplateId( - (TemplateIdAnnotation *)Tok.getAnnotationValue()); + TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); + // FIXME: Carry on and build an AST representation for tooling. + if (TemplateId->isInvalid()) + return ExprError(); + FirstTypeName.setTemplateId(TemplateId); ConsumeAnnotationToken(); assert(Tok.is(tok::coloncolon) &&"ParseOptionalCXXScopeSpecifier fail"); CCLoc = ConsumeToken(); } else { + assert(SS.isEmpty() && "missing last component of nested name specifier"); FirstTypeName.setIdentifier(nullptr, SourceLocation()); } @@ -1742,7 +1754,7 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc, assert(Tok.is(tok::tilde) && "ParseOptionalCXXScopeSpecifier fail"); SourceLocation TildeLoc = ConsumeToken(); - if (Tok.is(tok::kw_decltype) && !FirstTypeName.isValid() && SS.isEmpty()) { + if (Tok.is(tok::kw_decltype) && !FirstTypeName.isValid()) { DeclSpec DS(AttrFactory); ParseDecltypeSpecifier(DS); if (DS.getTypeSpecType() == TST_error) @@ -1764,11 +1776,17 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc, // If there is a '<', the second type name is a template-id. Parse // it as such. + // + // FIXME: This is not a context in which a '<' is assumed to start a template + // argument list. This affects examples such as + // void f(auto *p) { p->~X<int>(); } + // ... but there's no ambiguity, and nowhere to write 'template' in such an + // example, so we accept it anyway. if (Tok.is(tok::less) && - ParseUnqualifiedIdTemplateId(SS, SourceLocation(), - Name, NameLoc, - false, ObjectType, SecondTypeName, - /*AssumeTemplateId=*/true)) + ParseUnqualifiedIdTemplateId( + SS, ObjectType, Base && Base->containsErrors(), SourceLocation(), + Name, NameLoc, false, SecondTypeName, + /*AssumeTemplateId=*/true)) return ExprError(); return Actions.ActOnPseudoDestructorExpr(getCurScope(), Base, OpLoc, OpKind, @@ -1862,6 +1880,7 @@ Parser::ParseCXXTypeConstructExpression(const DeclSpec &DS) { && "Expected '(' or '{'!"); if (Tok.is(tok::l_brace)) { + PreferredType.enterTypeCast(Tok.getLocation(), TypeRep.get()); ExprResult Init = ParseBraceInitializer(); if (Init.isInvalid()) return Init; @@ -2131,12 +2150,8 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) { // type-name case tok::annot_typename: { - if (getTypeAnnotation(Tok)) - DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, DiagID, - getTypeAnnotation(Tok), Policy); - else - DS.SetTypeSpecError(); - + DS.SetTypeSpecType(DeclSpec::TST_typename, Loc, PrevSpec, DiagID, + getTypeAnnotation(Tok), Policy); DS.SetRangeEnd(Tok.getAnnotationEndLoc()); ConsumeAnnotationToken(); @@ -2144,6 +2159,19 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) { return; } + case tok::kw__ExtInt: { + ExprResult ER = ParseExtIntegerArgument(); + if (ER.isInvalid()) + DS.SetTypeSpecError(); + else + DS.SetExtIntType(Loc, ER.get(), PrevSpec, DiagID, Policy); + + // Do this here because we have already consumed the close paren. + DS.SetRangeEnd(PrevTokLocation); + DS.Finish(Actions, Policy); + return; + } + // builtin types case tok::kw_short: DS.SetTypeSpecWidth(DeclSpec::TSW_short, Loc, PrevSpec, DiagID, Policy); @@ -2172,6 +2200,9 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) { case tok::kw___int128: DS.SetTypeSpecType(DeclSpec::TST_int128, Loc, PrevSpec, DiagID, Policy); break; + case tok::kw___bf16: + DS.SetTypeSpecType(DeclSpec::TST_BFloat16, Loc, PrevSpec, DiagID, Policy); + break; case tok::kw_half: DS.SetTypeSpecType(DeclSpec::TST_half, Loc, PrevSpec, DiagID, Policy); break; @@ -2254,6 +2285,12 @@ bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS) { /// \param SS the nested-name-specifier that precedes this template-id, if /// we're actually parsing a qualified-id. /// +/// \param ObjectType if this unqualified-id occurs within a member access +/// expression, the type of the base object whose member is being accessed. +/// +/// \param ObjectHadErrors this unqualified-id occurs within a member access +/// expression, indicates whether the original subexpressions had any errors. +/// /// \param Name for constructor and destructor names, this is the actual /// identifier that may be a template-name. /// @@ -2263,9 +2300,6 @@ bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS) { /// \param EnteringContext whether we're entering the scope of the /// nested-name-specifier. /// -/// \param ObjectType if this unqualified-id occurs within a member access -/// expression, the type of the base object whose member is being accessed. -/// /// \param Id as input, describes the template-name or operator-function-id /// that precedes the '<'. If template arguments were parsed successfully, /// will be updated with the template-id. @@ -2274,14 +2308,10 @@ bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS) { /// refers to a template without performing name lookup to verify. /// /// \returns true if a parse error occurred, false otherwise. -bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, - SourceLocation TemplateKWLoc, - IdentifierInfo *Name, - SourceLocation NameLoc, - bool EnteringContext, - ParsedType ObjectType, - UnqualifiedId &Id, - bool AssumeTemplateId) { +bool Parser::ParseUnqualifiedIdTemplateId( + CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors, + SourceLocation TemplateKWLoc, IdentifierInfo *Name, SourceLocation NameLoc, + bool EnteringContext, UnqualifiedId &Id, bool AssumeTemplateId) { assert(Tok.is(tok::less) && "Expected '<' to finish parsing a template-id"); TemplateTy Template; @@ -2293,11 +2323,9 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, if (AssumeTemplateId) { // We defer the injected-class-name checks until we've found whether // this template-id is used to form a nested-name-specifier or not. - TNK = Actions.ActOnDependentTemplateName( - getCurScope(), SS, TemplateKWLoc, Id, ObjectType, EnteringContext, - Template, /*AllowInjectedClassName*/ true); - if (TNK == TNK_Non_template) - return true; + TNK = Actions.ActOnTemplateName(getCurScope(), SS, TemplateKWLoc, Id, + ObjectType, EnteringContext, Template, + /*AllowInjectedClassName*/ true); } else { bool MemberOfUnknownSpecialization; TNK = Actions.isTemplateName(getCurScope(), SS, @@ -2313,28 +2341,32 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, if (TNK == TNK_Non_template && MemberOfUnknownSpecialization && ObjectType && isTemplateArgumentList(0) == TPResult::True) { - // We have something like t->getAs<T>(), where getAs is a - // member of an unknown specialization. However, this will only - // parse correctly as a template, so suggest the keyword 'template' - // before 'getAs' and treat this as a dependent template name. - std::string Name; - if (Id.getKind() == UnqualifiedIdKind::IK_Identifier) - Name = Id.Identifier->getName(); - else { - Name = "operator "; - if (Id.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId) - Name += getOperatorSpelling(Id.OperatorFunctionId.Operator); - else - Name += Id.Identifier->getName(); + // If we had errors before, ObjectType can be dependent even without any + // templates, do not report missing template keyword in that case. + if (!ObjectHadErrors) { + // We have something like t->getAs<T>(), where getAs is a + // member of an unknown specialization. However, this will only + // parse correctly as a template, so suggest the keyword 'template' + // before 'getAs' and treat this as a dependent template name. + std::string Name; + if (Id.getKind() == UnqualifiedIdKind::IK_Identifier) + Name = std::string(Id.Identifier->getName()); + else { + Name = "operator "; + if (Id.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId) + Name += getOperatorSpelling(Id.OperatorFunctionId.Operator); + else + Name += Id.Identifier->getName(); + } + Diag(Id.StartLocation, diag::err_missing_dependent_template_keyword) + << Name + << FixItHint::CreateInsertion(Id.StartLocation, "template "); } - Diag(Id.StartLocation, diag::err_missing_dependent_template_keyword) - << Name - << FixItHint::CreateInsertion(Id.StartLocation, "template "); - TNK = Actions.ActOnDependentTemplateName( + TNK = Actions.ActOnTemplateName( getCurScope(), SS, TemplateKWLoc, Id, ObjectType, EnteringContext, Template, /*AllowInjectedClassName*/ true); - if (TNK == TNK_Non_template) - return true; + } else if (TNK == TNK_Non_template) { + return false; } } break; @@ -2347,6 +2379,8 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, TemplateName, ObjectType, EnteringContext, Template, MemberOfUnknownSpecialization); + if (TNK == TNK_Non_template) + return false; break; } @@ -2355,11 +2389,9 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, bool MemberOfUnknownSpecialization; TemplateName.setIdentifier(Name, NameLoc); if (ObjectType) { - TNK = Actions.ActOnDependentTemplateName( + TNK = Actions.ActOnTemplateName( getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType, EnteringContext, Template, /*AllowInjectedClassName*/ true); - if (TNK == TNK_Non_template) - return true; } else { TNK = Actions.isTemplateName(getCurScope(), SS, TemplateKWLoc.isValid(), TemplateName, ObjectType, @@ -2369,7 +2401,7 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, if (TNK == TNK_Non_template && !Id.DestructorName.get()) { Diag(NameLoc, diag::err_destructor_template_id) << Name << SS.getRange(); - return true; + // Carry on to parse the template arguments before bailing out. } } break; @@ -2379,9 +2411,6 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, return false; } - if (TNK == TNK_Non_template) - return false; - // Parse the enclosed template argument list. SourceLocation LAngleLoc, RAngleLoc; TemplateArgList TemplateArgs; @@ -2389,6 +2418,10 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, RAngleLoc)) return true; + // If this is a non-template, we already issued a diagnostic. + if (TNK == TNK_Non_template) + return true; + if (Id.getKind() == UnqualifiedIdKind::IK_Identifier || Id.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId || Id.getKind() == UnqualifiedIdKind::IK_LiteralOperatorId) { @@ -2405,8 +2438,8 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, : Id.OperatorFunctionId.Operator; TemplateIdAnnotation *TemplateId = TemplateIdAnnotation::Create( - SS, TemplateKWLoc, Id.StartLocation, TemplateII, OpKind, Template, TNK, - LAngleLoc, RAngleLoc, TemplateArgs, TemplateIds); + TemplateKWLoc, Id.StartLocation, TemplateII, OpKind, Template, TNK, + LAngleLoc, RAngleLoc, TemplateArgs, /*ArgsInvalid*/false, TemplateIds); Id.setTemplateId(TemplateId); return false; @@ -2686,6 +2719,13 @@ bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext, /// \param SS The nested-name-specifier that preceded this unqualified-id. If /// non-empty, then we are parsing the unqualified-id of a qualified-id. /// +/// \param ObjectType if this unqualified-id occurs within a member access +/// expression, the type of the base object whose member is being accessed. +/// +/// \param ObjectHadErrors if this unqualified-id occurs within a member access +/// expression, indicates whether the original subexpressions had any errors. +/// When true, diagnostics for missing 'template' keyword will be supressed. +/// /// \param EnteringContext whether we are entering the scope of the /// nested-name-specifier. /// @@ -2695,17 +2735,14 @@ bool Parser::ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext, /// /// \param AllowDeductionGuide whether we allow parsing a deduction guide name. /// -/// \param ObjectType if this unqualified-id occurs within a member access -/// expression, the type of the base object whose member is being accessed. -/// /// \param Result on a successful parse, contains the parsed unqualified-id. /// /// \returns true if parsing fails, false otherwise. -bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, +bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType, + bool ObjectHadErrors, bool EnteringContext, bool AllowDestructorName, bool AllowConstructorName, bool AllowDeductionGuide, - ParsedType ObjectType, SourceLocation *TemplateKWLoc, UnqualifiedId &Result) { if (TemplateKWLoc) @@ -2764,10 +2801,11 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, TemplateTy Template; if (Tok.is(tok::less)) return ParseUnqualifiedIdTemplateId( - SS, TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), Id, IdLoc, - EnteringContext, ObjectType, Result, TemplateSpecified); + SS, ObjectType, ObjectHadErrors, + TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), Id, IdLoc, + EnteringContext, Result, TemplateSpecified); else if (TemplateSpecified && - Actions.ActOnDependentTemplateName( + Actions.ActOnTemplateName( getCurScope(), SS, *TemplateKWLoc, Result, ObjectType, EnteringContext, Template, /*AllowInjectedClassName*/ true) == TNK_Non_template) @@ -2781,6 +2819,13 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, if (Tok.is(tok::annot_template_id)) { TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); + // FIXME: Consider passing invalid template-ids on to callers; they may + // be able to recover better than we can. + if (TemplateId->isInvalid()) { + ConsumeAnnotationToken(); + return true; + } + // If the template-name names the current class, then this is a constructor if (AllowConstructorName && TemplateId->Name && Actions.isCurrentClassName(*TemplateId->Name, getCurScope(), &SS)) { @@ -2842,11 +2887,11 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, Result.getKind() == UnqualifiedIdKind::IK_LiteralOperatorId) && Tok.is(tok::less)) return ParseUnqualifiedIdTemplateId( - SS, TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), nullptr, - SourceLocation(), EnteringContext, ObjectType, Result, - TemplateSpecified); + SS, ObjectType, ObjectHadErrors, + TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), nullptr, + SourceLocation(), EnteringContext, Result, TemplateSpecified); else if (TemplateSpecified && - Actions.ActOnDependentTemplateName( + Actions.ActOnTemplateName( getCurScope(), SS, *TemplateKWLoc, Result, ObjectType, EnteringContext, Template, /*AllowInjectedClassName*/ true) == TNK_Non_template) @@ -2865,6 +2910,22 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, // Parse the '~'. SourceLocation TildeLoc = ConsumeToken(); + if (TemplateSpecified) { + // C++ [temp.names]p3: + // A name prefixed by the keyword template shall be a template-id [...] + // + // A template-id cannot begin with a '~' token. This would never work + // anyway: x.~A<int>() would specify that the destructor is a template, + // not that 'A' is a template. + // + // FIXME: Suggest replacing the attempted destructor name with a correct + // destructor name and recover. (This is not trivial if this would become + // a pseudo-destructor name). + Diag(*TemplateKWLoc, diag::err_unexpected_template_in_destructor_name) + << Tok.getLocation(); + return true; + } + if (SS.isEmpty() && Tok.is(tok::kw_decltype)) { DeclSpec DS(AttrFactory); SourceLocation EndLoc = ParseDecltypeSpecifier(DS); @@ -2884,7 +2945,7 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, // If the user wrote ~T::T, correct it to T::~T. DeclaratorScopeObj DeclScopeObj(*this, SS); - if (!TemplateSpecified && NextToken().is(tok::coloncolon)) { + if (NextToken().is(tok::coloncolon)) { // Don't let ParseOptionalCXXScopeSpecifier() "correct" // `int A; struct { ~A::A(); };` to `int A; struct { ~A:A(); };`, // it will confuse this recovery logic. @@ -2894,7 +2955,8 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, AnnotateScopeToken(SS, /*NewAnnotation*/true); SS.clear(); } - if (ParseOptionalCXXScopeSpecifier(SS, ObjectType, EnteringContext)) + if (ParseOptionalCXXScopeSpecifier(SS, ObjectType, ObjectHadErrors, + EnteringContext)) return true; if (SS.isNotEmpty()) ObjectType = nullptr; @@ -2921,8 +2983,9 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, if (Tok.is(tok::less)) { Result.setDestructorName(TildeLoc, nullptr, ClassNameLoc); return ParseUnqualifiedIdTemplateId( - SS, TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), ClassName, - ClassNameLoc, EnteringContext, ObjectType, Result, TemplateSpecified); + SS, ObjectType, ObjectHadErrors, + TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), ClassName, + ClassNameLoc, EnteringContext, Result, TemplateSpecified); } // Note that this is a destructor name. @@ -3057,10 +3120,14 @@ Parser::ParseCXXNewExpression(bool UseGlobal, SourceLocation Start) { auto RunSignatureHelp = [&]() { ParsedType TypeRep = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo).get(); - assert(TypeRep && "invalid types should be handled before"); - QualType PreferredType = Actions.ProduceConstructorSignatureHelp( - getCurScope(), TypeRep.get()->getCanonicalTypeInternal(), - DeclaratorInfo.getEndLoc(), ConstructorArgs, ConstructorLParen); + QualType PreferredType; + // ActOnTypeName might adjust DeclaratorInfo and return a null type even + // the passing DeclaratorInfo is valid, e.g. running SignatureHelp on + // `new decltype(invalid) (^)`. + if (TypeRep) + PreferredType = Actions.ProduceConstructorSignatureHelp( + getCurScope(), TypeRep.get()->getCanonicalTypeInternal(), + DeclaratorInfo.getEndLoc(), ConstructorArgs, ConstructorLParen); CalledSignatureHelp = true; return PreferredType; }; @@ -3262,6 +3329,310 @@ Parser::ParseCXXDeleteExpression(bool UseGlobal, SourceLocation Start) { return Actions.ActOnCXXDelete(Start, UseGlobal, ArrayDelete, Operand.get()); } +/// ParseRequiresExpression - Parse a C++2a requires-expression. +/// C++2a [expr.prim.req]p1 +/// A requires-expression provides a concise way to express requirements on +/// template arguments. A requirement is one that can be checked by name +/// lookup (6.4) or by checking properties of types and expressions. +/// +/// requires-expression: +/// 'requires' requirement-parameter-list[opt] requirement-body +/// +/// requirement-parameter-list: +/// '(' parameter-declaration-clause[opt] ')' +/// +/// requirement-body: +/// '{' requirement-seq '}' +/// +/// requirement-seq: +/// requirement +/// requirement-seq requirement +/// +/// requirement: +/// simple-requirement +/// type-requirement +/// compound-requirement +/// nested-requirement +ExprResult Parser::ParseRequiresExpression() { + assert(Tok.is(tok::kw_requires) && "Expected 'requires' keyword"); + SourceLocation RequiresKWLoc = ConsumeToken(); // Consume 'requires' + + llvm::SmallVector<ParmVarDecl *, 2> LocalParameterDecls; + if (Tok.is(tok::l_paren)) { + // requirement parameter list is present. + ParseScope LocalParametersScope(this, Scope::FunctionPrototypeScope | + Scope::DeclScope); + BalancedDelimiterTracker Parens(*this, tok::l_paren); + Parens.consumeOpen(); + if (!Tok.is(tok::r_paren)) { + ParsedAttributes FirstArgAttrs(getAttrFactory()); + SourceLocation EllipsisLoc; + llvm::SmallVector<DeclaratorChunk::ParamInfo, 2> LocalParameters; + ParseParameterDeclarationClause(DeclaratorContext::RequiresExprContext, + FirstArgAttrs, LocalParameters, + EllipsisLoc); + if (EllipsisLoc.isValid()) + Diag(EllipsisLoc, diag::err_requires_expr_parameter_list_ellipsis); + for (auto &ParamInfo : LocalParameters) + LocalParameterDecls.push_back(cast<ParmVarDecl>(ParamInfo.Param)); + } + Parens.consumeClose(); + } + + BalancedDelimiterTracker Braces(*this, tok::l_brace); + if (Braces.expectAndConsume()) + return ExprError(); + + // Start of requirement list + llvm::SmallVector<concepts::Requirement *, 2> Requirements; + + // C++2a [expr.prim.req]p2 + // Expressions appearing within a requirement-body are unevaluated operands. + EnterExpressionEvaluationContext Ctx( + Actions, Sema::ExpressionEvaluationContext::Unevaluated); + + ParseScope BodyScope(this, Scope::DeclScope); + RequiresExprBodyDecl *Body = Actions.ActOnStartRequiresExpr( + RequiresKWLoc, LocalParameterDecls, getCurScope()); + + if (Tok.is(tok::r_brace)) { + // Grammar does not allow an empty body. + // requirement-body: + // { requirement-seq } + // requirement-seq: + // requirement + // requirement-seq requirement + Diag(Tok, diag::err_empty_requires_expr); + // Continue anyway and produce a requires expr with no requirements. + } else { + while (!Tok.is(tok::r_brace)) { + switch (Tok.getKind()) { + case tok::l_brace: { + // Compound requirement + // C++ [expr.prim.req.compound] + // compound-requirement: + // '{' expression '}' 'noexcept'[opt] + // return-type-requirement[opt] ';' + // return-type-requirement: + // trailing-return-type + // '->' cv-qualifier-seq[opt] constrained-parameter + // cv-qualifier-seq[opt] abstract-declarator[opt] + BalancedDelimiterTracker ExprBraces(*this, tok::l_brace); + ExprBraces.consumeOpen(); + ExprResult Expression = + Actions.CorrectDelayedTyposInExpr(ParseExpression()); + if (!Expression.isUsable()) { + ExprBraces.skipToEnd(); + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (ExprBraces.consumeClose()) + ExprBraces.skipToEnd(); + + concepts::Requirement *Req = nullptr; + SourceLocation NoexceptLoc; + TryConsumeToken(tok::kw_noexcept, NoexceptLoc); + if (Tok.is(tok::semi)) { + Req = Actions.ActOnCompoundRequirement(Expression.get(), NoexceptLoc); + if (Req) + Requirements.push_back(Req); + break; + } + if (!TryConsumeToken(tok::arrow)) + // User probably forgot the arrow, remind them and try to continue. + Diag(Tok, diag::err_requires_expr_missing_arrow) + << FixItHint::CreateInsertion(Tok.getLocation(), "->"); + // Try to parse a 'type-constraint' + if (TryAnnotateTypeConstraint()) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (!isTypeConstraintAnnotation()) { + Diag(Tok, diag::err_requires_expr_expected_type_constraint); + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + CXXScopeSpec SS; + if (Tok.is(tok::annot_cxxscope)) { + Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(), + Tok.getAnnotationRange(), + SS); + ConsumeAnnotationToken(); + } + + Req = Actions.ActOnCompoundRequirement( + Expression.get(), NoexceptLoc, SS, takeTemplateIdAnnotation(Tok), + TemplateParameterDepth); + ConsumeAnnotationToken(); + if (Req) + Requirements.push_back(Req); + break; + } + default: { + bool PossibleRequiresExprInSimpleRequirement = false; + if (Tok.is(tok::kw_requires)) { + auto IsNestedRequirement = [&] { + RevertingTentativeParsingAction TPA(*this); + ConsumeToken(); // 'requires' + if (Tok.is(tok::l_brace)) + // This is a requires expression + // requires (T t) { + // requires { t++; }; + // ... ^ + // } + return false; + if (Tok.is(tok::l_paren)) { + // This might be the parameter list of a requires expression + ConsumeParen(); + auto Res = TryParseParameterDeclarationClause(); + if (Res != TPResult::False) { + // Skip to the closing parenthesis + // FIXME: Don't traverse these tokens twice (here and in + // TryParseParameterDeclarationClause). + unsigned Depth = 1; + while (Depth != 0) { + if (Tok.is(tok::l_paren)) + Depth++; + else if (Tok.is(tok::r_paren)) + Depth--; + ConsumeAnyToken(); + } + // requires (T t) { + // requires () ? + // ... ^ + // - OR - + // requires (int x) ? + // ... ^ + // } + if (Tok.is(tok::l_brace)) + // requires (...) { + // ^ - a requires expression as a + // simple-requirement. + return false; + } + } + return true; + }; + if (IsNestedRequirement()) { + ConsumeToken(); + // Nested requirement + // C++ [expr.prim.req.nested] + // nested-requirement: + // 'requires' constraint-expression ';' + ExprResult ConstraintExpr = + Actions.CorrectDelayedTyposInExpr(ParseConstraintExpression()); + if (ConstraintExpr.isInvalid() || !ConstraintExpr.isUsable()) { + SkipUntil(tok::semi, tok::r_brace, + SkipUntilFlags::StopBeforeMatch); + break; + } + if (auto *Req = + Actions.ActOnNestedRequirement(ConstraintExpr.get())) + Requirements.push_back(Req); + else { + SkipUntil(tok::semi, tok::r_brace, + SkipUntilFlags::StopBeforeMatch); + break; + } + break; + } else + PossibleRequiresExprInSimpleRequirement = true; + } else if (Tok.is(tok::kw_typename)) { + // This might be 'typename T::value_type;' (a type requirement) or + // 'typename T::value_type{};' (a simple requirement). + TentativeParsingAction TPA(*this); + + // We need to consume the typename to allow 'requires { typename a; }' + SourceLocation TypenameKWLoc = ConsumeToken(); + if (TryAnnotateCXXScopeToken()) { + TPA.Commit(); + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + CXXScopeSpec SS; + if (Tok.is(tok::annot_cxxscope)) { + Actions.RestoreNestedNameSpecifierAnnotation( + Tok.getAnnotationValue(), Tok.getAnnotationRange(), SS); + ConsumeAnnotationToken(); + } + + if (Tok.isOneOf(tok::identifier, tok::annot_template_id) && + !NextToken().isOneOf(tok::l_brace, tok::l_paren)) { + TPA.Commit(); + SourceLocation NameLoc = Tok.getLocation(); + IdentifierInfo *II = nullptr; + TemplateIdAnnotation *TemplateId = nullptr; + if (Tok.is(tok::identifier)) { + II = Tok.getIdentifierInfo(); + ConsumeToken(); + } else { + TemplateId = takeTemplateIdAnnotation(Tok); + ConsumeAnnotationToken(); + if (TemplateId->isInvalid()) + break; + } + + if (auto *Req = Actions.ActOnTypeRequirement(TypenameKWLoc, SS, + NameLoc, II, + TemplateId)) { + Requirements.push_back(Req); + } + break; + } + TPA.Revert(); + } + // Simple requirement + // C++ [expr.prim.req.simple] + // simple-requirement: + // expression ';' + SourceLocation StartLoc = Tok.getLocation(); + ExprResult Expression = + Actions.CorrectDelayedTyposInExpr(ParseExpression()); + if (!Expression.isUsable()) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (!Expression.isInvalid() && PossibleRequiresExprInSimpleRequirement) + Diag(StartLoc, diag::warn_requires_expr_in_simple_requirement) + << FixItHint::CreateInsertion(StartLoc, "requires"); + if (auto *Req = Actions.ActOnSimpleRequirement(Expression.get())) + Requirements.push_back(Req); + else { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + // User may have tried to put some compound requirement stuff here + if (Tok.is(tok::kw_noexcept)) { + Diag(Tok, diag::err_requires_expr_simple_requirement_noexcept) + << FixItHint::CreateInsertion(StartLoc, "{") + << FixItHint::CreateInsertion(Tok.getLocation(), "}"); + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + break; + } + } + if (ExpectAndConsumeSemi(diag::err_expected_semi_requirement)) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + TryConsumeToken(tok::semi); + break; + } + } + if (Requirements.empty()) { + // Don't emit an empty requires expr here to avoid confusing the user with + // other diagnostics quoting an empty requires expression they never + // wrote. + Braces.consumeClose(); + Actions.ActOnFinishRequiresExpr(); + return ExprError(); + } + } + Braces.consumeClose(); + Actions.ActOnFinishRequiresExpr(); + return Actions.ActOnRequiresExpr(RequiresKWLoc, Body, LocalParameterDecls, + Requirements, Braces.getCloseLocation()); +} + static TypeTrait TypeTraitFromTokKind(tok::TokenKind kind) { switch (kind) { default: llvm_unreachable("Not a known type trait"); @@ -3277,18 +3648,24 @@ case tok::kw_ ## Spelling: return BTT_ ## Name; } static ArrayTypeTrait ArrayTypeTraitFromTokKind(tok::TokenKind kind) { - switch(kind) { - default: llvm_unreachable("Not a known binary type trait"); - case tok::kw___array_rank: return ATT_ArrayRank; - case tok::kw___array_extent: return ATT_ArrayExtent; + switch (kind) { + default: + llvm_unreachable("Not a known array type trait"); +#define ARRAY_TYPE_TRAIT(Spelling, Name, Key) \ + case tok::kw_##Spelling: \ + return ATT_##Name; +#include "clang/Basic/TokenKinds.def" } } static ExpressionTrait ExpressionTraitFromTokKind(tok::TokenKind kind) { - switch(kind) { - default: llvm_unreachable("Not a known unary expression trait."); - case tok::kw___is_lvalue_expr: return ET_IsLValueExpr; - case tok::kw___is_rvalue_expr: return ET_IsRValueExpr; + switch (kind) { + default: + llvm_unreachable("Not a known unary expression trait."); +#define EXPRESSION_TRAIT(Spelling, Name, Key) \ + case tok::kw_##Spelling: \ + return ET_##Name; +#include "clang/Basic/TokenKinds.def" } } diff --git a/clang/lib/Parse/ParseInit.cpp b/clang/lib/Parse/ParseInit.cpp index 5ab055130dc2..9ac2b2e6f79b 100644 --- a/clang/lib/Parse/ParseInit.cpp +++ b/clang/lib/Parse/ParseInit.cpp @@ -10,11 +10,14 @@ // //===----------------------------------------------------------------------===// +#include "clang/Basic/TokenKinds.h" #include "clang/Parse/ParseDiagnostic.h" #include "clang/Parse/Parser.h" #include "clang/Parse/RAIIObjectsForParser.h" #include "clang/Sema/Designator.h" +#include "clang/Sema/Ownership.h" #include "clang/Sema/Scope.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" using namespace clang; @@ -154,7 +157,9 @@ static void CheckArrayDesignatorSyntax(Parser &P, SourceLocation Loc, /// initializer (because it is an expression). We need to consider this case /// when parsing array designators. /// -ExprResult Parser::ParseInitializerWithPotentialDesignator() { +/// \p CodeCompleteCB is called with Designation parsed so far. +ExprResult Parser::ParseInitializerWithPotentialDesignator( + llvm::function_ref<void(const Designation &)> CodeCompleteCB) { // If this is the old-style GNU extension: // designation ::= identifier ':' @@ -193,6 +198,11 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator() { // designator: '.' identifier SourceLocation DotLoc = ConsumeToken(); + if (Tok.is(tok::code_completion)) { + CodeCompleteCB(Desig); + cutOffParsing(); + return ExprError(); + } if (Tok.isNot(tok::identifier)) { Diag(Tok.getLocation(), diag::err_expected_field_designator); return ExprError(); @@ -407,7 +417,6 @@ ExprResult Parser::ParseInitializerWithPotentialDesignator() { return ExprError(); } - /// ParseBraceInitializer - Called when parsing an initializer that has a /// leading open brace. /// @@ -444,6 +453,10 @@ ExprResult Parser::ParseBraceInitializer() { Actions, EnterExpressionEvaluationContext::InitList); bool InitExprsOk = true; + auto CodeCompleteDesignation = [&](const Designation &D) { + Actions.CodeCompleteDesignator(PreferredType.get(T.getOpenLocation()), + InitExprs, D); + }; while (1) { // Handle Microsoft __if_exists/if_not_exists if necessary. @@ -463,7 +476,7 @@ ExprResult Parser::ParseBraceInitializer() { // initializer directly. ExprResult SubElt; if (MayBeDesignationStart()) - SubElt = ParseInitializerWithPotentialDesignator(); + SubElt = ParseInitializerWithPotentialDesignator(CodeCompleteDesignation); else SubElt = ParseInitializer(); @@ -543,13 +556,17 @@ bool Parser::ParseMicrosoftIfExistsBraceInitializer(ExprVector &InitExprs, return false; } + auto CodeCompleteDesignation = [&](const Designation &D) { + Actions.CodeCompleteDesignator(PreferredType.get(Braces.getOpenLocation()), + InitExprs, D); + }; while (!isEofOrEom()) { trailingComma = false; // If we know that this cannot be a designation, just parse the nested // initializer directly. ExprResult SubElt; if (MayBeDesignationStart()) - SubElt = ParseInitializerWithPotentialDesignator(); + SubElt = ParseInitializerWithPotentialDesignator(CodeCompleteDesignation); else SubElt = ParseInitializer(); diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index efcef6d3b123..eaea8666bc10 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -10,11 +10,12 @@ // //===----------------------------------------------------------------------===// -#include "clang/Parse/Parser.h" #include "clang/AST/ASTContext.h" #include "clang/AST/PrettyDeclStackTrace.h" #include "clang/Basic/CharInfo.h" +#include "clang/Basic/TargetInfo.h" #include "clang/Parse/ParseDiagnostic.h" +#include "clang/Parse/Parser.h" #include "clang/Parse/RAIIObjectsForParser.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/Scope.h" @@ -522,10 +523,9 @@ ObjCTypeParamList *Parser::parseObjCTypeParamListOrProtocolRefs( SkipUntil(tok::greater, tok::at, StopBeforeMatch); if (Tok.is(tok::greater)) ConsumeToken(); - } else if (ParseGreaterThanInTemplateList(rAngleLoc, + } else if (ParseGreaterThanInTemplateList(lAngleLoc, rAngleLoc, /*ConsumeLastToken=*/true, /*ObjCGenericList=*/true)) { - Diag(lAngleLoc, diag::note_matching) << "'<'"; SkipUntil({tok::greater, tok::greaterequal, tok::at, tok::minus, tok::minus, tok::plus, tok::colon, tok::l_paren, tok::l_brace, tok::comma, tok::semi }, @@ -740,7 +740,8 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey, // Map a nullability property attribute to a context-sensitive keyword // attribute. - if (OCDS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability) + if (OCDS.getPropertyAttributes() & + ObjCPropertyAttribute::kind_nullability) addContextSensitiveTypeNullability(*this, FD.D, OCDS.getNullability(), OCDS.getNullabilityLoc(), addedToDeclSpec); @@ -860,25 +861,25 @@ void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) { SourceLocation AttrName = ConsumeToken(); // consume last attribute name if (II->isStr("readonly")) - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_readonly); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_readonly); else if (II->isStr("assign")) - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_assign); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_assign); else if (II->isStr("unsafe_unretained")) - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_unsafe_unretained); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_unsafe_unretained); else if (II->isStr("readwrite")) - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_readwrite); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_readwrite); else if (II->isStr("retain")) - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_retain); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_retain); else if (II->isStr("strong")) - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_strong); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_strong); else if (II->isStr("copy")) - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_copy); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_copy); else if (II->isStr("nonatomic")) - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nonatomic); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_nonatomic); else if (II->isStr("atomic")) - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_atomic); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_atomic); else if (II->isStr("weak")) - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_weak); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_weak); else if (II->isStr("getter") || II->isStr("setter")) { bool IsSetter = II->getNameStart()[0] == 's'; @@ -910,7 +911,7 @@ void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) { } if (IsSetter) { - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_setter); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_setter); DS.setSetterName(SelIdent, SelLoc); if (ExpectAndConsume(tok::colon, @@ -919,44 +920,44 @@ void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) { return; } } else { - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_getter); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_getter); DS.setGetterName(SelIdent, SelLoc); } } else if (II->isStr("nonnull")) { - if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability) + if (DS.getPropertyAttributes() & ObjCPropertyAttribute::kind_nullability) diagnoseRedundantPropertyNullability(*this, DS, NullabilityKind::NonNull, Tok.getLocation()); - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_nullability); DS.setNullability(Tok.getLocation(), NullabilityKind::NonNull); } else if (II->isStr("nullable")) { - if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability) + if (DS.getPropertyAttributes() & ObjCPropertyAttribute::kind_nullability) diagnoseRedundantPropertyNullability(*this, DS, NullabilityKind::Nullable, Tok.getLocation()); - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_nullability); DS.setNullability(Tok.getLocation(), NullabilityKind::Nullable); } else if (II->isStr("null_unspecified")) { - if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability) + if (DS.getPropertyAttributes() & ObjCPropertyAttribute::kind_nullability) diagnoseRedundantPropertyNullability(*this, DS, NullabilityKind::Unspecified, Tok.getLocation()); - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_nullability); DS.setNullability(Tok.getLocation(), NullabilityKind::Unspecified); } else if (II->isStr("null_resettable")) { - if (DS.getPropertyAttributes() & ObjCDeclSpec::DQ_PR_nullability) + if (DS.getPropertyAttributes() & ObjCPropertyAttribute::kind_nullability) diagnoseRedundantPropertyNullability(*this, DS, NullabilityKind::Unspecified, Tok.getLocation()); - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nullability); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_nullability); DS.setNullability(Tok.getLocation(), NullabilityKind::Unspecified); // Also set the null_resettable bit. - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_null_resettable); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_null_resettable); } else if (II->isStr("class")) { - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_class); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_class); } else if (II->isStr("direct")) { - DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_direct); + DS.setPropertyAttributes(ObjCPropertyAttribute::kind_direct); } else { Diag(AttrName, diag::err_objc_expected_property_attr) << II; SkipUntil(tok::r_paren, StopAtSemi); @@ -1550,7 +1551,7 @@ ParseObjCProtocolReferences(SmallVectorImpl<Decl *> &Protocols, } // Consume the '>'. - if (ParseGreaterThanInTemplateList(EndLoc, consumeLastToken, + if (ParseGreaterThanInTemplateList(LAngleLoc, EndLoc, consumeLastToken, /*ObjCGenericList=*/false)) return true; @@ -1648,7 +1649,7 @@ void Parser::parseObjCTypeArgsOrProtocolQualifiers( if (allSingleIdentifiers) { // Parse the closing '>'. SourceLocation rAngleLoc; - (void)ParseGreaterThanInTemplateList(rAngleLoc, consumeLastToken, + (void)ParseGreaterThanInTemplateList(lAngleLoc, rAngleLoc, consumeLastToken, /*ObjCGenericList=*/true); // Let Sema figure out what we parsed. @@ -1754,7 +1755,7 @@ void Parser::parseObjCTypeArgsOrProtocolQualifiers( // Parse the closing '>'. SourceLocation rAngleLoc; - (void)ParseGreaterThanInTemplateList(rAngleLoc, consumeLastToken, + (void)ParseGreaterThanInTemplateList(lAngleLoc, rAngleLoc, consumeLastToken, /*ObjCGenericList=*/true); if (invalid) { @@ -2978,7 +2979,7 @@ bool Parser::isStartOfObjCClassMessageMissingOpenBracket() { InMessageExpression) return false; - ParsedType Type; + TypeResult Type; if (Tok.is(tok::annot_typename)) Type = getTypeAnnotation(Tok); @@ -2988,7 +2989,8 @@ bool Parser::isStartOfObjCClassMessageMissingOpenBracket() { else return false; - if (!Type.get().isNull() && Type.get()->isObjCObjectOrInterfaceType()) { + // FIXME: Should not be querying properties of types from the parser. + if (Type.isUsable() && Type.get().get()->isObjCObjectOrInterfaceType()) { const Token &AfterNext = GetLookAheadToken(2); if (AfterNext.isOneOf(tok::colon, tok::r_square)) { if (Tok.is(tok::identifier)) diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index 1095919baa7d..5223755c8fdf 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -11,14 +11,18 @@ //===----------------------------------------------------------------------===// #include "clang/AST/ASTContext.h" +#include "clang/AST/OpenMPClause.h" #include "clang/AST/StmtOpenMP.h" #include "clang/Basic/OpenMPKinds.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TokenKinds.h" #include "clang/Parse/ParseDiagnostic.h" #include "clang/Parse/Parser.h" #include "clang/Parse/RAIIObjectsForParser.h" #include "clang/Sema/Scope.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/UniqueVector.h" +#include "llvm/Frontend/OpenMP/OMPContext.h" using namespace clang; using namespace llvm::omp; @@ -29,7 +33,7 @@ using namespace llvm::omp; namespace { enum OpenMPDirectiveKindEx { - OMPD_cancellation = unsigned(OMPD_unknown) + 1, + OMPD_cancellation = llvm::omp::Directive_enumSize + 1, OMPD_data, OMPD_declare, OMPD_end, @@ -46,6 +50,8 @@ enum OpenMPDirectiveKindEx { OMPD_target_teams_distribute_parallel, OMPD_mapper, OMPD_variant, + OMPD_begin, + OMPD_begin_declare, }; // Helper to unify the enum class OpenMPDirectiveKind with its extension @@ -99,6 +105,7 @@ static unsigned getOpenMPDirectiveKindEx(StringRef S) { .Case("update", OMPD_update) .Case("mapper", OMPD_mapper) .Case("variant", OMPD_variant) + .Case("begin", OMPD_begin) .Default(OMPD_unknown); } @@ -107,18 +114,21 @@ static OpenMPDirectiveKindExWrapper parseOpenMPDirectiveKind(Parser &P) { // E.g.: OMPD_for OMPD_simd ===> OMPD_for_simd // TODO: add other combined directives in topological order. static const OpenMPDirectiveKindExWrapper F[][3] = { + {OMPD_begin, OMPD_declare, OMPD_begin_declare}, + {OMPD_end, OMPD_declare, OMPD_end_declare}, {OMPD_cancellation, OMPD_point, OMPD_cancellation_point}, {OMPD_declare, OMPD_reduction, OMPD_declare_reduction}, {OMPD_declare, OMPD_mapper, OMPD_declare_mapper}, {OMPD_declare, OMPD_simd, OMPD_declare_simd}, {OMPD_declare, OMPD_target, OMPD_declare_target}, {OMPD_declare, OMPD_variant, OMPD_declare_variant}, + {OMPD_begin_declare, OMPD_variant, OMPD_begin_declare_variant}, + {OMPD_end_declare, OMPD_variant, OMPD_end_declare_variant}, {OMPD_distribute, OMPD_parallel, OMPD_distribute_parallel}, {OMPD_distribute_parallel, OMPD_for, OMPD_distribute_parallel_for}, {OMPD_distribute_parallel_for, OMPD_simd, OMPD_distribute_parallel_for_simd}, {OMPD_distribute, OMPD_simd, OMPD_distribute_simd}, - {OMPD_end, OMPD_declare, OMPD_end_declare}, {OMPD_end_declare, OMPD_target, OMPD_end_declare_target}, {OMPD_target, OMPD_data, OMPD_target_data}, {OMPD_target, OMPD_enter, OMPD_target_enter}, @@ -184,8 +194,9 @@ static OpenMPDirectiveKindExWrapper parseOpenMPDirectiveKind(Parser &P) { DKind = F[I][2]; } } - return DKind < OMPD_unknown ? static_cast<OpenMPDirectiveKind>(DKind) - : OMPD_unknown; + return unsigned(DKind) < llvm::omp::Directive_enumSize + ? static_cast<OpenMPDirectiveKind>(DKind) + : OMPD_unknown; } static DeclarationName parseOpenMPReductionId(Parser &P) { @@ -637,16 +648,14 @@ namespace { class FNContextRAII final { Parser &P; Sema::CXXThisScopeRAII *ThisScope; - Parser::ParseScope *TempScope; - Parser::ParseScope *FnScope; - bool HasTemplateScope = false; + Parser::MultiParseScope Scopes; bool HasFunScope = false; FNContextRAII() = delete; FNContextRAII(const FNContextRAII &) = delete; FNContextRAII &operator=(const FNContextRAII &) = delete; public: - FNContextRAII(Parser &P, Parser::DeclGroupPtrTy Ptr) : P(P) { + FNContextRAII(Parser &P, Parser::DeclGroupPtrTy Ptr) : P(P), Scopes(P) { Decl *D = *Ptr.get().begin(); NamedDecl *ND = dyn_cast<NamedDecl>(D); RecordDecl *RD = dyn_cast_or_null<RecordDecl>(D->getDeclContext()); @@ -657,29 +666,20 @@ public: ND && ND->isCXXInstanceMember()); // If the Decl is templatized, add template parameters to scope. - HasTemplateScope = D->isTemplateDecl(); - TempScope = - new Parser::ParseScope(&P, Scope::TemplateParamScope, HasTemplateScope); - if (HasTemplateScope) - Actions.ActOnReenterTemplateScope(Actions.getCurScope(), D); + // FIXME: Track CurTemplateDepth? + P.ReenterTemplateScopes(Scopes, D); // If the Decl is on a function, add function parameters to the scope. - HasFunScope = D->isFunctionOrFunctionTemplate(); - FnScope = new Parser::ParseScope( - &P, Scope::FnScope | Scope::DeclScope | Scope::CompoundStmtScope, - HasFunScope); - if (HasFunScope) + if (D->isFunctionOrFunctionTemplate()) { + HasFunScope = true; + Scopes.Enter(Scope::FnScope | Scope::DeclScope | + Scope::CompoundStmtScope); Actions.ActOnReenterFunctionContext(Actions.getCurScope(), D); + } } ~FNContextRAII() { - if (HasFunScope) { + if (HasFunScope) P.getActions().ActOnExitFunctionContext(); - FnScope->Exit(); // Pop scope, and remove Decls from IdResolver - } - if (HasTemplateScope) - TempScope->Exit(); - delete FnScope; - delete TempScope; delete ThisScope; } }; @@ -746,18 +746,19 @@ static bool parseDeclareSimdClauses( getOpenMPClauseKind(ClauseName), *Vars, Data)) IsError = true; if (CKind == OMPC_aligned) { - Alignments.append(Aligneds.size() - Alignments.size(), Data.TailExpr); + Alignments.append(Aligneds.size() - Alignments.size(), + Data.DepModOrTailExpr); } else if (CKind == OMPC_linear) { assert(0 <= Data.ExtraModifier && Data.ExtraModifier <= OMPC_LINEAR_unknown && "Unexpected linear modifier."); if (P.getActions().CheckOpenMPLinearModifier( static_cast<OpenMPLinearClauseKind>(Data.ExtraModifier), - Data.DepLinMapLastLoc)) + Data.ExtraModifierLoc)) Data.ExtraModifier = OMPC_LINEAR_val; LinModifiers.append(Linears.size() - LinModifiers.size(), Data.ExtraModifier); - Steps.append(Linears.size() - Steps.size(), Data.TailExpr); + Steps.append(Linears.size() - Steps.size(), Data.DepModOrTailExpr); } } else // TODO: add parsing of other clauses. @@ -794,13 +795,7 @@ Parser::ParseOMPDeclareSimdClauses(Parser::DeclGroupPtrTy Ptr, bool IsError = parseDeclareSimdClauses(*this, BS, Simdlen, Uniforms, Aligneds, Alignments, Linears, LinModifiers, Steps); - // Need to check for extra tokens. - if (Tok.isNot(tok::annot_pragma_openmp_end)) { - Diag(Tok, diag::warn_omp_extra_tokens_at_eol) - << getOpenMPDirectiveName(OMPD_declare_simd); - while (Tok.isNot(tok::annot_pragma_openmp_end)) - ConsumeAnyToken(); - } + skipUntilPragmaOpenMPEnd(OMPD_declare_simd); // Skip the last annot_pragma_openmp_end. SourceLocation EndLoc = ConsumeAnnotationToken(); if (IsError) @@ -810,10 +805,268 @@ Parser::ParseOMPDeclareSimdClauses(Parser::DeclGroupPtrTy Ptr, LinModifiers, Steps, SourceRange(Loc, EndLoc)); } +namespace { +/// Constant used in the diagnostics to distinguish the levels in an OpenMP +/// contexts: selector-set={selector(trait, ...), ...}, .... +enum OMPContextLvl { + CONTEXT_SELECTOR_SET_LVL = 0, + CONTEXT_SELECTOR_LVL = 1, + CONTEXT_TRAIT_LVL = 2, +}; + +static StringRef stringLiteralParser(Parser &P) { + ExprResult Res = P.ParseStringLiteralExpression(true); + return Res.isUsable() ? Res.getAs<StringLiteral>()->getString() : ""; +} + +static StringRef getNameFromIdOrString(Parser &P, Token &Tok, + OMPContextLvl Lvl) { + if (Tok.is(tok::identifier)) { + llvm::SmallString<16> Buffer; + StringRef Name = P.getPreprocessor().getSpelling(Tok, Buffer); + (void)P.ConsumeToken(); + return Name; + } + + if (tok::isStringLiteral(Tok.getKind())) + return stringLiteralParser(P); + + P.Diag(Tok.getLocation(), + diag::warn_omp_declare_variant_string_literal_or_identifier) + << Lvl; + return ""; +} + +static bool checkForDuplicates(Parser &P, StringRef Name, + SourceLocation NameLoc, + llvm::StringMap<SourceLocation> &Seen, + OMPContextLvl Lvl) { + auto Res = Seen.try_emplace(Name, NameLoc); + if (Res.second) + return false; + + // Each trait-set-selector-name, trait-selector-name and trait-name can + // only be specified once. + P.Diag(NameLoc, diag::warn_omp_declare_variant_ctx_mutiple_use) + << Lvl << Name; + P.Diag(Res.first->getValue(), diag::note_omp_declare_variant_ctx_used_here) + << Lvl << Name; + return true; +} +} // namespace + +void Parser::parseOMPTraitPropertyKind( + OMPTraitProperty &TIProperty, llvm::omp::TraitSet Set, + llvm::omp::TraitSelector Selector, llvm::StringMap<SourceLocation> &Seen) { + TIProperty.Kind = TraitProperty::invalid; + + SourceLocation NameLoc = Tok.getLocation(); + StringRef Name = + getNameFromIdOrString(*this, Tok, CONTEXT_TRAIT_LVL); + if (Name.empty()) { + Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_options) + << CONTEXT_TRAIT_LVL << listOpenMPContextTraitProperties(Set, Selector); + return; + } + + TIProperty.Kind = getOpenMPContextTraitPropertyKind(Set, Name); + if (TIProperty.Kind != TraitProperty::invalid) { + if (checkForDuplicates(*this, Name, NameLoc, Seen, CONTEXT_TRAIT_LVL)) + TIProperty.Kind = TraitProperty::invalid; + return; + } + + // It follows diagnosis and helping notes. + // FIXME: We should move the diagnosis string generation into libFrontend. + Diag(NameLoc, diag::warn_omp_declare_variant_ctx_not_a_property) + << Name << getOpenMPContextTraitSelectorName(Selector) + << getOpenMPContextTraitSetName(Set); + + TraitSet SetForName = getOpenMPContextTraitSetKind(Name); + if (SetForName != TraitSet::invalid) { + Diag(NameLoc, diag::note_omp_declare_variant_ctx_is_a) + << Name << CONTEXT_SELECTOR_SET_LVL << CONTEXT_TRAIT_LVL; + Diag(NameLoc, diag::note_omp_declare_variant_ctx_try) + << Name << "<selector-name>" + << "(<property-name>)"; + return; + } + TraitSelector SelectorForName = getOpenMPContextTraitSelectorKind(Name); + if (SelectorForName != TraitSelector::invalid) { + Diag(NameLoc, diag::note_omp_declare_variant_ctx_is_a) + << Name << CONTEXT_SELECTOR_LVL << CONTEXT_TRAIT_LVL; + bool AllowsTraitScore = false; + bool RequiresProperty = false; + isValidTraitSelectorForTraitSet( + SelectorForName, getOpenMPContextTraitSetForSelector(SelectorForName), + AllowsTraitScore, RequiresProperty); + Diag(NameLoc, diag::note_omp_declare_variant_ctx_try) + << getOpenMPContextTraitSetName( + getOpenMPContextTraitSetForSelector(SelectorForName)) + << Name << (RequiresProperty ? "(<property-name>)" : ""); + return; + } + for (const auto &PotentialSet : + {TraitSet::construct, TraitSet::user, TraitSet::implementation, + TraitSet::device}) { + TraitProperty PropertyForName = + getOpenMPContextTraitPropertyKind(PotentialSet, Name); + if (PropertyForName == TraitProperty::invalid) + continue; + Diag(NameLoc, diag::note_omp_declare_variant_ctx_try) + << getOpenMPContextTraitSetName( + getOpenMPContextTraitSetForProperty(PropertyForName)) + << getOpenMPContextTraitSelectorName( + getOpenMPContextTraitSelectorForProperty(PropertyForName)) + << ("(" + Name + ")").str(); + return; + } + Diag(NameLoc, diag::note_omp_declare_variant_ctx_options) + << CONTEXT_TRAIT_LVL << listOpenMPContextTraitProperties(Set, Selector); +} + +static bool checkExtensionProperty(Parser &P, SourceLocation Loc, + OMPTraitProperty &TIProperty, + OMPTraitSelector &TISelector, + llvm::StringMap<SourceLocation> &Seen) { + assert(TISelector.Kind == + llvm::omp::TraitSelector::implementation_extension && + "Only for extension properties, e.g., " + "`implementation={extension(PROPERTY)}`"); + if (TIProperty.Kind == TraitProperty::invalid) + return false; + + auto IsMatchExtension = [](OMPTraitProperty &TP) { + return (TP.Kind == + llvm::omp::TraitProperty::implementation_extension_match_all || + TP.Kind == + llvm::omp::TraitProperty::implementation_extension_match_any || + TP.Kind == + llvm::omp::TraitProperty::implementation_extension_match_none); + }; + + if (IsMatchExtension(TIProperty)) { + for (OMPTraitProperty &SeenProp : TISelector.Properties) + if (IsMatchExtension(SeenProp)) { + P.Diag(Loc, diag::err_omp_variant_ctx_second_match_extension); + StringRef SeenName = + llvm::omp::getOpenMPContextTraitPropertyName(SeenProp.Kind); + SourceLocation SeenLoc = Seen[SeenName]; + P.Diag(SeenLoc, diag::note_omp_declare_variant_ctx_used_here) + << CONTEXT_TRAIT_LVL << SeenName; + return false; + } + return true; + } + + llvm_unreachable("Unknown extension property!"); +} + +void Parser::parseOMPContextProperty(OMPTraitSelector &TISelector, + llvm::omp::TraitSet Set, + llvm::StringMap<SourceLocation> &Seen) { + assert(TISelector.Kind != TraitSelector::user_condition && + "User conditions are special properties not handled here!"); + + SourceLocation PropertyLoc = Tok.getLocation(); + OMPTraitProperty TIProperty; + parseOMPTraitPropertyKind(TIProperty, Set, TISelector.Kind, Seen); + + if (TISelector.Kind == llvm::omp::TraitSelector::implementation_extension) + if (!checkExtensionProperty(*this, Tok.getLocation(), TIProperty, + TISelector, Seen)) + TIProperty.Kind = TraitProperty::invalid; + + // If we have an invalid property here we already issued a warning. + if (TIProperty.Kind == TraitProperty::invalid) { + if (PropertyLoc != Tok.getLocation()) + Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_continue_here) + << CONTEXT_TRAIT_LVL; + return; + } + + if (isValidTraitPropertyForTraitSetAndSelector(TIProperty.Kind, + TISelector.Kind, Set)) { + + // If we make it here the property, selector, set, score, condition, ... are + // all valid (or have been corrected). Thus we can record the property. + TISelector.Properties.push_back(TIProperty); + return; + } + + Diag(PropertyLoc, diag::warn_omp_ctx_incompatible_property_for_selector) + << getOpenMPContextTraitPropertyName(TIProperty.Kind) + << getOpenMPContextTraitSelectorName(TISelector.Kind) + << getOpenMPContextTraitSetName(Set); + Diag(PropertyLoc, diag::note_omp_ctx_compatible_set_and_selector_for_property) + << getOpenMPContextTraitPropertyName(TIProperty.Kind) + << getOpenMPContextTraitSelectorName( + getOpenMPContextTraitSelectorForProperty(TIProperty.Kind)) + << getOpenMPContextTraitSetName( + getOpenMPContextTraitSetForProperty(TIProperty.Kind)); + Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_continue_here) + << CONTEXT_TRAIT_LVL; +} + +void Parser::parseOMPTraitSelectorKind( + OMPTraitSelector &TISelector, llvm::omp::TraitSet Set, + llvm::StringMap<SourceLocation> &Seen) { + TISelector.Kind = TraitSelector::invalid; + + SourceLocation NameLoc = Tok.getLocation(); + StringRef Name = getNameFromIdOrString(*this, Tok, CONTEXT_SELECTOR_LVL + ); + if (Name.empty()) { + Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_options) + << CONTEXT_SELECTOR_LVL << listOpenMPContextTraitSelectors(Set); + return; + } + + TISelector.Kind = getOpenMPContextTraitSelectorKind(Name); + if (TISelector.Kind != TraitSelector::invalid) { + if (checkForDuplicates(*this, Name, NameLoc, Seen, CONTEXT_SELECTOR_LVL)) + TISelector.Kind = TraitSelector::invalid; + return; + } + + // It follows diagnosis and helping notes. + Diag(NameLoc, diag::warn_omp_declare_variant_ctx_not_a_selector) + << Name << getOpenMPContextTraitSetName(Set); + + TraitSet SetForName = getOpenMPContextTraitSetKind(Name); + if (SetForName != TraitSet::invalid) { + Diag(NameLoc, diag::note_omp_declare_variant_ctx_is_a) + << Name << CONTEXT_SELECTOR_SET_LVL << CONTEXT_SELECTOR_LVL; + Diag(NameLoc, diag::note_omp_declare_variant_ctx_try) + << Name << "<selector-name>" + << "<property-name>"; + return; + } + for (const auto &PotentialSet : + {TraitSet::construct, TraitSet::user, TraitSet::implementation, + TraitSet::device}) { + TraitProperty PropertyForName = + getOpenMPContextTraitPropertyKind(PotentialSet, Name); + if (PropertyForName == TraitProperty::invalid) + continue; + Diag(NameLoc, diag::note_omp_declare_variant_ctx_is_a) + << Name << CONTEXT_TRAIT_LVL << CONTEXT_SELECTOR_LVL; + Diag(NameLoc, diag::note_omp_declare_variant_ctx_try) + << getOpenMPContextTraitSetName( + getOpenMPContextTraitSetForProperty(PropertyForName)) + << getOpenMPContextTraitSelectorName( + getOpenMPContextTraitSelectorForProperty(PropertyForName)) + << ("(" + Name + ")").str(); + return; + } + Diag(NameLoc, diag::note_omp_declare_variant_ctx_options) + << CONTEXT_SELECTOR_LVL << listOpenMPContextTraitSelectors(Set); +} + /// Parse optional 'score' '(' <expr> ')' ':'. static ExprResult parseContextScore(Parser &P) { ExprResult ScoreExpr; - Sema::OMPCtxStringType Buffer; + llvm::SmallString<16> Buffer; StringRef SelectorName = P.getPreprocessor().getSpelling(P.getCurToken(), Buffer); if (!SelectorName.equals("score")) @@ -825,246 +1078,272 @@ static ExprResult parseContextScore(Parser &P) { if (P.getCurToken().is(tok::colon)) (void)P.ConsumeAnyToken(); else - P.Diag(P.getCurToken(), diag::warn_pragma_expected_colon) - << "context selector score clause"; + P.Diag(P.getCurToken(), diag::warn_omp_declare_variant_expected) + << "':'" + << "score expression"; return ScoreExpr; } -/// Parse context selector for 'implementation' selector set: -/// 'vendor' '(' [ 'score' '(' <score _expr> ')' ':' ] <vendor> { ',' <vendor> } -/// ')' -static void -parseImplementationSelector(Parser &P, SourceLocation Loc, - llvm::StringMap<SourceLocation> &UsedCtx, - SmallVectorImpl<Sema::OMPCtxSelectorData> &Data) { - const Token &Tok = P.getCurToken(); - // Parse inner context selector set name, if any. - if (!Tok.is(tok::identifier)) { - P.Diag(Tok.getLocation(), diag::warn_omp_declare_variant_cs_name_expected) - << "implementation"; - // Skip until either '}', ')', or end of directive. - while (!P.SkipUntil(tok::r_brace, tok::r_paren, - tok::annot_pragma_openmp_end, Parser::StopBeforeMatch)) - ; - return; - } - Sema::OMPCtxStringType Buffer; - StringRef CtxSelectorName = P.getPreprocessor().getSpelling(Tok, Buffer); - auto Res = UsedCtx.try_emplace(CtxSelectorName, Tok.getLocation()); - if (!Res.second) { - // OpenMP 5.0, 2.3.2 Context Selectors, Restrictions. - // Each trait-selector-name can only be specified once. - P.Diag(Tok.getLocation(), diag::err_omp_declare_variant_ctx_mutiple_use) - << CtxSelectorName << "implementation"; - P.Diag(Res.first->getValue(), diag::note_omp_declare_variant_ctx_used_here) - << CtxSelectorName; - } - OpenMPContextSelectorKind CSKind = getOpenMPContextSelector(CtxSelectorName); - (void)P.ConsumeToken(); - switch (CSKind) { - case OMP_CTX_vendor: { - // Parse '('. - BalancedDelimiterTracker T(P, tok::l_paren, tok::annot_pragma_openmp_end); - (void)T.expectAndConsume(diag::err_expected_lparen_after, - CtxSelectorName.data()); - ExprResult Score = parseContextScore(P); - llvm::UniqueVector<Sema::OMPCtxStringType> Vendors; - do { - // Parse <vendor>. - StringRef VendorName; - if (Tok.is(tok::identifier)) { - Buffer.clear(); - VendorName = P.getPreprocessor().getSpelling(P.getCurToken(), Buffer); - (void)P.ConsumeToken(); - if (!VendorName.empty()) - Vendors.insert(VendorName); - } else { - P.Diag(Tok.getLocation(), diag::err_omp_declare_variant_item_expected) - << "vendor identifier" - << "vendor" - << "implementation"; +/// Parses an OpenMP context selector. +/// +/// <trait-selector-name> ['('[<trait-score>] <trait-property> [, <t-p>]* ')'] +void Parser::parseOMPContextSelector( + OMPTraitSelector &TISelector, llvm::omp::TraitSet Set, + llvm::StringMap<SourceLocation> &SeenSelectors) { + unsigned short OuterPC = ParenCount; + + // If anything went wrong we issue an error or warning and then skip the rest + // of the selector. However, commas are ambiguous so we look for the nesting + // of parentheses here as well. + auto FinishSelector = [OuterPC, this]() -> void { + bool Done = false; + while (!Done) { + while (!SkipUntil({tok::r_brace, tok::r_paren, tok::comma, + tok::annot_pragma_openmp_end}, + StopBeforeMatch)) + ; + if (Tok.is(tok::r_paren) && OuterPC > ParenCount) + (void)ConsumeParen(); + if (OuterPC <= ParenCount) { + Done = true; + break; } - if (!P.TryConsumeToken(tok::comma) && Tok.isNot(tok::r_paren)) { - P.Diag(Tok, diag::err_expected_punc) - << (VendorName.empty() ? "vendor name" : VendorName); + if (!Tok.is(tok::comma) && !Tok.is(tok::r_paren)) { + Done = true; + break; } - } while (Tok.is(tok::identifier)); - // Parse ')'. - (void)T.consumeClose(); - if (!Vendors.empty()) - Data.emplace_back(OMP_CTX_SET_implementation, CSKind, Score, Vendors); - break; + (void)ConsumeAnyToken(); + } + Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_continue_here) + << CONTEXT_SELECTOR_LVL; + }; + + SourceLocation SelectorLoc = Tok.getLocation(); + parseOMPTraitSelectorKind(TISelector, Set, SeenSelectors); + if (TISelector.Kind == TraitSelector::invalid) + return FinishSelector(); + + bool AllowsTraitScore = false; + bool RequiresProperty = false; + if (!isValidTraitSelectorForTraitSet(TISelector.Kind, Set, AllowsTraitScore, + RequiresProperty)) { + Diag(SelectorLoc, diag::warn_omp_ctx_incompatible_selector_for_set) + << getOpenMPContextTraitSelectorName(TISelector.Kind) + << getOpenMPContextTraitSetName(Set); + Diag(SelectorLoc, diag::note_omp_ctx_compatible_set_for_selector) + << getOpenMPContextTraitSelectorName(TISelector.Kind) + << getOpenMPContextTraitSetName( + getOpenMPContextTraitSetForSelector(TISelector.Kind)) + << RequiresProperty; + return FinishSelector(); + } + + if (!RequiresProperty) { + TISelector.Properties.push_back( + {getOpenMPContextTraitPropertyForSelector(TISelector.Kind)}); + return; } - case OMP_CTX_kind: - case OMP_CTX_unknown: - P.Diag(Tok.getLocation(), diag::warn_omp_declare_variant_cs_name_expected) - << "implementation"; - // Skip until either '}', ')', or end of directive. - while (!P.SkipUntil(tok::r_brace, tok::r_paren, - tok::annot_pragma_openmp_end, Parser::StopBeforeMatch)) - ; + + if (!Tok.is(tok::l_paren)) { + Diag(SelectorLoc, diag::warn_omp_ctx_selector_without_properties) + << getOpenMPContextTraitSelectorName(TISelector.Kind) + << getOpenMPContextTraitSetName(Set); + return FinishSelector(); + } + + if (TISelector.Kind == TraitSelector::user_condition) { + SourceLocation RLoc; + ExprResult Condition = ParseOpenMPParensExpr("user condition", RLoc); + if (!Condition.isUsable()) + return FinishSelector(); + TISelector.ScoreOrCondition = Condition.get(); + TISelector.Properties.push_back({TraitProperty::user_condition_unknown}); return; } + + BalancedDelimiterTracker BDT(*this, tok::l_paren, + tok::annot_pragma_openmp_end); + // Parse '('. + (void)BDT.consumeOpen(); + + SourceLocation ScoreLoc = Tok.getLocation(); + ExprResult Score = parseContextScore(*this); + + if (!AllowsTraitScore && !Score.isUnset()) { + if (Score.isUsable()) { + Diag(ScoreLoc, diag::warn_omp_ctx_incompatible_score_for_property) + << getOpenMPContextTraitSelectorName(TISelector.Kind) + << getOpenMPContextTraitSetName(Set) << Score.get(); + } else { + Diag(ScoreLoc, diag::warn_omp_ctx_incompatible_score_for_property) + << getOpenMPContextTraitSelectorName(TISelector.Kind) + << getOpenMPContextTraitSetName(Set) << "<invalid>"; + } + Score = ExprResult(); + } + + if (Score.isUsable()) + TISelector.ScoreOrCondition = Score.get(); + + llvm::StringMap<SourceLocation> SeenProperties; + do { + parseOMPContextProperty(TISelector, Set, SeenProperties); + } while (TryConsumeToken(tok::comma)); + + // Parse ')'. + BDT.consumeClose(); } -/// Parse context selector for 'device' selector set: -/// 'kind' '(' <kind> { ',' <kind> } ')' -static void -parseDeviceSelector(Parser &P, SourceLocation Loc, - llvm::StringMap<SourceLocation> &UsedCtx, - SmallVectorImpl<Sema::OMPCtxSelectorData> &Data) { - const Token &Tok = P.getCurToken(); - // Parse inner context selector set name, if any. - if (!Tok.is(tok::identifier)) { - P.Diag(Tok.getLocation(), diag::warn_omp_declare_variant_cs_name_expected) - << "device"; - // Skip until either '}', ')', or end of directive. - while (!P.SkipUntil(tok::r_brace, tok::r_paren, - tok::annot_pragma_openmp_end, Parser::StopBeforeMatch)) - ; +void Parser::parseOMPTraitSetKind(OMPTraitSet &TISet, + llvm::StringMap<SourceLocation> &Seen) { + TISet.Kind = TraitSet::invalid; + + SourceLocation NameLoc = Tok.getLocation(); + StringRef Name = getNameFromIdOrString(*this, Tok, CONTEXT_SELECTOR_SET_LVL + ); + if (Name.empty()) { + Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_options) + << CONTEXT_SELECTOR_SET_LVL << listOpenMPContextTraitSets(); return; } - Sema::OMPCtxStringType Buffer; - StringRef CtxSelectorName = P.getPreprocessor().getSpelling(Tok, Buffer); - auto Res = UsedCtx.try_emplace(CtxSelectorName, Tok.getLocation()); - if (!Res.second) { - // OpenMP 5.0, 2.3.2 Context Selectors, Restrictions. - // Each trait-selector-name can only be specified once. - P.Diag(Tok.getLocation(), diag::err_omp_declare_variant_ctx_mutiple_use) - << CtxSelectorName << "device"; - P.Diag(Res.first->getValue(), diag::note_omp_declare_variant_ctx_used_here) - << CtxSelectorName; - } - OpenMPContextSelectorKind CSKind = getOpenMPContextSelector(CtxSelectorName); - (void)P.ConsumeToken(); - switch (CSKind) { - case OMP_CTX_kind: { - // Parse '('. - BalancedDelimiterTracker T(P, tok::l_paren, tok::annot_pragma_openmp_end); - (void)T.expectAndConsume(diag::err_expected_lparen_after, - CtxSelectorName.data()); - llvm::UniqueVector<Sema::OMPCtxStringType> Kinds; - do { - // Parse <kind>. - StringRef KindName; - if (Tok.is(tok::identifier)) { - Buffer.clear(); - KindName = P.getPreprocessor().getSpelling(P.getCurToken(), Buffer); - SourceLocation SLoc = P.getCurToken().getLocation(); - (void)P.ConsumeToken(); - if (llvm::StringSwitch<bool>(KindName) - .Case("host", false) - .Case("nohost", false) - .Case("cpu", false) - .Case("gpu", false) - .Case("fpga", false) - .Default(true)) { - P.Diag(SLoc, diag::err_omp_wrong_device_kind_trait) << KindName; - } else { - Kinds.insert(KindName); - } - } else { - P.Diag(Tok.getLocation(), diag::err_omp_declare_variant_item_expected) - << "'host', 'nohost', 'cpu', 'gpu', or 'fpga'" - << "kind" - << "device"; + + TISet.Kind = getOpenMPContextTraitSetKind(Name); + if (TISet.Kind != TraitSet::invalid) { + if (checkForDuplicates(*this, Name, NameLoc, Seen, + CONTEXT_SELECTOR_SET_LVL)) + TISet.Kind = TraitSet::invalid; + return; + } + + // It follows diagnosis and helping notes. + Diag(NameLoc, diag::warn_omp_declare_variant_ctx_not_a_set) << Name; + + TraitSelector SelectorForName = getOpenMPContextTraitSelectorKind(Name); + if (SelectorForName != TraitSelector::invalid) { + Diag(NameLoc, diag::note_omp_declare_variant_ctx_is_a) + << Name << CONTEXT_SELECTOR_LVL << CONTEXT_SELECTOR_SET_LVL; + bool AllowsTraitScore = false; + bool RequiresProperty = false; + isValidTraitSelectorForTraitSet( + SelectorForName, getOpenMPContextTraitSetForSelector(SelectorForName), + AllowsTraitScore, RequiresProperty); + Diag(NameLoc, diag::note_omp_declare_variant_ctx_try) + << getOpenMPContextTraitSetName( + getOpenMPContextTraitSetForSelector(SelectorForName)) + << Name << (RequiresProperty ? "(<property-name>)" : ""); + return; + } + for (const auto &PotentialSet : + {TraitSet::construct, TraitSet::user, TraitSet::implementation, + TraitSet::device}) { + TraitProperty PropertyForName = + getOpenMPContextTraitPropertyKind(PotentialSet, Name); + if (PropertyForName == TraitProperty::invalid) + continue; + Diag(NameLoc, diag::note_omp_declare_variant_ctx_is_a) + << Name << CONTEXT_TRAIT_LVL << CONTEXT_SELECTOR_SET_LVL; + Diag(NameLoc, diag::note_omp_declare_variant_ctx_try) + << getOpenMPContextTraitSetName( + getOpenMPContextTraitSetForProperty(PropertyForName)) + << getOpenMPContextTraitSelectorName( + getOpenMPContextTraitSelectorForProperty(PropertyForName)) + << ("(" + Name + ")").str(); + return; + } + Diag(NameLoc, diag::note_omp_declare_variant_ctx_options) + << CONTEXT_SELECTOR_SET_LVL << listOpenMPContextTraitSets(); +} + +/// Parses an OpenMP context selector set. +/// +/// <trait-set-selector-name> '=' '{' <trait-selector> [, <trait-selector>]* '}' +void Parser::parseOMPContextSelectorSet( + OMPTraitSet &TISet, + llvm::StringMap<SourceLocation> &SeenSets) { + auto OuterBC = BraceCount; + + // If anything went wrong we issue an error or warning and then skip the rest + // of the set. However, commas are ambiguous so we look for the nesting + // of braces here as well. + auto FinishSelectorSet = [this, OuterBC]() -> void { + bool Done = false; + while (!Done) { + while (!SkipUntil({tok::comma, tok::r_brace, tok::r_paren, + tok::annot_pragma_openmp_end}, + StopBeforeMatch)) + ; + if (Tok.is(tok::r_brace) && OuterBC > BraceCount) + (void)ConsumeBrace(); + if (OuterBC <= BraceCount) { + Done = true; + break; } - if (!P.TryConsumeToken(tok::comma) && Tok.isNot(tok::r_paren)) { - P.Diag(Tok, diag::err_expected_punc) - << (KindName.empty() ? "kind of device" : KindName); + if (!Tok.is(tok::comma) && !Tok.is(tok::r_brace)) { + Done = true; + break; } - } while (Tok.is(tok::identifier)); - // Parse ')'. - (void)T.consumeClose(); - if (!Kinds.empty()) - Data.emplace_back(OMP_CTX_SET_device, CSKind, ExprResult(), Kinds); - break; + (void)ConsumeAnyToken(); + } + Diag(Tok.getLocation(), diag::note_omp_declare_variant_ctx_continue_here) + << CONTEXT_SELECTOR_SET_LVL; + }; + + parseOMPTraitSetKind(TISet, SeenSets); + if (TISet.Kind == TraitSet::invalid) + return FinishSelectorSet(); + + // Parse '='. + if (!TryConsumeToken(tok::equal)) + Diag(Tok.getLocation(), diag::warn_omp_declare_variant_expected) + << "=" + << ("context set name \"" + getOpenMPContextTraitSetName(TISet.Kind) + + "\"") + .str(); + + // Parse '{'. + if (Tok.is(tok::l_brace)) { + (void)ConsumeBrace(); + } else { + Diag(Tok.getLocation(), diag::warn_omp_declare_variant_expected) + << "{" + << ("'=' that follows the context set name \"" + + getOpenMPContextTraitSetName(TISet.Kind) + "\"") + .str(); } - case OMP_CTX_vendor: - case OMP_CTX_unknown: - P.Diag(Tok.getLocation(), diag::warn_omp_declare_variant_cs_name_expected) - << "device"; - // Skip until either '}', ')', or end of directive. - while (!P.SkipUntil(tok::r_brace, tok::r_paren, - tok::annot_pragma_openmp_end, Parser::StopBeforeMatch)) - ; - return; + + llvm::StringMap<SourceLocation> SeenSelectors; + do { + OMPTraitSelector TISelector; + parseOMPContextSelector(TISelector, TISet.Kind, SeenSelectors); + if (TISelector.Kind != TraitSelector::invalid && + !TISelector.Properties.empty()) + TISet.Selectors.push_back(TISelector); + } while (TryConsumeToken(tok::comma)); + + // Parse '}'. + if (Tok.is(tok::r_brace)) { + (void)ConsumeBrace(); + } else { + Diag(Tok.getLocation(), diag::warn_omp_declare_variant_expected) + << "}" + << ("context selectors for the context set \"" + + getOpenMPContextTraitSetName(TISet.Kind) + "\"") + .str(); } } -/// Parses clauses for 'declare variant' directive. -/// clause: -/// <selector_set_name> '=' '{' <context_selectors> '}' -/// [ ',' <selector_set_name> '=' '{' <context_selectors> '}' ] -bool Parser::parseOpenMPContextSelectors( - SourceLocation Loc, SmallVectorImpl<Sema::OMPCtxSelectorData> &Data) { - llvm::StringMap<SourceLocation> UsedCtxSets; +/// Parse OpenMP context selectors: +/// +/// <trait-set-selector> [, <trait-set-selector>]* +bool Parser::parseOMPContextSelectors(SourceLocation Loc, OMPTraitInfo& TI) { + llvm::StringMap<SourceLocation> SeenSets; do { - // Parse inner context selector set name. - if (!Tok.is(tok::identifier)) { - Diag(Tok.getLocation(), diag::err_omp_declare_variant_no_ctx_selector) - << getOpenMPClauseName(OMPC_match); - return true; - } - Sema::OMPCtxStringType Buffer; - StringRef CtxSelectorSetName = PP.getSpelling(Tok, Buffer); - auto Res = UsedCtxSets.try_emplace(CtxSelectorSetName, Tok.getLocation()); - if (!Res.second) { - // OpenMP 5.0, 2.3.2 Context Selectors, Restrictions. - // Each trait-set-selector-name can only be specified once. - Diag(Tok.getLocation(), diag::err_omp_declare_variant_ctx_set_mutiple_use) - << CtxSelectorSetName; - Diag(Res.first->getValue(), - diag::note_omp_declare_variant_ctx_set_used_here) - << CtxSelectorSetName; - } - // Parse '='. - (void)ConsumeToken(); - if (Tok.isNot(tok::equal)) { - Diag(Tok.getLocation(), diag::err_omp_declare_variant_equal_expected) - << CtxSelectorSetName; - return true; - } - (void)ConsumeToken(); - // TBD: add parsing of known context selectors. - // Unknown selector - just ignore it completely. - { - // Parse '{'. - BalancedDelimiterTracker TBr(*this, tok::l_brace, - tok::annot_pragma_openmp_end); - if (TBr.expectAndConsume(diag::err_expected_lbrace_after, "=")) - return true; - OpenMPContextSelectorSetKind CSSKind = - getOpenMPContextSelectorSet(CtxSelectorSetName); - llvm::StringMap<SourceLocation> UsedCtx; - do { - switch (CSSKind) { - case OMP_CTX_SET_implementation: - parseImplementationSelector(*this, Loc, UsedCtx, Data); - break; - case OMP_CTX_SET_device: - parseDeviceSelector(*this, Loc, UsedCtx, Data); - break; - case OMP_CTX_SET_unknown: - // Skip until either '}', ')', or end of directive. - while (!SkipUntil(tok::r_brace, tok::r_paren, - tok::annot_pragma_openmp_end, StopBeforeMatch)) - ; - break; - } - const Token PrevTok = Tok; - if (!TryConsumeToken(tok::comma) && Tok.isNot(tok::r_brace)) - Diag(Tok, diag::err_omp_expected_comma_brace) - << (PrevTok.isAnnotation() ? "context selector trait" - : PP.getSpelling(PrevTok)); - } while (Tok.is(tok::identifier)); - // Parse '}'. - (void)TBr.consumeClose(); - } - // Consume ',' - if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end)) - (void)ExpectAndConsume(tok::comma); - } while (Tok.isAnyIdentifier()); + OMPTraitSet TISet; + parseOMPContextSelectorSet(TISet, SeenSets); + if (TISet.Kind != TraitSet::invalid && !TISet.Selectors.empty()) + TI.Sets.push_back(TISet); + } while (TryConsumeToken(tok::comma)); + return false; } @@ -1102,10 +1381,30 @@ void Parser::ParseOMPDeclareVariantClauses(Parser::DeclGroupPtrTy Ptr, (void)ConsumeAnnotationToken(); return; } + + OMPTraitInfo &TI = Actions.getASTContext().getNewOMPTraitInfo(); + if (parseOMPDeclareVariantMatchClause(Loc, TI)) + return; + Optional<std::pair<FunctionDecl *, Expr *>> DeclVarData = Actions.checkOpenMPDeclareVariantFunction( - Ptr, AssociatedFunction.get(), SourceRange(Loc, Tok.getLocation())); + Ptr, AssociatedFunction.get(), TI, + SourceRange(Loc, Tok.getLocation())); + // Skip last tokens. + while (Tok.isNot(tok::annot_pragma_openmp_end)) + ConsumeAnyToken(); + if (DeclVarData && !TI.Sets.empty()) + Actions.ActOnOpenMPDeclareVariantDirective( + DeclVarData->first, DeclVarData->second, TI, + SourceRange(Loc, Tok.getLocation())); + + // Skip the last annot_pragma_openmp_end. + (void)ConsumeAnnotationToken(); +} + +bool Parser::parseOMPDeclareVariantMatchClause(SourceLocation Loc, + OMPTraitInfo &TI) { // Parse 'match'. OpenMPClauseKind CKind = Tok.isAnnotation() ? OMPC_unknown @@ -1117,47 +1416,32 @@ void Parser::ParseOMPDeclareVariantClauses(Parser::DeclGroupPtrTy Ptr, ; // Skip the last annot_pragma_openmp_end. (void)ConsumeAnnotationToken(); - return; + return true; } (void)ConsumeToken(); // Parse '('. BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); if (T.expectAndConsume(diag::err_expected_lparen_after, - getOpenMPClauseName(OMPC_match))) { + getOpenMPClauseName(OMPC_match).data())) { while (!SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch)) ; // Skip the last annot_pragma_openmp_end. (void)ConsumeAnnotationToken(); - return; + return true; } // Parse inner context selectors. - SmallVector<Sema::OMPCtxSelectorData, 4> Data; - if (!parseOpenMPContextSelectors(Loc, Data)) { - // Parse ')'. - (void)T.consumeClose(); - // Need to check for extra tokens. - if (Tok.isNot(tok::annot_pragma_openmp_end)) { - Diag(Tok, diag::warn_omp_extra_tokens_at_eol) - << getOpenMPDirectiveName(OMPD_declare_variant); - } - } + parseOMPContextSelectors(Loc, TI); - // Skip last tokens. - while (Tok.isNot(tok::annot_pragma_openmp_end)) - ConsumeAnyToken(); - if (DeclVarData.hasValue()) - Actions.ActOnOpenMPDeclareVariantDirective( - DeclVarData.getValue().first, DeclVarData.getValue().second, - SourceRange(Loc, Tok.getLocation()), Data); - // Skip the last annot_pragma_openmp_end. - (void)ConsumeAnnotationToken(); + // Parse ')' + (void)T.consumeClose(); + return false; } /// Parsing of simple OpenMP clauses like 'default' or 'proc_bind'. /// /// default-clause: -/// 'default' '(' 'none' | 'shared' ') +/// 'default' '(' 'none' | 'shared' | 'firstprivate' ') /// /// proc_bind-clause: /// 'proc_bind' '(' 'master' | 'close' | 'spread' ') @@ -1185,7 +1469,7 @@ parseOpenMPSimpleClause(Parser &P, OpenMPClauseKind Kind) { // Parse '('. BalancedDelimiterTracker T(P, tok::l_paren, tok::annot_pragma_openmp_end); if (T.expectAndConsume(diag::err_expected_lparen_after, - getOpenMPClauseName(Kind))) + getOpenMPClauseName(Kind).data())) return llvm::None; unsigned Type = getOpenMPSimpleClauseType( @@ -1289,21 +1573,48 @@ Parser::DeclGroupPtrTy Parser::ParseOMPDeclareTargetClauses() { return Actions.BuildDeclaratorGroup(Decls); } -void Parser::ParseOMPEndDeclareTargetDirective(OpenMPDirectiveKind DKind, - SourceLocation DTLoc) { - if (DKind != OMPD_end_declare_target) { - Diag(Tok, diag::err_expected_end_declare_target); - Diag(DTLoc, diag::note_matching) << "'#pragma omp declare target'"; +void Parser::skipUntilPragmaOpenMPEnd(OpenMPDirectiveKind DKind) { + // The last seen token is annot_pragma_openmp_end - need to check for + // extra tokens. + if (Tok.is(tok::annot_pragma_openmp_end)) + return; + + Diag(Tok, diag::warn_omp_extra_tokens_at_eol) + << getOpenMPDirectiveName(DKind); + while (Tok.isNot(tok::annot_pragma_openmp_end)) + ConsumeAnyToken(); +} + +void Parser::parseOMPEndDirective(OpenMPDirectiveKind BeginKind, + OpenMPDirectiveKind ExpectedKind, + OpenMPDirectiveKind FoundKind, + SourceLocation BeginLoc, + SourceLocation FoundLoc, + bool SkipUntilOpenMPEnd) { + int DiagSelection = ExpectedKind == OMPD_end_declare_target ? 0 : 1; + + if (FoundKind == ExpectedKind) { + ConsumeAnyToken(); + skipUntilPragmaOpenMPEnd(ExpectedKind); return; } - ConsumeAnyToken(); - if (Tok.isNot(tok::annot_pragma_openmp_end)) { - Diag(Tok, diag::warn_omp_extra_tokens_at_eol) - << getOpenMPDirectiveName(OMPD_end_declare_target); + + Diag(FoundLoc, diag::err_expected_end_declare_target_or_variant) + << DiagSelection; + Diag(BeginLoc, diag::note_matching) + << ("'#pragma omp " + getOpenMPDirectiveName(BeginKind) + "'").str(); + if (SkipUntilOpenMPEnd) SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); - } +} + +void Parser::ParseOMPEndDeclareTargetDirective(OpenMPDirectiveKind DKind, + SourceLocation DKLoc) { + parseOMPEndDirective(OMPD_declare_target, OMPD_end_declare_target, DKind, + DKLoc, Tok.getLocation(), + /* SkipUntilOpenMPEnd */ false); // Skip the last annot_pragma_openmp_end. - ConsumeAnyToken(); + if (Tok.is(tok::annot_pragma_openmp_end)) + ConsumeAnnotationToken(); } /// Parsing of declarative OpenMP directives. @@ -1381,13 +1692,7 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( DeclDirectiveListParserHelper Helper(this, DKind); if (!ParseOpenMPSimpleVarList(DKind, Helper, /*AllowScopeSpecifier=*/true)) { - // The last seen token is annot_pragma_openmp_end - need to check for - // extra tokens. - if (Tok.isNot(tok::annot_pragma_openmp_end)) { - Diag(Tok, diag::warn_omp_extra_tokens_at_eol) - << getOpenMPDirectiveName(DKind); - SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); - } + skipUntilPragmaOpenMPEnd(DKind); // Skip the last annot_pragma_openmp_end. ConsumeAnnotationToken(); return Actions.ActOnOpenMPThreadprivateDirective(Loc, @@ -1403,18 +1708,18 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( SmallVector<OMPClause *, 1> Clauses; if (Tok.isNot(tok::annot_pragma_openmp_end)) { SmallVector<llvm::PointerIntPair<OMPClause *, 1, bool>, - OMPC_unknown + 1> - FirstClauses(OMPC_unknown + 1); + llvm::omp::Clause_enumSize + 1> + FirstClauses(llvm::omp::Clause_enumSize + 1); while (Tok.isNot(tok::annot_pragma_openmp_end)) { OpenMPClauseKind CKind = Tok.isAnnotation() ? OMPC_unknown : getOpenMPClauseKind(PP.getSpelling(Tok)); Actions.StartOpenMPClause(CKind); - OMPClause *Clause = ParseOpenMPClause(OMPD_allocate, CKind, - !FirstClauses[CKind].getInt()); + OMPClause *Clause = ParseOpenMPClause( + OMPD_allocate, CKind, !FirstClauses[unsigned(CKind)].getInt()); SkipUntil(tok::comma, tok::identifier, tok::annot_pragma_openmp_end, StopBeforeMatch); - FirstClauses[CKind].setInt(true); + FirstClauses[unsigned(CKind)].setInt(true); if (Clause != nullptr) Clauses.push_back(Clause); if (Tok.is(tok::annot_pragma_openmp_end)) { @@ -1426,13 +1731,7 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( ConsumeToken(); Actions.EndOpenMPClause(); } - // The last seen token is annot_pragma_openmp_end - need to check for - // extra tokens. - if (Tok.isNot(tok::annot_pragma_openmp_end)) { - Diag(Tok, diag::warn_omp_extra_tokens_at_eol) - << getOpenMPDirectiveName(DKind); - SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); - } + skipUntilPragmaOpenMPEnd(DKind); } // Skip the last annot_pragma_openmp_end. ConsumeAnnotationToken(); @@ -1444,8 +1743,9 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( case OMPD_requires: { SourceLocation StartLoc = ConsumeToken(); SmallVector<OMPClause *, 5> Clauses; - SmallVector<llvm::PointerIntPair<OMPClause *, 1, bool>, OMPC_unknown + 1> - FirstClauses(OMPC_unknown + 1); + SmallVector<llvm::PointerIntPair<OMPClause *, 1, bool>, + llvm::omp::Clause_enumSize + 1> + FirstClauses(llvm::omp::Clause_enumSize + 1); if (Tok.is(tok::annot_pragma_openmp_end)) { Diag(Tok, diag::err_omp_expected_clause) << getOpenMPDirectiveName(OMPD_requires); @@ -1456,11 +1756,11 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( ? OMPC_unknown : getOpenMPClauseKind(PP.getSpelling(Tok)); Actions.StartOpenMPClause(CKind); - OMPClause *Clause = ParseOpenMPClause(OMPD_requires, CKind, - !FirstClauses[CKind].getInt()); + OMPClause *Clause = ParseOpenMPClause( + OMPD_requires, CKind, !FirstClauses[unsigned(CKind)].getInt()); SkipUntil(tok::comma, tok::identifier, tok::annot_pragma_openmp_end, StopBeforeMatch); - FirstClauses[CKind].setInt(true); + FirstClauses[unsigned(CKind)].setInt(true); if (Clause != nullptr) Clauses.push_back(Clause); if (Tok.is(tok::annot_pragma_openmp_end)) { @@ -1473,7 +1773,7 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( Actions.EndOpenMPClause(); } // Consume final annot_pragma_openmp_end - if (Clauses.size() == 0) { + if (Clauses.empty()) { Diag(Tok, diag::err_omp_expected_clause) << getOpenMPDirectiveName(OMPD_requires); ConsumeAnnotationToken(); @@ -1485,14 +1785,7 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( case OMPD_declare_reduction: ConsumeToken(); if (DeclGroupPtrTy Res = ParseOpenMPDeclareReductionDirective(AS)) { - // The last seen token is annot_pragma_openmp_end - need to check for - // extra tokens. - if (Tok.isNot(tok::annot_pragma_openmp_end)) { - Diag(Tok, diag::warn_omp_extra_tokens_at_eol) - << getOpenMPDirectiveName(OMPD_declare_reduction); - while (Tok.isNot(tok::annot_pragma_openmp_end)) - ConsumeAnyToken(); - } + skipUntilPragmaOpenMPEnd(OMPD_declare_reduction); // Skip the last annot_pragma_openmp_end. ConsumeAnnotationToken(); return Res; @@ -1507,6 +1800,63 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( } break; } + case OMPD_begin_declare_variant: { + // The syntax is: + // { #pragma omp begin declare variant clause } + // <function-declaration-or-definition-sequence> + // { #pragma omp end declare variant } + // + ConsumeToken(); + OMPTraitInfo &TI = Actions.getASTContext().getNewOMPTraitInfo(); + if (parseOMPDeclareVariantMatchClause(Loc, TI)) + break; + + // Skip last tokens. + skipUntilPragmaOpenMPEnd(OMPD_begin_declare_variant); + + ParsingOpenMPDirectiveRAII NormalScope(*this, /*Value=*/false); + + VariantMatchInfo VMI; + ASTContext &ASTCtx = Actions.getASTContext(); + TI.getAsVariantMatchInfo(ASTCtx, VMI); + OMPContext OMPCtx(ASTCtx.getLangOpts().OpenMPIsDevice, + ASTCtx.getTargetInfo().getTriple()); + + if (isVariantApplicableInContext(VMI, OMPCtx, /* DeviceSetOnly */ true)) { + Actions.ActOnOpenMPBeginDeclareVariant(Loc, TI); + break; + } + + // Elide all the code till the matching end declare variant was found. + unsigned Nesting = 1; + SourceLocation DKLoc; + OpenMPDirectiveKind DK = OMPD_unknown; + do { + DKLoc = Tok.getLocation(); + DK = parseOpenMPDirectiveKind(*this); + if (DK == OMPD_end_declare_variant) + --Nesting; + else if (DK == OMPD_begin_declare_variant) + ++Nesting; + if (!Nesting || isEofOrEom()) + break; + ConsumeAnyToken(); + } while (true); + + parseOMPEndDirective(OMPD_begin_declare_variant, OMPD_end_declare_variant, + DK, Loc, DKLoc, /* SkipUntilOpenMPEnd */ true); + if (isEofOrEom()) + return nullptr; + break; + } + case OMPD_end_declare_variant: { + if (Actions.isInOpenMPDeclareVariantScope()) + Actions.ActOnOpenMPEndDeclareVariant(); + else + Diag(Loc, diag::err_expected_begin_declare_variant); + ConsumeToken(); + break; + } case OMPD_declare_variant: case OMPD_declare_simd: { // The syntax is: @@ -1563,6 +1913,7 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( if (!Actions.ActOnStartOpenMPDeclareTargetDirective(DTLoc)) return DeclGroupPtrTy(); + ParsingOpenMPDirectiveRAII NormalScope(*this, /*Value=*/false); llvm::SmallVector<Decl *, 4> Decls; DKind = parseOpenMPDirectiveKind(*this); while (DKind != OMPD_end_declare_target && Tok.isNot(tok::eof) && @@ -1608,6 +1959,8 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( case OMPD_taskwait: case OMPD_taskgroup: case OMPD_flush: + case OMPD_depobj: + case OMPD_scan: case OMPD_for: case OMPD_for_simd: case OMPD_sections: @@ -1656,6 +2009,8 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( Diag(Tok, diag::err_omp_unexpected_directive) << 1 << getOpenMPDirectiveName(DKind); break; + default: + break; } while (Tok.isNot(tok::annot_pragma_openmp_end)) ConsumeAnyToken(); @@ -1709,8 +2064,9 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) { ParsingOpenMPDirectiveRAII DirScope(*this); ParenBraceBracketBalancer BalancerRAIIObj(*this); SmallVector<OMPClause *, 5> Clauses; - SmallVector<llvm::PointerIntPair<OMPClause *, 1, bool>, OMPC_unknown + 1> - FirstClauses(OMPC_unknown + 1); + SmallVector<llvm::PointerIntPair<OMPClause *, 1, bool>, + llvm::omp::Clause_enumSize + 1> + FirstClauses(llvm::omp::Clause_enumSize + 1); unsigned ScopeFlags = Scope::FnScope | Scope::DeclScope | Scope::CompoundStmtScope | Scope::OpenMPDirectiveScope; SourceLocation Loc = ConsumeAnnotationToken(), EndLoc; @@ -1720,7 +2076,6 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) { DeclarationNameInfo DirName; StmtResult Directive = StmtError(); bool HasAssociatedStatement = true; - bool FlushHasClause = false; switch (DKind) { case OMPD_threadprivate: { @@ -1734,13 +2089,7 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) { DeclDirectiveListParserHelper Helper(this, DKind); if (!ParseOpenMPSimpleVarList(DKind, Helper, /*AllowScopeSpecifier=*/false)) { - // The last seen token is annot_pragma_openmp_end - need to check for - // extra tokens. - if (Tok.isNot(tok::annot_pragma_openmp_end)) { - Diag(Tok, diag::warn_omp_extra_tokens_at_eol) - << getOpenMPDirectiveName(DKind); - SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); - } + skipUntilPragmaOpenMPEnd(DKind); DeclGroupPtrTy Res = Actions.ActOnOpenMPThreadprivateDirective( Loc, Helper.getIdentifiers()); Directive = Actions.ActOnDeclStmt(Res, Loc, Tok.getLocation()); @@ -1762,18 +2111,18 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) { SmallVector<OMPClause *, 1> Clauses; if (Tok.isNot(tok::annot_pragma_openmp_end)) { SmallVector<llvm::PointerIntPair<OMPClause *, 1, bool>, - OMPC_unknown + 1> - FirstClauses(OMPC_unknown + 1); + llvm::omp::Clause_enumSize + 1> + FirstClauses(llvm::omp::Clause_enumSize + 1); while (Tok.isNot(tok::annot_pragma_openmp_end)) { OpenMPClauseKind CKind = Tok.isAnnotation() ? OMPC_unknown : getOpenMPClauseKind(PP.getSpelling(Tok)); Actions.StartOpenMPClause(CKind); - OMPClause *Clause = ParseOpenMPClause(OMPD_allocate, CKind, - !FirstClauses[CKind].getInt()); + OMPClause *Clause = ParseOpenMPClause( + OMPD_allocate, CKind, !FirstClauses[unsigned(CKind)].getInt()); SkipUntil(tok::comma, tok::identifier, tok::annot_pragma_openmp_end, StopBeforeMatch); - FirstClauses[CKind].setInt(true); + FirstClauses[unsigned(CKind)].setInt(true); if (Clause != nullptr) Clauses.push_back(Clause); if (Tok.is(tok::annot_pragma_openmp_end)) { @@ -1785,13 +2134,7 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) { ConsumeToken(); Actions.EndOpenMPClause(); } - // The last seen token is annot_pragma_openmp_end - need to check for - // extra tokens. - if (Tok.isNot(tok::annot_pragma_openmp_end)) { - Diag(Tok, diag::warn_omp_extra_tokens_at_eol) - << getOpenMPDirectiveName(DKind); - SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); - } + skipUntilPragmaOpenMPEnd(DKind); } DeclGroupPtrTy Res = Actions.ActOnOpenMPAllocateDirective( Loc, Helper.getIdentifiers(), Clauses); @@ -1804,14 +2147,7 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) { ConsumeToken(); if (DeclGroupPtrTy Res = ParseOpenMPDeclareReductionDirective(/*AS=*/AS_none)) { - // The last seen token is annot_pragma_openmp_end - need to check for - // extra tokens. - if (Tok.isNot(tok::annot_pragma_openmp_end)) { - Diag(Tok, diag::warn_omp_extra_tokens_at_eol) - << getOpenMPDirectiveName(OMPD_declare_reduction); - while (Tok.isNot(tok::annot_pragma_openmp_end)) - ConsumeAnyToken(); - } + skipUntilPragmaOpenMPEnd(OMPD_declare_reduction); ConsumeAnyToken(); Directive = Actions.ActOnDeclStmt(Res, Loc, Tok.getLocation()); } else { @@ -1831,13 +2167,8 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) { break; } case OMPD_flush: - if (PP.LookAhead(0).is(tok::l_paren)) { - FlushHasClause = true; - // Push copy of the current token back to stream to properly parse - // pseudo-clause OMPFlushClause. - PP.EnterToken(Tok, /*IsReinject*/ true); - } - LLVM_FALLTHROUGH; + case OMPD_depobj: + case OMPD_scan: case OMPD_taskyield: case OMPD_barrier: case OMPD_taskwait: @@ -1897,6 +2228,13 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) { case OMPD_target_teams_distribute_parallel_for: case OMPD_target_teams_distribute_parallel_for_simd: case OMPD_target_teams_distribute_simd: { + // Special processing for flush and depobj clauses. + Token ImplicitTok; + bool ImplicitClauseAllowed = false; + if (DKind == OMPD_flush || DKind == OMPD_depobj) { + ImplicitTok = Tok; + ImplicitClauseAllowed = true; + } ConsumeToken(); // Parse directive name of the 'critical' directive if any. if (DKind == OMPD_critical) { @@ -1926,18 +2264,37 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) { Actions.StartOpenMPDSABlock(DKind, DirName, Actions.getCurScope(), Loc); while (Tok.isNot(tok::annot_pragma_openmp_end)) { - OpenMPClauseKind CKind = - Tok.isAnnotation() - ? OMPC_unknown - : FlushHasClause ? OMPC_flush - : getOpenMPClauseKind(PP.getSpelling(Tok)); + bool HasImplicitClause = false; + if (ImplicitClauseAllowed && Tok.is(tok::l_paren)) { + HasImplicitClause = true; + // Push copy of the current token back to stream to properly parse + // pseudo-clause OMPFlushClause or OMPDepobjClause. + PP.EnterToken(Tok, /*IsReinject*/ true); + PP.EnterToken(ImplicitTok, /*IsReinject*/ true); + ConsumeAnyToken(); + } + OpenMPClauseKind CKind = Tok.isAnnotation() + ? OMPC_unknown + : getOpenMPClauseKind(PP.getSpelling(Tok)); + if (HasImplicitClause) { + assert(CKind == OMPC_unknown && "Must be unknown implicit clause."); + if (DKind == OMPD_flush) { + CKind = OMPC_flush; + } else { + assert(DKind == OMPD_depobj && + "Expected flush or depobj directives."); + CKind = OMPC_depobj; + } + } + // No more implicit clauses allowed. + ImplicitClauseAllowed = false; Actions.StartOpenMPClause(CKind); - FlushHasClause = false; - OMPClause *Clause = - ParseOpenMPClause(DKind, CKind, !FirstClauses[CKind].getInt()); - FirstClauses[CKind].setInt(true); + HasImplicitClause = false; + OMPClause *Clause = ParseOpenMPClause( + DKind, CKind, !FirstClauses[unsigned(CKind)].getInt()); + FirstClauses[unsigned(CKind)].setInt(true); if (Clause) { - FirstClauses[CKind].setPointer(Clause); + FirstClauses[unsigned(CKind)].setPointer(Clause); Clauses.push_back(Clause); } @@ -1954,7 +2311,7 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) { // OpenMP [2.13.8, ordered Construct, Syntax] // If the depend clause is specified, the ordered construct is a stand-alone // directive. - if (DKind == OMPD_ordered && FirstClauses[OMPC_depend].getInt()) { + if (DKind == OMPD_ordered && FirstClauses[unsigned(OMPC_depend)].getInt()) { if ((StmtCtx & ParsedStmtContext::AllowStandaloneOpenMPDirectives) == ParsedStmtContext()) { Diag(Loc, diag::err_omp_immediate_directive) @@ -1971,6 +2328,7 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) { // FIXME: We create a bogus CompoundStmt scope to hold the contents of // the captured region. Code elsewhere assumes that any FunctionScopeInfo // should have at least one compound statement scope within it. + ParsingOpenMPDirectiveRAII NormalScope(*this, /*Value=*/false); AssociatedStmt = (Sema::CompoundScopeRAII(Actions), ParseStatement()); AssociatedStmt = Actions.ActOnOpenMPRegionEnd(AssociatedStmt, Clauses); } else if (DKind == OMPD_target_update || DKind == OMPD_target_enter_data || @@ -1994,12 +2352,15 @@ Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) { case OMPD_declare_target: case OMPD_end_declare_target: case OMPD_requires: + case OMPD_begin_declare_variant: + case OMPD_end_declare_variant: case OMPD_declare_variant: Diag(Tok, diag::err_omp_unexpected_directive) << 1 << getOpenMPDirectiveName(DKind); SkipUntil(tok::annot_pragma_openmp_end); break; case OMPD_unknown: + default: Diag(Tok, diag::err_omp_unknown_directive); SkipUntil(tok::annot_pragma_openmp_end); break; @@ -2033,12 +2394,14 @@ bool Parser::ParseOpenMPSimpleVarList( NoIdentIsFound = false; if (AllowScopeSpecifier && getLangOpts().CPlusPlus && - ParseOptionalCXXScopeSpecifier(SS, nullptr, false)) { + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, false)) { IsCorrect = false; SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch); - } else if (ParseUnqualifiedId(SS, false, false, false, false, nullptr, - nullptr, Name)) { + } else if (ParseUnqualifiedId(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, false, false, + false, false, nullptr, Name)) { IsCorrect = false; SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch); @@ -2070,6 +2433,50 @@ bool Parser::ParseOpenMPSimpleVarList( return !IsCorrect; } +OMPClause *Parser::ParseOpenMPUsesAllocatorClause(OpenMPDirectiveKind DKind) { + SourceLocation Loc = Tok.getLocation(); + ConsumeAnyToken(); + + // Parse '('. + BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); + if (T.expectAndConsume(diag::err_expected_lparen_after, "uses_allocator")) + return nullptr; + SmallVector<Sema::UsesAllocatorsData, 4> Data; + do { + ExprResult Allocator = ParseCXXIdExpression(); + if (Allocator.isInvalid()) { + SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, + StopBeforeMatch); + break; + } + Sema::UsesAllocatorsData &D = Data.emplace_back(); + D.Allocator = Allocator.get(); + if (Tok.is(tok::l_paren)) { + BalancedDelimiterTracker T(*this, tok::l_paren, + tok::annot_pragma_openmp_end); + T.consumeOpen(); + ExprResult AllocatorTraits = ParseCXXIdExpression(); + T.consumeClose(); + if (AllocatorTraits.isInvalid()) { + SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, + StopBeforeMatch); + break; + } + D.AllocatorTraits = AllocatorTraits.get(); + D.LParenLoc = T.getOpenLocation(); + D.RParenLoc = T.getCloseLocation(); + } + if (Tok.isNot(tok::comma) && Tok.isNot(tok::r_paren)) + Diag(Tok, diag::err_omp_expected_punc) << "uses_allocators" << 0; + // Parse ',' + if (Tok.is(tok::comma)) + ConsumeAnyToken(); + } while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end)); + T.consumeClose(); + return Actions.ActOnOpenMPUsesAllocatorClause(Loc, T.getOpenLocation(), + T.getCloseLocation(), Data); +} + /// Parsing of OpenMP clauses. /// /// clause: @@ -2084,10 +2491,14 @@ bool Parser::ParseOpenMPSimpleVarList( /// thread_limit-clause | priority-clause | grainsize-clause | /// nogroup-clause | num_tasks-clause | hint-clause | to-clause | /// from-clause | is_device_ptr-clause | task_reduction-clause | -/// in_reduction-clause | allocator-clause | allocate-clause +/// in_reduction-clause | allocator-clause | allocate-clause | +/// acq_rel-clause | acquire-clause | release-clause | relaxed-clause | +/// depobj-clause | destroy-clause | detach-clause | inclusive-clause | +/// exclusive-clause | uses_allocators-clause | use_device_addr-clause /// OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, OpenMPClauseKind CKind, bool FirstClause) { + OMPClauseKind = CKind; OMPClause *Clause = nullptr; bool ErrorFound = false; bool WrongDirective = false; @@ -2107,7 +2518,6 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, case OMPC_simdlen: case OMPC_collapse: case OMPC_ordered: - case OMPC_device: case OMPC_num_teams: case OMPC_thread_limit: case OMPC_priority: @@ -2115,14 +2525,14 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, case OMPC_num_tasks: case OMPC_hint: case OMPC_allocator: + case OMPC_depobj: + case OMPC_detach: // OpenMP [2.5, Restrictions] // At most one num_threads clause can appear on the directive. // OpenMP [2.8.1, simd construct, Restrictions] // Only one safelen clause can appear on a simd directive. // Only one simdlen clause can appear on a simd directive. // Only one collapse clause can appear on a simd directive. - // OpenMP [2.9.1, target data construct, Restrictions] - // At most one device clause can appear on the directive. // OpenMP [2.11.1, task Construct, Restrictions] // At most one if clause can appear on the directive. // At most one final clause can appear on the directive. @@ -2137,6 +2547,8 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, // At most one num_tasks clause can appear on the directive. // OpenMP [2.11.3, allocate Directive, Restrictions] // At most one allocator clause can appear on the directive. + // OpenMP 5.0, 2.10.1 task Construct, Restrictions. + // At most one detach clause can appear on the directive. if (!FirstClause) { Diag(Tok, diag::err_omp_more_one_clause) << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind) << 0; @@ -2151,6 +2563,7 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, case OMPC_default: case OMPC_proc_bind: case OMPC_atomic_default_mem_order: + case OMPC_order: // OpenMP [2.14.3.1, Restrictions] // Only a single default clause may be specified on a parallel, task or // teams directive. @@ -2159,7 +2572,7 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, // OpenMP [5.0, Requires directive, Restrictions] // At most one atomic_default_mem_order clause can appear // on the directive - if (!FirstClause) { + if (!FirstClause && CKind != OMPC_order) { Diag(Tok, diag::err_omp_more_one_clause) << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind) << 0; ErrorFound = true; @@ -2167,6 +2580,7 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, Clause = ParseOpenMPSimpleClause(CKind, WrongDirective); break; + case OMPC_device: case OMPC_schedule: case OMPC_dist_schedule: case OMPC_defaultmap: @@ -2174,6 +2588,8 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, // Only one schedule clause can appear on a loop directive. // OpenMP 4.5 [2.10.4, Restrictions, p. 106] // At most one defaultmap clause can appear on the directive. + // OpenMP 5.0 [2.12.5, target construct, Restrictions] + // At most one device clause can appear on the directive. if ((getLangOpts().OpenMP < 50 || CKind != OMPC_defaultmap) && !FirstClause) { Diag(Tok, diag::err_omp_more_one_clause) @@ -2182,16 +2598,19 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, } LLVM_FALLTHROUGH; case OMPC_if: - Clause = ParseOpenMPSingleExprWithArgClause(CKind, WrongDirective); + Clause = ParseOpenMPSingleExprWithArgClause(DKind, CKind, WrongDirective); break; case OMPC_nowait: case OMPC_untied: case OMPC_mergeable: case OMPC_read: case OMPC_write: - case OMPC_update: case OMPC_capture: case OMPC_seq_cst: + case OMPC_acq_rel: + case OMPC_acquire: + case OMPC_release: + case OMPC_relaxed: case OMPC_threads: case OMPC_simd: case OMPC_nogroup: @@ -2199,6 +2618,7 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, case OMPC_unified_shared_memory: case OMPC_reverse_offload: case OMPC_dynamic_allocators: + case OMPC_destroy: // OpenMP [2.7.1, Restrictions, p. 9] // Only one ordered clause can appear on a loop directive. // OpenMP [2.7.1, Restrictions, C/C++, p. 4] @@ -2213,6 +2633,17 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, Clause = ParseOpenMPClause(CKind, WrongDirective); break; + case OMPC_update: + if (!FirstClause) { + Diag(Tok, diag::err_omp_more_one_clause) + << getOpenMPDirectiveName(DKind) << getOpenMPClauseName(CKind) << 0; + ErrorFound = true; + } + + Clause = (DKind == OMPD_depobj) + ? ParseOpenMPSimpleClause(CKind, WrongDirective) + : ParseOpenMPClause(CKind, WrongDirective); + break; case OMPC_private: case OMPC_firstprivate: case OMPC_lastprivate: @@ -2230,16 +2661,21 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, case OMPC_to: case OMPC_from: case OMPC_use_device_ptr: + case OMPC_use_device_addr: case OMPC_is_device_ptr: case OMPC_allocate: case OMPC_nontemporal: + case OMPC_inclusive: + case OMPC_exclusive: + case OMPC_affinity: Clause = ParseOpenMPVarListClause(DKind, CKind, WrongDirective); break; + case OMPC_uses_allocators: + Clause = ParseOpenMPUsesAllocatorClause(DKind); + break; case OMPC_device_type: case OMPC_unknown: - Diag(Tok, diag::warn_omp_extra_tokens_at_eol) - << getOpenMPDirectiveName(DKind); - SkipUntil(tok::annot_pragma_openmp_end, StopBeforeMatch); + skipUntilPragmaOpenMPEnd(DKind); break; case OMPC_threadprivate: case OMPC_uniform: @@ -2249,6 +2685,8 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind, << getOpenMPClauseName(CKind) << getOpenMPDirectiveName(DKind); SkipUntil(tok::comma, tok::annot_pragma_openmp_end, StopBeforeMatch); break; + default: + break; } return ErrorFound ? nullptr : Clause; } @@ -2279,7 +2717,8 @@ ExprResult Parser::ParseOpenMPParensExpr(StringRef ClauseName, /// Parsing of OpenMP clauses with single expressions like 'final', /// 'collapse', 'safelen', 'num_threads', 'simdlen', 'num_teams', -/// 'thread_limit', 'simdlen', 'priority', 'grainsize', 'num_tasks' or 'hint'. +/// 'thread_limit', 'simdlen', 'priority', 'grainsize', 'num_tasks', 'hint' or +/// 'detach'. /// /// final-clause: /// 'final' '(' expression ')' @@ -2311,6 +2750,9 @@ ExprResult Parser::ParseOpenMPParensExpr(StringRef ClauseName, /// allocator-clause: /// 'allocator' '(' expression ')' /// +/// detach-clause: +/// 'detach' '(' event-handler-expression ')' +/// OMPClause *Parser::ParseOpenMPSingleExprClause(OpenMPClauseKind Kind, bool ParseOnly) { SourceLocation Loc = ConsumeToken(); @@ -2330,16 +2772,27 @@ OMPClause *Parser::ParseOpenMPSingleExprClause(OpenMPClauseKind Kind, /// Parsing of simple OpenMP clauses like 'default' or 'proc_bind'. /// /// default-clause: -/// 'default' '(' 'none' | 'shared' ') +/// 'default' '(' 'none' | 'shared' | 'firstprivate' ')' /// /// proc_bind-clause: -/// 'proc_bind' '(' 'master' | 'close' | 'spread' ') +/// 'proc_bind' '(' 'master' | 'close' | 'spread' ')' +/// +/// update-clause: +/// 'update' '(' 'in' | 'out' | 'inout' | 'mutexinoutset' ')' /// OMPClause *Parser::ParseOpenMPSimpleClause(OpenMPClauseKind Kind, bool ParseOnly) { llvm::Optional<SimpleClauseData> Val = parseOpenMPSimpleClause(*this, Kind); if (!Val || ParseOnly) return nullptr; + if (getLangOpts().OpenMP < 51 && Kind == OMPC_default && + static_cast<DefaultKind>(Val.getValue().Type) == + OMP_DEFAULT_firstprivate) { + Diag(Val.getValue().LOpen, diag::err_omp_invalid_dsa) + << getOpenMPClauseName(OMPC_firstprivate) + << getOpenMPClauseName(OMPC_default) << "5.1"; + return nullptr; + } return Actions.ActOnOpenMPSimpleClause( Kind, Val.getValue().Type, Val.getValue().TypeLoc, Val.getValue().LOpen, Val.getValue().Loc, Val.getValue().RLoc); @@ -2380,7 +2833,6 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPClauseKind Kind, bool ParseOnly) { return Actions.ActOnOpenMPClause(Kind, Loc, Tok.getLocation()); } - /// Parsing of OpenMP clauses with single expressions and some additional /// argument like 'schedule' or 'dist_schedule'. /// @@ -2392,16 +2844,20 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPClauseKind Kind, bool ParseOnly) { /// 'if' '(' [ directive-name-modifier ':' ] expression ')' /// /// defaultmap: -/// 'defaultmap' '(' modifier ':' kind ')' +/// 'defaultmap' '(' modifier [ ':' kind ] ')' +/// +/// device-clause: +/// 'device' '(' [ device-modifier ':' ] expression ')' /// -OMPClause *Parser::ParseOpenMPSingleExprWithArgClause(OpenMPClauseKind Kind, +OMPClause *Parser::ParseOpenMPSingleExprWithArgClause(OpenMPDirectiveKind DKind, + OpenMPClauseKind Kind, bool ParseOnly) { SourceLocation Loc = ConsumeToken(); SourceLocation DelimLoc; // Parse '('. BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); if (T.expectAndConsume(diag::err_expected_lparen_after, - getOpenMPClauseName(Kind))) + getOpenMPClauseName(Kind).data())) return nullptr; ExprResult Val; @@ -2477,17 +2933,37 @@ OMPClause *Parser::ParseOpenMPSingleExprWithArgClause(OpenMPClauseKind Kind, Tok.isNot(tok::annot_pragma_openmp_end)) ConsumeAnyToken(); // Parse ':' - if (Tok.is(tok::colon)) + if (Tok.is(tok::colon) || getLangOpts().OpenMP < 50) { + if (Tok.is(tok::colon)) + ConsumeAnyToken(); + else if (Arg.back() != OMPC_DEFAULTMAP_MODIFIER_unknown) + Diag(Tok, diag::warn_pragma_expected_colon) << "defaultmap modifier"; + // Get a defaultmap kind + Arg.push_back(getOpenMPSimpleClauseType( + Kind, Tok.isAnnotation() ? "" : PP.getSpelling(Tok))); + KLoc.push_back(Tok.getLocation()); + if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::comma) && + Tok.isNot(tok::annot_pragma_openmp_end)) + ConsumeAnyToken(); + } else { + Arg.push_back(OMPC_DEFAULTMAP_unknown); + KLoc.push_back(SourceLocation()); + } + } else if (Kind == OMPC_device) { + // Only target executable directives support extended device construct. + if (isOpenMPTargetExecutionDirective(DKind) && getLangOpts().OpenMP >= 50 && + NextToken().is(tok::colon)) { + // Parse optional <device modifier> ':' + Arg.push_back(getOpenMPSimpleClauseType( + Kind, Tok.isAnnotation() ? "" : PP.getSpelling(Tok))); + KLoc.push_back(Tok.getLocation()); ConsumeAnyToken(); - else if (Arg.back() != OMPC_DEFAULTMAP_MODIFIER_unknown) - Diag(Tok, diag::warn_pragma_expected_colon) << "defaultmap modifier"; - // Get a defaultmap kind - Arg.push_back(getOpenMPSimpleClauseType( - Kind, Tok.isAnnotation() ? "" : PP.getSpelling(Tok))); - KLoc.push_back(Tok.getLocation()); - if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::comma) && - Tok.isNot(tok::annot_pragma_openmp_end)) + // Parse ':' ConsumeAnyToken(); + } else { + Arg.push_back(OMPC_DEVICE_unknown); + KLoc.emplace_back(); + } } else { assert(Kind == OMPC_if); KLoc.push_back(Tok.getLocation()); @@ -2510,7 +2986,7 @@ OMPClause *Parser::ParseOpenMPSingleExprWithArgClause(OpenMPClauseKind Kind, bool NeedAnExpression = (Kind == OMPC_schedule && DelimLoc.isValid()) || (Kind == OMPC_dist_schedule && DelimLoc.isValid()) || - Kind == OMPC_if; + Kind == OMPC_if || Kind == OMPC_device; if (NeedAnExpression) { SourceLocation ELoc = Tok.getLocation(); ExprResult LHS(ParseCastExpression(AnyCastExpr, false, NotTypeCast)); @@ -2572,11 +3048,12 @@ static bool ParseReductionId(Parser &P, CXXScopeSpec &ReductionIdScopeSpec, return false; } } - return P.ParseUnqualifiedId(ReductionIdScopeSpec, /*EnteringContext*/ false, - /*AllowDestructorName*/ false, - /*AllowConstructorName*/ false, - /*AllowDeductionGuide*/ false, - nullptr, nullptr, ReductionId); + return P.ParseUnqualifiedId( + ReductionIdScopeSpec, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, /*EnteringContext*/ false, + /*AllowDestructorName*/ false, + /*AllowConstructorName*/ false, + /*AllowDeductionGuide*/ false, nullptr, ReductionId); } /// Checks if the token is a valid map-type-modifier. @@ -2604,6 +3081,7 @@ bool Parser::parseMapperModifier(OpenMPVarListDataTy &Data) { if (getLangOpts().CPlusPlus) ParseOptionalCXXScopeSpecifier(Data.ReductionOrMapperIdScopeSpec, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, /*EnteringContext=*/false); if (Tok.isNot(tok::identifier) && Tok.isNot(tok::kw_default)) { Diag(Tok.getLocation(), diag::err_omp_mapper_illegal_identifier); @@ -2684,6 +3162,114 @@ static void parseMapType(Parser &P, Parser::OpenMPVarListDataTy &Data) { P.ConsumeToken(); } +/// Parses simple expression in parens for single-expression clauses of OpenMP +/// constructs. +/// \param RLoc Returned location of right paren. +ExprResult Parser::ParseOpenMPIteratorsExpr() { + assert(Tok.is(tok::identifier) && PP.getSpelling(Tok) == "iterator" && + "Expected 'iterator' token."); + SourceLocation IteratorKwLoc = ConsumeToken(); + + BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); + if (T.expectAndConsume(diag::err_expected_lparen_after, "iterator")) + return ExprError(); + + SourceLocation LLoc = T.getOpenLocation(); + SmallVector<Sema::OMPIteratorData, 4> Data; + while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::annot_pragma_openmp_end)) { + // Check if the type parsing is required. + ParsedType IteratorType; + if (Tok.isNot(tok::identifier) || NextToken().isNot(tok::equal)) { + // identifier '=' is not found - parse type. + TypeResult TR = ParseTypeName(); + if (TR.isInvalid()) { + T.skipToEnd(); + return ExprError(); + } + IteratorType = TR.get(); + } + + // Parse identifier. + IdentifierInfo *II = nullptr; + SourceLocation IdLoc; + if (Tok.is(tok::identifier)) { + II = Tok.getIdentifierInfo(); + IdLoc = ConsumeToken(); + } else { + Diag(Tok, diag::err_expected_unqualified_id) << 0; + } + + // Parse '='. + SourceLocation AssignLoc; + if (Tok.is(tok::equal)) + AssignLoc = ConsumeToken(); + else + Diag(Tok, diag::err_omp_expected_equal_in_iterator); + + // Parse range-specification - <begin> ':' <end> [ ':' <step> ] + ColonProtectionRAIIObject ColonRAII(*this); + // Parse <begin> + SourceLocation Loc = Tok.getLocation(); + ExprResult LHS = ParseCastExpression(AnyCastExpr); + ExprResult Begin = Actions.CorrectDelayedTyposInExpr( + ParseRHSOfBinaryExpression(LHS, prec::Conditional)); + Begin = Actions.ActOnFinishFullExpr(Begin.get(), Loc, + /*DiscardedValue=*/false); + // Parse ':'. + SourceLocation ColonLoc; + if (Tok.is(tok::colon)) + ColonLoc = ConsumeToken(); + + // Parse <end> + Loc = Tok.getLocation(); + LHS = ParseCastExpression(AnyCastExpr); + ExprResult End = Actions.CorrectDelayedTyposInExpr( + ParseRHSOfBinaryExpression(LHS, prec::Conditional)); + End = Actions.ActOnFinishFullExpr(End.get(), Loc, + /*DiscardedValue=*/false); + + SourceLocation SecColonLoc; + ExprResult Step; + // Parse optional step. + if (Tok.is(tok::colon)) { + // Parse ':' + SecColonLoc = ConsumeToken(); + // Parse <step> + Loc = Tok.getLocation(); + LHS = ParseCastExpression(AnyCastExpr); + Step = Actions.CorrectDelayedTyposInExpr( + ParseRHSOfBinaryExpression(LHS, prec::Conditional)); + Step = Actions.ActOnFinishFullExpr(Step.get(), Loc, + /*DiscardedValue=*/false); + } + + // Parse ',' or ')' + if (Tok.isNot(tok::comma) && Tok.isNot(tok::r_paren)) + Diag(Tok, diag::err_omp_expected_punc_after_iterator); + if (Tok.is(tok::comma)) + ConsumeToken(); + + Sema::OMPIteratorData &D = Data.emplace_back(); + D.DeclIdent = II; + D.DeclIdentLoc = IdLoc; + D.Type = IteratorType; + D.AssignLoc = AssignLoc; + D.ColonLoc = ColonLoc; + D.SecColonLoc = SecColonLoc; + D.Range.Begin = Begin.get(); + D.Range.End = End.get(); + D.Range.Step = Step.get(); + } + + // Parse ')'. + SourceLocation RLoc = Tok.getLocation(); + if (!T.consumeClose()) + RLoc = T.getCloseLocation(); + + return Actions.ActOnOMPIteratorExpr(getCurScope(), IteratorKwLoc, LLoc, RLoc, + Data); +} + /// Parses clauses with list. bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, OpenMPClauseKind Kind, @@ -2696,19 +3282,32 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, // Parse '('. BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end); if (T.expectAndConsume(diag::err_expected_lparen_after, - getOpenMPClauseName(Kind))) + getOpenMPClauseName(Kind).data())) return true; + bool HasIterator = false; bool NeedRParenForLinear = false; BalancedDelimiterTracker LinearT(*this, tok::l_paren, tok::annot_pragma_openmp_end); // Handle reduction-identifier for reduction clause. if (Kind == OMPC_reduction || Kind == OMPC_task_reduction || Kind == OMPC_in_reduction) { + Data.ExtraModifier = OMPC_REDUCTION_unknown; + if (Kind == OMPC_reduction && getLangOpts().OpenMP >= 50 && + (Tok.is(tok::identifier) || Tok.is(tok::kw_default)) && + NextToken().is(tok::comma)) { + // Parse optional reduction modifier. + Data.ExtraModifier = getOpenMPSimpleClauseType(Kind, PP.getSpelling(Tok)); + Data.ExtraModifierLoc = Tok.getLocation(); + ConsumeToken(); + assert(Tok.is(tok::comma) && "Expected comma."); + (void)ConsumeToken(); + } ColonProtectionRAIIObject ColonRAII(*this); if (getLangOpts().CPlusPlus) ParseOptionalCXXScopeSpecifier(Data.ReductionOrMapperIdScopeSpec, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, /*EnteringContext=*/false); InvalidReductionId = ParseReductionId( *this, Data.ReductionOrMapperIdScopeSpec, UnqualifiedReductionId); @@ -2724,11 +3323,27 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, Data.ReductionOrMapperId = Actions.GetNameFromUnqualifiedId(UnqualifiedReductionId); } else if (Kind == OMPC_depend) { + if (getLangOpts().OpenMP >= 50) { + if (Tok.is(tok::identifier) && PP.getSpelling(Tok) == "iterator") { + // Handle optional dependence modifier. + // iterator(iterators-definition) + // where iterators-definition is iterator-specifier [, + // iterators-definition ] + // where iterator-specifier is [ iterator-type ] identifier = + // range-specification + HasIterator = true; + EnterScope(Scope::OpenMPDirectiveScope | Scope::DeclScope); + ExprResult IteratorRes = ParseOpenMPIteratorsExpr(); + Data.DepModOrTailExpr = IteratorRes.get(); + // Parse ',' + ExpectAndConsume(tok::comma); + } + } // Handle dependency type for depend clause. ColonProtectionRAIIObject ColonRAII(*this); Data.ExtraModifier = getOpenMPSimpleClauseType( Kind, Tok.is(tok::identifier) ? PP.getSpelling(Tok) : ""); - Data.DepLinMapLastLoc = Tok.getLocation(); + Data.ExtraModifierLoc = Tok.getLocation(); if (Data.ExtraModifier == OMPC_DEPEND_unknown) { SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch); @@ -2753,7 +3368,7 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, Data.ExtraModifier = OMPC_LINEAR_val; if (Tok.is(tok::identifier) && PP.LookAhead(0).is(tok::l_paren)) { Data.ExtraModifier = getOpenMPSimpleClauseType(Kind, PP.getSpelling(Tok)); - Data.DepLinMapLastLoc = ConsumeToken(); + Data.ExtraModifierLoc = ConsumeToken(); LinearT.consumeOpen(); NeedRParenForLinear = true; } @@ -2766,13 +3381,8 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, !isOpenMPTaskLoopDirective(DKind)) && Tok.is(tok::identifier) && PP.LookAhead(0).is(tok::colon)) { Data.ExtraModifier = getOpenMPSimpleClauseType(Kind, PP.getSpelling(Tok)); - Data.DepLinMapLastLoc = Tok.getLocation(); - if (Data.ExtraModifier == OMPC_LASTPRIVATE_unknown) { - SkipUntil(tok::colon, tok::r_paren, tok::annot_pragma_openmp_end, - StopBeforeMatch); - } else { - ConsumeToken(); - } + Data.ExtraModifierLoc = Tok.getLocation(); + ConsumeToken(); assert(Tok.is(tok::colon) && "Expected colon."); Data.ColonLoc = ConsumeToken(); } @@ -2784,7 +3394,7 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, // map-type-modifier. The map-type can also be delete which has the same // spelling of the C++ delete keyword. Data.ExtraModifier = OMPC_MAP_unknown; - Data.DepLinMapLastLoc = Tok.getLocation(); + Data.ExtraModifierLoc = Tok.getLocation(); // Check for presence of a colon in the map clause. TentativeParsingAction TPA(*this); @@ -2840,22 +3450,33 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, ConsumeToken(); } } - } else if (Kind == OMPC_allocate) { + } else if (Kind == OMPC_allocate || + (Kind == OMPC_affinity && Tok.is(tok::identifier) && + PP.getSpelling(Tok) == "iterator")) { // Handle optional allocator expression followed by colon delimiter. ColonProtectionRAIIObject ColonRAII(*this); TentativeParsingAction TPA(*this); - ExprResult Tail = - Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()); + // OpenMP 5.0, 2.10.1, task Construct. + // where aff-modifier is one of the following: + // iterator(iterators-definition) + ExprResult Tail; + if (Kind == OMPC_allocate) { + Tail = ParseAssignmentExpression(); + } else { + HasIterator = true; + EnterScope(Scope::OpenMPDirectiveScope | Scope::DeclScope); + Tail = ParseOpenMPIteratorsExpr(); + } + Tail = Actions.CorrectDelayedTyposInExpr(Tail); Tail = Actions.ActOnFinishFullExpr(Tail.get(), T.getOpenLocation(), /*DiscardedValue=*/false); if (Tail.isUsable()) { if (Tok.is(tok::colon)) { - Data.TailExpr = Tail.get(); + Data.DepModOrTailExpr = Tail.get(); Data.ColonLoc = ConsumeToken(); TPA.Commit(); } else { - // colon not found, no allocator specified, parse only list of - // variables. + // Colon not found, parse only list of variables. TPA.Revert(); } } else { @@ -2876,6 +3497,7 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, const bool MayHaveTail = (Kind == OMPC_linear || Kind == OMPC_aligned); while (IsComma || (Tok.isNot(tok::r_paren) && Tok.isNot(tok::colon) && Tok.isNot(tok::annot_pragma_openmp_end))) { + ParseScope OMPListScope(this, Scope::OpenMPDirectiveScope); ColonProtectionRAIIObject ColonRAII(*this, MayHaveTail); // Parse variable ExprResult VarExpr = @@ -2912,7 +3534,7 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, Tail = Actions.ActOnFinishFullExpr(Tail.get(), ELoc, /*DiscardedValue*/ false); if (Tail.isUsable()) - Data.TailExpr = Tail.get(); + Data.DepModOrTailExpr = Tail.get(); else SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch); @@ -2922,16 +3544,17 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, Data.RLoc = Tok.getLocation(); if (!T.consumeClose()) Data.RLoc = T.getCloseLocation(); - return (Kind == OMPC_depend && Data.ExtraModifier != OMPC_DEPEND_unknown && - Vars.empty()) || - (Kind != OMPC_depend && Kind != OMPC_map && Vars.empty()) || - (MustHaveTail && !Data.TailExpr) || InvalidReductionId || + // Exit from scope when the iterator is used in depend clause. + if (HasIterator) + ExitScope(); + return (Kind != OMPC_depend && Kind != OMPC_map && Vars.empty()) || + (MustHaveTail && !Data.DepModOrTailExpr) || InvalidReductionId || IsInvalidMapperModifier; } /// Parsing of OpenMP clause 'private', 'firstprivate', 'lastprivate', -/// 'shared', 'copyin', 'copyprivate', 'flush', 'reduction', 'task_reduction' or -/// 'in_reduction'. +/// 'shared', 'copyin', 'copyprivate', 'flush', 'reduction', 'task_reduction', +/// 'in_reduction', 'nontemporal', 'exclusive' or 'inclusive'. /// /// private-clause: /// 'private' '(' list ')' @@ -2946,7 +3569,7 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, /// aligned-clause: /// 'aligned' '(' list [ ':' alignment ] ')' /// reduction-clause: -/// 'reduction' '(' reduction-identifier ':' list ')' +/// 'reduction' '(' [ modifier ',' ] reduction-identifier ':' list ')' /// task_reduction-clause: /// 'task_reduction' '(' reduction-identifier ':' list ')' /// in_reduction-clause: @@ -2967,10 +3590,18 @@ bool Parser::ParseOpenMPVarList(OpenMPDirectiveKind DKind, /// 'from' '(' [ mapper '(' mapper-identifier ')' ':' ] list ')' /// use_device_ptr-clause: /// 'use_device_ptr' '(' list ')' +/// use_device_addr-clause: +/// 'use_device_addr' '(' list ')' /// is_device_ptr-clause: /// 'is_device_ptr' '(' list ')' /// allocate-clause: /// 'allocate' '(' [ allocator ':' ] list ')' +/// nontemporal-clause: +/// 'nontemporal' '(' list ')' +/// inclusive-clause: +/// 'inclusive' '(' list ')' +/// exclusive-clause: +/// 'exclusive' '(' list ')' /// /// For 'linear' clause linear-list may have the following forms: /// list @@ -2991,9 +3622,9 @@ OMPClause *Parser::ParseOpenMPVarListClause(OpenMPDirectiveKind DKind, return nullptr; OMPVarListLocTy Locs(Loc, LOpen, Data.RLoc); return Actions.ActOnOpenMPVarListClause( - Kind, Vars, Data.TailExpr, Locs, Data.ColonLoc, + Kind, Vars, Data.DepModOrTailExpr, Locs, Data.ColonLoc, Data.ReductionOrMapperIdScopeSpec, Data.ReductionOrMapperId, Data.ExtraModifier, Data.MapTypeModifiers, Data.MapTypeModifiersLoc, - Data.IsMapTypeImplicit, Data.DepLinMapLastLoc); + Data.IsMapTypeImplicit, Data.ExtraModifierLoc); } diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp index df411e1928d6..6402b31d00b2 100644 --- a/clang/lib/Parse/ParsePragma.cpp +++ b/clang/lib/Parse/ParsePragma.cpp @@ -108,6 +108,7 @@ struct PragmaSTDC_FENV_ACCESSHandler : public PragmaHandler { return; if (OOS == tok::OOS_ON) { PP.Diag(Tok, diag::warn_stdc_fenv_access_not_supported); + return; } MutableArrayRef<Token> Toks(PP.getPreprocessorAllocator().Allocate<Token>(1), @@ -184,6 +185,13 @@ private: Sema &Actions; }; +struct PragmaFloatControlHandler : public PragmaHandler { + PragmaFloatControlHandler(Sema &Actions) + : PragmaHandler("float_control") {} + void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, + Token &FirstToken) override; +}; + struct PragmaMSPointersToMembers : public PragmaHandler { explicit PragmaMSPointersToMembers() : PragmaHandler("pointers_to_members") {} void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, @@ -262,6 +270,18 @@ struct PragmaAttributeHandler : public PragmaHandler { ParsedAttributes AttributesForPragmaAttribute; }; +struct PragmaMaxTokensHereHandler : public PragmaHandler { + PragmaMaxTokensHereHandler() : PragmaHandler("max_tokens_here") {} + void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, + Token &FirstToken) override; +}; + +struct PragmaMaxTokensTotalHandler : public PragmaHandler { + PragmaMaxTokensTotalHandler() : PragmaHandler("max_tokens_total") {} + void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer, + Token &FirstToken) override; +}; + } // end namespace void Parser::initializePragmaHandlers() { @@ -322,6 +342,8 @@ void Parser::initializePragmaHandlers() { PP.AddPragmaHandler(MSCommentHandler.get()); } + FloatControlHandler = std::make_unique<PragmaFloatControlHandler>(Actions); + PP.AddPragmaHandler(FloatControlHandler.get()); if (getLangOpts().MicrosoftExt) { MSDetectMismatchHandler = std::make_unique<PragmaDetectMismatchHandler>(Actions); @@ -382,6 +404,12 @@ void Parser::initializePragmaHandlers() { AttributePragmaHandler = std::make_unique<PragmaAttributeHandler>(AttrFactory); PP.AddPragmaHandler("clang", AttributePragmaHandler.get()); + + MaxTokensHerePragmaHandler = std::make_unique<PragmaMaxTokensHereHandler>(); + PP.AddPragmaHandler("clang", MaxTokensHerePragmaHandler.get()); + + MaxTokensTotalPragmaHandler = std::make_unique<PragmaMaxTokensTotalHandler>(); + PP.AddPragmaHandler("clang", MaxTokensTotalPragmaHandler.get()); } void Parser::resetPragmaHandlers() { @@ -420,6 +448,8 @@ void Parser::resetPragmaHandlers() { PP.RemovePragmaHandler("clang", PCSectionHandler.get()); PCSectionHandler.reset(); + PP.RemovePragmaHandler(FloatControlHandler.get()); + FloatControlHandler.reset(); if (getLangOpts().MicrosoftExt) { PP.RemovePragmaHandler(MSDetectMismatchHandler.get()); MSDetectMismatchHandler.reset(); @@ -487,6 +517,12 @@ void Parser::resetPragmaHandlers() { PP.RemovePragmaHandler("clang", AttributePragmaHandler.get()); AttributePragmaHandler.reset(); + + PP.RemovePragmaHandler("clang", MaxTokensHerePragmaHandler.get()); + MaxTokensHerePragmaHandler.reset(); + + PP.RemovePragmaHandler("clang", MaxTokensTotalPragmaHandler.get()); + MaxTokensTotalPragmaHandler.reset(); } /// Handle the annotation token produced for #pragma unused(...) @@ -605,21 +641,37 @@ void Parser::HandlePragmaFPContract() { static_cast<tok::OnOffSwitch>( reinterpret_cast<uintptr_t>(Tok.getAnnotationValue())); - LangOptions::FPContractModeKind FPC; + LangOptions::FPModeKind FPC; switch (OOS) { case tok::OOS_ON: - FPC = LangOptions::FPC_On; + FPC = LangOptions::FPM_On; break; case tok::OOS_OFF: - FPC = LangOptions::FPC_Off; + FPC = LangOptions::FPM_Off; break; case tok::OOS_DEFAULT: FPC = getLangOpts().getDefaultFPContractMode(); break; } - Actions.ActOnPragmaFPContract(FPC); - ConsumeAnnotationToken(); + SourceLocation PragmaLoc = ConsumeAnnotationToken(); + Actions.ActOnPragmaFPContract(PragmaLoc, FPC); +} + +void Parser::HandlePragmaFloatControl() { + assert(Tok.is(tok::annot_pragma_float_control)); + + // The value that is held on the PragmaFloatControlStack encodes + // the PragmaFloatControl kind and the MSStackAction kind + // into a single 32-bit word. The MsStackAction is the high 16 bits + // and the FloatControl is the lower 16 bits. Use shift and bit-and + // to decode the parts. + uintptr_t Value = reinterpret_cast<uintptr_t>(Tok.getAnnotationValue()); + Sema::PragmaMsStackAction Action = + static_cast<Sema::PragmaMsStackAction>((Value >> 16) & 0xFFFF); + PragmaFloatControlKind Kind = PragmaFloatControlKind(Value & 0xFFFF); + SourceLocation PragmaLoc = ConsumeAnnotationToken(); + Actions.ActOnPragmaFloatControl(PragmaLoc, Action, Kind); } void Parser::HandlePragmaFEnvAccess() { @@ -628,21 +680,21 @@ void Parser::HandlePragmaFEnvAccess() { static_cast<tok::OnOffSwitch>( reinterpret_cast<uintptr_t>(Tok.getAnnotationValue())); - LangOptions::FEnvAccessModeKind FPC; + bool IsEnabled; switch (OOS) { case tok::OOS_ON: - FPC = LangOptions::FEA_On; + IsEnabled = true; break; case tok::OOS_OFF: - FPC = LangOptions::FEA_Off; + IsEnabled = false; break; case tok::OOS_DEFAULT: // FIXME: Add this cli option when it makes sense. - FPC = LangOptions::FEA_Off; + IsEnabled = false; break; } - Actions.ActOnPragmaFEnvAccess(FPC); - ConsumeAnnotationToken(); + SourceLocation PragmaLoc = ConsumeAnnotationToken(); + Actions.ActOnPragmaFEnvAccess(PragmaLoc, IsEnabled); } @@ -1008,11 +1060,11 @@ struct PragmaLoopHintInfo { static std::string PragmaLoopHintString(Token PragmaName, Token Option) { StringRef Str = PragmaName.getIdentifierInfo()->getName(); std::string ClangLoopStr = (llvm::Twine("clang loop ") + Str).str(); - return llvm::StringSwitch<StringRef>(Str) - .Case("loop", ClangLoopStr) - .Case("unroll_and_jam", Str) - .Case("unroll", Str) - .Default(""); + return std::string(llvm::StringSwitch<StringRef>(Str) + .Case("loop", ClangLoopStr) + .Case("unroll_and_jam", Str) + .Case("unroll", Str) + .Default("")); } bool Parser::HandlePragmaLoopHint(LoopHint &Hint) { @@ -1821,6 +1873,7 @@ void PragmaClangSectionHandler::HandlePragma(Preprocessor &PP, return; } + SourceLocation PragmaLocation = Tok.getLocation(); PP.Lex(Tok); // eat ['bss'|'data'|'rodata'|'text'] if (Tok.isNot(tok::equal)) { PP.Diag(Tok.getLocation(), diag::err_pragma_clang_section_expected_equal) << SecKind; @@ -1831,10 +1884,11 @@ void PragmaClangSectionHandler::HandlePragma(Preprocessor &PP, if (!PP.LexStringLiteral(Tok, SecName, "pragma clang section", false)) return; - Actions.ActOnPragmaClangSection(Tok.getLocation(), - (SecName.size()? Sema::PragmaClangSectionAction::PCSA_Set : - Sema::PragmaClangSectionAction::PCSA_Clear), - SecKind, SecName); + Actions.ActOnPragmaClangSection( + PragmaLocation, + (SecName.size() ? Sema::PragmaClangSectionAction::PCSA_Set + : Sema::PragmaClangSectionAction::PCSA_Clear), + SecKind, SecName); } } @@ -2465,6 +2519,129 @@ void PragmaMSPragma::HandlePragma(Preprocessor &PP, PP.EnterToken(AnnotTok, /*IsReinject*/ false); } +/// Handle the \#pragma float_control extension. +/// +/// The syntax is: +/// \code +/// #pragma float_control(keyword[, setting] [,push]) +/// \endcode +/// Where 'keyword' and 'setting' are identifiers. +// 'keyword' can be: precise, except, push, pop +// 'setting' can be: on, off +/// The optional arguments 'setting' and 'push' are supported only +/// when the keyword is 'precise' or 'except'. +void PragmaFloatControlHandler::HandlePragma(Preprocessor &PP, + PragmaIntroducer Introducer, + Token &Tok) { + Sema::PragmaMsStackAction Action = Sema::PSK_Set; + SourceLocation FloatControlLoc = Tok.getLocation(); + PP.Lex(Tok); + if (Tok.isNot(tok::l_paren)) { + PP.Diag(FloatControlLoc, diag::err_expected) << tok::l_paren; + return; + } + + // Read the identifier. + PP.Lex(Tok); + if (Tok.isNot(tok::identifier)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed); + return; + } + + // Verify that this is one of the float control options. + IdentifierInfo *II = Tok.getIdentifierInfo(); + PragmaFloatControlKind Kind = + llvm::StringSwitch<PragmaFloatControlKind>(II->getName()) + .Case("precise", PFC_Precise) + .Case("except", PFC_Except) + .Case("push", PFC_Push) + .Case("pop", PFC_Pop) + .Default(PFC_Unknown); + PP.Lex(Tok); // the identifier + if (Kind == PFC_Unknown) { + PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed); + return; + } else if (Kind == PFC_Push || Kind == PFC_Pop) { + if (Tok.isNot(tok::r_paren)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed); + return; + } + PP.Lex(Tok); // Eat the r_paren + Action = (Kind == PFC_Pop) ? Sema::PSK_Pop : Sema::PSK_Push; + } else { + if (Tok.is(tok::r_paren)) + // Selecting Precise or Except + PP.Lex(Tok); // the r_paren + else if (Tok.isNot(tok::comma)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed); + return; + } else { + PP.Lex(Tok); // , + if (!Tok.isAnyIdentifier()) { + PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed); + return; + } + StringRef PushOnOff = Tok.getIdentifierInfo()->getName(); + if (PushOnOff == "on") + // Kind is set correctly + ; + else if (PushOnOff == "off") { + if (Kind == PFC_Precise) + Kind = PFC_NoPrecise; + if (Kind == PFC_Except) + Kind = PFC_NoExcept; + } else if (PushOnOff == "push") { + Action = Sema::PSK_Push_Set; + } else { + PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed); + return; + } + PP.Lex(Tok); // the identifier + if (Tok.is(tok::comma)) { + PP.Lex(Tok); // , + if (!Tok.isAnyIdentifier()) { + PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed); + return; + } + StringRef ExpectedPush = Tok.getIdentifierInfo()->getName(); + if (ExpectedPush == "push") { + Action = Sema::PSK_Push_Set; + } else { + PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed); + return; + } + PP.Lex(Tok); // the push identifier + } + if (Tok.isNot(tok::r_paren)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_float_control_malformed); + return; + } + PP.Lex(Tok); // the r_paren + } + } + SourceLocation EndLoc = Tok.getLocation(); + if (Tok.isNot(tok::eod)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) + << "float_control"; + return; + } + + // Note: there is no accomodation for PP callback for this pragma. + + // Enter the annotation. + auto TokenArray = std::make_unique<Token[]>(1); + TokenArray[0].startToken(); + TokenArray[0].setKind(tok::annot_pragma_float_control); + TokenArray[0].setLocation(FloatControlLoc); + TokenArray[0].setAnnotationEndLoc(EndLoc); + // Create an encoding of Action and Value by shifting the Action into + // the high 16 bits then union with the Kind. + TokenArray[0].setAnnotationValue(reinterpret_cast<void *>( + static_cast<uintptr_t>((Action << 16) | (Kind & 0xFFFF)))); + PP.EnterTokenStream(std::move(TokenArray), 1, + /*DisableMacroExpansion=*/false, /*IsReinject=*/false); +} + /// Handle the Microsoft \#pragma detect_mismatch extension. /// /// The syntax is: @@ -2548,7 +2725,7 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP, return; } - // Verify that this is one of the 5 whitelisted options. + // Verify that this is one of the 5 explicitly listed options. IdentifierInfo *II = Tok.getIdentifierInfo(); PragmaMSCommentKind Kind = llvm::StringSwitch<PragmaMSCommentKind>(II->getName()) @@ -2589,7 +2766,7 @@ void PragmaCommentHandler::HandlePragma(Preprocessor &PP, // FIXME: If the kind is "compiler" warn if the string is present (it is // ignored). // The MSDN docs say that "lib" and "linker" require a string and have a short - // whitelist of linker options they support, but in practice MSVC doesn't + // list of linker options they support, but in practice MSVC doesn't // issue a diagnostic. Therefore neither does clang. if (Tok.isNot(tok::r_paren)) { @@ -2651,7 +2828,7 @@ void PragmaOptimizeHandler::HandlePragma(Preprocessor &PP, namespace { /// Used as the annotation value for tok::annot_pragma_fp. struct TokFPAnnotValue { - enum FlagKinds { Contract }; + enum FlagKinds { Contract, Reassociate }; enum FlagValues { On, Off, Fast }; FlagKinds FlagKind; @@ -2679,6 +2856,7 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP, llvm::StringSwitch<llvm::Optional<TokFPAnnotValue::FlagKinds>>( OptionInfo->getName()) .Case("contract", TokFPAnnotValue::Contract) + .Case("reassociate", TokFPAnnotValue::Reassociate) .Default(None); if (!FlagKind) { PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_option) @@ -2696,7 +2874,8 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP, if (Tok.isNot(tok::identifier)) { PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument) - << PP.getSpelling(Tok) << OptionInfo->getName(); + << PP.getSpelling(Tok) << OptionInfo->getName() + << (FlagKind == TokFPAnnotValue::Reassociate); return; } const IdentifierInfo *II = Tok.getIdentifierInfo(); @@ -2709,9 +2888,11 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP, .Case("fast", TokFPAnnotValue::Fast) .Default(llvm::None); - if (!FlagValue) { + if (!FlagValue || (FlagKind == TokFPAnnotValue::Reassociate && + FlagValue == TokFPAnnotValue::Fast)) { PP.Diag(Tok.getLocation(), diag::err_pragma_fp_invalid_argument) - << PP.getSpelling(Tok) << OptionInfo->getName(); + << PP.getSpelling(Tok) << OptionInfo->getName() + << (FlagKind == TokFPAnnotValue::Reassociate); return; } PP.Lex(Tok); @@ -2725,7 +2906,7 @@ void PragmaFPHandler::HandlePragma(Preprocessor &PP, auto *AnnotValue = new (PP.getPreprocessorAllocator()) TokFPAnnotValue{*FlagKind, *FlagValue}; - // Generate the loop hint token. + // Generate the fp annotation token. Token FPTok; FPTok.startToken(); FPTok.setKind(tok::annot_pragma_fp); @@ -2753,20 +2934,24 @@ void Parser::HandlePragmaFP() { auto *AnnotValue = reinterpret_cast<TokFPAnnotValue *>(Tok.getAnnotationValue()); - LangOptions::FPContractModeKind FPC; - switch (AnnotValue->FlagValue) { - case TokFPAnnotValue::On: - FPC = LangOptions::FPC_On; - break; - case TokFPAnnotValue::Fast: - FPC = LangOptions::FPC_Fast; - break; - case TokFPAnnotValue::Off: - FPC = LangOptions::FPC_Off; - break; + if (AnnotValue->FlagKind == TokFPAnnotValue::Reassociate) + Actions.ActOnPragmaFPReassociate( + Tok.getLocation(), AnnotValue->FlagValue == TokFPAnnotValue::On); + else { + LangOptions::FPModeKind FPC; + switch (AnnotValue->FlagValue) { + case TokFPAnnotValue::Off: + FPC = LangOptions::FPM_Off; + break; + case TokFPAnnotValue::On: + FPC = LangOptions::FPM_On; + break; + case TokFPAnnotValue::Fast: + FPC = LangOptions::FPM_Fast; + break; + } + Actions.ActOnPragmaFPContract(Tok.getLocation(), FPC); } - - Actions.ActOnPragmaFPContract(FPC); ConsumeAnnotationToken(); } @@ -2914,7 +3099,7 @@ void PragmaLoopHintHandler::HandlePragma(Preprocessor &PP, Token LoopHintTok; LoopHintTok.startToken(); LoopHintTok.setKind(tok::annot_pragma_loop_hint); - LoopHintTok.setLocation(PragmaName.getLocation()); + LoopHintTok.setLocation(Introducer.Loc); LoopHintTok.setAnnotationEndLoc(PragmaName.getLocation()); LoopHintTok.setAnnotationValue(static_cast<void *>(Info)); TokenList.push_back(LoopHintTok); @@ -3001,7 +3186,7 @@ void PragmaUnrollHintHandler::HandlePragma(Preprocessor &PP, auto TokenArray = std::make_unique<Token[]>(1); TokenArray[0].startToken(); TokenArray[0].setKind(tok::annot_pragma_loop_hint); - TokenArray[0].setLocation(PragmaName.getLocation()); + TokenArray[0].setLocation(Introducer.Loc); TokenArray[0].setAnnotationEndLoc(PragmaName.getLocation()); TokenArray[0].setAnnotationValue(static_cast<void *>(Info)); PP.EnterTokenStream(std::move(TokenArray), 1, @@ -3279,3 +3464,64 @@ void PragmaAttributeHandler::HandlePragma(Preprocessor &PP, PP.EnterTokenStream(std::move(TokenArray), 1, /*DisableMacroExpansion=*/false, /*IsReinject=*/false); } + +// Handle '#pragma clang max_tokens 12345'. +void PragmaMaxTokensHereHandler::HandlePragma(Preprocessor &PP, + PragmaIntroducer Introducer, + Token &Tok) { + PP.Lex(Tok); + if (Tok.is(tok::eod)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_missing_argument) + << "clang max_tokens_here" << /*Expected=*/true << "integer"; + return; + } + + SourceLocation Loc = Tok.getLocation(); + uint64_t MaxTokens; + if (Tok.isNot(tok::numeric_constant) || + !PP.parseSimpleIntegerLiteral(Tok, MaxTokens)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_expected_integer) + << "clang max_tokens_here"; + return; + } + + if (Tok.isNot(tok::eod)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) + << "clang max_tokens_here"; + return; + } + + if (PP.getTokenCount() > MaxTokens) { + PP.Diag(Loc, diag::warn_max_tokens) + << PP.getTokenCount() << (unsigned)MaxTokens; + } +} + +// Handle '#pragma clang max_tokens_total 12345'. +void PragmaMaxTokensTotalHandler::HandlePragma(Preprocessor &PP, + PragmaIntroducer Introducer, + Token &Tok) { + PP.Lex(Tok); + if (Tok.is(tok::eod)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_missing_argument) + << "clang max_tokens_total" << /*Expected=*/true << "integer"; + return; + } + + SourceLocation Loc = Tok.getLocation(); + uint64_t MaxTokens; + if (Tok.isNot(tok::numeric_constant) || + !PP.parseSimpleIntegerLiteral(Tok, MaxTokens)) { + PP.Diag(Tok.getLocation(), diag::err_pragma_expected_integer) + << "clang max_tokens_total"; + return; + } + + if (Tok.isNot(tok::eod)) { + PP.Diag(Tok.getLocation(), diag::warn_pragma_extra_tokens_at_eol) + << "clang max_tokens_total"; + return; + } + + PP.overrideMaxTokens(MaxTokens, Loc); +} diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index 0339328ca513..89a6a2b829ae 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -105,6 +105,7 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts, StmtResult Res = ParseStatementOrDeclarationAfterAttributes( Stmts, StmtCtx, TrailingElseLoc, Attrs); + MaybeDestroyTemplateIds(); assert((Attrs.empty() || Res.isInvalid() || Res.isUsable()) && "attributes on empty statement"); @@ -353,13 +354,13 @@ Retry: case tok::annot_pragma_fp_contract: ProhibitAttributes(Attrs); - Diag(Tok, diag::err_pragma_fp_contract_scope); + Diag(Tok, diag::err_pragma_file_or_compound_scope) << "fp_contract"; ConsumeAnnotationToken(); return StmtError(); case tok::annot_pragma_fp: ProhibitAttributes(Attrs); - Diag(Tok, diag::err_pragma_fp_scope); + Diag(Tok, diag::err_pragma_file_or_compound_scope) << "clang fp"; ConsumeAnnotationToken(); return StmtError(); @@ -368,6 +369,12 @@ Retry: HandlePragmaFEnvAccess(); return StmtEmpty(); + case tok::annot_pragma_float_control: + ProhibitAttributes(Attrs); + Diag(Tok, diag::err_pragma_file_or_compound_scope) << "float_control"; + ConsumeAnnotationToken(); + return StmtError(); + case tok::annot_pragma_opencl_extension: ProhibitAttributes(Attrs); HandlePragmaOpenCLExtension(); @@ -936,6 +943,9 @@ void Parser::ParseCompoundStatementLeadingPragmas() { case tok::annot_pragma_fenv_access: HandlePragmaFEnvAccess(); break; + case tok::annot_pragma_float_control: + HandlePragmaFloatControl(); + break; case tok::annot_pragma_ms_pointers_to_members: HandlePragmaMSPointersToMembers(); break; @@ -1014,9 +1024,9 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { Tok.getLocation(), "in compound statement ('{}')"); - // Record the state of the FP_CONTRACT pragma, restore on leaving the + // Record the state of the FPFeatures, restore on leaving the // compound statement. - Sema::FPContractStateRAII SaveFPContractState(Actions); + Sema::FPFeaturesStateRAII SaveFPContractState(Actions); InMessageExpressionRAIIObject InMessage(*this, false); BalancedDelimiterTracker T(*this, tok::l_brace); @@ -1146,10 +1156,14 @@ StmtResult Parser::ParseCompoundStatementBody(bool isStmtExpr) { /// should try to recover harder. It returns false if the condition is /// successfully parsed. Note that a successful parse can still have semantic /// errors in the condition. +/// Additionally, if LParenLoc and RParenLoc are non-null, it will assign +/// the location of the outer-most '(' and ')', respectively, to them. bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt, Sema::ConditionResult &Cond, SourceLocation Loc, - Sema::ConditionKind CK) { + Sema::ConditionKind CK, + SourceLocation *LParenLoc, + SourceLocation *RParenLoc) { BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); @@ -1179,6 +1193,13 @@ bool Parser::ParseParenExprOrCondition(StmtResult *InitStmt, // Otherwise the condition is valid or the rparen is present. T.consumeClose(); + if (LParenLoc != nullptr) { + *LParenLoc = T.getOpenLocation(); + } + if (RParenLoc != nullptr) { + *RParenLoc = T.getCloseLocation(); + } + // Check for extraneous ')'s to catch things like "if (foo())) {". We know // that all callers are looking for a statement after the condition, so ")" // isn't valid. @@ -1338,6 +1359,8 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) { if (IsConstexpr) ConstexprCondition = Cond.getKnownValue(); + bool IsBracedThen = Tok.is(tok::l_brace); + // C99 6.8.4p3 - In C99, the body of the if statement is a scope, even if // there is no compound stmt. C90 does not have this clause. We only do this // if the body isn't a compound statement to avoid push/pop in common cases. @@ -1356,7 +1379,7 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) { // would have to notify ParseStatement not to create a new scope. It's // simpler to let it create a new scope. // - ParseScope InnerScope(this, Scope::DeclScope, C99orCXX, Tok.is(tok::l_brace)); + ParseScope InnerScope(this, Scope::DeclScope, C99orCXX, IsBracedThen); MisleadingIndentationChecker MIChecker(*this, MSK_if, IfLoc); @@ -1417,7 +1440,7 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) { // Pop the 'else' scope if needed. InnerScope.Exit(); } else if (Tok.is(tok::code_completion)) { - Actions.CodeCompleteAfterIf(getCurScope()); + Actions.CodeCompleteAfterIf(getCurScope(), IsBracedThen); cutOffParsing(); return StmtError(); } else if (InnerStatementTrailingElseLoc.isValid()) { @@ -1570,8 +1593,10 @@ StmtResult Parser::ParseWhileStatement(SourceLocation *TrailingElseLoc) { // Parse the condition. Sema::ConditionResult Cond; + SourceLocation LParen; + SourceLocation RParen; if (ParseParenExprOrCondition(nullptr, Cond, WhileLoc, - Sema::ConditionKind::Boolean)) + Sema::ConditionKind::Boolean, &LParen, &RParen)) return StmtError(); // C99 6.8.5p5 - In C99, the body of the while statement is a scope, even if @@ -1601,7 +1626,7 @@ StmtResult Parser::ParseWhileStatement(SourceLocation *TrailingElseLoc) { if (Cond.isInvalid() || Body.isInvalid()) return StmtError(); - return Actions.ActOnWhileStmt(WhileLoc, Cond, Body.get()); + return Actions.ActOnWhileStmt(WhileLoc, LParen, Cond, RParen, Body.get()); } /// ParseDoStatement @@ -1921,7 +1946,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { if (ForRangeInfo.ParsedForRangeDecl()) { Diag(FirstPart.get() ? FirstPart.get()->getBeginLoc() : ForRangeInfo.ColonLoc, - getLangOpts().CPlusPlus2a + getLangOpts().CPlusPlus20 ? diag::warn_cxx17_compat_for_range_init_stmt : diag::ext_for_range_init_stmt) << (FirstPart.get() ? FirstPart.get()->getSourceRange() @@ -2162,6 +2187,8 @@ StmtResult Parser::ParsePragmaLoopHint(StmtVector &Stmts, // Create temporary attribute list. ParsedAttributesWithRange TempAttrs(AttrFactory); + SourceLocation StartLoc = Tok.getLocation(); + // Get loop hints and consume annotated token. while (Tok.is(tok::annot_pragma_loop_hint)) { LoopHint Hint; @@ -2182,6 +2209,12 @@ StmtResult Parser::ParsePragmaLoopHint(StmtVector &Stmts, Stmts, StmtCtx, TrailingElseLoc, Attrs); Attrs.takeAllFrom(TempAttrs); + + // Start of attribute range may already be set for some invalid input. + // See PR46336. + if (Attrs.Range.getBegin().isInvalid()) + Attrs.Range.setBegin(StartLoc); + return S; } diff --git a/clang/lib/Parse/ParseStmtAsm.cpp b/clang/lib/Parse/ParseStmtAsm.cpp index ea2c871d6a82..7d0818840a4f 100644 --- a/clang/lib/Parse/ParseStmtAsm.cpp +++ b/clang/lib/Parse/ParseStmtAsm.cpp @@ -220,9 +220,10 @@ ExprResult Parser::ParseMSAsmIdentifier(llvm::SmallVectorImpl<Token> &LineToks, // Parse an optional scope-specifier if we're in C++. CXXScopeSpec SS; - if (getLangOpts().CPlusPlus) { - ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false); - } + if (getLangOpts().CPlusPlus) + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, + /*EnteringContext=*/false); // Require an identifier here. SourceLocation TemplateKWLoc; @@ -233,12 +234,13 @@ ExprResult Parser::ParseMSAsmIdentifier(llvm::SmallVectorImpl<Token> &LineToks, Result = ParseCXXThis(); Invalid = false; } else { - Invalid = ParseUnqualifiedId(SS, - /*EnteringContext=*/false, - /*AllowDestructorName=*/false, - /*AllowConstructorName=*/false, - /*AllowDeductionGuide=*/false, - /*ObjectType=*/nullptr, &TemplateKWLoc, Id); + Invalid = + ParseUnqualifiedId(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, + /*EnteringContext=*/false, + /*AllowDestructorName=*/false, + /*AllowConstructorName=*/false, + /*AllowDeductionGuide=*/false, &TemplateKWLoc, Id); // Perform the lookup. Result = Actions.LookupInlineAsmIdentifier(SS, TemplateKWLoc, Id, IsUnevaluatedContext); @@ -349,31 +351,13 @@ static bool buildMSAsmString(Preprocessor &PP, SourceLocation AsmLoc, return false; } -/// isTypeQualifier - Return true if the current token could be the -/// start of a type-qualifier-list. -static bool isTypeQualifier(const Token &Tok) { - switch (Tok.getKind()) { - default: return false; - // type-qualifier - case tok::kw_const: - case tok::kw_volatile: - case tok::kw_restrict: - case tok::kw___private: - case tok::kw___local: - case tok::kw___global: - case tok::kw___constant: - case tok::kw___generic: - case tok::kw___read_only: - case tok::kw___read_write: - case tok::kw___write_only: - return true; - } +// Determine if this is a GCC-style asm statement. +bool Parser::isGCCAsmStatement(const Token &TokAfterAsm) const { + return TokAfterAsm.is(tok::l_paren) || isGNUAsmQualifier(TokAfterAsm); } -// Determine if this is a GCC-style asm statement. -static bool isGCCAsmStatement(const Token &TokAfterAsm) { - return TokAfterAsm.is(tok::l_paren) || TokAfterAsm.is(tok::kw_goto) || - isTypeQualifier(TokAfterAsm); +bool Parser::isGNUAsmQualifier(const Token &TokAfterAsm) const { + return getGNUAsmQualifier(TokAfterAsm) != GNUAsmQualifiers::AQ_unspecified; } /// ParseMicrosoftAsmStatement. When -fms-extensions/-fasm-blocks is enabled, @@ -631,8 +615,8 @@ StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) { // Change to the Intel dialect. Parser->setAssemblerDialect(1); Parser->setTargetParser(*TargetParser.get()); - Parser->setParsingInlineAsm(true); - TargetParser->setParsingInlineAsm(true); + Parser->setParsingMSInlineAsm(true); + TargetParser->setParsingMSInlineAsm(true); ClangAsmParserCallback Callback(*this, AsmLoc, AsmString, AsmToks, TokOffsets); @@ -684,13 +668,41 @@ StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) { ClobberRefs, Exprs, EndLoc); } +/// parseGNUAsmQualifierListOpt - Parse a GNU extended asm qualifier list. +/// asm-qualifier: +/// volatile +/// inline +/// goto +/// +/// asm-qualifier-list: +/// asm-qualifier +/// asm-qualifier-list asm-qualifier +bool Parser::parseGNUAsmQualifierListOpt(GNUAsmQualifiers &AQ) { + while (1) { + const GNUAsmQualifiers::AQ A = getGNUAsmQualifier(Tok); + if (A == GNUAsmQualifiers::AQ_unspecified) { + if (Tok.isNot(tok::l_paren)) { + Diag(Tok.getLocation(), diag::err_asm_qualifier_ignored); + SkipUntil(tok::r_paren, StopAtSemi); + return true; + } + return false; + } + if (AQ.setAsmQualifier(A)) + Diag(Tok.getLocation(), diag::err_asm_duplicate_qual) + << GNUAsmQualifiers::getQualifierName(A); + ConsumeToken(); + } + return false; +} + /// ParseAsmStatement - Parse a GNU extended asm statement. /// asm-statement: /// gnu-asm-statement /// ms-asm-statement /// /// [GNU] gnu-asm-statement: -/// 'asm' type-qualifier[opt] '(' asm-argument ')' ';' +/// 'asm' asm-qualifier-list[opt] '(' asm-argument ')' ';' /// /// [GNU] asm-argument: /// asm-string-literal @@ -712,34 +724,14 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) { return ParseMicrosoftAsmStatement(AsmLoc); } - DeclSpec DS(AttrFactory); SourceLocation Loc = Tok.getLocation(); - ParseTypeQualifierListOpt(DS, AR_VendorAttributesParsed); - - // GNU asms accept, but warn, about type-qualifiers other than volatile. - if (DS.getTypeQualifiers() & DeclSpec::TQ_const) - Diag(Loc, diag::warn_asm_qualifier_ignored) << "const"; - if (DS.getTypeQualifiers() & DeclSpec::TQ_restrict) - Diag(Loc, diag::warn_asm_qualifier_ignored) << "restrict"; - // FIXME: Once GCC supports _Atomic, check whether it permits it here. - if (DS.getTypeQualifiers() & DeclSpec::TQ_atomic) - Diag(Loc, diag::warn_asm_qualifier_ignored) << "_Atomic"; - - // Remember if this was a volatile asm. - bool isVolatile = DS.getTypeQualifiers() & DeclSpec::TQ_volatile; - // Remember if this was a goto asm. - bool isGotoAsm = false; - - if (Tok.is(tok::kw_goto)) { - isGotoAsm = true; - ConsumeToken(); - } - - if (Tok.isNot(tok::l_paren)) { - Diag(Tok, diag::err_expected_lparen_after) << "asm"; - SkipUntil(tok::r_paren, StopAtSemi); + GNUAsmQualifiers GAQ; + if (parseGNUAsmQualifierListOpt(GAQ)) return StmtError(); - } + + if (GAQ.isGoto() && getLangOpts().SpeculativeLoadHardening) + Diag(Loc, diag::warn_slh_does_not_support_asm_goto); + BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); @@ -767,11 +759,10 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) { if (Tok.is(tok::r_paren)) { // We have a simple asm expression like 'asm("foo")'. T.consumeClose(); - return Actions.ActOnGCCAsmStmt(AsmLoc, /*isSimple*/ true, isVolatile, - /*NumOutputs*/ 0, /*NumInputs*/ 0, nullptr, - Constraints, Exprs, AsmString.get(), - Clobbers, /*NumLabels*/ 0, - T.getCloseLocation()); + return Actions.ActOnGCCAsmStmt( + AsmLoc, /*isSimple*/ true, GAQ.isVolatile(), + /*NumOutputs*/ 0, /*NumInputs*/ 0, nullptr, Constraints, Exprs, + AsmString.get(), Clobbers, /*NumLabels*/ 0, T.getCloseLocation()); } // Parse Outputs, if present. @@ -781,12 +772,6 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) { AteExtraColon = Tok.is(tok::coloncolon); ConsumeToken(); - if (!AteExtraColon && isGotoAsm && Tok.isNot(tok::colon)) { - Diag(Tok, diag::err_asm_goto_cannot_have_output); - SkipUntil(tok::r_paren, StopAtSemi); - return StmtError(); - } - if (!AteExtraColon && ParseAsmOperandsOpt(Names, Constraints, Exprs)) return StmtError(); } @@ -835,7 +820,7 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) { } } } - if (!isGotoAsm && (Tok.isNot(tok::r_paren) || AteExtraColon)) { + if (!GAQ.isGoto() && (Tok.isNot(tok::r_paren) || AteExtraColon)) { Diag(Tok, diag::err_expected) << tok::r_paren; SkipUntil(tok::r_paren, StopAtSemi); return StmtError(); @@ -868,16 +853,16 @@ StmtResult Parser::ParseAsmStatement(bool &msAsm) { if (!TryConsumeToken(tok::comma)) break; } - } else if (isGotoAsm) { + } else if (GAQ.isGoto()) { Diag(Tok, diag::err_expected) << tok::colon; SkipUntil(tok::r_paren, StopAtSemi); return StmtError(); } T.consumeClose(); - return Actions.ActOnGCCAsmStmt( - AsmLoc, false, isVolatile, NumOutputs, NumInputs, Names.data(), - Constraints, Exprs, AsmString.get(), Clobbers, NumLabels, - T.getCloseLocation()); + return Actions.ActOnGCCAsmStmt(AsmLoc, false, GAQ.isVolatile(), NumOutputs, + NumInputs, Names.data(), Constraints, Exprs, + AsmString.get(), Clobbers, NumLabels, + T.getCloseLocation()); } /// ParseAsmOperands - Parse the asm-operands production as used by @@ -948,3 +933,28 @@ bool Parser::ParseAsmOperandsOpt(SmallVectorImpl<IdentifierInfo *> &Names, return false; } } + +const char *Parser::GNUAsmQualifiers::getQualifierName(AQ Qualifier) { + switch (Qualifier) { + case AQ_volatile: return "volatile"; + case AQ_inline: return "inline"; + case AQ_goto: return "goto"; + case AQ_unspecified: return "unspecified"; + } + llvm_unreachable("Unknown GNUAsmQualifier"); +} + +Parser::GNUAsmQualifiers::AQ +Parser::getGNUAsmQualifier(const Token &Tok) const { + switch (Tok.getKind()) { + case tok::kw_volatile: return GNUAsmQualifiers::AQ_volatile; + case tok::kw_inline: return GNUAsmQualifiers::AQ_inline; + case tok::kw_goto: return GNUAsmQualifiers::AQ_goto; + default: return GNUAsmQualifiers::AQ_unspecified; + } +} +bool Parser::GNUAsmQualifiers::setAsmQualifier(AQ Qualifier) { + bool IsDuplicate = Qualifiers & Qualifier; + Qualifiers |= Qualifier; + return IsDuplicate; +} diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index 1b9301b6591d..3ef73f579123 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -22,6 +22,16 @@ #include "llvm/Support/TimeProfiler.h" using namespace clang; +/// Re-enter a possible template scope, creating as many template parameter +/// scopes as necessary. +/// \return The number of template parameter scopes entered. +unsigned Parser::ReenterTemplateScopes(MultiParseScope &S, Decl *D) { + return Actions.ActOnReenterTemplateScope(D, [&] { + S.Enter(Scope::TemplateParamScope); + return Actions.getCurScope(); + }); +} + /// Parse a template declaration, explicit instantiation, or /// explicit specialization. Decl *Parser::ParseDeclarationStartingWithTemplate( @@ -67,8 +77,7 @@ Decl *Parser::ParseTemplateDeclarationOrSpecialization( assert(Tok.isOneOf(tok::kw_export, tok::kw_template) && "Token does not start a template declaration."); - // Enter template-parameter scope. - ParseScope TemplateParmScope(this, Scope::TemplateParamScope); + MultiParseScope TemplateParamScopes(*this); // Tell the action that names should be checked in the context of // the declaration to come. @@ -116,7 +125,8 @@ Decl *Parser::ParseTemplateDeclarationOrSpecialization( // Parse the '<' template-parameter-list '>' SourceLocation LAngleLoc, RAngleLoc; SmallVector<NamedDecl*, 4> TemplateParams; - if (ParseTemplateParameters(CurTemplateDepthTracker.getDepth(), + if (ParseTemplateParameters(TemplateParamScopes, + CurTemplateDepthTracker.getDepth(), TemplateParams, LAngleLoc, RAngleLoc)) { // Skip until the semi-colon or a '}'. SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch); @@ -150,9 +160,6 @@ Decl *Parser::ParseTemplateDeclarationOrSpecialization( TemplateParams, RAngleLoc, OptionalRequiresClauseConstraintER.get())); } while (Tok.isOneOf(tok::kw_export, tok::kw_template)); - unsigned NewFlags = getCurScope()->getFlags() & ~Scope::TemplateParamScope; - ParseScopeFlags TemplateScopeFlags(this, NewFlags, isSpecialization); - // Parse the actual template declaration. if (Tok.is(tok::kw_concept)) return ParseConceptDefinition( @@ -240,6 +247,8 @@ Decl *Parser::ParseSingleDeclarationAfterTemplate( // Parse the declarator. ParsingDeclarator DeclaratorInfo(*this, DS, (DeclaratorContext)Context); + if (TemplateInfo.TemplateParams) + DeclaratorInfo.setTemplateParameterLists(*TemplateInfo.TemplateParams); ParseDeclarator(DeclaratorInfo); // Error parsing the declarator? if (!DeclaratorInfo.hasName()) { @@ -251,9 +260,9 @@ Decl *Parser::ParseSingleDeclarationAfterTemplate( } llvm::TimeTraceScope TimeScope("ParseTemplate", [&]() { - return DeclaratorInfo.getIdentifier() != nullptr - ? DeclaratorInfo.getIdentifier()->getName() - : "<unknown>"; + return std::string(DeclaratorInfo.getIdentifier() != nullptr + ? DeclaratorInfo.getIdentifier()->getName() + : "<unknown>"); }); LateParsedAttrList LateParsedAttrs(true); @@ -361,9 +370,11 @@ Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo, DiagnoseAndSkipCXX11Attributes(); CXXScopeSpec SS; - if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), - /*EnteringContext=*/false, /*MayBePseudoDestructor=*/nullptr, - /*IsTypename=*/false, /*LastII=*/nullptr, /*OnlyNamespace=*/true) || + if (ParseOptionalCXXScopeSpecifier( + SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, /*EnteringContext=*/false, + /*MayBePseudoDestructor=*/nullptr, + /*IsTypename=*/false, /*LastII=*/nullptr, /*OnlyNamespace=*/true) || SS.isInvalid()) { SkipUntil(tok::semi); return nullptr; @@ -374,12 +385,12 @@ Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo, diag::err_concept_definition_not_identifier); UnqualifiedId Result; - if (ParseUnqualifiedId(SS, /*EnteringContext=*/false, + if (ParseUnqualifiedId(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, /*EnteringContext=*/false, /*AllowDestructorName=*/false, /*AllowConstructorName=*/false, /*AllowDeductionGuide=*/false, - /*ObjectType=*/ParsedType(), /*TemplateKWLoc=*/nullptr, - Result)) { + /*TemplateKWLoc=*/nullptr, Result)) { SkipUntil(tok::semi); return nullptr; } @@ -426,8 +437,9 @@ Parser::ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo, /// /// \returns true if an error occurred, false otherwise. bool Parser::ParseTemplateParameters( - unsigned Depth, SmallVectorImpl<NamedDecl *> &TemplateParams, - SourceLocation &LAngleLoc, SourceLocation &RAngleLoc) { + MultiParseScope &TemplateScopes, unsigned Depth, + SmallVectorImpl<NamedDecl *> &TemplateParams, SourceLocation &LAngleLoc, + SourceLocation &RAngleLoc) { // Get the template parameter list. if (!TryConsumeToken(tok::less, LAngleLoc)) { Diag(Tok.getLocation(), diag::err_expected_less_after) << "template"; @@ -436,8 +448,11 @@ bool Parser::ParseTemplateParameters( // Try to parse the template parameter list. bool Failed = false; - if (!Tok.is(tok::greater) && !Tok.is(tok::greatergreater)) + // FIXME: Missing greatergreatergreater support. + if (!Tok.is(tok::greater) && !Tok.is(tok::greatergreater)) { + TemplateScopes.Enter(Scope::TemplateParamScope); Failed = ParseTemplateParameterList(Depth, TemplateParams); + } if (Tok.is(tok::greatergreater)) { // No diagnostic required here: a template-parameter-list can only be @@ -499,10 +514,7 @@ Parser::ParseTemplateParameterList(const unsigned Depth, /// Determine whether the parser is at the start of a template /// type parameter. -/// \param ScopeError will receive true if there was an error parsing a -/// scope specifier at the current location. -bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { - ScopeError = false; +Parser::TPResult Parser::isStartOfTemplateTypeParameter() { if (Tok.is(tok::kw_class)) { // "class" may be the start of an elaborated-type-specifier or a // type-parameter. Per C++ [temp.param]p3, we prefer the type-parameter. @@ -512,7 +524,7 @@ bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { case tok::greater: case tok::greatergreater: case tok::ellipsis: - return true; + return TPResult::True; case tok::identifier: // This may be either a type-parameter or an elaborated-type-specifier. @@ -520,7 +532,7 @@ bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { break; default: - return false; + return TPResult::False; } switch (GetLookAheadToken(2).getKind()) { @@ -528,51 +540,28 @@ bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { case tok::comma: case tok::greater: case tok::greatergreater: - return true; + return TPResult::True; default: - return false; + return TPResult::False; } } - bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope); - CXXScopeSpec SS; - ScopeError = - ParseOptionalCXXScopeSpecifier(SS, ParsedType(), - /*EnteringContext=*/false, - /*MayBePseudoDestructor=*/nullptr, - // If this is not a type-constraint, then - // this scope-spec is part of the typename - // of a non-type template parameter - /*IsTypename=*/true, /*LastII=*/nullptr, - // We won't find concepts in - // non-namespaces anyway, so might as well - // parse this correctly for possible type - // names. - /*OnlyNamespace=*/false); - if (ScopeError) - return false; - if (TryAnnotateTypeConstraint(SS)) - return false; - bool IsTypeConstraint = isTypeConstraintAnnotation(); - if (!IsTypeConstraint && SS.isNotEmpty()) { - // This isn't a type-constraint but we've already parsed this scope - // specifier - annotate it. - AnnotateScopeToken(SS, /*isNewAnnotation=*/!WasScopeAnnotation); - return false; - } + if (TryAnnotateTypeConstraint()) + return TPResult::Error; - if (IsTypeConstraint && + if (isTypeConstraintAnnotation() && // Next token might be 'auto' or 'decltype', indicating that this // type-constraint is in fact part of a placeholder-type-specifier of a // non-type template parameter. - !NextToken().isOneOf(tok::kw_auto, tok::kw_decltype)) - return true; + !GetLookAheadToken(Tok.is(tok::annot_cxxscope) ? 2 : 1) + .isOneOf(tok::kw_auto, tok::kw_decltype)) + return TPResult::True; // 'typedef' is a reasonably-common typo/thinko for 'typename', and is // ill-formed otherwise. if (Tok.isNot(tok::kw_typename) && Tok.isNot(tok::kw_typedef)) - return false; + return TPResult::False; // C++ [temp.param]p2: // There is no semantic difference between class and typename in a @@ -592,17 +581,17 @@ bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { case tok::greater: case tok::greatergreater: case tok::ellipsis: - return true; + return TPResult::True; case tok::kw_typename: case tok::kw_typedef: case tok::kw_class: // These indicate that a comma was missed after a type parameter, not that // we have found a non-type parameter. - return true; + return TPResult::True; default: - return false; + return TPResult::False; } } @@ -627,13 +616,9 @@ bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { /// typename /// NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { - // We could be facing a type-constraint, which (could) start a type parameter. - // Annotate it now (we might end up not using it if we determine this - // type-constraint is in fact part of a placeholder-type-specifier of a - // non-type template parameter. - bool ScopeError; - if (isStartOfTemplateTypeParameter(ScopeError)) { + switch (isStartOfTemplateTypeParameter()) { + case TPResult::True: // Is there just a typo in the input code? ('typedef' instead of // 'typename') if (Tok.is(tok::kw_typedef)) { @@ -649,8 +634,10 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { } return ParseTypeParameter(Depth, Position); - } - if (ScopeError) { + case TPResult::False: + break; + + case TPResult::Error: { // We return an invalid parameter as opposed to null to avoid having bogus // diagnostics about an empty template parameter list. // FIXME: Fix ParseTemplateParameterList to better handle nullptr results @@ -670,6 +657,11 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { StopAtSemi | StopBeforeMatch); return ErrorParam; } + + case TPResult::Ambiguous: + llvm_unreachable("template param classification can't be ambiguous"); + } + if (Tok.is(tok::kw_template)) return ParseTemplateTemplateParameter(Depth, Position); @@ -682,15 +674,15 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { /// Check whether the current token is a template-id annotation denoting a /// type-constraint. bool Parser::isTypeConstraintAnnotation() { - if (Tok.isNot(tok::annot_template_id)) + const Token &T = Tok.is(tok::annot_cxxscope) ? NextToken() : Tok; + if (T.isNot(tok::annot_template_id)) return false; const auto *ExistingAnnot = - static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue()); + static_cast<TemplateIdAnnotation *>(T.getAnnotationValue()); return ExistingAnnot->Kind == TNK_Concept_template; } -/// Try parsing a type-constraint construct at the current location, after the -/// optional scope specifier. +/// Try parsing a type-constraint at the current location. /// /// type-constraint: /// nested-name-specifier[opt] concept-name @@ -698,35 +690,62 @@ bool Parser::isTypeConstraintAnnotation() { /// '<' template-argument-list[opt] '>'[opt] /// /// \returns true if an error occurred, and false otherwise. -bool Parser::TryAnnotateTypeConstraint(CXXScopeSpec &SS) { - if (!getLangOpts().ConceptsTS || Tok.isNot(tok::identifier)) +bool Parser::TryAnnotateTypeConstraint() { + if (!getLangOpts().CPlusPlus20) return false; + CXXScopeSpec SS; + bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope); + if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, + /*EnteringContext=*/false, + /*MayBePseudoDestructor=*/nullptr, + // If this is not a type-constraint, then + // this scope-spec is part of the typename + // of a non-type template parameter + /*IsTypename=*/true, /*LastII=*/nullptr, + // We won't find concepts in + // non-namespaces anyway, so might as well + // parse this correctly for possible type + // names. + /*OnlyNamespace=*/false)) + return true; - UnqualifiedId PossibleConceptName; - PossibleConceptName.setIdentifier(Tok.getIdentifierInfo(), - Tok.getLocation()); - - TemplateTy PossibleConcept; - bool MemberOfUnknownSpecialization = false; - auto TNK = Actions.isTemplateName(getCurScope(), SS, - /*hasTemplateKeyword=*/false, - PossibleConceptName, - /*ObjectType=*/ParsedType(), - /*EnteringContext=*/false, - PossibleConcept, - MemberOfUnknownSpecialization); - assert(!MemberOfUnknownSpecialization - && "Member when we only allowed namespace scope qualifiers??"); - if (!PossibleConcept || TNK != TNK_Concept_template) - return false; + if (Tok.is(tok::identifier)) { + UnqualifiedId PossibleConceptName; + PossibleConceptName.setIdentifier(Tok.getIdentifierInfo(), + Tok.getLocation()); + + TemplateTy PossibleConcept; + bool MemberOfUnknownSpecialization = false; + auto TNK = Actions.isTemplateName(getCurScope(), SS, + /*hasTemplateKeyword=*/false, + PossibleConceptName, + /*ObjectType=*/ParsedType(), + /*EnteringContext=*/false, + PossibleConcept, + MemberOfUnknownSpecialization, + /*Disambiguation=*/true); + if (MemberOfUnknownSpecialization || !PossibleConcept || + TNK != TNK_Concept_template) { + if (SS.isNotEmpty()) + AnnotateScopeToken(SS, !WasScopeAnnotation); + return false; + } - // At this point we're sure we're dealing with a constrained parameter. It - // may or may not have a template parameter list following the concept name. - return AnnotateTemplateIdToken(PossibleConcept, TNK, SS, - /*TemplateKWLoc=*/SourceLocation(), - PossibleConceptName, - /*AllowTypeAnnotation=*/false, - /*TypeConstraint=*/true); + // At this point we're sure we're dealing with a constrained parameter. It + // may or may not have a template parameter list following the concept + // name. + if (AnnotateTemplateIdToken(PossibleConcept, TNK, SS, + /*TemplateKWLoc=*/SourceLocation(), + PossibleConceptName, + /*AllowTypeAnnotation=*/false, + /*TypeConstraint=*/true)) + return true; + } + + if (SS.isNotEmpty()) + AnnotateScopeToken(SS, !WasScopeAnnotation); + return false; } /// ParseTypeParameter - Parse a template type parameter (C++ [temp.param]). @@ -739,13 +758,18 @@ bool Parser::TryAnnotateTypeConstraint(CXXScopeSpec &SS) { /// 'typename' ...[opt][C++0x] identifier[opt] /// 'typename' identifier[opt] '=' type-id NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { - assert(Tok.isOneOf(tok::kw_class, tok::kw_typename, tok::annot_template_id) && + assert((Tok.isOneOf(tok::kw_class, tok::kw_typename) || + isTypeConstraintAnnotation()) && "A type-parameter starts with 'class', 'typename' or a " "type-constraint"); + CXXScopeSpec TypeConstraintSS; TemplateIdAnnotation *TypeConstraint = nullptr; bool TypenameKeyword = false; SourceLocation KeyLoc; + ParseOptionalCXXScopeSpecifier(TypeConstraintSS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, + /*EnteringContext*/ false); if (Tok.is(tok::annot_template_id)) { // Consume the 'type-constraint'. TypeConstraint = @@ -754,6 +778,9 @@ NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { "stray non-concept template-id annotation"); KeyLoc = ConsumeAnnotationToken(); } else { + assert(TypeConstraintSS.isEmpty() && + "expected type constraint after scope specifier"); + // Consume the 'class' or 'typename' keyword. TypenameKeyword = Tok.is(tok::kw_typename); KeyLoc = ConsumeToken(); @@ -795,7 +822,8 @@ NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { ParsedType DefaultArg; if (TryConsumeToken(tok::equal, EqualLoc)) DefaultArg = ParseTypeName(/*Range=*/nullptr, - DeclaratorContext::TemplateTypeArgContext).get(); + DeclaratorContext::TemplateTypeArgContext) + .get(); NamedDecl *NewDecl = Actions.ActOnTypeParameter(getCurScope(), TypenameKeyword, EllipsisLoc, @@ -804,10 +832,11 @@ NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { DefaultArg, TypeConstraint != nullptr); - if (TypeConstraint) - Actions.ActOnTypeConstraint(TypeConstraint, + if (TypeConstraint) { + Actions.ActOnTypeConstraint(TypeConstraintSS, TypeConstraint, cast<TemplateTypeParmDecl>(NewDecl), EllipsisLoc); + } return NewDecl; } @@ -832,9 +861,9 @@ Parser::ParseTemplateTemplateParameter(unsigned Depth, unsigned Position) { SmallVector<NamedDecl*,8> TemplateParams; SourceLocation LAngleLoc, RAngleLoc; { - ParseScope TemplateParmScope(this, Scope::TemplateParamScope); - if (ParseTemplateParameters(Depth + 1, TemplateParams, LAngleLoc, - RAngleLoc)) { + MultiParseScope TemplateParmScope(*this); + if (ParseTemplateParameters(TemplateParmScope, Depth + 1, TemplateParams, + LAngleLoc, RAngleLoc)) { return nullptr; } } @@ -972,7 +1001,7 @@ Parser::ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position) { // Create the parameter. return Actions.ActOnNonTypeTemplateParameter(getCurScope(), ParamDecl, - Depth, Position, EqualLoc, + Depth, Position, EqualLoc, DefaultArg.get()); } @@ -1013,7 +1042,8 @@ void Parser::DiagnoseMisplacedEllipsisInDeclarator(SourceLocation EllipsisLoc, /// or argument list. /// /// \returns true, if current token does not start with '>', false otherwise. -bool Parser::ParseGreaterThanInTemplateList(SourceLocation &RAngleLoc, +bool Parser::ParseGreaterThanInTemplateList(SourceLocation LAngleLoc, + SourceLocation &RAngleLoc, bool ConsumeLastToken, bool ObjCGenericList) { // What will be left once we've consumed the '>'. @@ -1023,7 +1053,8 @@ bool Parser::ParseGreaterThanInTemplateList(SourceLocation &RAngleLoc, switch (Tok.getKind()) { default: - Diag(Tok.getLocation(), diag::err_expected) << tok::greater; + Diag(getEndOfPreviousToken(), diag::err_expected) << tok::greater; + Diag(LAngleLoc, diag::note_matching) << tok::less; return true; case tok::greater: @@ -1202,16 +1233,17 @@ Parser::ParseTemplateIdAfterTemplateName(bool ConsumeLastToken, if (Invalid) { // Try to find the closing '>'. - if (ConsumeLastToken) - SkipUntil(tok::greater, StopAtSemi); + if (getLangOpts().CPlusPlus11) + SkipUntil(tok::greater, tok::greatergreater, + tok::greatergreatergreater, StopAtSemi | StopBeforeMatch); else SkipUntil(tok::greater, StopAtSemi | StopBeforeMatch); - return true; } } - return ParseGreaterThanInTemplateList(RAngleLoc, ConsumeLastToken, - /*ObjCGenericList=*/false); + return ParseGreaterThanInTemplateList(LAngleLoc, RAngleLoc, ConsumeLastToken, + /*ObjCGenericList=*/false) || + Invalid; } /// Replace the tokens that form a simple-template-id with an @@ -1262,12 +1294,13 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK, bool AllowTypeAnnotation, bool TypeConstraint) { assert(getLangOpts().CPlusPlus && "Can only annotate template-ids in C++"); - assert(Template && (Tok.is(tok::less) || TypeConstraint) && + assert((Tok.is(tok::less) || TypeConstraint) && "Parser isn't at the beginning of a template-id"); assert(!(TypeConstraint && AllowTypeAnnotation) && "type-constraint can't be " "a type annotation"); assert((!TypeConstraint || TNK == TNK_Concept_template) && "type-constraint " "must accompany a concept name"); + assert((Template || TNK == TNK_Non_template) && "missing template name"); // Consume the template-name. SourceLocation TemplateNameLoc = TemplateName.getSourceRange().getBegin(); @@ -1275,40 +1308,31 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK, // Parse the enclosed template argument list. SourceLocation LAngleLoc, RAngleLoc; TemplateArgList TemplateArgs; + bool ArgsInvalid = false; if (!TypeConstraint || Tok.is(tok::less)) { - bool Invalid = ParseTemplateIdAfterTemplateName(false, LAngleLoc, - TemplateArgs, - RAngleLoc); - - if (Invalid) { - // If we failed to parse the template ID but skipped ahead to a >, we're not - // going to be able to form a token annotation. Eat the '>' if present. - TryConsumeToken(tok::greater); - // FIXME: Annotate the token stream so we don't produce the same errors - // again if we're doing this annotation as part of a tentative parse. + ArgsInvalid = ParseTemplateIdAfterTemplateName(false, LAngleLoc, + TemplateArgs, RAngleLoc); + // If we couldn't recover from invalid arguments, don't form an annotation + // token -- we don't know how much to annotate. + // FIXME: This can lead to duplicate diagnostics if we retry parsing this + // template-id in another context. Try to annotate anyway? + if (RAngleLoc.isInvalid()) return true; - } } ASTTemplateArgsPtr TemplateArgsPtr(TemplateArgs); // Build the annotation token. if (TNK == TNK_Type_template && AllowTypeAnnotation) { - TypeResult Type = Actions.ActOnTemplateIdType( - getCurScope(), SS, TemplateKWLoc, Template, TemplateName.Identifier, - TemplateNameLoc, LAngleLoc, TemplateArgsPtr, RAngleLoc); - if (Type.isInvalid()) { - // If we failed to parse the template ID but skipped ahead to a >, we're - // not going to be able to form a token annotation. Eat the '>' if - // present. - TryConsumeToken(tok::greater); - // FIXME: Annotate the token stream so we don't produce the same errors - // again if we're doing this annotation as part of a tentative parse. - return true; - } + TypeResult Type = ArgsInvalid + ? TypeError() + : Actions.ActOnTemplateIdType( + getCurScope(), SS, TemplateKWLoc, Template, + TemplateName.Identifier, TemplateNameLoc, + LAngleLoc, TemplateArgsPtr, RAngleLoc); Tok.setKind(tok::annot_typename); - setTypeAnnotation(Tok, Type.get()); + setTypeAnnotation(Tok, Type); if (SS.isNotEmpty()) Tok.setLocation(SS.getBeginLoc()); else if (TemplateKWLoc.isValid()) @@ -1331,8 +1355,8 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK, : TemplateName.OperatorFunctionId.Operator; TemplateIdAnnotation *TemplateId = TemplateIdAnnotation::Create( - SS, TemplateKWLoc, TemplateNameLoc, TemplateII, OpKind, Template, TNK, - LAngleLoc, RAngleLoc, TemplateArgs, TemplateIds); + TemplateKWLoc, TemplateNameLoc, TemplateII, OpKind, Template, TNK, + LAngleLoc, RAngleLoc, TemplateArgs, ArgsInvalid, TemplateIds); Tok.setAnnotationValue(TemplateId); if (TemplateKWLoc.isValid()) @@ -1357,39 +1381,37 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK, /// a type annotation token will still be created, but will have a /// NULL type pointer to signify an error. /// +/// \param SS The scope specifier appearing before the template-id, if any. +/// /// \param IsClassName Is this template-id appearing in a context where we /// know it names a class, such as in an elaborated-type-specifier or /// base-specifier? ('typename' and 'template' are unneeded and disallowed /// in those contexts.) -void Parser::AnnotateTemplateIdTokenAsType(bool IsClassName) { +void Parser::AnnotateTemplateIdTokenAsType(CXXScopeSpec &SS, + bool IsClassName) { assert(Tok.is(tok::annot_template_id) && "Requires template-id tokens"); TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); - assert((TemplateId->Kind == TNK_Type_template || - TemplateId->Kind == TNK_Dependent_template_name || - TemplateId->Kind == TNK_Undeclared_template) && + assert(TemplateId->mightBeType() && "Only works for type and dependent templates"); ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), TemplateId->NumArgs); - TypeResult Type - = Actions.ActOnTemplateIdType(getCurScope(), - TemplateId->SS, - TemplateId->TemplateKWLoc, - TemplateId->Template, - TemplateId->Name, - TemplateId->TemplateNameLoc, - TemplateId->LAngleLoc, - TemplateArgsPtr, - TemplateId->RAngleLoc, - /*IsCtorOrDtorName*/false, - IsClassName); + TypeResult Type = + TemplateId->isInvalid() + ? TypeError() + : Actions.ActOnTemplateIdType( + getCurScope(), SS, TemplateId->TemplateKWLoc, + TemplateId->Template, TemplateId->Name, + TemplateId->TemplateNameLoc, TemplateId->LAngleLoc, + TemplateArgsPtr, TemplateId->RAngleLoc, + /*IsCtorOrDtorName*/ false, IsClassName); // Create the new "type" annotation token. Tok.setKind(tok::annot_typename); - setTypeAnnotation(Tok, Type.isInvalid() ? nullptr : Type.get()); - if (TemplateId->SS.isNotEmpty()) // it was a C++ qualified type name. - Tok.setLocation(TemplateId->SS.getBeginLoc()); + setTypeAnnotation(Tok, Type); + if (SS.isNotEmpty()) // it was a C++ qualified type name. + Tok.setLocation(SS.getBeginLoc()); // End location stays the same // Replace the template-id annotation token, and possible the scope-specifier @@ -1399,7 +1421,9 @@ void Parser::AnnotateTemplateIdTokenAsType(bool IsClassName) { /// Determine whether the given token can end a template argument. static bool isEndOfTemplateArgument(Token Tok) { - return Tok.isOneOf(tok::comma, tok::greater, tok::greatergreater); + // FIXME: Handle '>>>'. + return Tok.isOneOf(tok::comma, tok::greater, tok::greatergreater, + tok::greatergreatergreater); } /// Parse a C++ template template argument. @@ -1420,7 +1444,8 @@ ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() { // followed by a token that terminates a template argument, such as ',', // '>', or (in some cases) '>>'. CXXScopeSpec SS; // nested-name-specifier, if present - ParseOptionalCXXScopeSpecifier(SS, nullptr, + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, /*EnteringContext=*/false); ParsedTemplateArgument Result; @@ -1438,15 +1463,14 @@ ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() { TryConsumeToken(tok::ellipsis, EllipsisLoc); - // If the next token signals the end of a template argument, - // then we have a dependent template name that could be a template - // template argument. + // If the next token signals the end of a template argument, then we have + // a (possibly-dependent) template name that could be a template template + // argument. TemplateTy Template; if (isEndOfTemplateArgument(Tok) && - Actions.ActOnDependentTemplateName( - getCurScope(), SS, TemplateKWLoc, Name, - /*ObjectType=*/nullptr, - /*EnteringContext=*/false, Template)) + Actions.ActOnTemplateName(getCurScope(), SS, TemplateKWLoc, Name, + /*ObjectType=*/nullptr, + /*EnteringContext=*/false, Template)) Result = ParsedTemplateArgument(SS, Template, Name.StartLocation); } } else if (Tok.is(tok::identifier)) { @@ -1550,10 +1574,8 @@ Parser::ParseTemplateArgumentList(TemplateArgList &TemplateArgs) { if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) Arg = Actions.ActOnPackExpansion(Arg, EllipsisLoc); - if (Arg.isInvalid()) { - SkipUntil(tok::comma, tok::greater, StopAtSemi | StopBeforeMatch); + if (Arg.isInvalid()) return true; - } // Save this template argument. TemplateArgs.push_back(Arg); @@ -1607,6 +1629,9 @@ void Parser::ParseLateTemplatedFuncDef(LateParsedTemplate &LPT) { if (!LPT.D) return; + // Destroy TemplateIdAnnotations when we're done, if possible. + DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(*this); + // Get the FunctionDecl. FunctionDecl *FunD = LPT.D->getAsFunction(); // Track template parameter depth. @@ -1616,40 +1641,22 @@ void Parser::ParseLateTemplatedFuncDef(LateParsedTemplate &LPT) { Sema::ContextRAII GlobalSavedContext( Actions, Actions.Context.getTranslationUnitDecl()); - SmallVector<ParseScope*, 4> TemplateParamScopeStack; - - // Get the list of DeclContexts to reenter. For inline methods, we only want - // to push the DeclContext of the outermost class. This matches the way the - // parser normally parses bodies of inline methods when the outermost class is - // complete. - struct ContainingDC { - ContainingDC(DeclContext *DC, bool ShouldPush) : Pair(DC, ShouldPush) {} - llvm::PointerIntPair<DeclContext *, 1, bool> Pair; - DeclContext *getDC() { return Pair.getPointer(); } - bool shouldPushDC() { return Pair.getInt(); } - }; - SmallVector<ContainingDC, 4> DeclContextsToReenter; - DeclContext *DD = FunD; - DeclContext *NextContaining = Actions.getContainingDC(DD); - while (DD && !DD->isTranslationUnit()) { - bool ShouldPush = DD == NextContaining; - DeclContextsToReenter.push_back({DD, ShouldPush}); - if (ShouldPush) - NextContaining = Actions.getContainingDC(DD); - DD = DD->getLexicalParent(); - } - - // Reenter template scopes from outermost to innermost. - for (ContainingDC CDC : reverse(DeclContextsToReenter)) { - TemplateParamScopeStack.push_back( - new ParseScope(this, Scope::TemplateParamScope)); - unsigned NumParamLists = Actions.ActOnReenterTemplateScope( - getCurScope(), cast<Decl>(CDC.getDC())); - CurTemplateDepthTracker.addDepth(NumParamLists); - if (CDC.shouldPushDC()) { - TemplateParamScopeStack.push_back(new ParseScope(this, Scope::DeclScope)); - Actions.PushDeclContext(Actions.getCurScope(), CDC.getDC()); - } + MultiParseScope Scopes(*this); + + // Get the list of DeclContexts to reenter. + SmallVector<DeclContext*, 4> DeclContextsToReenter; + for (DeclContext *DC = FunD; DC && !DC->isTranslationUnit(); + DC = DC->getLexicalParent()) + DeclContextsToReenter.push_back(DC); + + // Reenter scopes from outermost to innermost. + for (DeclContext *DC : reverse(DeclContextsToReenter)) { + CurTemplateDepthTracker.addDepth( + ReenterTemplateScopes(Scopes, cast<Decl>(DC))); + Scopes.Enter(Scope::DeclScope); + // We'll reenter the function context itself below. + if (DC != FunD) + Actions.PushDeclContext(Actions.getCurScope(), DC); } assert(!LPT.Toks.empty() && "Empty body!"); @@ -1670,8 +1677,7 @@ void Parser::ParseLateTemplatedFuncDef(LateParsedTemplate &LPT) { Scope::CompoundStmtScope); // Recreate the containing function DeclContext. - Sema::ContextRAII FunctionSavedContext(Actions, - Actions.getContainingDC(FunD)); + Sema::ContextRAII FunctionSavedContext(Actions, FunD->getLexicalParent()); Actions.ActOnStartOfFunctionDef(getCurScope(), FunD); @@ -1695,13 +1701,6 @@ void Parser::ParseLateTemplatedFuncDef(LateParsedTemplate &LPT) { } else Actions.ActOnFinishFunctionBody(LPT.D, nullptr); } - - // Exit scopes. - FnScope.Exit(); - SmallVectorImpl<ParseScope *>::reverse_iterator I = - TemplateParamScopeStack.rbegin(); - for (; I != TemplateParamScopeStack.rend(); ++I) - delete *I; } /// Lex a delayed template function for late parsing. @@ -1733,7 +1732,7 @@ bool Parser::diagnoseUnknownTemplateId(ExprResult LHS, SourceLocation Less) { TPA.Commit(); SourceLocation Greater; - ParseGreaterThanInTemplateList(Greater, true, false); + ParseGreaterThanInTemplateList(Less, Greater, true, false); Actions.diagnoseExprIntendedAsTemplateName(getCurScope(), LHS, Less, Greater); return true; @@ -1762,7 +1761,7 @@ void Parser::checkPotentialAngleBracket(ExprResult &PotentialTemplateName) { NextToken().isOneOf(tok::greatergreater, tok::greatergreatergreater))) { SourceLocation Less = ConsumeToken(); SourceLocation Greater; - ParseGreaterThanInTemplateList(Greater, true, false); + ParseGreaterThanInTemplateList(Less, Greater, true, false); Actions.diagnoseExprIntendedAsTemplateName( getCurScope(), PotentialTemplateName, Less, Greater); // FIXME: Perform error recovery. diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 4d69fb4693fb..f026f3a1bfb2 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -186,25 +186,10 @@ Parser::TPResult Parser::TryConsumeDeclarationSpecifier() { ConsumeToken(); // Skip attributes. - while (Tok.isOneOf(tok::l_square, tok::kw___attribute, tok::kw___declspec, - tok::kw_alignas)) { - if (Tok.is(tok::l_square)) { - ConsumeBracket(); - if (!SkipUntil(tok::r_square)) - return TPResult::Error; - } else { - ConsumeToken(); - if (Tok.isNot(tok::l_paren)) - return TPResult::Error; - ConsumeParen(); - if (!SkipUntil(tok::r_paren)) - return TPResult::Error; - } - } + if (!TrySkipAttributes()) + return TPResult::Error; - if (Tok.isOneOf(tok::identifier, tok::coloncolon, tok::kw_decltype, - tok::annot_template_id) && - TryAnnotateCXXScopeToken()) + if (TryAnnotateOptionalCXXScopeToken()) return TPResult::Error; if (Tok.is(tok::annot_cxxscope)) ConsumeAnnotationToken(); @@ -443,6 +428,38 @@ struct Parser::ConditionDeclarationOrInitStatementState { } }; +bool Parser::isEnumBase(bool AllowSemi) { + assert(Tok.is(tok::colon) && "should be looking at the ':'"); + + RevertingTentativeParsingAction PA(*this); + // ':' + ConsumeToken(); + + // type-specifier-seq + bool InvalidAsDeclSpec = false; + // 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, + &InvalidAsDeclSpec); + if (R == TPResult::Ambiguous) { + // We either have a decl-specifier followed by '(' or an undeclared + // identifier. + if (TryConsumeDeclarationSpecifier() == TPResult::Error) + return true; + + // If we get to the end of the enum-base, we hit either a '{' or a ';'. + // Don't bother checking the enumerator-list. + if (Tok.is(tok::l_brace) || (AllowSemi && Tok.is(tok::semi))) + return true; + + // A second decl-specifier unambiguously indicatges an enum-base. + R = isCXXDeclarationSpecifier(TPResult::True, &InvalidAsDeclSpec); + } + + return R != TPResult::False; +} + /// Disambiguates between a declaration in a condition, a /// simple-declaration in an init-statement, and an expression for /// a condition of a if/switch statement. @@ -783,19 +800,49 @@ Parser::isCXX11AttributeSpecifier(bool Disambiguate, return CAK_NotAttributeSpecifier; } +bool Parser::TrySkipAttributes() { + while (Tok.isOneOf(tok::l_square, tok::kw___attribute, tok::kw___declspec, + tok::kw_alignas)) { + if (Tok.is(tok::l_square)) { + ConsumeBracket(); + if (Tok.isNot(tok::l_square)) + return false; + ConsumeBracket(); + if (!SkipUntil(tok::r_square) || Tok.isNot(tok::r_square)) + return false; + // Note that explicitly checking for `[[` and `]]` allows to fail as + // expected in the case of the Objective-C message send syntax. + ConsumeBracket(); + } else { + ConsumeToken(); + if (Tok.isNot(tok::l_paren)) + return false; + ConsumeParen(); + if (!SkipUntil(tok::r_paren)) + return false; + } + } + + return true; +} + Parser::TPResult Parser::TryParsePtrOperatorSeq() { while (true) { - if (Tok.isOneOf(tok::coloncolon, tok::identifier)) - if (TryAnnotateCXXScopeToken(true)) - return TPResult::Error; + if (TryAnnotateOptionalCXXScopeToken(true)) + return TPResult::Error; if (Tok.isOneOf(tok::star, tok::amp, tok::caret, tok::ampamp) || (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::star))) { // ptr-operator ConsumeAnyToken(); + + // Skip attributes. + if (!TrySkipAttributes()) + return TPResult::Error; + while (Tok.isOneOf(tok::kw_const, tok::kw_volatile, tok::kw_restrict, tok::kw__Nonnull, tok::kw__Nullable, - tok::kw__Null_unspecified)) + tok::kw__Null_unspecified, tok::kw__Atomic)) ConsumeToken(); } else { return TPResult::True; @@ -969,10 +1016,16 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, NextToken().is(tok::kw_operator)))) && mayHaveIdentifier) { // declarator-id - if (Tok.is(tok::annot_cxxscope)) + if (Tok.is(tok::annot_cxxscope)) { + CXXScopeSpec SS; + Actions.RestoreNestedNameSpecifierAnnotation( + Tok.getAnnotationValue(), Tok.getAnnotationRange(), SS); + if (SS.isInvalid()) + return TPResult::Error; ConsumeAnnotationToken(); - else if (Tok.is(tok::identifier)) + } else if (Tok.is(tok::identifier)) { TentativelyDeclaredIdentifiers.push_back(Tok.getIdentifierInfo()); + } if (Tok.is(tok::kw_operator)) { if (TryParseOperatorId() == TPResult::Error) return TPResult::Error; @@ -1046,130 +1099,6 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, return TPResult::Ambiguous; } -Parser::TPResult -Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) { - switch (Kind) { - // Obviously starts an expression. - case tok::numeric_constant: - case tok::char_constant: - case tok::wide_char_constant: - case tok::utf8_char_constant: - case tok::utf16_char_constant: - case tok::utf32_char_constant: - case tok::string_literal: - case tok::wide_string_literal: - case tok::utf8_string_literal: - case tok::utf16_string_literal: - case tok::utf32_string_literal: - case tok::l_square: - case tok::l_paren: - case tok::amp: - case tok::ampamp: - case tok::star: - case tok::plus: - case tok::plusplus: - case tok::minus: - case tok::minusminus: - case tok::tilde: - case tok::exclaim: - case tok::kw_sizeof: - case tok::kw___func__: - case tok::kw_const_cast: - case tok::kw_delete: - case tok::kw_dynamic_cast: - case tok::kw_false: - case tok::kw_new: - case tok::kw_operator: - case tok::kw_reinterpret_cast: - case tok::kw_static_cast: - case tok::kw_this: - case tok::kw_throw: - case tok::kw_true: - case tok::kw_typeid: - case tok::kw_alignof: - case tok::kw_noexcept: - case tok::kw_nullptr: - case tok::kw__Alignof: - case tok::kw___null: - case tok::kw___alignof: - case tok::kw___builtin_choose_expr: - case tok::kw___builtin_offsetof: - case tok::kw___builtin_va_arg: - case tok::kw___imag: - case tok::kw___real: - case tok::kw___FUNCTION__: - case tok::kw___FUNCDNAME__: - case tok::kw___FUNCSIG__: - case tok::kw_L__FUNCTION__: - case tok::kw_L__FUNCSIG__: - case tok::kw___PRETTY_FUNCTION__: - case tok::kw___uuidof: -#define TYPE_TRAIT(N,Spelling,K) \ - case tok::kw_##Spelling: -#include "clang/Basic/TokenKinds.def" - return TPResult::True; - - // Obviously starts a type-specifier-seq: - case tok::kw_char: - case tok::kw_const: - case tok::kw_double: - case tok::kw__Float16: - case tok::kw___float128: - case tok::kw_enum: - case tok::kw_half: - case tok::kw_float: - case tok::kw_int: - case tok::kw_long: - case tok::kw___int64: - case tok::kw___int128: - case tok::kw_restrict: - case tok::kw_short: - case tok::kw_signed: - case tok::kw_struct: - case tok::kw_union: - case tok::kw_unsigned: - case tok::kw_void: - case tok::kw_volatile: - case tok::kw__Bool: - case tok::kw__Complex: - case tok::kw_class: - case tok::kw_typename: - case tok::kw_wchar_t: - case tok::kw_char8_t: - case tok::kw_char16_t: - case tok::kw_char32_t: - 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: - case tok::kw___thiscall: - case tok::kw___regcall: - case tok::kw___vectorcall: - case tok::kw___unaligned: - case tok::kw___vector: - case tok::kw___pixel: - case tok::kw___bool: - case tok::kw__Atomic: -#define GENERIC_IMAGE_TYPE(ImgType, Id) case tok::kw_##ImgType##_t: -#include "clang/Basic/OpenCLImageTypes.def" - case tok::kw___unknown_anytype: - return TPResult::False; - - default: - break; - } - - return TPResult::Ambiguous; -} - bool Parser::isTentativelyDeclared(IdentifierInfo *II) { return std::find(TentativelyDeclaredIdentifiers.begin(), TentativelyDeclaredIdentifiers.end(), II) @@ -1181,8 +1110,9 @@ class TentativeParseCCC final : public CorrectionCandidateCallback { public: TentativeParseCCC(const Token &Next) { WantRemainingKeywords = false; - WantTypeSpecifiers = Next.isOneOf(tok::l_paren, tok::r_paren, tok::greater, - tok::l_brace, tok::identifier); + WantTypeSpecifiers = + Next.isOneOf(tok::l_paren, tok::r_paren, tok::greater, tok::l_brace, + tok::identifier, tok::comma); } bool ValidateCandidate(const TypoCorrection &Candidate) override { @@ -1316,6 +1246,18 @@ public: Parser::TPResult Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, bool *InvalidAsDeclSpec) { + 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); + }; switch (Tok.getKind()) { case tok::identifier: { // Check for need to substitute AltiVec __vector keyword @@ -1334,6 +1276,15 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, // this is ambiguous. Typo-correct to type and expression keywords and // to types and identifiers, in order to try to recover from errors. TentativeParseCCC CCC(Next); + // Tentative parsing may not be done in the right evaluation context + // for the ultimate expression. Enter an unevaluated context to prevent + // Sema from immediately e.g. treating this lookup as a potential ODR-use. + // If we generate an expression annotation token and the parser actually + // claims it as an expression, we'll transform the expression to a + // potentially-evaluated one then. + EnterExpressionEvaluationContext Unevaluated( + Actions, Sema::ExpressionEvaluationContext::Unevaluated, + Sema::ReuseLambdaContextDecl); switch (TryAnnotateName(&CCC)) { case ANK_Error: return TPResult::Error; @@ -1511,7 +1462,9 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); // If lookup for the template-name found nothing, don't assume we have a // definitive disambiguation result yet. - if (TemplateId->Kind == TNK_Undeclared_template && InvalidAsDeclSpec) { + if ((TemplateId->hasInvalidName() || + TemplateId->Kind == TNK_Undeclared_template) && + InvalidAsDeclSpec) { // 'template-id(' can be a valid expression but not a valid decl spec if // the template-name is not declared, but we don't consider this to be a // definitive disambiguation. In any other context, it's an error either @@ -1519,10 +1472,14 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, *InvalidAsDeclSpec = NextToken().is(tok::l_paren); return TPResult::Ambiguous; } + if (TemplateId->hasInvalidName()) + return TPResult::Error; + if (IsPlaceholderSpecifier(TemplateId, /*Lookahead=*/0)) + return TPResult::True; if (TemplateId->Kind != TNK_Type_template) return TPResult::False; CXXScopeSpec SS; - AnnotateTemplateIdTokenAsType(); + AnnotateTemplateIdTokenAsType(SS); assert(Tok.is(tok::annot_typename)); goto case_typename; } @@ -1532,6 +1489,20 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, if (TryAnnotateTypeOrScopeToken()) return TPResult::Error; if (!Tok.is(tok::annot_typename)) { + if (Tok.is(tok::annot_cxxscope) && + NextToken().is(tok::annot_template_id)) { + TemplateIdAnnotation *TemplateId = + takeTemplateIdAnnotation(NextToken()); + if (TemplateId->hasInvalidName()) { + if (InvalidAsDeclSpec) { + *InvalidAsDeclSpec = NextToken().is(tok::l_paren); + return TPResult::Ambiguous; + } + return TPResult::Error; + } + if (IsPlaceholderSpecifier(TemplateId, /*Lookahead=*/1)) + return TPResult::True; + } // If the next token is an identifier or a type qualifier, then this // can't possibly be a valid expression either. if (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::identifier)) { @@ -1670,6 +1641,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, case tok::kw_half: case tok::kw_float: case tok::kw_double: + case tok::kw___bf16: case tok::kw__Float16: case tok::kw___float128: case tok::kw_void: @@ -1724,6 +1696,24 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, case tok::kw__Atomic: return TPResult::True; + case tok::kw__ExtInt: { + if (NextToken().isNot(tok::l_paren)) + return TPResult::Error; + RevertingTentativeParsingAction PA(*this); + ConsumeToken(); + ConsumeParen(); + + if (!SkipUntil(tok::r_paren, StopAtSemi)) + return TPResult::Error; + + if (Tok.is(tok::l_paren)) + return TPResult::Ambiguous; + + if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) + return BracedCastResult; + + return TPResult::True; + } default: return TPResult::False; } @@ -1756,6 +1746,7 @@ bool Parser::isCXXDeclarationSpecifierAType() { case tok::kw_bool: case tok::kw_short: case tok::kw_int: + case tok::kw__ExtInt: case tok::kw_long: case tok::kw___int64: case tok::kw___int128: @@ -1764,6 +1755,7 @@ bool Parser::isCXXDeclarationSpecifierAType() { case tok::kw_half: case tok::kw_float: case tok::kw_double: + case tok::kw___bf16: case tok::kw__Float16: case tok::kw___float128: case tok::kw_void: @@ -1975,17 +1967,14 @@ Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration, // (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. + // FIXME: If we reach a ')' without consuming any '>'s, then this must + // also be a function parameter (that's missing its default argument). if (VersusTemplateArgument) - return Tok.isOneOf(tok::equal, tok::r_paren) ? TPResult::True - : TPResult::False; + return Tok.is(tok::equal) ? 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, StopAtSemi | StopBeforeMatch)) return TPResult::Error; } @@ -2137,3 +2126,58 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) { return TPResult::Ambiguous; return TPResult::False; } + +/// Determine whether we might be looking at the '(' of a C++20 explicit(bool) +/// in an earlier language mode. +Parser::TPResult Parser::isExplicitBool() { + assert(Tok.is(tok::l_paren) && "expected to be looking at a '(' token"); + + RevertingTentativeParsingAction PA(*this); + ConsumeParen(); + + // We can only have 'explicit' on a constructor, conversion function, or + // deduction guide. The declarator of a deduction guide cannot be + // parenthesized, so we know this isn't a deduction guide. So the only + // thing we need to check for is some number of parens followed by either + // the current class name or 'operator'. + while (Tok.is(tok::l_paren)) + ConsumeParen(); + + if (TryAnnotateOptionalCXXScopeToken()) + return TPResult::Error; + + // Class-scope constructor and conversion function names can't really be + // qualified, but we get better diagnostics if we assume they can be. + CXXScopeSpec SS; + if (Tok.is(tok::annot_cxxscope)) { + Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(), + Tok.getAnnotationRange(), + SS); + ConsumeAnnotationToken(); + } + + // 'explicit(operator' might be explicit(bool) or the declaration of a + // conversion function, but it's probably a conversion function. + if (Tok.is(tok::kw_operator)) + return TPResult::Ambiguous; + + // If this can't be a constructor name, it can only be explicit(bool). + if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id)) + return TPResult::True; + if (!Actions.isCurrentClassName(Tok.is(tok::identifier) + ? *Tok.getIdentifierInfo() + : *takeTemplateIdAnnotation(Tok)->Name, + getCurScope(), &SS)) + return TPResult::True; + // Formally, we must have a right-paren after the constructor name to match + // the grammar for a constructor. But clang permits a parenthesized + // constructor declarator, so also allow a constructor declarator to follow + // with no ')' token after the constructor name. + if (!NextToken().is(tok::r_paren) && + !isConstructorDeclarator(/*Unqualified=*/SS.isEmpty(), + /*DeductionGuide=*/false)) + return TPResult::True; + + // Might be explicit(bool) or a parenthesized constructor name. + return TPResult::Ambiguous; +} diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 4249de361b89..764d4e8e9d52 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -14,6 +14,7 @@ #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclTemplate.h" +#include "clang/Basic/FileManager.h" #include "clang/Parse/ParseDiagnostic.h" #include "clang/Parse/RAIIObjectsForParser.h" #include "clang/Sema/DeclSpec.h" @@ -432,16 +433,7 @@ Parser::~Parser() { PP.clearCodeCompletionHandler(); - if (getLangOpts().DelayedTemplateParsing && - !PP.isIncrementalProcessingEnabled() && !TemplateIds.empty()) { - // If an ASTConsumer parsed delay-parsed templates in their - // HandleTranslationUnit() method, TemplateIds created there were not - // guarded by a DestroyTemplateIdAnnotationsRAIIObj object in - // ParseTopLevelDecl(). Destroy them here. - DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(TemplateIds); - } - - assert(TemplateIds.empty() && "Still alive TemplateIdAnnotations around?"); + DestroyTemplateIds(); } /// Initialize - Warm up the parser. @@ -537,11 +529,10 @@ void Parser::Initialize() { ConsumeToken(); } -void Parser::LateTemplateParserCleanupCallback(void *P) { - // While this RAII helper doesn't bracket any actual work, the destructor will - // clean up annotations that were created during ActOnEndOfTranslationUnit - // when incremental processing is enabled. - DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(((Parser *)P)->TemplateIds); +void Parser::DestroyTemplateIds() { + for (TemplateIdAnnotation *Id : TemplateIds) + Id->Destroy(); + TemplateIds.clear(); } /// Parse the first top-level declaration in a translation unit. @@ -576,7 +567,7 @@ bool Parser::ParseFirstTopLevelDecl(DeclGroupPtrTy &Result) { /// declaration /// [C++20] module-import-declaration bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result, bool IsFirstDecl) { - DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(TemplateIds); + DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(*this); // Skip over the EOF token, flagging end of previous input for incremental // processing @@ -650,12 +641,18 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result, bool IsFirstDecl) { return false; case tok::eof: + // Check whether -fmax-tokens= was reached. + if (PP.getMaxTokens() != 0 && PP.getTokenCount() > PP.getMaxTokens()) { + PP.Diag(Tok.getLocation(), diag::warn_max_tokens_total) + << PP.getTokenCount() << PP.getMaxTokens(); + SourceLocation OverrideLoc = PP.getMaxTokensOverrideLoc(); + if (OverrideLoc.isValid()) { + PP.Diag(OverrideLoc, diag::note_max_tokens_total_override); + } + } + // Late template parsing can begin. - if (getLangOpts().DelayedTemplateParsing) - Actions.SetLateTemplateParser(LateTemplateParserCallback, - PP.isIncrementalProcessingEnabled() ? - LateTemplateParserCleanupCallback : nullptr, - this); + Actions.SetLateTemplateParser(LateTemplateParserCallback, nullptr, this); if (!PP.isIncrementalProcessingEnabled()) Actions.ActOnEndOfTranslationUnit(); //else don't tell Sema that we ended parsing: more input might come. @@ -716,7 +713,7 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result, bool IsFirstDecl) { Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs, ParsingDeclSpec *DS) { - DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(TemplateIds); + DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(*this); ParenBraceBracketBalancer BalancerRAIIObj(*this); if (PP.isCodeCompletionReached()) { @@ -753,6 +750,9 @@ Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs, case tok::annot_pragma_fenv_access: HandlePragmaFEnvAccess(); return nullptr; + case tok::annot_pragma_float_control: + HandlePragmaFloatControl(); + return nullptr; case tok::annot_pragma_fp: HandlePragmaFP(); break; @@ -1136,6 +1136,7 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, // Poison SEH identifiers so they are flagged as illegal in function bodies. PoisonSEHIdentifiersRAIIObject PoisonSEHIdentifiers(*this, true); const DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo(); + TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); // If this is C90 and the declspecs were completely missing, fudge in an // implicit int. We do this here because this is the only place where @@ -1262,6 +1263,15 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, // safe because we're always the sole owner. D.getMutableDeclSpec().abort(); + // With abbreviated function templates - we need to explicitly add depth to + // account for the implicit template parameter list induced by the template. + if (auto *Template = dyn_cast_or_null<FunctionTemplateDecl>(Res)) + if (Template->isAbbreviated() && + Template->getTemplateParameters()->getParam(0)->isImplicit()) + // First template parameter is implicit - meaning no explicit template + // parameter list was specified. + CurTemplateDepthTracker.addDepth(1); + if (TryConsumeToken(tok::equal)) { assert(getLangOpts().CPlusPlus && "Only C++ function definitions have '='"); @@ -1508,13 +1518,13 @@ ExprResult Parser::ParseSimpleAsm(bool ForAsmLabel, SourceLocation *EndLoc) { assert(Tok.is(tok::kw_asm) && "Not an asm!"); SourceLocation Loc = ConsumeToken(); - if (Tok.is(tok::kw_volatile)) { - // Remove from the end of 'asm' to the end of 'volatile'. + if (isGNUAsmQualifier(Tok)) { + // Remove from the end of 'asm' to the end of the asm qualifier. SourceRange RemovalRange(PP.getLocForEndOfToken(Loc), PP.getLocForEndOfToken(Tok.getLocation())); - - Diag(Tok, diag::warn_file_asm_volatile) - << FixItHint::CreateRemoval(RemovalRange); + Diag(Tok, diag::err_global_asm_qualifier_ignored) + << GNUAsmQualifiers::getQualifierName(getGNUAsmQualifier(Tok)) + << FixItHint::CreateRemoval(RemovalRange); ConsumeToken(); } @@ -1584,7 +1594,9 @@ Parser::TryAnnotateName(CorrectionCandidateCallback *CCC) { CXXScopeSpec SS; if (getLangOpts().CPlusPlus && - ParseOptionalCXXScopeSpecifier(SS, nullptr, EnteringContext)) + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, + EnteringContext)) return ANK_Error; if (Tok.isNot(tok::identifier) || SS.isInvalid()) { @@ -1680,7 +1692,8 @@ Parser::TryAnnotateName(CorrectionCandidateCallback *CCC) { } case Sema::NC_ContextIndependentExpr: - Tok.setKind(tok::annot_primary_expr); + Tok.setKind(Actions.isUnevaluatedContext() ? tok::annot_uneval_primary_expr + : tok::annot_primary_expr); setExprAnnotation(Tok, Classification.getExpression()); Tok.setAnnotationEndLoc(NameLoc); if (SS.isNotEmpty()) @@ -1732,6 +1745,20 @@ Parser::TryAnnotateName(CorrectionCandidateCallback *CCC) { return ANK_Error; return ANK_Success; } + case Sema::NC_Concept: { + UnqualifiedId Id; + Id.setIdentifier(Name, NameLoc); + if (Next.is(tok::less)) + // We have a concept name followed by '<'. Consume the identifier token so + // we reach the '<' and annotate it. + ConsumeToken(); + if (AnnotateTemplateIdToken( + TemplateTy::make(Classification.getTemplateName()), + Classification.getTemplateNameKind(), SS, SourceLocation(), Id, + /*AllowTypeAnnotation=*/false, /*TypeConstraint=*/true)) + return ANK_Error; + return ANK_Success; + } } // Unable to classify the name, but maybe we can annotate a scope specifier. @@ -1807,10 +1834,11 @@ bool Parser::TryAnnotateTypeOrScopeToken() { SourceLocation TypenameLoc = ConsumeToken(); CXXScopeSpec SS; if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, /*EnteringContext=*/false, nullptr, /*IsTypename*/ true)) return true; - if (!SS.isSet()) { + if (SS.isEmpty()) { if (Tok.is(tok::identifier) || Tok.is(tok::annot_template_id) || Tok.is(tok::annot_decltype)) { // Attempt to recover by skipping the invalid 'typename' @@ -1840,9 +1868,7 @@ bool Parser::TryAnnotateTypeOrScopeToken() { Tok.getLocation()); } else if (Tok.is(tok::annot_template_id)) { TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); - if (TemplateId->Kind != TNK_Type_template && - TemplateId->Kind != TNK_Dependent_template_name && - TemplateId->Kind != TNK_Undeclared_template) { + if (!TemplateId->mightBeType()) { Diag(Tok, diag::err_typename_refers_to_non_type_template) << Tok.getAnnotationRange(); return true; @@ -1851,14 +1877,13 @@ bool Parser::TryAnnotateTypeOrScopeToken() { ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), TemplateId->NumArgs); - Ty = Actions.ActOnTypenameType(getCurScope(), TypenameLoc, SS, - TemplateId->TemplateKWLoc, - TemplateId->Template, - TemplateId->Name, - TemplateId->TemplateNameLoc, - TemplateId->LAngleLoc, - TemplateArgsPtr, - TemplateId->RAngleLoc); + Ty = TemplateId->isInvalid() + ? TypeError() + : Actions.ActOnTypenameType( + getCurScope(), TypenameLoc, SS, TemplateId->TemplateKWLoc, + TemplateId->Template, TemplateId->Name, + TemplateId->TemplateNameLoc, TemplateId->LAngleLoc, + TemplateArgsPtr, TemplateId->RAngleLoc); } else { Diag(Tok, diag::err_expected_type_name_after_typename) << SS.getRange(); @@ -1867,7 +1892,7 @@ bool Parser::TryAnnotateTypeOrScopeToken() { SourceLocation EndLoc = Tok.getLastLoc(); Tok.setKind(tok::annot_typename); - setTypeAnnotation(Tok, Ty.isInvalid() ? nullptr : Ty.get()); + setTypeAnnotation(Tok, Ty); Tok.setAnnotationEndLoc(EndLoc); Tok.setLocation(TypenameLoc); PP.AnnotateCachedTokens(Tok); @@ -1879,7 +1904,9 @@ bool Parser::TryAnnotateTypeOrScopeToken() { CXXScopeSpec SS; if (getLangOpts().CPlusPlus) - if (ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext*/false)) + if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, + /*EnteringContext*/ false)) return true; return TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation); @@ -1983,7 +2010,7 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(CXXScopeSpec &SS, // template-id annotation in a context where we weren't allowed // to produce a type annotation token. Update the template-id // annotation token to a type annotation token now. - AnnotateTemplateIdTokenAsType(); + AnnotateTemplateIdTokenAsType(SS); return false; } } @@ -2005,13 +2032,12 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(CXXScopeSpec &SS, bool Parser::TryAnnotateCXXScopeToken(bool EnteringContext) { assert(getLangOpts().CPlusPlus && "Call sites of this function should be guarded by checking for C++"); - assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) || - (Tok.is(tok::annot_template_id) && NextToken().is(tok::coloncolon)) || - Tok.is(tok::kw_decltype) || Tok.is(tok::kw___super)) && - "Cannot be a type or scope token!"); + assert(MightBeCXXScopeToken() && "Cannot be a type or scope token!"); CXXScopeSpec SS; - if (ParseOptionalCXXScopeSpecifier(SS, nullptr, EnteringContext)) + if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, + EnteringContext)) return true; if (SS.isEmpty()) return false; @@ -2120,7 +2146,8 @@ bool Parser::ParseMicrosoftIfExistsCondition(IfExistsCondition& Result) { // Parse nested-name-specifier. if (getLangOpts().CPlusPlus) - ParseOptionalCXXScopeSpecifier(Result.SS, nullptr, + ParseOptionalCXXScopeSpecifier(Result.SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, /*EnteringContext=*/false); // Check nested-name specifier. @@ -2131,10 +2158,12 @@ bool Parser::ParseMicrosoftIfExistsCondition(IfExistsCondition& Result) { // Parse the unqualified-id. SourceLocation TemplateKWLoc; // FIXME: parsed, but unused. - if (ParseUnqualifiedId( - Result.SS, /*EnteringContext*/false, /*AllowDestructorName*/true, - /*AllowConstructorName*/true, /*AllowDeductionGuide*/false, nullptr, - &TemplateKWLoc, Result.Name)) { + if (ParseUnqualifiedId(Result.SS, /*ObjectType=*/nullptr, + /*ObjectHadErrors=*/false, /*EnteringContext*/ false, + /*AllowDestructorName*/ true, + /*AllowConstructorName*/ true, + /*AllowDeductionGuide*/ false, &TemplateKWLoc, + Result.Name)) { T.skipToEnd(); return true; } |