diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5425,6 +5425,12 @@ "%0 in %1">; def note_using_value_decl_missing_typename : Note< "add 'typename' to treat this using declaration as a type">; +def warn_cxx17_compat_implicit_typename : Warning<"use of implicit 'typename' is " + "incompatible with C++ standards before C++20">, InGroup, + DefaultIgnore; +def ext_implicit_typename : ExtWarn<"missing 'typename' prior to dependent " + "type name %0%1; implicit 'typename' is a C++20 extension">, + InGroup; def err_template_kw_refers_to_non_template : Error< "%0%select{| following the 'template' keyword}1 " diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -854,9 +854,12 @@ public: // If NeedType is true, then TryAnnotateTypeOrScopeToken will try harder to // find a type name by attempting typo correction. - bool TryAnnotateTypeOrScopeToken(); - bool TryAnnotateTypeOrScopeTokenAfterScopeSpec(CXXScopeSpec &SS, - bool IsNewScope); + bool + TryAnnotateTypeOrScopeToken(ImplicitTypenameContext AllowImplicitTypename = + ImplicitTypenameContext::No); + bool TryAnnotateTypeOrScopeTokenAfterScopeSpec( + CXXScopeSpec &SS, bool IsNewScope, + ImplicitTypenameContext AllowImplicitTypename); bool TryAnnotateCXXScopeToken(bool EnteringContext = false); bool MightBeCXXScopeToken() { @@ -882,7 +885,11 @@ /// Annotation was successful. ANK_Success }; - AnnotatedNameKind TryAnnotateName(CorrectionCandidateCallback *CCC = nullptr); + + AnnotatedNameKind + TryAnnotateName(CorrectionCandidateCallback *CCC = nullptr, + ImplicitTypenameContext AllowImplicitTypename = + ImplicitTypenameContext::No); /// Push a tok::annot_cxxscope token onto the token stream. void AnnotateScopeToken(CXXScopeSpec &SS, bool IsNewAnnotation); @@ -1976,7 +1983,9 @@ /// simple-type-specifier. void ParseCXXSimpleTypeSpecifier(DeclSpec &DS); - bool ParseCXXTypeSpecifierSeq(DeclSpec &DS); + bool ParseCXXTypeSpecifierSeq( + DeclSpec &DS, + DeclaratorContext Context = DeclaratorContext::TypeName); //===--------------------------------------------------------------------===// // C++ 5.3.4 and 5.3.5: C++ new and delete @@ -2195,8 +2204,10 @@ DSC_type_specifier, // C++ type-specifier-seq or C specifier-qualifier-list DSC_trailing, // C++11 trailing-type-specifier in a trailing return type DSC_alias_declaration, // C++11 type-specifier-seq in an alias-declaration + DSC_conv_operator, // C++ type-specifier-seq in an conversion operator DSC_top_level, // top-level/namespace declaration context DSC_template_param, // template parameter context + DSC_template_arg, // template argument context DSC_template_type_arg, // template type argument context DSC_objc_method_result, // ObjC method result context, enables 'instancetype' DSC_condition, // condition declaration context @@ -2209,6 +2220,7 @@ switch (DSC) { case DeclSpecContext::DSC_normal: case DeclSpecContext::DSC_template_param: + case DeclSpecContext::DSC_template_arg: case DeclSpecContext::DSC_class: case DeclSpecContext::DSC_top_level: case DeclSpecContext::DSC_objc_method_result: @@ -2217,6 +2229,7 @@ case DeclSpecContext::DSC_template_type_arg: case DeclSpecContext::DSC_type_specifier: + case DeclSpecContext::DSC_conv_operator: case DeclSpecContext::DSC_trailing: case DeclSpecContext::DSC_alias_declaration: case DeclSpecContext::DSC_association: @@ -2266,6 +2279,8 @@ : AllowDefiningTypeSpec::Yes; case DeclSpecContext::DSC_trailing: + case DeclSpecContext::DSC_conv_operator: + case DeclSpecContext::DSC_template_arg: return AllowDefiningTypeSpec::No; } llvm_unreachable("Missing DeclSpecContext case"); @@ -2287,6 +2302,9 @@ case DeclSpecContext::DSC_type_specifier: case DeclSpecContext::DSC_trailing: case DeclSpecContext::DSC_association: + case DeclSpecContext::DSC_conv_operator: + case DeclSpecContext::DSC_template_arg: + return false; } llvm_unreachable("Missing DeclSpecContext case"); @@ -2298,11 +2316,13 @@ switch (DSC) { case DeclSpecContext::DSC_normal: case DeclSpecContext::DSC_template_param: + case DeclSpecContext::DSC_template_arg: case DeclSpecContext::DSC_class: case DeclSpecContext::DSC_top_level: case DeclSpecContext::DSC_condition: case DeclSpecContext::DSC_type_specifier: case DeclSpecContext::DSC_association: + case DeclSpecContext::DSC_conv_operator: return true; case DeclSpecContext::DSC_objc_method_result: @@ -2314,6 +2334,29 @@ llvm_unreachable("Missing DeclSpecContext case"); } + // Is this a context in which an implicit 'typename' is allowed? + static ImplicitTypenameContext isImplicitTypenameContext(DeclSpecContext DSC) { + switch (DSC) { + case DeclSpecContext::DSC_class: + case DeclSpecContext::DSC_top_level: + case DeclSpecContext::DSC_type_specifier: + case DeclSpecContext::DSC_template_type_arg: + case DeclSpecContext::DSC_trailing: + case DeclSpecContext::DSC_alias_declaration: + case DeclSpecContext::DSC_template_param: + return ImplicitTypenameContext::Yes; + + case DeclSpecContext::DSC_normal: + case DeclSpecContext::DSC_objc_method_result: + case DeclSpecContext::DSC_condition: + case DeclSpecContext::DSC_template_arg: + case DeclSpecContext::DSC_conv_operator: + case DeclSpecContext::DSC_association: + return ImplicitTypenameContext::No; + } + llvm_unreachable("Missing DeclSpecContext case"); + } + /// Information on a C++0x for-range-initializer found while parsing a /// declaration which turns out to be a for-range-declaration. struct ForRangeInit { @@ -2369,13 +2412,31 @@ const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(), AccessSpecifier AS = AS_none, DeclSpecContext DSC = DeclSpecContext::DSC_normal, - LateParsedAttrList *LateAttrs = nullptr); + LateParsedAttrList *LateAttrs = nullptr) { + return ParseDeclarationSpecifiers( + DS, TemplateInfo, AS, DSC, LateAttrs, isImplicitTypenameContext(DSC)); + } + void ParseDeclarationSpecifiers( + DeclSpec &DS, + const ParsedTemplateInfo &TemplateInfo, + AccessSpecifier AS, + DeclSpecContext DSC, + LateParsedAttrList *LateAttrs, + ImplicitTypenameContext AllowImplicitTypename); + bool DiagnoseMissingSemiAfterTagDefinition( DeclSpec &DS, AccessSpecifier AS, DeclSpecContext DSContext, LateParsedAttrList *LateAttrs = nullptr); void ParseSpecifierQualifierList( DeclSpec &DS, AccessSpecifier AS = AS_none, + DeclSpecContext DSC = DeclSpecContext::DSC_normal) { + ParseSpecifierQualifierList(DS, isImplicitTypenameContext(DSC), AS, DSC); + } + + void ParseSpecifierQualifierList( + DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename, + AccessSpecifier AS = AS_none, DeclSpecContext DSC = DeclSpecContext::DSC_normal); void ParseObjCTypeQualifierList(ObjCDeclSpec &DS, @@ -2392,7 +2453,8 @@ ParsingDeclSpec &DS, llvm::function_ref FieldsCallback); - bool isDeclarationSpecifier(bool DisambiguatingWithExpression = false); + bool isDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename, + bool DisambiguatingWithExpression = false); bool isTypeSpecifierQualifier(); /// isKnownToBeTypeSpecifier - Return true if we know that the specified token @@ -2405,8 +2467,8 @@ /// cast. Return false if it's no a decl-specifier, or we're not sure. bool isKnownToBeDeclarationSpecifier() { if (getLangOpts().CPlusPlus) - return isCXXDeclarationSpecifier() == TPResult::True; - return isDeclarationSpecifier(true); + return isCXXDeclarationSpecifier(ImplicitTypenameContext::No) == TPResult::True; + return isDeclarationSpecifier(ImplicitTypenameContext::No, true); } /// isDeclarationStatement - Disambiguates between a declaration or an @@ -2415,7 +2477,7 @@ bool isDeclarationStatement() { if (getLangOpts().CPlusPlus) return isCXXDeclarationStatement(); - return isDeclarationSpecifier(true); + return isDeclarationSpecifier(ImplicitTypenameContext::No, true); } /// isForInitDeclaration - Disambiguates between a declaration or an @@ -2428,7 +2490,7 @@ if (getLangOpts().CPlusPlus) return Tok.is(tok::kw_using) || isCXXSimpleDeclaration(/*AllowForRangeDecl=*/true); - return isDeclarationSpecifier(true); + return isDeclarationSpecifier(ImplicitTypenameContext::No, true); } /// Determine whether this is a C++1z for-range-identifier. @@ -2441,7 +2503,8 @@ /// Starting with a scope specifier, identifier, or /// template-id that refers to the current class, determine whether /// this is a constructor declarator. - bool isConstructorDeclarator(bool Unqualified, bool DeductionGuide = false); + bool isConstructorDeclarator(bool Unqualified, bool DeductionGuide = false, + bool IsFriend = false); /// Specifies the context in which type-id/expression /// disambiguation will occur. @@ -2495,7 +2558,9 @@ /// might be a constructor-style initializer. /// If during the disambiguation process a parsing error is encountered, /// the function returns true to let the declaration parsing code handle it. - bool isCXXFunctionDeclarator(bool *IsAmbiguous = nullptr); + bool isCXXFunctionDeclarator( + bool *IsAmbiguous = nullptr, + ImplicitTypenameContext AllowImplicitTypename = ImplicitTypenameContext::No); struct ConditionDeclarationOrInitStatementState; enum class ConditionOrInitStatement { @@ -2541,7 +2606,8 @@ /// BracedCastResult. /// Doesn't consume tokens. TPResult - isCXXDeclarationSpecifier(TPResult BracedCastResult = TPResult::False, + isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename, + TPResult BracedCastResult = TPResult::False, bool *InvalidAsDeclSpec = nullptr); /// Given that isCXXDeclarationSpecifier returns \c TPResult::True or @@ -2579,9 +2645,9 @@ TPResult TryParseInitDeclaratorList(); TPResult TryParseDeclarator(bool mayBeAbstract, bool mayHaveIdentifier = true, bool mayHaveDirectInit = false); - TPResult - TryParseParameterDeclarationClause(bool *InvalidAsDeclaration = nullptr, - bool VersusTemplateArg = false); + TPResult TryParseParameterDeclarationClause( + bool *InvalidAsDeclaration = nullptr, bool VersusTemplateArg = false, + ImplicitTypenameContext AllowImplicitTypename = ImplicitTypenameContext::No); TPResult TryParseFunctionDeclarator(); TPResult TryParseBracketDeclarator(); TPResult TryConsumeDeclarationSpecifier(); @@ -2997,10 +3063,19 @@ Declarator &D, SmallVectorImpl &ParamInfo); void ParseParameterDeclarationClause( - DeclaratorContext DeclaratorContext, - ParsedAttributes &attrs, - SmallVectorImpl &ParamInfo, - SourceLocation &EllipsisLoc); + Declarator &D, ParsedAttributes &attrs, + SmallVectorImpl &ParamInfo, + SourceLocation &EllipsisLoc) { + return ParseParameterDeclarationClause( + D.getContext(), attrs, ParamInfo, EllipsisLoc, + D.getCXXScopeSpec().isSet() && + D.isFunctionDeclaratorAFunctionDeclaration()); + } + void ParseParameterDeclarationClause( + DeclaratorContext DeclaratorContext, ParsedAttributes &attrs, + SmallVectorImpl &ParamInfo, + SourceLocation &EllipsisLoc, bool IsACXXFunctionDeclaration = false); + void ParseBracketDeclarator(Declarator &D); void ParseMisplacedBracketDeclarator(Declarator &D); bool MaybeParseTypeTransformTypeSpecifier(DeclSpec &DS); @@ -3427,6 +3502,7 @@ bool AllowTypeAnnotation = true, bool TypeConstraint = false); void AnnotateTemplateIdTokenAsType(CXXScopeSpec &SS, + ImplicitTypenameContext AllowImplicitTypename, bool IsClassName = false); bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs, TemplateTy Template, SourceLocation OpenLoc); diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -36,12 +36,14 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" +#include namespace clang { class ASTContext; class CXXRecordDecl; class TypeLoc; class LangOptions; + class LookupResult; class IdentifierInfo; class NamespaceAliasDecl; class NamespaceDecl; @@ -1801,6 +1803,13 @@ Association // C11 _Generic selection expression association. }; +// Describes whether the current context is a context where an implicit +// typename is allowed (C++2a [temp.res]p5]). +enum class ImplicitTypenameContext { + No, + Yes, +}; + /// Information about one declarator, including the parsed type /// information and the identifier. /// @@ -1905,6 +1914,9 @@ /// this declarator as a parameter pack. SourceLocation EllipsisLoc; + /// Lookup result of declarator, if any. + std::unique_ptr PrevLookupResult; + friend struct DeclaratorChunk; public: @@ -1925,27 +1937,10 @@ /// This attribute appertains to all of the entities declared in the /// declaration, i.e. `x` and `y` in this case. Declarator(const DeclSpec &DS, const ParsedAttributesView &DeclarationAttrs, - DeclaratorContext C) - : DS(DS), Range(DS.getSourceRange()), Context(C), - InvalidType(DS.getTypeSpecType() == DeclSpec::TST_error), - GroupingParens(false), FunctionDefinition(static_cast( - FunctionDefinitionKind::Declaration)), - Redeclaration(false), Extension(false), ObjCIvar(false), - ObjCWeakProperty(false), InlineStorageUsed(false), - HasInitializer(false), Attrs(DS.getAttributePool().getFactory()), - DeclarationAttrs(DeclarationAttrs), AsmLabel(nullptr), - TrailingRequiresClause(nullptr), - InventedTemplateParameterList(nullptr) { - assert(llvm::all_of(DeclarationAttrs, - [](const ParsedAttr &AL) { - return AL.isStandardAttributeSyntax(); - }) && - "DeclarationAttrs may only contain [[]] attributes"); - } - - ~Declarator() { - clear(); - } + DeclaratorContext C); + + ~Declarator(); + /// getDeclSpec - Return the declaration-specifier that this declarator was /// declared with. const DeclSpec &getDeclSpec() const { return DS; } @@ -2652,6 +2647,10 @@ void setRedeclaration(bool Val) { Redeclaration = Val; } bool isRedeclaration() const { return Redeclaration; } + + void setPrevLookupResult(std::unique_ptr LR); + LookupResult consumePrevLookupResult(); + bool hasPrevLookupResult() const { return (bool)PrevLookupResult; } }; /// This little struct is used to capture information about diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2566,6 +2566,8 @@ bool IsCtorOrDtorName = false, bool WantNontrivialTypeSourceInfo = false, bool IsClassTemplateDeductionContext = true, + ImplicitTypenameContext AllowImplicitTypename = + ImplicitTypenameContext::No, IdentifierInfo **CorrectedII = nullptr); TypeSpecifierType isTagName(IdentifierInfo &II, Scope *S); bool isMicrosoftMissingTypename(const CXXScopeSpec *SS, Scope *S); @@ -3066,6 +3068,10 @@ /// \c constexpr in C++11 or has an 'auto' return type in C++14). bool canSkipFunctionBody(Decl *D); + /// Determine whether \param D is function like (function or function + /// template) for parsing. + bool isDeclaratorFunctionLike(Declarator &D); + void computeNRVO(Stmt *Body, sema::FunctionScopeInfo *Scope); Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body); Decl *ActOnFinishFunctionBody(Decl *Decl, Stmt *Body, bool IsInstantiation); @@ -7963,7 +7969,9 @@ TemplateTy Template, IdentifierInfo *TemplateII, SourceLocation TemplateIILoc, SourceLocation LAngleLoc, ASTTemplateArgsPtr TemplateArgs, SourceLocation RAngleLoc, - bool IsCtorOrDtorName = false, bool IsClassName = false); + bool IsCtorOrDtorName = false, bool IsClassName = false, + ImplicitTypenameContext AllowImplicitTypename = + ImplicitTypenameContext::No); /// Parsed an elaborated-type-specifier that refers to a template-id, /// such as \c class T::template apply. @@ -8225,10 +8233,12 @@ /// \param SS the nested-name-specifier following the typename (e.g., 'T::'). /// \param II the identifier we're retrieving (e.g., 'type' in the example). /// \param IdLoc the location of the identifier. - TypeResult - ActOnTypenameType(Scope *S, SourceLocation TypenameLoc, - const CXXScopeSpec &SS, const IdentifierInfo &II, - SourceLocation IdLoc); + /// \param IsImplicitTypename context where T::type refers to a type. + TypeResult ActOnTypenameType(Scope *S, SourceLocation TypenameLoc, + const CXXScopeSpec &SS, const IdentifierInfo &II, + SourceLocation IdLoc, + ImplicitTypenameContext IsImplicitTypename = + ImplicitTypenameContext::No); /// Called when the parser has parsed a C++ typename /// specifier that ends in a template-id, e.g., diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2119,7 +2119,7 @@ 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 @@ -2242,7 +2242,7 @@ // 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); } @@ -2561,12 +2561,14 @@ /// 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(); @@ -2876,24 +2878,50 @@ /// 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. @@ -3129,11 +3157,10 @@ /// [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 @@ -3142,6 +3169,14 @@ 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; @@ -3330,7 +3365,9 @@ 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 @@ -3342,7 +3379,7 @@ ConsumeAnnotationToken(); // The C++ scope. assert(Tok.is(tok::annot_template_id) && "ParseOptionalCXXScopeSpecifier not working"); - AnnotateTemplateIdTokenAsType(SS); + AnnotateTemplateIdTokenAsType(SS, AllowImplicitTypename); continue; } @@ -3369,6 +3406,16 @@ ConsumeAnnotationToken(); // The typename } + if (AllowImplicitTypename == ImplicitTypenameContext::Yes && + Next.is(tok::annot_template_id) && + static_cast(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; @@ -3379,7 +3426,9 @@ 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,7 +3442,8 @@ getCurScope(), &SS, false, false, nullptr, /*IsCtorOrDtorName=*/false, /*WantNontrivialTypeSourceInfo=*/true, - isClassTemplateDeductionContext(DSContext)); + isClassTemplateDeductionContext(DSContext), + AllowImplicitTypename); if (IsTemplateSpecOrInst) SAC.done(); @@ -3557,7 +3607,9 @@ // 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( @@ -3690,13 +3742,15 @@ // 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; } @@ -4736,7 +4790,10 @@ // 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); @@ -5295,9 +5352,13 @@ /// 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; @@ -5317,7 +5378,7 @@ 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; @@ -5333,7 +5394,7 @@ isStartOfObjCClassMessageMissingOpenBracket()) return false; - return isDeclarationSpecifier(); + return isDeclarationSpecifier(AllowImplicitTypename); case tok::coloncolon: // ::foo::bar if (NextToken().is(tok::kw_new) || // ::new @@ -5344,7 +5405,7 @@ // recurse to handle whatever we get. if (TryAnnotateTypeOrScopeToken()) return true; - return isDeclarationSpecifier(); + return isDeclarationSpecifier(ImplicitTypenameContext::No); // storage-class-specifier case tok::kw_typedef: @@ -5523,7 +5584,8 @@ } } -bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide) { +bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide, + bool IsFriend) { TentativeParsingAction TPA(*this); // Parse the C++ scope specifier. @@ -5587,8 +5649,11 @@ // 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))) { @@ -6403,10 +6468,28 @@ // 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) { + llvm::dbgs() << __FUNCTION__ << ": " << __LINE__ << "\n"; + 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; @@ -6565,11 +6648,12 @@ // 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. + NextToken().is(tok::r_paren)) || // C++ int(...) + 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; @@ -6737,7 +6821,7 @@ ProhibitAttributes(FnAttrs); } else { if (Tok.isNot(tok::r_paren)) - ParseParameterDeclarationClause(D.getContext(), FirstArgAttrs, ParamInfo, + ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo, EllipsisLoc); else if (RequiresArg) Diag(Tok, diag::err_argument_required_after_attribute); @@ -6997,7 +7081,7 @@ void Parser::ParseParameterDeclarationClause( DeclaratorContext DeclaratorCtx, ParsedAttributes &FirstArgAttrs, SmallVectorImpl &ParamInfo, - SourceLocation &EllipsisLoc) { + SourceLocation &EllipsisLoc, bool IsACXXFunctionDeclaration) { // Avoid exceeding the maximum function scope depth. // See https://bugs.llvm.org/show_bug.cgi?id=19607 @@ -7011,6 +7095,23 @@ 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 || + IsACXXFunctionDeclaration) { + llvm::dbgs() << __FUNCTION__ << ": " << __LINE__ << "\n"; + AllowImplicitTypename = ImplicitTypenameContext::Yes; + } + do { // FIXME: Issue a diagnostic if we parsed an attribute-specifier-seq // before deciding this was a parameter-declaration-clause. @@ -7040,7 +7141,9 @@ 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 diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1245,7 +1245,8 @@ if (Tok.is(tok::annot_template_id)) { TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); if (TemplateId->mightBeType()) { - AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/ true); + AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::No, + /*IsClassName=*/ true); assert(Tok.is(tok::annot_typename) && "template-id -> type failed"); TypeResult Type = getTypeAnnotation(Tok); @@ -1287,7 +1288,8 @@ return true; if (Tok.is(tok::annot_template_id) && takeTemplateIdAnnotation(Tok)->mightBeType()) - AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/ true); + AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::No, + /*IsClassName=*/true); // If we didn't end up with a typename token, there's nothing more we // can do. @@ -1308,7 +1310,8 @@ *Id, IdLoc, getCurScope(), &SS, /*isClassName=*/true, false, nullptr, /*IsCtorOrDtorName=*/false, /*WantNontrivialTypeSourceInfo=*/true, - /*IsClassTemplateDeductionContext*/ false, &CorrectedII); + /*IsClassTemplateDeductionContext=*/false, ImplicitTypenameContext::No, + &CorrectedII); if (!Type) { Diag(IdLoc, diag::err_expected_class_name); return true; @@ -3733,7 +3736,8 @@ ? takeTemplateIdAnnotation(Tok) : nullptr; if (TemplateId && TemplateId->mightBeType()) { - AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/ true); + AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::No, + /*IsClassName=*/true); assert(Tok.is(tok::annot_typename) && "template-id -> type failed"); TemplateTypeTy = getTypeAnnotation(Tok); ConsumeAnnotationToken(); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1599,7 +1599,8 @@ ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, /*ObjectHasErrors=*/false, /*EnteringContext=*/false); - AnnotateTemplateIdTokenAsType(SS); + llvm::dbgs() << __FUNCTION__ << ": " << __LINE__ << "\n"; + AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::Yes); return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, isTypeCast, isVectorLiteral, NotPrimaryExpression); @@ -1618,7 +1619,8 @@ // translate it into a type and continue parsing as a cast // expression. CXXScopeSpec SS; - AnnotateTemplateIdTokenAsType(SS); + llvm::dbgs() << __FUNCTION__ << ": " << __LINE__ << "\n"; + AnnotateTemplateIdTokenAsType(SS, ImplicitTypenameContext::Yes); return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, isTypeCast, isVectorLiteral, NotPrimaryExpression); diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -1414,7 +1414,7 @@ Actions.RecordParsingTemplateParameterDepth( CurTemplateDepthTracker.getOriginalDepth()); - ParseParameterDeclarationClause(D.getContext(), Attr, ParamInfo, + ParseParameterDeclarationClause(D, Attr, ParamInfo, EllipsisLoc); // For a generic lambda, each 'auto' within the parameter declaration // clause creates a template type parameter, so increment the depth. @@ -1522,7 +1522,8 @@ // Parse the common declaration-specifiers piece. DeclSpec DS(AttrFactory); - ParseSpecifierQualifierList(DS); + ParseSpecifierQualifierList(DS, /*AccessSpecifier=*/AS_none, + DeclSpecContext::DSC_type_specifier); // Parse the abstract-declarator, if present. Declarator DeclaratorInfo(DS, ParsedAttributesView::none(), @@ -2320,8 +2321,9 @@ /// type-specifier-seq: [C++ 8.1] /// type-specifier type-specifier-seq[opt] /// -bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS) { - ParseSpecifierQualifierList(DS, AS_none, DeclSpecContext::DSC_type_specifier); +bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS, DeclaratorContext Context) { + ParseSpecifierQualifierList(DS, AS_none, + getDeclSpecContextFromDeclaratorContext(Context)); DS.Finish(Actions, Actions.getASTContext().getPrintingPolicy()); return false; } @@ -2737,7 +2739,8 @@ // Parse the type-specifier-seq. DeclSpec DS(AttrFactory); - if (ParseCXXTypeSpecifierSeq(DS)) // FIXME: ObjectType? + if (ParseCXXTypeSpecifierSeq( + DS, DeclaratorContext::ConversionId)) // FIXME: ObjectType? return true; // Parse the conversion-declarator, which is merely a sequence of diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -17,6 +17,7 @@ #include "clang/Parse/Parser.h" #include "clang/Parse/RAIIObjectsForParser.h" #include "clang/Sema/DeclSpec.h" +#include "clang/Sema/Lookup.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" #include "clang/Sema/SemaDiagnostic.h" @@ -1420,11 +1421,13 @@ /// /// \param SS The scope specifier appearing before the template-id, if any. /// +/// \param AllowImplicitTypename whether this is a context where T::type +/// denotes a dependent type. /// \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(CXXScopeSpec &SS, +void Parser::AnnotateTemplateIdTokenAsType(CXXScopeSpec &SS, ImplicitTypenameContext AllowImplicitTypename, bool IsClassName) { assert(Tok.is(tok::annot_template_id) && "Requires template-id tokens"); @@ -1443,7 +1446,7 @@ TemplateId->Template, TemplateId->Name, TemplateId->TemplateNameLoc, TemplateId->LAngleLoc, TemplateArgsPtr, TemplateId->RAngleLoc, - /*IsCtorOrDtorName*/ false, IsClassName); + /*IsCtorOrDtorName*/ false, IsClassName, AllowImplicitTypename); // Create the new "type" annotation token. Tok.setKind(tok::annot_typename); setTypeAnnotation(Tok, Type); diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -111,8 +111,8 @@ // a case. bool InvalidAsDeclaration = false; - TPResult TPR = isCXXDeclarationSpecifier(TPResult::False, - &InvalidAsDeclaration); + TPResult TPR = isCXXDeclarationSpecifier( + ImplicitTypenameContext::No, TPResult::False, &InvalidAsDeclaration); if (TPR != TPResult::Ambiguous) return TPR != TPResult::False; // Returns true for TPResult::True or // TPResult::Error. @@ -233,7 +233,7 @@ // simple-declaration. Don't bother calling isCXXDeclarationSpecifier in the // overwhelmingly common case that the next token is a '('. if (Tok.isNot(tok::l_paren)) { - TPResult TPR = isCXXDeclarationSpecifier(); + TPResult TPR = isCXXDeclarationSpecifier(ImplicitTypenameContext::No); if (TPR == TPResult::Ambiguous) return TPResult::True; if (TPR == TPResult::True || TPR == TPResult::Error) @@ -442,7 +442,8 @@ // FIXME: We could disallow non-type decl-specifiers here, but it makes no // difference: those specifiers are ill-formed regardless of the // interpretation. - TPResult R = isCXXDeclarationSpecifier(/*BracedCastResult*/ TPResult::True, + TPResult R = isCXXDeclarationSpecifier(ImplicitTypenameContext::No, + /*BracedCastResult=*/ TPResult::True, &InvalidAsDeclSpec); if (R == TPResult::Ambiguous) { // We either have a decl-specifier followed by '(' or an undeclared @@ -456,7 +457,7 @@ return true; // A second decl-specifier unambiguously indicatges an enum-base. - R = isCXXDeclarationSpecifier(TPResult::True, &InvalidAsDeclSpec); + R = isCXXDeclarationSpecifier(ImplicitTypenameContext::No, TPResult::True, &InvalidAsDeclSpec); } return R != TPResult::False; @@ -487,7 +488,7 @@ if (CanBeInitStatement && Tok.is(tok::kw_using)) return ConditionOrInitStatement::InitStmtDecl; - if (State.update(isCXXDeclarationSpecifier())) + if (State.update(isCXXDeclarationSpecifier(ImplicitTypenameContext::No))) return State.result(); // It might be a declaration; we need tentative parsing. @@ -573,7 +574,7 @@ // type. The resolution is that any construct that could possibly be a type-id // in its syntactic context shall be considered a type-id. - TPResult TPR = isCXXDeclarationSpecifier(); + TPResult TPR = isCXXDeclarationSpecifier(ImplicitTypenameContext::No); if (TPR != TPResult::Ambiguous) return TPR != TPResult::False; // Returns true for TPResult::True or // TPResult::Error. @@ -934,7 +935,7 @@ // Maybe this is a conversion-function-id. bool AnyDeclSpecifiers = false; while (true) { - TPResult TPR = isCXXDeclarationSpecifier(); + TPResult TPR = isCXXDeclarationSpecifier(ImplicitTypenameContext::No); if (TPR == TPResult::Error) return TPR; if (TPR == TPResult::False) { @@ -1039,10 +1040,11 @@ } else if (Tok.is(tok::l_paren)) { ConsumeParen(); if (mayBeAbstract && - (Tok.is(tok::r_paren) || // 'int()' is a function. - // 'int(...)' is a function. + (Tok.is(tok::r_paren) || // 'int()' is a function. + // 'int(...)' is a function. (Tok.is(tok::ellipsis) && NextToken().is(tok::r_paren)) || - isDeclarationSpecifier())) { // 'int(int)' is a function. + isDeclarationSpecifier( + ImplicitTypenameContext::No))) { // 'int(int)' is a function. // '(' parameter-declaration-clause ')' cv-qualifier-seq[opt] // exception-specification[opt] TPResult TPR = TryParseFunctionDeclarator(); @@ -1247,7 +1249,8 @@ /// [GNU] restrict /// Parser::TPResult -Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, +Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename, + Parser::TPResult BracedCastResult, bool *InvalidAsDeclSpec) { auto IsPlaceholderSpecifier = [&] (TemplateIdAnnotation *TemplateId, int Lookahead) { @@ -1290,7 +1293,7 @@ // template template argument, we'll undo this when checking the // validity of the argument. if (getLangOpts().CPlusPlus17) { - if (TryAnnotateTypeOrScopeToken()) + if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename)) return TPResult::Error; if (Tok.isNot(tok::identifier)) break; @@ -1311,7 +1314,7 @@ // a missing 'typename' keyword. Don't use TryAnnotateName in this case, // since it will annotate as a primary expression, and we want to use the // "missing 'typename'" logic. - if (TryAnnotateTypeOrScopeToken()) + if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename)) return TPResult::Error; // If annotation failed, assume it's a non-type. // FIXME: If this happens due to an undeclared identifier, treat it as @@ -1321,15 +1324,18 @@ } // We annotated this token as something. Recurse to handle whatever we got. - return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec); + return isCXXDeclarationSpecifier(AllowImplicitTypename, BracedCastResult, + InvalidAsDeclSpec); } case tok::kw_typename: // typename T::type // Annotate typenames and C++ scope specifiers. If we get one, just // recurse to handle whatever we get. - if (TryAnnotateTypeOrScopeToken()) + llvm::dbgs() << __FUNCTION__ << ": " << __LINE__ << "\n"; + if (TryAnnotateTypeOrScopeToken(ImplicitTypenameContext::Yes)) return TPResult::Error; - return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec); + return isCXXDeclarationSpecifier(ImplicitTypenameContext::Yes, + BracedCastResult, InvalidAsDeclSpec); case tok::coloncolon: { // ::foo::bar const Token &Next = NextToken(); @@ -1342,9 +1348,10 @@ case tok::kw_decltype: // Annotate typenames and C++ scope specifiers. If we get one, just // recurse to handle whatever we get. - if (TryAnnotateTypeOrScopeToken()) + if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename)) return TPResult::Error; - return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec); + return isCXXDeclarationSpecifier(AllowImplicitTypename, BracedCastResult, + InvalidAsDeclSpec); // decl-specifier: // storage-class-specifier @@ -1474,14 +1481,14 @@ if (TemplateId->Kind != TNK_Type_template) return TPResult::False; CXXScopeSpec SS; - AnnotateTemplateIdTokenAsType(SS); + AnnotateTemplateIdTokenAsType(SS, AllowImplicitTypename); assert(Tok.is(tok::annot_typename)); goto case_typename; } case tok::annot_cxxscope: // foo::bar or ::foo::bar, but already parsed // We've already annotated a scope; try to annotate a type. - if (TryAnnotateTypeOrScopeToken()) + if (TryAnnotateTypeOrScopeToken(AllowImplicitTypename)) return TPResult::Error; if (!Tok.is(tok::annot_typename)) { if (Tok.is(tok::annot_cxxscope) && @@ -1512,8 +1519,8 @@ bool isIdentifier = Tok.is(tok::identifier); TPResult TPR = TPResult::False; if (!isIdentifier) - TPR = isCXXDeclarationSpecifier(BracedCastResult, - InvalidAsDeclSpec); + TPR = isCXXDeclarationSpecifier( + AllowImplicitTypename, BracedCastResult, InvalidAsDeclSpec); if (isIdentifier || TPR == TPResult::True || TPR == TPResult::Error) @@ -1539,7 +1546,7 @@ } else { // Try to resolve the name. If it doesn't exist, assume it was // intended to name a type and keep disambiguating. - switch (TryAnnotateName()) { + switch (TryAnnotateName(/*CCC=*/nullptr, AllowImplicitTypename)) { case ANK_Error: return TPResult::Error; case ANK_TentativeDecl: @@ -1569,7 +1576,8 @@ // Annotated it, check again. assert(Tok.isNot(tok::annot_cxxscope) || NextToken().isNot(tok::identifier)); - return isCXXDeclarationSpecifier(BracedCastResult, InvalidAsDeclSpec); + return isCXXDeclarationSpecifier( + AllowImplicitTypename, BracedCastResult, InvalidAsDeclSpec); } } return TPResult::False; @@ -1828,7 +1836,8 @@ /// '(' parameter-declaration-clause ')' cv-qualifier-seq[opt] /// exception-specification[opt] /// -bool Parser::isCXXFunctionDeclarator(bool *IsAmbiguous) { +bool Parser::isCXXFunctionDeclarator( + bool *IsAmbiguous, ImplicitTypenameContext AllowImplicitTypename) { // C++ 8.2p1: // The ambiguity arising from the similarity between a function-style cast and @@ -1843,7 +1852,9 @@ ConsumeParen(); bool InvalidAsDeclaration = false; - TPResult TPR = TryParseParameterDeclarationClause(&InvalidAsDeclaration); + TPResult TPR = TryParseParameterDeclarationClause( + &InvalidAsDeclaration, /*VersusTemplateArgument=*/false, + AllowImplicitTypename); if (TPR == TPResult::Ambiguous) { if (Tok.isNot(tok::r_paren)) TPR = TPResult::False; @@ -1887,9 +1898,9 @@ /// attribute-specifier-seq[opt] decl-specifier-seq abstract-declarator[opt] /// attributes[opt] '=' assignment-expression /// -Parser::TPResult -Parser::TryParseParameterDeclarationClause(bool *InvalidAsDeclaration, - bool VersusTemplateArgument) { +Parser::TPResult Parser::TryParseParameterDeclarationClause( + bool *InvalidAsDeclaration, bool VersusTemplateArgument, + ImplicitTypenameContext AllowImplicitTypename) { if (Tok.is(tok::r_paren)) return TPResult::Ambiguous; @@ -1922,8 +1933,8 @@ // decl-specifier-seq // A parameter-declaration's initializer must be preceded by an '=', so // decl-specifier-seq '{' is not a parameter in C++11. - TPResult TPR = isCXXDeclarationSpecifier(TPResult::False, - InvalidAsDeclaration); + TPResult TPR = isCXXDeclarationSpecifier( + AllowImplicitTypename, TPResult::False, InvalidAsDeclaration); // A declaration-specifier (not followed by '(' or '{') means this can't be // an expression, but it could still be a template argument. if (TPR != TPResult::Ambiguous && @@ -1940,7 +1951,7 @@ if (SeenType && Tok.is(tok::identifier)) return TPResult::True; - TPR = isCXXDeclarationSpecifier(TPResult::False, + TPR = isCXXDeclarationSpecifier(AllowImplicitTypename, TPResult::False, InvalidAsDeclaration); if (TPR == TPResult::Error) return TPR; @@ -2102,9 +2113,9 @@ // but one good distinguishing factor is that a "decl-specifier" not // followed by '(' or '{' can't appear in an expression. bool InvalidAsTemplateArgumentList = false; - if (isCXXDeclarationSpecifier(TPResult::False, - &InvalidAsTemplateArgumentList) == - TPResult::True) + if (isCXXDeclarationSpecifier(ImplicitTypenameContext::No, TPResult::False, + &InvalidAsTemplateArgumentList) == + TPResult::True) return TPResult::True; if (InvalidAsTemplateArgumentList) return TPResult::False; diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1053,7 +1053,7 @@ // Handle K&R C argument lists: int X(f) int f; {} if (!getLangOpts().CPlusPlus && Declarator.getFunctionTypeInfo().isKNRPrototype()) - return isDeclarationSpecifier(); + return isDeclarationSpecifier(ImplicitTypenameContext::No); if (getLangOpts().CPlusPlus && Tok.is(tok::equal)) { const Token &KW = NextToken(); @@ -1462,7 +1462,7 @@ Scope::FunctionDeclarationScope | Scope::DeclScope); // Read all the argument declarations. - while (isDeclarationSpecifier()) { + while (isDeclarationSpecifier(ImplicitTypenameContext::No)) { SourceLocation DSStart = Tok.getLocation(); // Parse the common declaration-specifiers piece. @@ -1675,8 +1675,12 @@ /// /// \param CCC Indicates how to perform typo-correction for this name. If NULL, /// no typo correction will be performed. +/// \param AllowImplicitTypename Whether we are in a context where a dependent +/// nested-name-specifier without typename is treated as a type (e.g. +/// T::type). Parser::AnnotatedNameKind -Parser::TryAnnotateName(CorrectionCandidateCallback *CCC) { +Parser::TryAnnotateName(CorrectionCandidateCallback *CCC, + ImplicitTypenameContext AllowImplicitTypename) { assert(Tok.is(tok::identifier) || Tok.is(tok::annot_cxxscope)); const bool EnteringContext = false; @@ -1690,7 +1694,8 @@ return ANK_Error; if (Tok.isNot(tok::identifier) || SS.isInvalid()) { - if (TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation)) + if (TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation, + AllowImplicitTypename)) return ANK_Error; return ANK_Unresolved; } @@ -1700,10 +1705,11 @@ // FIXME: Move the tentative declaration logic into ClassifyName so we can // typo-correct to tentatively-declared identifiers. - if (isTentativelyDeclared(Name)) { + if (isTentativelyDeclared(Name) && SS.isEmpty()) { // Identifier has been tentatively declared, and thus cannot be resolved as // an expression. Fall back to annotating it as a type. - if (TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation)) + if (TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation, + AllowImplicitTypename)) return ANK_Error; return Tok.is(tok::annot_typename) ? ANK_Success : ANK_TentativeDecl; } @@ -1899,7 +1905,8 @@ /// /// Note that this routine emits an error if you call it with ::new or ::delete /// as the current tokens, so only call it in contexts where these are invalid. -bool Parser::TryAnnotateTypeOrScopeToken() { +bool Parser::TryAnnotateTypeOrScopeToken( + ImplicitTypenameContext AllowImplicitTypename) { assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) || Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope) || Tok.is(tok::kw_decltype) || Tok.is(tok::annot_template_id) || @@ -1916,7 +1923,7 @@ if (getLangOpts().MSVCCompat && NextToken().is(tok::kw_typedef)) { Token TypedefToken; PP.Lex(TypedefToken); - bool Result = TryAnnotateTypeOrScopeToken(); + bool Result = TryAnnotateTypeOrScopeToken(AllowImplicitTypename); PP.EnterToken(Tok, /*IsReinject=*/true); Tok = TypedefToken; if (!Result) @@ -1942,7 +1949,8 @@ Tok.is(tok::annot_decltype)) { // Attempt to recover by skipping the invalid 'typename' if (Tok.is(tok::annot_decltype) || - (!TryAnnotateTypeOrScopeToken() && Tok.isAnnotation())) { + (!TryAnnotateTypeOrScopeToken(AllowImplicitTypename) && + Tok.isAnnotation())) { unsigned DiagID = diag::err_expected_qualified_after_typename; // MS compatibility: MSVC permits using known types with typename. // e.g. "typedef typename T* pointer_type" @@ -2008,22 +2016,24 @@ /*EnteringContext*/ false)) return true; - return TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation); + return TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, !WasScopeAnnotation, + AllowImplicitTypename); } /// Try to annotate a type or scope token, having already parsed an /// optional scope specifier. \p IsNewScope should be \c true unless the scope /// specifier was extracted from an existing tok::annot_cxxscope annotation. -bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(CXXScopeSpec &SS, - bool IsNewScope) { +bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec( + CXXScopeSpec &SS, bool IsNewScope, + ImplicitTypenameContext AllowImplicitTypename) { if (Tok.is(tok::identifier)) { // Determine whether the identifier is a type name. if (ParsedType Ty = Actions.getTypeName( *Tok.getIdentifierInfo(), Tok.getLocation(), getCurScope(), &SS, false, NextToken().is(tok::period), nullptr, /*IsCtorOrDtorName=*/false, - /*NonTrivialTypeSourceInfo*/true, - /*IsClassTemplateDeductionContext*/true)) { + /*NonTrivialTypeSourceInfo=*/ true, + /*IsClassTemplateDeductionContext=*/ true, AllowImplicitTypename)) { SourceLocation BeginLoc = Tok.getLocation(); if (SS.isNotEmpty()) // it was a C++ qualified type name. BeginLoc = SS.getBeginLoc(); @@ -2109,7 +2119,7 @@ // 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(SS); + AnnotateTemplateIdTokenAsType(SS, AllowImplicitTypename); return false; } } diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -20,6 +20,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetInfo.h" +#include "clang/Sema/Lookup.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Sema.h" #include "clang/Sema/SemaDiagnostic.h" @@ -1468,6 +1469,41 @@ } } + +Declarator::Declarator(const DeclSpec &DS, + const ParsedAttributesView &DeclarationAttrs, + DeclaratorContext C) + : DS(DS), Range(DS.getSourceRange()), Context(C), + InvalidType(DS.getTypeSpecType() == DeclSpec::TST_error), + GroupingParens(false), FunctionDefinition(static_cast( + FunctionDefinitionKind::Declaration)), + Redeclaration(false), Extension(false), ObjCIvar(false), + ObjCWeakProperty(false), InlineStorageUsed(false), HasInitializer(false), + Attrs(DS.getAttributePool().getFactory()), + DeclarationAttrs(DeclarationAttrs), AsmLabel(nullptr), + TrailingRequiresClause(nullptr), InventedTemplateParameterList(nullptr) { + assert(llvm::all_of(DeclarationAttrs, + [](const ParsedAttr &AL) { + return AL.isStandardAttributeSyntax(); + }) && + "DeclarationAttrs may only contain [[]] attributes"); +} + +Declarator::~Declarator() { + clear(); +} + +void Declarator::setPrevLookupResult(std::unique_ptr LR) { + PrevLookupResult = std::move(LR); +} + +LookupResult Declarator::consumePrevLookupResult() { + assert(PrevLookupResult); + LookupResult LR = std::move(*PrevLookupResult); + PrevLookupResult.reset(); + return LR; +} + bool VirtSpecifiers::SetSpecifier(Specifier VS, SourceLocation Loc, const char *&PrevSpec) { if (!FirstLocation.isValid()) diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -47,6 +47,8 @@ #include "clang/Sema/TypoCorrection.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/TimeProfiler.h" using namespace clang; @@ -2679,3 +2681,30 @@ S.FpPragmaStack.CurrentValue = OldOverrides; S.PP.setCurrentFPEvalMethod(OldFPPragmaLocation, OldEvalMethod); } + +bool Sema::isDeclaratorFunctionLike(Declarator &D) { +assert(D.getCXXScopeSpec().isSet() && + "can only be called for qualified names"); + assert(!D.hasPrevLookupResult() && "we looked this up already"); + + auto LR = std::make_unique( + *this, D.getIdentifier(), D.getBeginLoc(), LookupOrdinaryName, + forRedeclarationInCurContext()); + DeclContext *DC = computeDeclContext(D.getCXXScopeSpec(), + !D.getDeclSpec().isFriendSpecified()); + if (!DC) + return false; + + LookupQualifiedName(*LR, DC); + bool Result = std::all_of(LR->begin(), LR->end(), [](Decl *Dcl) { + if (NamedDecl *ND = dyn_cast(Dcl)) { + ND = ND->getUnderlyingDecl(); + return isa(ND) || isa(ND) || + isa(ND); + } + return false; + }); + + D.setPrevLookupResult(std::move(LR)); + return Result; +} diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -330,6 +330,7 @@ bool IsCtorOrDtorName, bool WantNontrivialTypeSourceInfo, bool IsClassTemplateDeductionContext, + ImplicitTypenameContext AllowImplicitTypename, IdentifierInfo **CorrectedII) { // FIXME: Consider allowing this outside C++1z mode as an extension. bool AllowDeducedTemplate = IsClassTemplateDeductionContext && @@ -356,17 +357,33 @@ // // We therefore do not perform any name lookup if the result would // refer to a member of an unknown specialization. - if (!isClassName && !IsCtorOrDtorName) + // In C++2a, in several contexts a 'typename' is not required. Also + // allow this as an extension. + if (AllowImplicitTypename == ImplicitTypenameContext::No && + !isClassName && !IsCtorOrDtorName) return nullptr; + bool IsImplicitTypename = !isClassName && !IsCtorOrDtorName; + if (IsImplicitTypename) { + SourceLocation QualifiedLoc = SS->getRange().getBegin(); + if (getLangOpts().CPlusPlus20) + Diag(QualifiedLoc, diag::warn_cxx17_compat_implicit_typename); + else + Diag(QualifiedLoc, diag::ext_implicit_typename) + << SS->getScopeRep() << II.getName() + << FixItHint::CreateInsertion(QualifiedLoc, "typename "); + } // We know from the grammar that this name refers to a type, // so build a dependent node to describe the type. if (WantNontrivialTypeSourceInfo) - return ActOnTypenameType(S, SourceLocation(), *SS, II, NameLoc).get(); + return ActOnTypenameType(S, SourceLocation(), *SS, II, NameLoc, + (ImplicitTypenameContext)IsImplicitTypename) + .get(); NestedNameSpecifierLoc QualifierLoc = SS->getWithLocInContext(Context); - QualType T = CheckTypenameType(ETK_None, SourceLocation(), QualifierLoc, - II, NameLoc); + QualType T = + CheckTypenameType(IsImplicitTypename ? ETK_Typename : ETK_None, + SourceLocation(), QualifierLoc, II, NameLoc); return ParsedType::make(T); } @@ -6274,7 +6291,10 @@ LookupName(Previous, S, CreateBuiltins); } else { // Something like "int foo::x;" - LookupQualifiedName(Previous, DC); + if (D.hasPrevLookupResult()) + Previous = D.consumePrevLookupResult(); + else + LookupQualifiedName(Previous, DC); // C++ [dcl.meaning]p1: // When the declarator-id is qualified, the declaration shall refer to a diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -3964,7 +3964,8 @@ TemplateTy TemplateD, IdentifierInfo *TemplateII, SourceLocation TemplateIILoc, SourceLocation LAngleLoc, ASTTemplateArgsPtr TemplateArgsIn, SourceLocation RAngleLoc, - bool IsCtorOrDtorName, bool IsClassName) { + bool IsCtorOrDtorName, bool IsClassName, + ImplicitTypenameContext AllowImplicitTypename) { if (SS.isInvalid()) return true; @@ -3978,9 +3979,19 @@ // qualified-id denotes a type, forming an // elaborated-type-specifier (7.1.5.3). if (!LookupCtx && isDependentScopeSpecifier(SS)) { - Diag(SS.getBeginLoc(), diag::err_typename_missing_template) - << SS.getScopeRep() << TemplateII->getName(); - // Recover as if 'typename' were specified. + // C++2a relaxes some of those restrictinos in [temp.res]p5. + if (AllowImplicitTypename == ImplicitTypenameContext::Yes) { + + if (getLangOpts().CPlusPlus20) + Diag(SS.getBeginLoc(), diag::warn_cxx17_compat_implicit_typename); + else + Diag(SS.getBeginLoc(), diag::ext_implicit_typename) + << SS.getScopeRep() << TemplateII->getName() + << FixItHint::CreateInsertion(SS.getBeginLoc(), "typename "); + } else + Diag(SS.getBeginLoc(), diag::err_typename_missing_template) + << SS.getScopeRep() << TemplateII->getName(); + // FIXME: This is not quite correct recovery as we don't transform SS // into the corresponding dependent form (and we don't diagnose missing // 'template' keywords within SS as a result). @@ -10478,10 +10489,11 @@ return CreateParsedType(Result, TLB.getTypeSourceInfo(Context, Result)); } -TypeResult -Sema::ActOnTypenameType(Scope *S, SourceLocation TypenameLoc, - const CXXScopeSpec &SS, const IdentifierInfo &II, - SourceLocation IdLoc) { +TypeResult Sema::ActOnTypenameType(Scope *S, SourceLocation TypenameLoc, + const CXXScopeSpec &SS, + const IdentifierInfo &II, + SourceLocation IdLoc, + ImplicitTypenameContext IsImplicitTypename) { if (SS.isInvalid()) return true; @@ -10494,9 +10506,13 @@ NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context); TypeSourceInfo *TSI = nullptr; - QualType T = CheckTypenameType(TypenameLoc.isValid()? ETK_Typename : ETK_None, - TypenameLoc, QualifierLoc, II, IdLoc, &TSI, - /*DeducedTSTContext=*/true); + QualType T = + CheckTypenameType((TypenameLoc.isValid() || + IsImplicitTypename == ImplicitTypenameContext::Yes) + ? ETK_Typename + : ETK_None, + TypenameLoc, QualifierLoc, II, IdLoc, &TSI, + /*DeducedTSTContext=*/true); if (T.isNull()) return true; return CreateParsedType(T, TSI); diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp --- a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp +++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp @@ -31,8 +31,8 @@ struct X0::X0 X0::f2() { return X0(); } -template X1::X1 X1::f2() { } // expected-error{{missing 'typename'}} -template X1::X1 (X1::f2)(int) { } // expected-error{{missing 'typename'}} +template X1::X1 X1::f2() { } // expected-warning{{missing 'typename'}} +template X1::X1 (X1::f2)(int) { } // expected-warning{{missing 'typename'}} template struct X1::X1 (X1::f2)(float) { } template struct X1::X1 (X1::f2)(double) { } template typename X1::template X1 X1::f2(short) { } // expected-warning {{qualified reference to 'X1' is a constructor name rather than a template name in this context}} diff --git a/clang/test/CXX/drs/dr1xx.cpp b/clang/test/CXX/drs/dr1xx.cpp --- a/clang/test/CXX/drs/dr1xx.cpp +++ b/clang/test/CXX/drs/dr1xx.cpp @@ -2,6 +2,7 @@ // RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors namespace dr100 { // dr100: yes template struct A {}; // expected-note 0-1{{declared here}} @@ -73,7 +74,10 @@ namespace dr108 { // dr108: yes template struct A { struct B { typedef int X; }; - B::X x; // expected-error {{missing 'typename'}} + B::X x; +#if __cplusplus <= 201703L + // expected-error@-2 {{implicit 'typename' is a C++20 extension}} +#endif struct C : B { X x; }; // expected-error {{unknown type name}} }; template<> struct A::B { int X; }; diff --git a/clang/test/CXX/drs/dr2xx.cpp b/clang/test/CXX/drs/dr2xx.cpp --- a/clang/test/CXX/drs/dr2xx.cpp +++ b/clang/test/CXX/drs/dr2xx.cpp @@ -243,13 +243,16 @@ // dr223: na -namespace dr224 { // dr224: no +namespace dr224 { // dr224: yes namespace example1 { template class A { typedef int type; A::type a; A::type b; - A::type c; // expected-error {{missing 'typename'}} + A::type c; +#if __cplusplus <= 201703L + // expected-error@-2 {{implicit 'typename' is a C++20 extension}} +#endif ::dr224::example1::A::type d; class B { @@ -257,12 +260,18 @@ A::type a; A::type b; - A::type c; // expected-error {{missing 'typename'}} + A::type c; +#if __cplusplus <= 201703L + // expected-error@-2 {{implicit 'typename' is a C++20 extension}} +#endif ::dr224::example1::A::type d; B::type e; A::B::type f; - A::B::type g; // expected-error {{missing 'typename'}} + A::B::type g; +#if __cplusplus <= 201703L + // expected-error@-2 {{implicit 'typename' is a C++20 extension}} +#endif typename A::B::type h; }; }; @@ -270,21 +279,32 @@ template class A { typedef int type; A::type a; - A::type b; // expected-error {{missing 'typename'}} + A::type b; +#if __cplusplus <= 201703L + // expected-error@-2 {{implicit 'typename' is a C++20 extension}} +#endif }; template struct B { typedef int type; B::type b1; - B::type b2; // expected-error {{missing 'typename'}} + B::type b2; +#if __cplusplus <= 201703L + // expected-error@-2 {{implicit 'typename' is a C++20 extension}} +#endif typedef T1 my_T1; static const int my_I = I; static const int my_I2 = I+0; static const int my_I3 = my_I; - B::type b3; // FIXME: expected-error {{missing 'typename'}} - B::type b4; // expected-error {{missing 'typename'}} - B::type b5; // FIXME: expected-error {{missing 'typename'}} + B::type b3; + B::type b4; + B::type b5; +#if __cplusplus <= 201703L + // expected-error@-4 {{implicit 'typename' is a C++20 extension}} + // expected-error@-4 {{implicit 'typename' is a C++20 extension}} + // expected-error@-4 {{implicit 'typename' is a C++20 extension}} +#endif }; } @@ -295,7 +315,10 @@ X::type w; X::type x; X::i, double>::type y; - X::i, long>::type z; // expected-error {{missing 'typename'}} + X::i, long>::type z; +#if __cplusplus <= 201703L + // expected-error@-2 {{implicit 'typename' is a C++20 extension}} +#endif int f(); }; template int A::f() { diff --git a/clang/test/CXX/drs/dr4xx.cpp b/clang/test/CXX/drs/dr4xx.cpp --- a/clang/test/CXX/drs/dr4xx.cpp +++ b/clang/test/CXX/drs/dr4xx.cpp @@ -2,6 +2,7 @@ // RUN: env ASAN_OPTIONS=detect_stack_use_after_return=0 %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: env ASAN_OPTIONS=detect_stack_use_after_return=0 %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: env ASAN_OPTIONS=detect_stack_use_after_return=0 %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=0 %clang_cc1 -std=c++20 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // FIXME: __SIZE_TYPE__ expands to 'long long' on some targets. __extension__ typedef __SIZE_TYPE__ size_t; @@ -176,7 +177,10 @@ B b1; A::B b2; A::B b3; - A::B b4; // expected-error {{missing 'typename'}} + A::B b4; +#if __cplusplus <= 201703L + // expected-error@-2 {{implicit 'typename' is a C++20 extension}} +#endif }; } diff --git a/clang/test/CXX/drs/dr5xx.cpp b/clang/test/CXX/drs/dr5xx.cpp --- a/clang/test/CXX/drs/dr5xx.cpp +++ b/clang/test/CXX/drs/dr5xx.cpp @@ -2,6 +2,7 @@ // RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +// RUN: %clang_cc1 -std=c++20 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // FIXME: This is included to avoid a diagnostic with no source location // pointing at the implicit operator new. We can't match such a diagnostic @@ -228,8 +229,12 @@ template struct X { typedef int type; X::type v1; - X<(N)>::type v2; // expected-error {{missing 'typename'}} - X<+N>::type v3; // expected-error {{missing 'typename'}} + X<(N)>::type v2; + X<+N>::type v3; +#if __cplusplus <= 201703L + // expected-error@-3 {{implicit 'typename' is a C++20 extension}} + // expected-error@-3 {{implicit 'typename' is a C++20 extension}} +#endif }; } @@ -345,12 +350,15 @@ template<> template template void A::B::h() {} template template void A::B::i() {} // expected-error {{should be empty}} +#if __cplusplus <= 201703L + // FIXME: All of those declarations shouldn't crash in C++20 mode. template<> template<> void A::B::f() {} template<> template<> template void A::B::h() {} template<> template<> template<> void A::B::h() {} template<> void A::B::f() {} // expected-error {{requires 'template<>'}} template<> template void A::B::h() {} // expected-error {{should be empty}} +#endif } } @@ -478,8 +486,15 @@ namespace dr542 { // dr542: yes #if __cplusplus >= 201103L + // In C++20 A and B are no longer aggregates and thus the constructor is + // called, which fails. struct A { A() = delete; int n; }; A a[32] = {}; // ok, constructor not called +#if __cplusplus > 201703L + // expected-error@-2 {{call to deleted constructor}} + // expected-note@-3 {{in implicit initialization}} + // expected-note@-5 {{marked deleted here}} +#endif struct B { int n; @@ -487,6 +502,10 @@ B() = default; }; B b[32] = {}; // ok, constructor not called +#if __cplusplus > 201703L + // expected-error@-2 {{calling a private constructor}} + // expected-note@-5 {{declared private here}} +#endif #endif } diff --git a/clang/test/CXX/temp/temp.res/p3.cpp b/clang/test/CXX/temp/temp.res/p3.cpp --- a/clang/test/CXX/temp/temp.res/p3.cpp +++ b/clang/test/CXX/temp/temp.res/p3.cpp @@ -12,9 +12,9 @@ }; }; -template A // expected-error {{missing 'typename' prior to dependent type template name 'A::B'}} +template A // expected-warning {{missing 'typename'}} ::B f1(); -template A::C f2(); // expected-error {{missing 'typename' prior to dependent type template name 'A::C'}} +template A::C f2(); // expected-warning {{missing 'typename'}} // FIXME: Should these cases really be valid? There doesn't appear to be a rule prohibiting them... template A::C::X(T) {} @@ -30,9 +30,6 @@ template template struct A::B { friend A::C f6(); // ok, same as 'friend T f6();' - // FIXME: Error recovery here is awful; we decide that the template-id names - // a type, and then complain about the rest of the tokens, and then complain - // that we didn't get a function declaration. - friend A::C f7(); // expected-error {{use 'template' keyword to treat 'C' as a dependent template name}} expected-error 3{{}} - friend A::template C f8(); // expected-error 4{{}} + friend A::C f7(); // expected-error {{use 'template' keyword to treat 'C' as a dependent template name}} expected-warning {{missing 'typename'}} + friend A::template C f8(); // expected-warning {{missing 'typename'}} }; diff --git a/clang/test/CXX/temp/temp.res/p5.cpp b/clang/test/CXX/temp/temp.res/p5.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/temp/temp.res/p5.cpp @@ -0,0 +1,176 @@ +// RUN: %clang_cc1 -std=c++20 -pedantic -verify %s + +struct X { + using type = int; + static constexpr int value = 1; + class tclass {}; +}; + +// [temp.res]p5 +// A qualified-id is assumed to name a type if + +template +void f() { + // it is a qualified name in a type-id-only context (see below), or + // [its smallest enclosing [/new/defining/]-type-id is]: + // - a new-type-id + auto *Ptr = new T::type(); + // - a defining-type-id + class T::tclass Empty1; + T::tclass Empty2; // expected-error{{missing 'typename'}} + // - a trailing-return-type + auto f()->T::type; + // - default argument of a type-parameter of a template [see below] + + // - type-id of a + // static_cast, + auto StaticCast = static_cast(1.2); + // const_cast, + const auto *ConstCast = const_cast(Ptr); + // reinterpret_cast, + int ReinterpretCast = reinterpret_cast(4); + // dynamic_cast + struct B { + virtual ~B() = default; + }; + struct D : T::tclass {}; + auto *Base = dynamic_cast(new B); + + T::type Invalid; // expected-error{{missing 'typename'}} +} + +template void f(); + +// As default argument. +template +struct DefaultArg {}; + +template struct DefaultArg; + +// it is a decl-specifier of the decl-specifier-seq of a +// - simple-declaration or a function-definition in namespace scope +template +T::type VarTemp = 1; + +template int VarTemp; + +template +T::type FuncDef() { return 1; } + +template int FuncDef(); + +template +T::type funcDecl(); + +template +void FuncParam(T::type); // ok, but variable template +// expected-error@-1{{variable has incomplete type 'void'}} + +template +void FuncParam2(const T::type, int); // expected-error{{missing 'typename'}} + +template +struct MemberDecl { + // member-declaration, + T::type Member; + + // parameter-declaration in a member-declaration, unless that + // parameter-declaration appears in a default argument + void NoDefault(T::type); + void Default(int A = T::value); +}; + +template struct MemberDecl; + +// parameter-declaration in a declarator of a function or function template +// declaration where the declarator-id is qualified, unless that +// parameter-declaration appears in a default argument, +struct QualifiedFunc { + template + void foo(typename T::type); + template + void bar(T::type); +}; + +template +void QualifiedFunc::foo(T::type) {} +template +void QualifiedFunc::bar(typename T::type) {} + +template +void g() { + // parameter-declaration in a lambda-declarator, unless that + // parameter-declaration appears in a default argument, or + auto Lambda1 = [](T::type) {}; + auto Lambda2 = [](int A = T::value) {}; +} + +template void g(); + +// parameter-declaration of a (non-type) template-parameter. +template +void NonTypeArg() {} + +template void NonTypeArg(); + +template +void f(T::type) {} // expected-error {{missing 'typename'}} + +namespace N { + template + int f(typename T::type); + template + extern int Var; +} + +template +int N::f(T::type); // ok, function +template +int N::Var(T::value); // ok, variable + +int h() { + return N::f(10) + N::Var; +} + +namespace NN { + inline namespace A { template int f(typename T::type); } // expected-note{{previous definition is here}} + inline namespace B { template int f(T::type); } +} + +template +int NN::f(T::type); // expected-error{{redefinition of 'f' as different kind of symbol}} + +template +struct videntity { + static constexpr auto value = V; +}; + +template ::value> +void f(int = T::value) {} + +template int test() = delete; +template int test(); + +template +int Test = test(); +template int Test; + +template struct A { + // FIXME: This diagnostic is horrible. + enum E : T::type {}; // expected-error{{missing 'typename'}} + operator T::type() {} // expected-error{{missing 'typename'}} + void f() { this->operator T::type(); } // expected-error{{missing 'typename'}} +}; + +template +struct C { + C(T::type); // implicit typename context + friend C (T::fn)(); // not implicit typename context, declarator-id of friend declaration + C(T::type::*x)[3]; // not implicit typename context, pointer-to-member type +}; + +template +C::C(T::type) {} diff --git a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp --- a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp +++ b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp @@ -17,7 +17,7 @@ template struct A>> { struct C {}; - B>::C bc; // expected-error {{missing 'typename'}} + B>::C bc; // expected-warning {{implicit 'typename' is a C++20 extension}} }; } diff --git a/clang/test/FixIt/fixit.cpp b/clang/test/FixIt/fixit.cpp --- a/clang/test/FixIt/fixit.cpp +++ b/clang/test/FixIt/fixit.cpp @@ -211,7 +211,7 @@ template struct Mystery; template typedef Mystery::type getMysteriousThing() { // \ expected-error {{function definition declared 'typedef'}} \ - expected-error {{missing 'typename' prior to dependent}} + expected-warning {{implicit 'typename' is a C++20 extension}} return Mystery::get(); } diff --git a/clang/test/Parser/cxx-member-initializers.cpp b/clang/test/Parser/cxx-member-initializers.cpp --- a/clang/test/Parser/cxx-member-initializers.cpp +++ b/clang/test/Parser/cxx-member-initializers.cpp @@ -103,5 +103,9 @@ void l(int x = C::C1>().f()) {} // This isn't, but it shouldn't crash. The diagnostics don't matter much. - void m(int x = C().f()) {} // expected-error {{declaration of anonymous union must be a definition}} expected-error {{expected a type}} expected-error {{expected '>'}} expected-note {{to match}} + void m(int x = C().f()) {} + // expected-error@-1 {{declaration of anonymous union must be a definition}} + // expected-error@-2 {{type name requires a specifier or qualifier}} + // expected-error@-3 {{expected '>'}} + // expected-note@-4 {{to match this '<'}} }; diff --git a/clang/test/SemaCXX/MicrosoftCompatibility.cpp b/clang/test/SemaCXX/MicrosoftCompatibility.cpp --- a/clang/test/SemaCXX/MicrosoftCompatibility.cpp +++ b/clang/test/SemaCXX/MicrosoftCompatibility.cpp @@ -211,14 +211,14 @@ typedef B Base2; typedef A Base3; - A::TYPE a1; // expected-warning {{missing 'typename' prior to dependent type name}} - Base1::TYPE a2; // expected-warning {{missing 'typename' prior to dependent type name}} + A::TYPE a1; // expected-warning {{implicit 'typename' is a C++20 extension}} + Base1::TYPE a2; // expected-warning {{implicit 'typename' is a C++20 extension}} - B::TYPE a3; // expected-warning {{missing 'typename' prior to dependent type name}} - Base2::TYPE a4; // expected-warning {{missing 'typename' prior to dependent type name}} + B::TYPE a3; // expected-warning {{implicit 'typename' is a C++20 extension}} + Base2::TYPE a4; // expected-warning {{implicit 'typename' is a C++20 extension}} - A::TYPE a5; // expected-error {{missing 'typename' prior to dependent type name}} - Base3::TYPE a6; // expected-error {{missing 'typename' prior to dependent type name}} + A::TYPE a5; // expected-warning {{implicit 'typename' is a C++20 extension}} + Base3::TYPE a6; // expected-warning {{implicit 'typename' is a C++20 extension}} }; class D { @@ -227,9 +227,9 @@ }; template -void function_missing_typename(const T::Type param)// expected-warning {{missing 'typename' prior to dependent type name}} +void function_missing_typename(const T::Type param)// expected-warning {{missing 'typename'}} { - const T::Type var = 2; // expected-warning {{missing 'typename' prior to dependent type name}} + const T::Type var = 2; // expected-warning {{missing 'typename'}} } template void function_missing_typename(const D::Type param); diff --git a/clang/test/SemaCXX/MicrosoftExtensions.cpp b/clang/test/SemaCXX/MicrosoftExtensions.cpp --- a/clang/test/SemaCXX/MicrosoftExtensions.cpp +++ b/clang/test/SemaCXX/MicrosoftExtensions.cpp @@ -594,7 +594,7 @@ namespace PR32750 { template struct A {}; -template struct B : A> { A::C::D d; }; // expected-error {{missing 'typename' prior to dependent type name 'A::C::D'}} +template struct B : A> { A::C::D d; }; // expected-warning {{implicit 'typename' is a C++20 extension}} } #else diff --git a/clang/test/SemaCXX/MicrosoftSuper.cpp b/clang/test/SemaCXX/MicrosoftSuper.cpp --- a/clang/test/SemaCXX/MicrosoftSuper.cpp +++ b/clang/test/SemaCXX/MicrosoftSuper.cpp @@ -108,8 +108,8 @@ typename __super::XXX a; typedef typename __super::XXX b; - __super::XXX c; // expected-error {{missing 'typename'}} - typedef __super::XXX d; // expected-error {{missing 'typename'}} + __super::XXX c; // expected-warning {{implicit 'typename' is a C++20 extension}} + typedef __super::XXX d; // expected-warning {{implicit 'typename' is a C++20 extension}} void foo() { typename __super::XXX e; @@ -127,8 +127,8 @@ typename __super::XXX a; typedef typename __super::XXX b; - __super::XXX c; // expected-error {{missing 'typename'}} - typedef __super::XXX d; // expected-error {{missing 'typename'}} + __super::XXX c; // expected-warning {{implicit 'typename' is a C++20 extension}} + typedef __super::XXX d; // expected-warning {{implicit 'typename' is a C++20 extension}} void foo() { typename __super::XXX e; diff --git a/clang/test/SemaCXX/rounding-math-crash.cpp b/clang/test/SemaCXX/rounding-math-crash.cpp --- a/clang/test/SemaCXX/rounding-math-crash.cpp +++ b/clang/test/SemaCXX/rounding-math-crash.cpp @@ -1,3 +1,5 @@ // RUN: %clang_cc1 -triple x86_64-linux -fsyntax-only -frounding-math -verify %s -template b::a() {} // expected-error {{nested name specifier}} +template b::a() {} +// expected-warning@-1 {{implicit 'typename' is a C++20 extension}} +// expected-error@-2 {{expected unqualified-id}} diff --git a/clang/test/SemaCXX/typo-correction.cpp b/clang/test/SemaCXX/typo-correction.cpp --- a/clang/test/SemaCXX/typo-correction.cpp +++ b/clang/test/SemaCXX/typo-correction.cpp @@ -753,7 +753,7 @@ // because it doesn't make the expression valid. // expected-error@+2 {{did you mean 'g_var_bool'}} // expected-error@+1 {{assigning to 'bool' from incompatible type 'void'}} - enum : decltype((g_var_long = throw))::a { + enum : typename decltype((g_var_long = throw))::a { b = g_volatile_uchar // expected-error {{did you mean 'g_volatile_char'}} }; } diff --git a/clang/test/SemaCXX/unknown-type-name.cpp b/clang/test/SemaCXX/unknown-type-name.cpp --- a/clang/test/SemaCXX/unknown-type-name.cpp +++ b/clang/test/SemaCXX/unknown-type-name.cpp @@ -36,15 +36,15 @@ static int n; static type m; - static int h(T::type, int); // expected-error{{missing 'typename'}} - static int h(T::type x, char); // expected-error{{missing 'typename'}} + static int h(T::type, int); // expected-warning{{implicit 'typename' is a C++20 extension}} + static int h(T::type x, char); // expected-warning{{implicit 'typename' is a C++20 extension}} }; template -A::type g(T t) { return t; } // expected-error{{missing 'typename'}} +A::type g(T t) { return t; } // expected-warning{{implicit 'typename' is a C++20 extension}} template -A::type A::f() { return type(); } // expected-error{{missing 'typename'}} +A::type A::f() { return type(); } // expected-warning{{implicit 'typename' is a C++20 extension}} template void f(T::type) { } // expected-error{{missing 'typename'}} @@ -84,11 +84,11 @@ template int A::n(T::value); // ok template -A::type // expected-error{{missing 'typename'}} +A::type // expected-warning {{implicit 'typename' is a C++20 extension}} A::m(T::value, 0); // ok -template int A::h(T::type, int) {} // expected-error{{missing 'typename'}} -template int A::h(T::type x, char) {} // expected-error{{missing 'typename'}} +template int A::h(T::type, int) {} // expected-warning{{implicit 'typename' is a C++20 extension}} +template int A::h(T::type x, char) {} // expected-warning{{implicit 'typename' is a C++20 extension}} template int h(T::type, int); // expected-error{{missing 'typename'}} template int h(T::type x, char); // expected-error{{missing 'typename'}} @@ -116,4 +116,5 @@ // FIXME: We know which type specifier should have been specified here. Provide // a fix-it to add 'typename A::type' template -A::g() { } // expected-error{{a type specifier is required}} +A::g() { } // expected-error{{expected unqualified-id}} +// expected-warning@-1{{implicit 'typename' is a C++20 extension}} diff --git a/clang/test/SemaTemplate/alias-templates.cpp b/clang/test/SemaTemplate/alias-templates.cpp --- a/clang/test/SemaTemplate/alias-templates.cpp +++ b/clang/test/SemaTemplate/alias-templates.cpp @@ -193,11 +193,10 @@ struct base { template struct derived; }; - // FIXME: The diagnostics here are terrible. template - using derived = base::template derived; // expected-error {{expected a type}} expected-error {{expected ';'}} + using derived = base::template derived; // expected-warning {{missing 'typename'}} template - using derived2 = ::PR16904::base::template derived; // expected-error {{expected a type}} expected-error {{expected ';'}} + using derived2 = ::PR16904::base::template derived; // expected-warning {{missing 'typename'}} } namespace PR14858 { diff --git a/clang/test/SemaTemplate/typename-specifier-3.cpp b/clang/test/SemaTemplate/typename-specifier-3.cpp --- a/clang/test/SemaTemplate/typename-specifier-3.cpp +++ b/clang/test/SemaTemplate/typename-specifier-3.cpp @@ -28,7 +28,7 @@ typedef int arg; }; struct C { - typedef B::X x; // expected-error {{missing 'typename'}} + typedef B::X x; // expected-warning {{missing 'typename'}} }; };