diff options
author | Nicolas Lesser <blitzrakete@gmail.com> | 2022-09-06 20:33:54 -0400 |
---|---|---|
committer | Alan Zhao <ayzhao@google.com> | 2022-09-28 09:50:19 -0700 |
commit | 4848f3bf2ff5ec57a8e2b8d3676c947dcf0fd735 (patch) | |
tree | a99e545a8200e4af082d6e2c2d2de2480715d358 /clang/lib/Parse/ParseDecl.cpp | |
parent | 6f2b34789541ff95d7f339eac5dc031d29655a58 (diff) | |
download | llvm-4848f3bf2ff5ec57a8e2b8d3676c947dcf0fd735.zip llvm-4848f3bf2ff5ec57a8e2b8d3676c947dcf0fd735.tar.gz llvm-4848f3bf2ff5ec57a8e2b8d3676c947dcf0fd735.tar.bz2 |
[C++2a] P0634r3: Down with typename!
This patch implements P0634r3 that removes the need for 'typename' in certain contexts.
For example,
```
template <typename T>
using foo = T::type; // ok
```
This is also allowed in previous language versions as an extension, because I think it's pretty useful. :)
Reviewed By: #clang-language-wg, erichkeane
Differential Revision: https://reviews.llvm.org/D53847
Diffstat (limited to 'clang/lib/Parse/ParseDecl.cpp')
-rw-r--r-- | clang/lib/Parse/ParseDecl.cpp | 196 |
1 files changed, 148 insertions, 48 deletions
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 4d25b22..9363827 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2124,7 +2124,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, return Actions.ConvertDeclToDeclGroup(TheDecl); } - if (isDeclarationSpecifier()) { + if (isDeclarationSpecifier(ImplicitTypenameContext::No)) { // 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 @@ -2247,7 +2247,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, // Okay, there was no semicolon and one was expected. If we see a // declaration specifier, just assume it was missing and continue parsing. // Otherwise things are very confused and we skip to recover. - if (!isDeclarationSpecifier()) { + if (!isDeclarationSpecifier(ImplicitTypenameContext::No)) { SkipUntil(tok::r_brace, StopAtSemi | StopBeforeMatch); TryConsumeToken(tok::semi); } @@ -2566,12 +2566,14 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( /// type-qualifier specifier-qualifier-list[opt] /// [GNU] attributes specifier-qualifier-list[opt] /// -void Parser::ParseSpecifierQualifierList(DeclSpec &DS, AccessSpecifier AS, - DeclSpecContext DSC) { +void Parser::ParseSpecifierQualifierList( + DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename, + AccessSpecifier AS, DeclSpecContext DSC) { /// specifier-qualifier-list is a subset of declaration-specifiers. Just /// parse declaration-specifiers and complain about extra stuff. /// TODO: diagnose attribute-specifiers and alignment-specifiers. - ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS, DSC); + ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS, DSC, nullptr, + AllowImplicitTypename); // Validate declspec for type-name. unsigned Specs = DS.getParsedSpecifiers(); @@ -2881,24 +2883,50 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, /// DeclaratorContext enumerator values. Parser::DeclSpecContext Parser::getDeclSpecContextFromDeclaratorContext(DeclaratorContext Context) { - if (Context == DeclaratorContext::Member) + switch (Context) { + case DeclaratorContext::Member: return DeclSpecContext::DSC_class; - if (Context == DeclaratorContext::File) + case DeclaratorContext::File: return DeclSpecContext::DSC_top_level; - if (Context == DeclaratorContext::TemplateParam) + case DeclaratorContext::TemplateParam: return DeclSpecContext::DSC_template_param; - if (Context == DeclaratorContext::TemplateArg || - Context == DeclaratorContext::TemplateTypeArg) + case DeclaratorContext::TemplateArg: + return DeclSpecContext::DSC_template_arg; + case DeclaratorContext::TemplateTypeArg: return DeclSpecContext::DSC_template_type_arg; - if (Context == DeclaratorContext::TrailingReturn || - Context == DeclaratorContext::TrailingReturnVar) + case DeclaratorContext::TrailingReturn: + case DeclaratorContext::TrailingReturnVar: return DeclSpecContext::DSC_trailing; - if (Context == DeclaratorContext::AliasDecl || - Context == DeclaratorContext::AliasTemplate) + case DeclaratorContext::AliasDecl: + case DeclaratorContext::AliasTemplate: return DeclSpecContext::DSC_alias_declaration; - if (Context == DeclaratorContext::Association) + case DeclaratorContext::Association: return DeclSpecContext::DSC_association; - return DeclSpecContext::DSC_normal; + case DeclaratorContext::TypeName: + return DeclSpecContext::DSC_type_specifier; + case DeclaratorContext::Condition: + return DeclSpecContext::DSC_condition; + case DeclaratorContext::ConversionId: + return DeclSpecContext::DSC_conv_operator; + case DeclaratorContext::Prototype: + case DeclaratorContext::ObjCResult: + case DeclaratorContext::ObjCParameter: + case DeclaratorContext::KNRTypeList: + case DeclaratorContext::FunctionalCast: + case DeclaratorContext::Block: + case DeclaratorContext::ForInit: + case DeclaratorContext::SelectionInit: + case DeclaratorContext::CXXNew: + case DeclaratorContext::CXXCatch: + case DeclaratorContext::ObjCCatch: + case DeclaratorContext::BlockLiteral: + case DeclaratorContext::LambdaExpr: + case DeclaratorContext::LambdaExprParameter: + case DeclaratorContext::RequiresExpr: + return DeclSpecContext::DSC_normal; + } + + llvm_unreachable("Missing DeclaratorContext case"); } /// ParseAlignArgument - Parse the argument to an alignment-specifier. @@ -3134,11 +3162,10 @@ static void SetupFixedPointError(const LangOptions &LangOpts, /// [OpenCL] '__kernel' /// 'friend': [C++ dcl.friend] /// 'constexpr': [C++0x dcl.constexpr] -void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, - const ParsedTemplateInfo &TemplateInfo, - AccessSpecifier AS, - DeclSpecContext DSContext, - LateParsedAttrList *LateAttrs) { +void Parser::ParseDeclarationSpecifiers( + DeclSpec &DS, const ParsedTemplateInfo &TemplateInfo, AccessSpecifier AS, + DeclSpecContext DSContext, LateParsedAttrList *LateAttrs, + ImplicitTypenameContext AllowImplicitTypename) { if (DS.getSourceRange().isInvalid()) { // Start the range at the current token but make the end of the range // invalid. This will make the entire range invalid unless we successfully @@ -3147,6 +3174,14 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, DS.SetRangeEnd(SourceLocation()); } + // If we are in a operator context, convert it back into a type specifier + // context for better error handling later on. + if (DSContext == DeclSpecContext::DSC_conv_operator) { + // No implicit typename here. + AllowImplicitTypename = ImplicitTypenameContext::No; + DSContext = DeclSpecContext::DSC_type_specifier; + } + bool EnteringContext = (DSContext == DeclSpecContext::DSC_class || DSContext == DeclSpecContext::DSC_top_level); bool AttrsLastTime = false; @@ -3335,7 +3370,9 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, DSContext == DeclSpecContext::DSC_class) && TemplateId->Name && Actions.isCurrentClassName(*TemplateId->Name, getCurScope(), &SS) && - isConstructorDeclarator(/*Unqualified=*/false)) { + isConstructorDeclarator(/*Unqualified=*/false, + /*DeductionGuide=*/false, + DS.isFriendSpecified())) { // 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 @@ -3347,7 +3384,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, ConsumeAnnotationToken(); // The C++ scope. assert(Tok.is(tok::annot_template_id) && "ParseOptionalCXXScopeSpecifier not working"); - AnnotateTemplateIdTokenAsType(SS); + AnnotateTemplateIdTokenAsType(SS, AllowImplicitTypename); continue; } @@ -3374,6 +3411,16 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, ConsumeAnnotationToken(); // The typename } + if (AllowImplicitTypename == ImplicitTypenameContext::Yes && + Next.is(tok::annot_template_id) && + static_cast<TemplateIdAnnotation *>(Next.getAnnotationValue()) + ->Kind == TNK_Dependent_template_name) { + DS.getTypeSpecScope() = SS; + ConsumeAnnotationToken(); // The C++ scope. + AnnotateTemplateIdTokenAsType(SS, AllowImplicitTypename); + continue; + } + if (Next.isNot(tok::identifier)) goto DoneWithDeclSpec; @@ -3384,7 +3431,9 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, DSContext == DeclSpecContext::DSC_class) && Actions.isCurrentClassName(*Next.getIdentifierInfo(), getCurScope(), &SS) && - isConstructorDeclarator(/*Unqualified*/ false)) + isConstructorDeclarator(/*Unqualified=*/false, + /*DeductionGuide=*/false, + DS.isFriendSpecified())) goto DoneWithDeclSpec; // C++20 [temp.spec] 13.9/6. @@ -3393,12 +3442,12 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // - `return type`. SuppressAccessChecks SAC(*this, IsTemplateSpecOrInst); - ParsedType TypeRep = - Actions.getTypeName(*Next.getIdentifierInfo(), Next.getLocation(), - getCurScope(), &SS, false, false, nullptr, - /*IsCtorOrDtorName=*/false, - /*WantNontrivialTypeSourceInfo=*/true, - isClassTemplateDeductionContext(DSContext)); + ParsedType TypeRep = Actions.getTypeName( + *Next.getIdentifierInfo(), Next.getLocation(), getCurScope(), &SS, + false, false, nullptr, + /*IsCtorOrDtorName=*/false, + /*WantNontrivialTypeSourceInfo=*/true, + isClassTemplateDeductionContext(DSContext), AllowImplicitTypename); if (IsTemplateSpecOrInst) SAC.done(); @@ -3562,7 +3611,9 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // check whether this is a constructor declaration. if (getLangOpts().CPlusPlus && DSContext == DeclSpecContext::DSC_class && Actions.isCurrentClassName(*Tok.getIdentifierInfo(), getCurScope()) && - isConstructorDeclarator(/*Unqualified*/true)) + isConstructorDeclarator(/*Unqualified=*/true, + /*DeductionGuide=*/false, + DS.isFriendSpecified())) goto DoneWithDeclSpec; ParsedType TypeRep = Actions.getTypeName( @@ -3695,13 +3746,15 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // constructor declaration. if (getLangOpts().CPlusPlus && DSContext == DeclSpecContext::DSC_class && Actions.isCurrentClassName(*TemplateId->Name, getCurScope()) && - isConstructorDeclarator(/*Unqualified=*/true)) + isConstructorDeclarator(/*Unqualified=*/true, + /*DeductionGuide=*/false, + DS.isFriendSpecified())) goto DoneWithDeclSpec; // Turn the template-id annotation token into a type annotation // token, then try again to parse it as a type-specifier. CXXScopeSpec SS; - AnnotateTemplateIdTokenAsType(SS); + AnnotateTemplateIdTokenAsType(SS, AllowImplicitTypename); continue; } @@ -4742,7 +4795,10 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, // 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); + // enum-base is not assumed to be a type and therefore requires the + // typename keyword [p0634r3]. + ParseSpecifierQualifierList(DS, ImplicitTypenameContext::No, AS, + DeclSpecContext::DSC_type_specifier); Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), DeclaratorContext::TypeName); BaseType = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo); @@ -5301,9 +5357,13 @@ bool Parser::isTypeSpecifierQualifier() { /// isDeclarationSpecifier() - Return true if the current token is part of a /// declaration specifier. /// +/// \param AllowImplicitTypename whether this is a context where T::type [T +/// dependent] can appear. /// \param DisambiguatingWithExpression True to indicate that the purpose of /// this check is to disambiguate between an expression and a declaration. -bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { +bool Parser::isDeclarationSpecifier( + ImplicitTypenameContext AllowImplicitTypename, + bool DisambiguatingWithExpression) { switch (Tok.getKind()) { default: return false; @@ -5323,7 +5383,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { case tok::kw_typename: // typename T::type // Annotate typenames and C++ scope specifiers. If we get one, just // recurse to handle whatever we get. - if (TryAnnotateTypeOrScopeToken()) + if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename)) return true; if (TryAnnotateTypeConstraint()) return true; @@ -5339,7 +5399,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { isStartOfObjCClassMessageMissingOpenBracket()) return false; - return isDeclarationSpecifier(); + return isDeclarationSpecifier(AllowImplicitTypename); case tok::coloncolon: // ::foo::bar if (NextToken().is(tok::kw_new) || // ::new @@ -5350,7 +5410,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { // recurse to handle whatever we get. if (TryAnnotateTypeOrScopeToken()) return true; - return isDeclarationSpecifier(); + return isDeclarationSpecifier(ImplicitTypenameContext::No); // storage-class-specifier case tok::kw_typedef: @@ -5529,7 +5589,8 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { } } -bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide) { +bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide, + DeclSpec::FriendSpecified IsFriend) { TentativeParsingAction TPA(*this); // Parse the C++ scope specifier. @@ -5593,8 +5654,11 @@ bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide) { // Check whether the next token(s) are part of a declaration // specifier, in which case we have the start of a parameter and, // therefore, we know that this is a constructor. + // Due to an ambiguity with implicit typename, the above is not enough. + // Additionally, check to see if we are a friend. bool IsConstructor = false; - if (isDeclarationSpecifier()) + if (isDeclarationSpecifier(IsFriend ? ImplicitTypenameContext::No + : ImplicitTypenameContext::Yes)) IsConstructor = true; else if (Tok.is(tok::identifier) || (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::identifier))) { @@ -6409,10 +6473,27 @@ void Parser::ParseDirectDeclarator(Declarator &D) { // is not, the declarator has been fully parsed. bool IsAmbiguous = false; if (getLangOpts().CPlusPlus && D.mayBeFollowedByCXXDirectInit()) { + // C++2a [temp.res]p5 + // A qualified-id is assumed to name a type if + // - [...] + // - it is a decl-specifier of the decl-specifier-seq of a + // - [...] + // - parameter-declaration in a member-declaration [...] + // - parameter-declaration in a declarator of a function or function + // template declaration whose declarator-id is qualified [...] + auto AllowImplicitTypename = ImplicitTypenameContext::No; + if (D.getCXXScopeSpec().isSet()) + AllowImplicitTypename = + (ImplicitTypenameContext)Actions.isDeclaratorFunctionLike(D); + else if (D.getContext() == DeclaratorContext::Member) { + AllowImplicitTypename = ImplicitTypenameContext::Yes; + } + // The name of the declarator, if any, is tentatively declared within // a possible direct initializer. TentativelyDeclaredIdentifiers.push_back(D.getIdentifier()); - bool IsFunctionDecl = isCXXFunctionDeclarator(&IsAmbiguous); + bool IsFunctionDecl = + isCXXFunctionDeclarator(&IsAmbiguous, AllowImplicitTypename); TentativelyDeclaredIdentifiers.pop_back(); if (!IsFunctionDecl) break; @@ -6571,11 +6652,12 @@ void Parser::ParseParenDeclarator(Declarator &D) { // If this can't be an abstract-declarator, this *must* be a grouping // paren, because we haven't seen the identifier yet. isGrouping = true; - } else if (Tok.is(tok::r_paren) || // 'int()' is a function. + } else if (Tok.is(tok::r_paren) || // 'int()' is a function. (getLangOpts().CPlusPlus && Tok.is(tok::ellipsis) && NextToken().is(tok::r_paren)) || // C++ int(...) - isDeclarationSpecifier() || // 'int(int)' is a function. - isCXX11AttributeSpecifier()) { // 'int([[]]int)' is a function. + isDeclarationSpecifier( + ImplicitTypenameContext::No) || // 'int(int)' is a function. + isCXX11AttributeSpecifier()) { // 'int([[]]int)' is a function. // This handles C99 6.7.5.3p11: in "typedef int X; void foo(X)", X is // considered to be a type, not a K&R identifier-list. isGrouping = false; @@ -6743,8 +6825,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D, ProhibitAttributes(FnAttrs); } else { if (Tok.isNot(tok::r_paren)) - ParseParameterDeclarationClause(D.getContext(), FirstArgAttrs, ParamInfo, - EllipsisLoc); + ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo, EllipsisLoc); else if (RequiresArg) Diag(Tok, diag::err_argument_required_after_attribute); @@ -7003,7 +7084,7 @@ void Parser::ParseFunctionDeclaratorIdentifierList( void Parser::ParseParameterDeclarationClause( DeclaratorContext DeclaratorCtx, ParsedAttributes &FirstArgAttrs, SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo, - SourceLocation &EllipsisLoc) { + SourceLocation &EllipsisLoc, bool IsACXXFunctionDeclaration) { // Avoid exceeding the maximum function scope depth. // See https://bugs.llvm.org/show_bug.cgi?id=19607 @@ -7017,6 +7098,23 @@ void Parser::ParseParameterDeclarationClause( return; } + // C++2a [temp.res]p5 + // A qualified-id is assumed to name a type if + // - [...] + // - it is a decl-specifier of the decl-specifier-seq of a + // - [...] + // - parameter-declaration in a member-declaration [...] + // - parameter-declaration in a declarator of a function or function + // template declaration whose declarator-id is qualified [...] + // - parameter-declaration in a lambda-declarator [...] + auto AllowImplicitTypename = ImplicitTypenameContext::No; + if (DeclaratorCtx == DeclaratorContext::Member || + DeclaratorCtx == DeclaratorContext::LambdaExpr || + DeclaratorCtx == DeclaratorContext::RequiresExpr || + IsACXXFunctionDeclaration) { + AllowImplicitTypename = ImplicitTypenameContext::Yes; + } + do { // FIXME: Issue a diagnostic if we parsed an attribute-specifier-seq // before deciding this was a parameter-declaration-clause. @@ -7046,7 +7144,9 @@ void Parser::ParseParameterDeclarationClause( SourceLocation DSStart = Tok.getLocation(); - ParseDeclarationSpecifiers(DS); + ParseDeclarationSpecifiers(DS, /*TemplateInfo=*/ParsedTemplateInfo(), + AS_none, DeclSpecContext::DSC_normal, + /*LateAttrs=*/nullptr, AllowImplicitTypename); DS.takeAttributesFrom(ArgDeclSpecAttrs); // Parse the declarator. This is "PrototypeContext" or |