diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 2bd67138ecc048..7990b6c14c4ce0 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -268,6 +268,9 @@ Resolutions to C++ Defect Reports by default. (`CWG2521: User-defined literals and reserved identifiers `_). +- Clang now correctly implements lookup for the terminal name of a member-qualified nested-name-specifier. + (`CWG1835: Dependent member lookup before < `_). + C Language Changes ------------------ diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 696a574833dad2..c16a81d610c9fd 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -3678,9 +3678,9 @@ class CXXUnresolvedConstructExpr final /// an implicit access if a qualifier is provided. class CXXDependentScopeMemberExpr final : public Expr, - private llvm::TrailingObjects { + private llvm::TrailingObjects< + CXXDependentScopeMemberExpr, NestedNameSpecifierLoc, DeclAccessPair, + ASTTemplateKWAndArgsInfo, TemplateArgumentLoc> { friend class ASTStmtReader; friend class ASTStmtWriter; friend TrailingObjects; @@ -3693,17 +3693,15 @@ class CXXDependentScopeMemberExpr final /// implicit accesses. QualType BaseType; - /// The nested-name-specifier that precedes the member name, if any. - /// FIXME: This could be in principle store as a trailing object. - /// However the performance impact of doing so should be investigated first. - NestedNameSpecifierLoc QualifierLoc; - /// The member to which this member expression refers, which /// can be name, overloaded operator, or destructor. /// /// FIXME: could also be a template-id DeclarationNameInfo MemberNameInfo; + /// The location of the '->' or '.' operator. + SourceLocation OperatorLoc; + // CXXDependentScopeMemberExpr is followed by several trailing objects, // some of which optional. They are in order: // @@ -3723,8 +3721,16 @@ class CXXDependentScopeMemberExpr final return CXXDependentScopeMemberExprBits.HasTemplateKWAndArgsInfo; } - bool hasFirstQualifierFoundInScope() const { - return CXXDependentScopeMemberExprBits.HasFirstQualifierFoundInScope; + unsigned getNumUnqualifiedLookups() const { + return CXXDependentScopeMemberExprBits.NumUnqualifiedLookups; + } + + unsigned numTrailingObjects(OverloadToken) const { + return hasQualifier(); + } + + unsigned numTrailingObjects(OverloadToken) const { + return getNumUnqualifiedLookups(); } unsigned numTrailingObjects(OverloadToken) const { @@ -3735,33 +3741,32 @@ class CXXDependentScopeMemberExpr final return getNumTemplateArgs(); } - unsigned numTrailingObjects(OverloadToken) const { - return hasFirstQualifierFoundInScope(); - } - CXXDependentScopeMemberExpr(const ASTContext &Ctx, Expr *Base, QualType BaseType, bool IsArrow, SourceLocation OperatorLoc, NestedNameSpecifierLoc QualifierLoc, SourceLocation TemplateKWLoc, - NamedDecl *FirstQualifierFoundInScope, + ArrayRef UnqualifiedLookups, DeclarationNameInfo MemberNameInfo, const TemplateArgumentListInfo *TemplateArgs); - CXXDependentScopeMemberExpr(EmptyShell Empty, bool HasTemplateKWAndArgsInfo, - bool HasFirstQualifierFoundInScope); + CXXDependentScopeMemberExpr(EmptyShell Empty, bool HasQualifier, + unsigned NumUnqualifiedLookups, + bool HasTemplateKWAndArgsInfo); public: static CXXDependentScopeMemberExpr * Create(const ASTContext &Ctx, Expr *Base, QualType BaseType, bool IsArrow, SourceLocation OperatorLoc, NestedNameSpecifierLoc QualifierLoc, - SourceLocation TemplateKWLoc, NamedDecl *FirstQualifierFoundInScope, + SourceLocation TemplateKWLoc, + ArrayRef UnqualifiedLookups, DeclarationNameInfo MemberNameInfo, const TemplateArgumentListInfo *TemplateArgs); static CXXDependentScopeMemberExpr * - CreateEmpty(const ASTContext &Ctx, bool HasTemplateKWAndArgsInfo, - unsigned NumTemplateArgs, bool HasFirstQualifierFoundInScope); + CreateEmpty(const ASTContext &Ctx, bool HasQualifier, + unsigned NumUnqualifiedLookups, bool HasTemplateKWAndArgsInfo, + unsigned NumTemplateArgs); /// True if this is an implicit access, i.e. one in which the /// member being accessed was not written in the source. The source @@ -3786,34 +3791,35 @@ class CXXDependentScopeMemberExpr final bool isArrow() const { return CXXDependentScopeMemberExprBits.IsArrow; } /// Retrieve the location of the '->' or '.' operator. - SourceLocation getOperatorLoc() const { - return CXXDependentScopeMemberExprBits.OperatorLoc; + SourceLocation getOperatorLoc() const { return OperatorLoc; } + + /// Determines whether this member expression had a nested-name-specifier + /// prior to the name of the member, e.g., x->Base::foo. + bool hasQualifier() const { + return CXXDependentScopeMemberExprBits.HasQualifier; } - /// Retrieve the nested-name-specifier that qualifies the member name. - NestedNameSpecifier *getQualifier() const { - return QualifierLoc.getNestedNameSpecifier(); + /// If the member name was qualified, retrieves the nested-name-specifier + /// that precedes the member name, with source-location information. + NestedNameSpecifierLoc getQualifierLoc() const { + if (!hasQualifier()) + return NestedNameSpecifierLoc(); + return *getTrailingObjects(); } - /// Retrieve the nested-name-specifier that qualifies the member - /// name, with source location information. - NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; } + /// If the member name was qualified, retrieves the + /// nested-name-specifier that precedes the member name. Otherwise, returns + /// NULL. + NestedNameSpecifier *getQualifier() const { + return getQualifierLoc().getNestedNameSpecifier(); + } - /// Retrieve the first part of the nested-name-specifier that was - /// found in the scope of the member access expression when the member access - /// was initially parsed. - /// - /// This function only returns a useful result when member access expression - /// uses a qualified member name, e.g., "x.Base::f". Here, the declaration - /// returned by this function describes what was found by unqualified name - /// lookup for the identifier "Base" within the scope of the member access - /// expression itself. At template instantiation time, this information is - /// combined with the results of name lookup into the type of the object - /// expression itself (the class type of x). - NamedDecl *getFirstQualifierFoundInScope() const { - if (!hasFirstQualifierFoundInScope()) - return nullptr; - return *getTrailingObjects(); + /// Retrieve the declarations found by unqualified lookup for the first + /// component name of the nested-name-specifier, if any. + ArrayRef unqualified_lookups() const { + if (!getNumUnqualifiedLookups()) + return std::nullopt; + return {getTrailingObjects(), getNumUnqualifiedLookups()}; } /// Retrieve the name of the member that this expression refers to. diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index 83fafbabb1d460..2c8db3fbb7ba8d 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -1040,18 +1040,19 @@ class alignas(void *) Stmt { LLVM_PREFERRED_TYPE(bool) unsigned IsArrow : 1; + /// True if this member expression used a nested-name-specifier to + /// refer to the member, e.g., "x->Base::f". + LLVM_PREFERRED_TYPE(bool) + unsigned HasQualifier : 1; + /// Whether this member expression has info for explicit template /// keyword and arguments. LLVM_PREFERRED_TYPE(bool) unsigned HasTemplateKWAndArgsInfo : 1; - /// See getFirstQualifierFoundInScope() and the comment listing - /// the trailing objects. - LLVM_PREFERRED_TYPE(bool) - unsigned HasFirstQualifierFoundInScope : 1; - - /// The location of the '->' or '.' operator. - SourceLocation OperatorLoc; + /// Number of declarations found by unqualified lookup for the + /// first component name of the nested-name-specifier. + unsigned NumUnqualifiedLookups; }; class OverloadExprBitfields { diff --git a/clang/include/clang/AST/UnresolvedSet.h b/clang/include/clang/AST/UnresolvedSet.h index 1369725ab4e96a..ef44499ce59264 100644 --- a/clang/include/clang/AST/UnresolvedSet.h +++ b/clang/include/clang/AST/UnresolvedSet.h @@ -97,6 +97,10 @@ class UnresolvedSetImpl { decls().push_back(DeclAccessPair::make(D, AS)); } + void addAllDecls(ArrayRef Other) { + append(iterator(Other.begin()), iterator(Other.end())); + } + /// Replaces the given declaration with the new one, once. /// /// \return true if the set changed diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 0da509280068ad..a37c6d3d9f3107 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -912,9 +912,7 @@ def missing_template_arg_list_after_template_kw : Extension< "keyword">, InGroup>, DefaultError; -def err_missing_dependent_template_keyword : Error< - "use 'template' keyword to treat '%0' as a dependent template name">; -def warn_missing_dependent_template_keyword : ExtWarn< +def ext_missing_dependent_template_keyword : ExtWarn< "use 'template' keyword to treat '%0' as a dependent template name">; def ext_extern_template : Extension< diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 045ee754a242b3..50358eeca76725 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1910,6 +1910,7 @@ class Parser : public CodeCompletionHandler { } bool diagnoseUnknownTemplateId(ExprResult TemplateName, SourceLocation Less); + bool isMissingTemplateKeywordBeforeScope(bool AnnotateInvalid); void checkPotentialAngleBracket(ExprResult &PotentialTemplateName); bool checkPotentialAngleBracketDelimiter(const AngleBracketTracker::Loc &, const Token &OpToken); @@ -1970,7 +1971,7 @@ class Parser : public CodeCompletionHandler { //===--------------------------------------------------------------------===// // C++ Expressions ExprResult tryParseCXXIdExpression(CXXScopeSpec &SS, bool isAddressOfOperand, - Token &Replacement); + Token *Replacement = nullptr); ExprResult tryParseCXXPackIndexingExpression(ExprResult PackIdExpression); ExprResult ParseCXXPackIndexingExpression(ExprResult PackIdExpression); @@ -2745,7 +2746,7 @@ class Parser : public CodeCompletionHandler { /// Determine whether the current token sequence might be /// '<' template-argument-list '>' /// rather than a less-than expression. - TPResult isTemplateArgumentList(unsigned TokensToSkip); + TPResult isTemplateArgumentList(unsigned TokensToSkip, TemplateNameKind TNK); /// Determine whether an '(' after an 'explicit' keyword is part of a C++20 /// 'explicit(bool)' declaration, in earlier language modes where that is an @@ -2768,6 +2769,7 @@ class Parser : public CodeCompletionHandler { TPResult TryParseTypeofSpecifier(); TPResult TryParseProtocolQualifiers(); TPResult TryParsePtrOperatorSeq(); + TPResult TryParseNonConversionOperatorId(); TPResult TryParseOperatorId(); TPResult TryParseInitDeclaratorList(bool MayHaveTrailingReturnType = false); TPResult TryParseDeclarator(bool mayBeAbstract, bool mayHaveIdentifier = true, @@ -3382,15 +3384,11 @@ class Parser : public CodeCompletionHandler { BaseResult ParseBaseSpecifier(Decl *ClassDecl); AccessSpecifier getAccessSpecifierIfPresent() const; - bool ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, - ParsedType ObjectType, - bool ObjectHadErrors, - SourceLocation TemplateKWLoc, - IdentifierInfo *Name, - SourceLocation NameLoc, - bool EnteringContext, - UnqualifiedId &Id, - bool AssumeTemplateId); + bool ParseUnqualifiedIdTemplateId( + CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors, + SourceLocation TemplateKWLoc, SourceLocation TildeLoc, + IdentifierInfo *Name, SourceLocation NameLoc, bool EnteringContext, + UnqualifiedId &Id, bool AssumeTemplateId); bool ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext, ParsedType ObjectType, UnqualifiedId &Result); diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 06243f2624876f..23a054fef9b56c 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -75,6 +75,7 @@ class CXXScopeSpec { SourceRange Range; NestedNameSpecifierLocBuilder Builder; ArrayRef TemplateParamLists; + ArrayRef UnqualifiedLookups; public: SourceRange getRange() const { return Range; } @@ -91,6 +92,13 @@ class CXXScopeSpec { return TemplateParamLists; } + void setUnqualifiedLookups(ArrayRef Found) { + UnqualifiedLookups = Found; + } + ArrayRef getUnqualifiedLookups() const { + return UnqualifiedLookups; + } + /// Retrieve the representation of the nested-name-specifier. NestedNameSpecifier *getScopeRep() const { return Builder.getRepresentation(); diff --git a/clang/include/clang/Sema/Lookup.h b/clang/include/clang/Sema/Lookup.h index b0a08a05ac6a0a..6b765ef3c980f6 100644 --- a/clang/include/clang/Sema/Lookup.h +++ b/clang/include/clang/Sema/Lookup.h @@ -483,11 +483,15 @@ class LookupResult { ResultKind = Found; } + void addAllDecls(ArrayRef Other) { + Decls.addAllDecls(Other); + ResultKind = Found; + } + /// Add all the declarations from another set of lookup /// results. void addAllDecls(const LookupResult &Other) { - Decls.append(Other.Decls.begin(), Other.Decls.end()); - ResultKind = Found; + addAllDecls(Other.Decls.pairs()); } /// Determine whether no result was found because we could not diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index d6f3508a5243f3..3cf17ff14a3081 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2779,7 +2779,8 @@ class Sema final : public SemaBase { /// (e.g., Base::), perform name lookup for that identifier as a /// nested-name-specifier within the given scope, and return the result of /// that name lookup. - NamedDecl *FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS); + bool LookupFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS, + UnresolvedSetImpl &R); /// Keeps information about an identifier in a nested-name-spec. /// @@ -2819,9 +2820,6 @@ class Sema final : public SemaBase { /// \param EnteringContext If true, enter the context specified by the /// nested-name-specifier. /// \param SS Optional nested name specifier preceding the identifier. - /// \param ScopeLookupResult Provides the result of name lookup within the - /// scope of the nested-name-specifier that was computed at template - /// definition time. /// \param ErrorRecoveryLookup Specifies if the method is called to improve /// error recovery and what kind of recovery is performed. /// \param IsCorrectedToColon If not null, suggestion of replace '::' -> ':' @@ -2830,11 +2828,6 @@ class Sema final : public SemaBase { /// not '::'. /// \param OnlyNamespace If true, only considers namespaces in lookup. /// - /// This routine differs only slightly from ActOnCXXNestedNameSpecifier, in - /// that it contains an extra parameter \p ScopeLookupResult, which provides - /// the result of name lookup within the scope of the nested-name-specifier - /// that was computed at template definition time. - /// /// If ErrorRecoveryLookup is true, then this call is used to improve error /// recovery. This means that it should not emit diagnostics, it should /// just return true on failure. It also means it should only return a valid @@ -2843,7 +2836,6 @@ class Sema final : public SemaBase { /// specifier. bool BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, bool EnteringContext, CXXScopeSpec &SS, - NamedDecl *ScopeLookupResult, bool ErrorRecoveryLookup, bool *IsCorrectedToColon = nullptr, bool OnlyNamespace = false); @@ -8536,11 +8528,12 @@ class Sema final : public SemaBase { const TemplateArgumentListInfo *TemplateArgs, bool IsDefiniteInstance, const Scope *S); - ExprResult ActOnDependentMemberExpr( - Expr *Base, QualType BaseType, bool IsArrow, SourceLocation OpLoc, - const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, - NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo, - const TemplateArgumentListInfo *TemplateArgs); + ExprResult + ActOnDependentMemberExpr(Expr *Base, QualType BaseType, bool IsArrow, + SourceLocation OpLoc, const CXXScopeSpec &SS, + SourceLocation TemplateKWLoc, + const DeclarationNameInfo &NameInfo, + const TemplateArgumentListInfo *TemplateArgs); /// The main callback when the parser finds something like /// expression . [nested-name-specifier] identifier @@ -8596,15 +8589,14 @@ class Sema final : public SemaBase { ExprResult BuildMemberReferenceExpr( Expr *Base, QualType BaseType, SourceLocation OpLoc, bool IsArrow, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, - NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo, + const DeclarationNameInfo &NameInfo, const TemplateArgumentListInfo *TemplateArgs, const Scope *S, ActOnMemberAccessExtraArgs *ExtraArgs = nullptr); ExprResult BuildMemberReferenceExpr(Expr *Base, QualType BaseType, SourceLocation OpLoc, bool IsArrow, const CXXScopeSpec &SS, - SourceLocation TemplateKWLoc, - NamedDecl *FirstQualifierInScope, LookupResult &R, + SourceLocation TemplateKWLoc, LookupResult &R, const TemplateArgumentListInfo *TemplateArgs, const Scope *S, bool SuppressQualifierCheck = false, ActOnMemberAccessExtraArgs *ExtraArgs = nullptr); @@ -11094,15 +11086,14 @@ class Sema final : public SemaBase { QualType ObjectType, bool EnteringContext, RequiredTemplateKind RequiredTemplate = SourceLocation(), AssumedTemplateKind *ATK = nullptr, - bool AllowTypoCorrection = true); + bool AllowTypoCorrection = true, bool MayBeNNS = false); - TemplateNameKind isTemplateName(Scope *S, CXXScopeSpec &SS, - bool hasTemplateKeyword, - const UnqualifiedId &Name, - ParsedType ObjectType, bool EnteringContext, - TemplateTy &Template, - bool &MemberOfUnknownSpecialization, - bool Disambiguation = false); + TemplateNameKind + isTemplateName(Scope *S, CXXScopeSpec &SS, bool hasTemplateKeyword, + const UnqualifiedId &Name, ParsedType ObjectType, + bool EnteringContext, TemplateTy &Template, + bool &MemberOfUnknownSpecialization, + bool Disambiguation = false, bool MayBeNNS = false); /// Try to resolve an undeclared template name as a type template. /// @@ -11436,12 +11427,11 @@ class Sema final : public SemaBase { /// For example, given "x.MetaFun::template apply", the scope specifier /// \p SS will be "MetaFun::", \p TemplateKWLoc contains the location /// of the "template" keyword, and "apply" is the \p Name. - TemplateNameKind ActOnTemplateName(Scope *S, CXXScopeSpec &SS, - SourceLocation TemplateKWLoc, - const UnqualifiedId &Name, - ParsedType ObjectType, - bool EnteringContext, TemplateTy &Template, - bool AllowInjectedClassName = false); + TemplateNameKind + ActOnTemplateName(Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, + const UnqualifiedId &Name, ParsedType ObjectType, + bool EnteringContext, TemplateTy &Template, + bool AllowInjectedClassName = false, bool MayBeNNS = false); DeclResult ActOnClassTemplateSpecialization( Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, @@ -12089,25 +12079,31 @@ class Sema final : public SemaBase { } }; + class DisableTypoCorrectionRAII { + Sema &SemaRef; + bool PrevDisableTypoCorrection; + + public: + explicit DisableTypoCorrectionRAII(Sema &SemaRef) + : SemaRef(SemaRef), PrevDisableTypoCorrection(std::exchange( + SemaRef.DisableTypoCorrection, true)) {} + + ~DisableTypoCorrectionRAII() { + SemaRef.DisableTypoCorrection = PrevDisableTypoCorrection; + } + }; + /// RAII class used to indicate that we are performing provisional /// semantic analysis to determine the validity of a construct, so /// typo-correction and diagnostics in the immediate context (not within /// implicitly-instantiated templates) should be suppressed. - class TentativeAnalysisScope { - Sema &SemaRef; + class TentativeAnalysisScope : public DisableTypoCorrectionRAII { // FIXME: Using a SFINAETrap for this is a hack. SFINAETrap Trap; - bool PrevDisableTypoCorrection; public: explicit TentativeAnalysisScope(Sema &SemaRef) - : SemaRef(SemaRef), Trap(SemaRef, true), - PrevDisableTypoCorrection(SemaRef.DisableTypoCorrection) { - SemaRef.DisableTypoCorrection = true; - } - ~TentativeAnalysisScope() { - SemaRef.DisableTypoCorrection = PrevDisableTypoCorrection; - } + : DisableTypoCorrectionRAII(SemaRef), Trap(SemaRef, true) {} }; /// For each declaration that involved template argument deduction, the diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index baed1416635432..c01202425f394f 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -8486,8 +8486,14 @@ ExpectedStmt ASTNodeImporter::VisitCXXDependentScopeMemberExpr( auto ToOperatorLoc = importChecked(Err, E->getOperatorLoc()); auto ToQualifierLoc = importChecked(Err, E->getQualifierLoc()); auto ToTemplateKeywordLoc = importChecked(Err, E->getTemplateKeywordLoc()); - auto ToFirstQualifierFoundInScope = - importChecked(Err, E->getFirstQualifierFoundInScope()); + + UnresolvedSet<8> ToUnqualifiedLookups; + for (auto D : E->unqualified_lookups()) + if (auto ToDOrErr = import(D.getDecl())) + ToUnqualifiedLookups.addDecl(*ToDOrErr); + else + return ToDOrErr.takeError(); + if (Err) return std::move(Err); @@ -8521,7 +8527,7 @@ ExpectedStmt ASTNodeImporter::VisitCXXDependentScopeMemberExpr( return CXXDependentScopeMemberExpr::Create( Importer.getToContext(), ToBase, ToType, E->isArrow(), ToOperatorLoc, - ToQualifierLoc, ToTemplateKeywordLoc, ToFirstQualifierFoundInScope, + ToQualifierLoc, ToTemplateKeywordLoc, ToUnqualifiedLookups.pairs(), ToMemberNameInfo, ResInfo); } diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index 0ce129de85f03f..f997ea9a984b01 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1491,19 +1491,26 @@ SourceLocation CXXUnresolvedConstructExpr::getBeginLoc() const { CXXDependentScopeMemberExpr::CXXDependentScopeMemberExpr( const ASTContext &Ctx, Expr *Base, QualType BaseType, bool IsArrow, SourceLocation OperatorLoc, NestedNameSpecifierLoc QualifierLoc, - SourceLocation TemplateKWLoc, NamedDecl *FirstQualifierFoundInScope, + SourceLocation TemplateKWLoc, ArrayRef UnqualifiedLookups, DeclarationNameInfo MemberNameInfo, const TemplateArgumentListInfo *TemplateArgs) : Expr(CXXDependentScopeMemberExprClass, Ctx.DependentTy, VK_LValue, OK_Ordinary), - Base(Base), BaseType(BaseType), QualifierLoc(QualifierLoc), - MemberNameInfo(MemberNameInfo) { + Base(Base), BaseType(BaseType), MemberNameInfo(MemberNameInfo), + OperatorLoc(OperatorLoc) { CXXDependentScopeMemberExprBits.IsArrow = IsArrow; + CXXDependentScopeMemberExprBits.HasQualifier = QualifierLoc.hasQualifier(); + CXXDependentScopeMemberExprBits.NumUnqualifiedLookups = + UnqualifiedLookups.size(); CXXDependentScopeMemberExprBits.HasTemplateKWAndArgsInfo = (TemplateArgs != nullptr) || TemplateKWLoc.isValid(); - CXXDependentScopeMemberExprBits.HasFirstQualifierFoundInScope = - FirstQualifierFoundInScope != nullptr; - CXXDependentScopeMemberExprBits.OperatorLoc = OperatorLoc; + + if (hasQualifier()) + new (getTrailingObjects()) + NestedNameSpecifierLoc(QualifierLoc); + + std::uninitialized_copy(UnqualifiedLookups.begin(), UnqualifiedLookups.end(), + getTrailingObjects()); if (TemplateArgs) { auto Deps = TemplateArgumentDependence::None; @@ -1515,54 +1522,59 @@ CXXDependentScopeMemberExpr::CXXDependentScopeMemberExpr( TemplateKWLoc); } - if (hasFirstQualifierFoundInScope()) - *getTrailingObjects() = FirstQualifierFoundInScope; setDependence(computeDependence(this)); } CXXDependentScopeMemberExpr::CXXDependentScopeMemberExpr( - EmptyShell Empty, bool HasTemplateKWAndArgsInfo, - bool HasFirstQualifierFoundInScope) + EmptyShell Empty, bool HasQualifier, unsigned NumUnqualifiedLookups, + bool HasTemplateKWAndArgsInfo) : Expr(CXXDependentScopeMemberExprClass, Empty) { + CXXDependentScopeMemberExprBits.HasQualifier = HasQualifier; + CXXDependentScopeMemberExprBits.NumUnqualifiedLookups = NumUnqualifiedLookups; CXXDependentScopeMemberExprBits.HasTemplateKWAndArgsInfo = HasTemplateKWAndArgsInfo; - CXXDependentScopeMemberExprBits.HasFirstQualifierFoundInScope = - HasFirstQualifierFoundInScope; } CXXDependentScopeMemberExpr *CXXDependentScopeMemberExpr::Create( const ASTContext &Ctx, Expr *Base, QualType BaseType, bool IsArrow, SourceLocation OperatorLoc, NestedNameSpecifierLoc QualifierLoc, - SourceLocation TemplateKWLoc, NamedDecl *FirstQualifierFoundInScope, + SourceLocation TemplateKWLoc, ArrayRef UnqualifiedLookups, DeclarationNameInfo MemberNameInfo, const TemplateArgumentListInfo *TemplateArgs) { + bool HasQualifier = QualifierLoc.hasQualifier(); + unsigned NumUnqualifiedLookups = UnqualifiedLookups.size(); + assert(!NumUnqualifiedLookups || HasQualifier); bool HasTemplateKWAndArgsInfo = (TemplateArgs != nullptr) || TemplateKWLoc.isValid(); unsigned NumTemplateArgs = TemplateArgs ? TemplateArgs->size() : 0; - bool HasFirstQualifierFoundInScope = FirstQualifierFoundInScope != nullptr; - - unsigned Size = totalSizeToAlloc( - HasTemplateKWAndArgsInfo, NumTemplateArgs, HasFirstQualifierFoundInScope); + unsigned Size = + totalSizeToAlloc( + HasQualifier, NumUnqualifiedLookups, HasTemplateKWAndArgsInfo, + NumTemplateArgs); void *Mem = Ctx.Allocate(Size, alignof(CXXDependentScopeMemberExpr)); return new (Mem) CXXDependentScopeMemberExpr( Ctx, Base, BaseType, IsArrow, OperatorLoc, QualifierLoc, TemplateKWLoc, - FirstQualifierFoundInScope, MemberNameInfo, TemplateArgs); + UnqualifiedLookups, MemberNameInfo, TemplateArgs); } CXXDependentScopeMemberExpr *CXXDependentScopeMemberExpr::CreateEmpty( - const ASTContext &Ctx, bool HasTemplateKWAndArgsInfo, - unsigned NumTemplateArgs, bool HasFirstQualifierFoundInScope) { - assert(NumTemplateArgs == 0 || HasTemplateKWAndArgsInfo); + const ASTContext &Ctx, bool HasQualifier, unsigned NumUnqualifiedLookups, + bool HasTemplateKWAndArgsInfo, unsigned NumTemplateArgs) { + assert(!NumTemplateArgs || HasTemplateKWAndArgsInfo); + assert(!NumUnqualifiedLookups || HasQualifier); - unsigned Size = totalSizeToAlloc( - HasTemplateKWAndArgsInfo, NumTemplateArgs, HasFirstQualifierFoundInScope); + unsigned Size = + totalSizeToAlloc( + HasQualifier, NumUnqualifiedLookups, HasTemplateKWAndArgsInfo, + NumTemplateArgs); void *Mem = Ctx.Allocate(Size, alignof(CXXDependentScopeMemberExpr)); - return new (Mem) CXXDependentScopeMemberExpr( - EmptyShell(), HasTemplateKWAndArgsInfo, HasFirstQualifierFoundInScope); + return new (Mem) CXXDependentScopeMemberExpr(EmptyShell(), HasQualifier, + NumUnqualifiedLookups, + HasTemplateKWAndArgsInfo); } CXXThisExpr *CXXThisExpr::Create(const ASTContext &Ctx, SourceLocation L, diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 27a993a631dae9..0431f203fce7c7 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -593,11 +593,10 @@ class CXXNameMangler { void mangleMemberExprBase(const Expr *base, bool isArrow); void mangleMemberExpr(const Expr *base, bool isArrow, NestedNameSpecifier *qualifier, - NamedDecl *firstQualifierLookup, + ArrayRef UnqualifiedLookups, DeclarationName name, const TemplateArgumentLoc *TemplateArgs, - unsigned NumTemplateArgs, - unsigned knownArity); + unsigned NumTemplateArgs, unsigned knownArity); void mangleCastExpression(const Expr *E, StringRef CastEncoding); void mangleInitListElements(const InitListExpr *InitList); void mangleRequirement(SourceLocation RequiresExprLoc, @@ -4584,14 +4583,11 @@ void CXXNameMangler::mangleMemberExprBase(const Expr *Base, bool IsArrow) { } /// Mangles a member expression. -void CXXNameMangler::mangleMemberExpr(const Expr *base, - bool isArrow, - NestedNameSpecifier *qualifier, - NamedDecl *firstQualifierLookup, - DeclarationName member, - const TemplateArgumentLoc *TemplateArgs, - unsigned NumTemplateArgs, - unsigned arity) { +void CXXNameMangler::mangleMemberExpr( + const Expr *base, bool isArrow, NestedNameSpecifier *qualifier, + ArrayRef UnqualifiedLookups, DeclarationName member, + const TemplateArgumentLoc *TemplateArgs, unsigned NumTemplateArgs, + unsigned arity) { // ::= dt // ::= pt if (base) @@ -5073,11 +5069,9 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, case Expr::MemberExprClass: { NotPrimaryExpr(); const MemberExpr *ME = cast(E); - mangleMemberExpr(ME->getBase(), ME->isArrow(), - ME->getQualifier(), nullptr, - ME->getMemberDecl()->getDeclName(), - ME->getTemplateArgs(), ME->getNumTemplateArgs(), - Arity); + mangleMemberExpr(ME->getBase(), ME->isArrow(), ME->getQualifier(), + std::nullopt, ME->getMemberDecl()->getDeclName(), + ME->getTemplateArgs(), ME->getNumTemplateArgs(), Arity); break; } @@ -5085,10 +5079,9 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, NotPrimaryExpr(); const UnresolvedMemberExpr *ME = cast(E); mangleMemberExpr(ME->isImplicitAccess() ? nullptr : ME->getBase(), - ME->isArrow(), ME->getQualifier(), nullptr, - ME->getMemberName(), - ME->getTemplateArgs(), ME->getNumTemplateArgs(), - Arity); + ME->isArrow(), ME->getQualifier(), std::nullopt, + ME->getMemberName(), ME->getTemplateArgs(), + ME->getNumTemplateArgs(), Arity); break; } @@ -5098,10 +5091,8 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, = cast(E); mangleMemberExpr(ME->isImplicitAccess() ? nullptr : ME->getBase(), ME->isArrow(), ME->getQualifier(), - ME->getFirstQualifierFoundInScope(), - ME->getMember(), - ME->getTemplateArgs(), ME->getNumTemplateArgs(), - Arity); + ME->unqualified_lookups(), ME->getMember(), + ME->getTemplateArgs(), ME->getNumTemplateArgs(), Arity); break; } diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 4570a18bc0d5e5..a5d49499f23ea1 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1160,8 +1160,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, case tok::annot_non_type_dependent: case tok::annot_non_type_undeclared: { CXXScopeSpec SS; - Token Replacement; - Res = tryParseCXXIdExpression(SS, isAddressOfOperand, Replacement); + Res = tryParseCXXIdExpression(SS, isAddressOfOperand); assert(!Res.isUnset() && "should not perform typo correction on annotation token"); break; @@ -2364,10 +2363,9 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { } if (!LHS.isInvalid()) - LHS = Actions.ActOnMemberAccessExpr(getCurScope(), LHS.get(), OpLoc, - OpKind, SS, TemplateKWLoc, Name, - CurParsedObjCImpl ? CurParsedObjCImpl->Dcl - : nullptr); + LHS = Actions.ActOnMemberAccessExpr( + getCurScope(), LHS.get(), OpLoc, OpKind, SS, TemplateKWLoc, Name, + CurParsedObjCImpl ? CurParsedObjCImpl->Dcl : nullptr); if (!LHS.isInvalid()) { if (Tok.is(tok::less)) checkPotentialAngleBracket(LHS); diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 03a58048e53a94..441e22966c23b9 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -100,7 +100,8 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType, bool MemberOfUnknownSpecialization; if (!Actions.isTemplateName(getCurScope(), SS, /*hasTemplateKeyword=*/false, TemplateName, ObjectType, EnteringContext, - Template, MemberOfUnknownSpecialization)) + Template, MemberOfUnknownSpecialization, + /*Disambiguation=*/false, /*MayBeNNS=*/true)) return; FixDigraph(*this, PP, Next, SecondToken, tok::unknown, @@ -353,7 +354,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier( TemplateTy Template; TemplateNameKind TNK = Actions.ActOnTemplateName( getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType, - EnteringContext, Template, /*AllowInjectedClassName*/ true); + EnteringContext, Template, /*AllowInjectedClassName=*/true, + /*MayBeNNS=*/true); if (AnnotateTemplateIdToken(Template, TNK, SS, TemplateKWLoc, TemplateName, false)) return true; @@ -532,13 +534,14 @@ bool Parser::ParseOptionalCXXScopeSpecifier( getCurScope(), SS, /*hasTemplateKeyword=*/false, TemplateName, ObjectType, EnteringContext, Template, MemberOfUnknownSpecialization, - Disambiguation)) { + Disambiguation, + /*MayBeNNS=*/true)) { // If lookup didn't find anything, we treat the name as a template-name // anyway. C++20 requires this, and in prior language modes it improves // error recovery. But before we commit to this, check that we actually // have something that looks like a template-argument-list next. - if (!IsTypename && TNK == TNK_Undeclared_template && - isTemplateArgumentList(1) == TPResult::False) + if (!IsTypename && (ObjectType || TNK == TNK_Undeclared_template) && + isTemplateArgumentList(1, TNK) == TPResult::False) break; // We have found a template name, so annotate this token @@ -557,7 +560,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier( if (MemberOfUnknownSpecialization && !Disambiguation && (ObjectType || SS.isSet()) && - (IsTypename || isTemplateArgumentList(1) == TPResult::True)) { + (IsTypename || + isTemplateArgumentList(1, TNK_Non_template) == TPResult::True)) { // If we had errors before, ObjectType can be dependent even without any // templates. Do not report missing template keyword in that case. if (!ObjectHadErrors) { @@ -565,11 +569,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier( // member of an unknown specialization. However, this will only // parse correctly as a template, so suggest the keyword 'template' // before 'getAs' and treat this as a dependent template name. - unsigned DiagID = diag::err_missing_dependent_template_keyword; - if (getLangOpts().MicrosoftExt) - DiagID = diag::warn_missing_dependent_template_keyword; - - Diag(Tok.getLocation(), DiagID) + Diag(Tok.getLocation(), diag::ext_missing_dependent_template_keyword) << II.getName() << FixItHint::CreateInsertion(Tok.getLocation(), "template "); } @@ -603,7 +603,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier( ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS, bool isAddressOfOperand, - Token &Replacement) { + Token *Replacement) { ExprResult E; // We may have already annotated this id-expression. @@ -654,10 +654,10 @@ ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS, if (isAddressOfOperand && isPostfixExpressionSuffixStart()) isAddressOfOperand = false; - E = Actions.ActOnIdExpression( - getCurScope(), SS, TemplateKWLoc, Name, Tok.is(tok::l_paren), - isAddressOfOperand, /*CCC=*/nullptr, /*IsInlineAsmIdentifier=*/false, - &Replacement); + E = Actions.ActOnIdExpression(getCurScope(), SS, TemplateKWLoc, Name, + Tok.is(tok::l_paren), isAddressOfOperand, + /*CCC=*/nullptr, + /*IsInlineAsmIdentifier=*/false, Replacement); break; } @@ -747,12 +747,12 @@ ExprResult Parser::ParseCXXIdExpression(bool isAddressOfOperand) { Token Replacement; ExprResult Result = - tryParseCXXIdExpression(SS, isAddressOfOperand, Replacement); + tryParseCXXIdExpression(SS, isAddressOfOperand, &Replacement); if (Result.isUnset()) { // If the ExprResult is valid but null, then typo correction suggested a // keyword replacement that needs to be reparsed. UnconsumeToken(Replacement); - Result = tryParseCXXIdExpression(SS, isAddressOfOperand, Replacement); + Result = tryParseCXXIdExpression(SS, isAddressOfOperand); } assert(!Result.isUnset() && "Typo correction suggested a keyword replacement " "for a previous keyword suggestion"); @@ -1923,11 +1923,11 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc, // void f(auto *p) { p->~X(); } // ... but there's no ambiguity, and nowhere to write 'template' in such an // example, so we accept it anyway. - if (Tok.is(tok::less) && - ParseUnqualifiedIdTemplateId( - SS, ObjectType, Base && Base->containsErrors(), SourceLocation(), - Name, NameLoc, false, SecondTypeName, - /*AssumeTemplateId=*/true)) + if (Tok.is(tok::less) && ParseUnqualifiedIdTemplateId( + SS, ObjectType, Base && Base->containsErrors(), + /*TemplateKWLoc=*/SourceLocation(), TildeLoc, + Name, NameLoc, false, SecondTypeName, + /*AssumeTemplateId=*/true)) return ExprError(); return Actions.ActOnPseudoDestructorExpr(getCurScope(), Base, OpLoc, OpKind, @@ -2539,8 +2539,9 @@ bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS, DeclaratorContext Context) { /// \returns true if a parse error occurred, false otherwise. bool Parser::ParseUnqualifiedIdTemplateId( CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors, - SourceLocation TemplateKWLoc, IdentifierInfo *Name, SourceLocation NameLoc, - bool EnteringContext, UnqualifiedId &Id, bool AssumeTemplateId) { + SourceLocation TemplateKWLoc, SourceLocation TildeLoc, IdentifierInfo *Name, + SourceLocation NameLoc, bool EnteringContext, UnqualifiedId &Id, + bool AssumeTemplateId) { assert(Tok.is(tok::less) && "Expected '<' to finish parsing a template-id"); TemplateTy Template; @@ -2554,22 +2555,23 @@ bool Parser::ParseUnqualifiedIdTemplateId( // this template-id is used to form a nested-name-specifier or not. TNK = Actions.ActOnTemplateName(getCurScope(), SS, TemplateKWLoc, Id, ObjectType, EnteringContext, Template, - /*AllowInjectedClassName*/ true); + /*AllowInjectedClassName=*/true, + TildeLoc.isValid()); } else { bool MemberOfUnknownSpecialization; - TNK = Actions.isTemplateName(getCurScope(), SS, - TemplateKWLoc.isValid(), Id, - ObjectType, EnteringContext, Template, - MemberOfUnknownSpecialization); + TNK = Actions.isTemplateName( + getCurScope(), SS, TemplateKWLoc.isValid(), Id, ObjectType, + EnteringContext, Template, MemberOfUnknownSpecialization, + /*Disambiguation=*/false, TildeLoc.isValid()); // If lookup found nothing but we're assuming that this is a template // name, double-check that makes sense syntactically before committing // to it. if (TNK == TNK_Undeclared_template && - isTemplateArgumentList(0) == TPResult::False) + isTemplateArgumentList(0, TNK) == TPResult::False) return false; if (TNK == TNK_Non_template && MemberOfUnknownSpecialization && - ObjectType && isTemplateArgumentList(0) == TPResult::True) { + ObjectType && isTemplateArgumentList(0, TNK) == TPResult::True) { // If we had errors before, ObjectType can be dependent even without any // templates, do not report missing template keyword in that case. if (!ObjectHadErrors) { @@ -2587,13 +2589,13 @@ bool Parser::ParseUnqualifiedIdTemplateId( else Name += Id.Identifier->getName(); } - Diag(Id.StartLocation, diag::err_missing_dependent_template_keyword) + Diag(Id.StartLocation, diag::ext_missing_dependent_template_keyword) << Name << FixItHint::CreateInsertion(Id.StartLocation, "template "); } TNK = Actions.ActOnTemplateName( getCurScope(), SS, TemplateKWLoc, Id, ObjectType, EnteringContext, - Template, /*AllowInjectedClassName*/ true); + Template, /*AllowInjectedClassName=*/true, TildeLoc.isValid()); } else if (TNK == TNK_Non_template) { return false; } @@ -2618,14 +2620,16 @@ bool Parser::ParseUnqualifiedIdTemplateId( bool MemberOfUnknownSpecialization; TemplateName.setIdentifier(Name, NameLoc); if (ObjectType) { - TNK = Actions.ActOnTemplateName( - getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType, - EnteringContext, Template, /*AllowInjectedClassName*/ true); + TNK = Actions.ActOnTemplateName(getCurScope(), SS, TemplateKWLoc, + TemplateName, ObjectType, EnteringContext, + Template, /*AllowInjectedClassName=*/true, + /*MayBeNNS=*/true); } else { TNK = Actions.isTemplateName(getCurScope(), SS, TemplateKWLoc.isValid(), - TemplateName, ObjectType, - EnteringContext, Template, - MemberOfUnknownSpecialization); + TemplateName, ObjectType, EnteringContext, + Template, MemberOfUnknownSpecialization, + /*Disambiguation=*/false, + /*MayBeNNS=*/true); if (TNK == TNK_Non_template && !Id.DestructorName.get()) { Diag(NameLoc, diag::err_destructor_template_id) @@ -2687,7 +2691,7 @@ bool Parser::ParseUnqualifiedIdTemplateId( if (Id.getKind() == UnqualifiedIdKind::IK_ConstructorName) Id.setConstructorName(Type.get(), NameLoc, RAngleLoc); else - Id.setDestructorName(Id.StartLocation, Type.get(), RAngleLoc); + Id.setDestructorName(TildeLoc, Type.get(), RAngleLoc); return false; } @@ -3035,8 +3039,9 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType, if (Tok.is(tok::less)) return ParseUnqualifiedIdTemplateId( SS, ObjectType, ObjectHadErrors, - TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), Id, IdLoc, - EnteringContext, Result, TemplateSpecified); + TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), + /*TildeLoc=*/SourceLocation(), Id, IdLoc, EnteringContext, Result, + TemplateSpecified); if (TemplateSpecified) { TemplateNameKind TNK = @@ -3131,13 +3136,15 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType, Tok.is(tok::less)) return ParseUnqualifiedIdTemplateId( SS, ObjectType, ObjectHadErrors, - TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), nullptr, - SourceLocation(), EnteringContext, Result, TemplateSpecified); + TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), + /*TildeLoc=*/SourceLocation(), /*Name=*/nullptr, + /*NameLoc=*/SourceLocation(), EnteringContext, Result, + TemplateSpecified); else if (TemplateSpecified && Actions.ActOnTemplateName( getCurScope(), SS, *TemplateKWLoc, Result, ObjectType, EnteringContext, Template, - /*AllowInjectedClassName*/ true) == TNK_Non_template) + /*AllowInjectedClassName=*/true) == TNK_Non_template) return true; return false; @@ -3227,8 +3234,8 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType, Result.setDestructorName(TildeLoc, nullptr, ClassNameLoc); return ParseUnqualifiedIdTemplateId( SS, ObjectType, ObjectHadErrors, - TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), ClassName, - ClassNameLoc, EnteringContext, Result, TemplateSpecified); + TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), TildeLoc, + ClassName, ClassNameLoc, EnteringContext, Result, TemplateSpecified); } // Note that this is a destructor name. diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index b91928063169ee..6bd8cf55dbae32 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -3103,12 +3103,10 @@ OMPClause *Parser::ParseOpenMPUsesAllocatorClause(OpenMPDirectiveKind DKind) { SmallVector Data; do { CXXScopeSpec SS; - Token Replacement; ExprResult Allocator = getLangOpts().CPlusPlus ? ParseCXXIdExpression() - : tryParseCXXIdExpression(SS, /*isAddressOfOperand=*/false, - Replacement); + : tryParseCXXIdExpression(SS, /*isAddressOfOperand=*/false); if (Allocator.isInvalid()) { SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end, StopBeforeMatch); diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index b2cb3d71e09f95..8f1f9ae2c1820b 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -1791,6 +1791,42 @@ void Parser::checkPotentialAngleBracket(ExprResult &PotentialTemplateName) { Priority); } +bool Parser::isMissingTemplateKeywordBeforeScope(bool AnnotateInvalid) { + assert(Tok.is(tok::coloncolon)); + Sema::DisableTypoCorrectionRAII DTC(Actions); + ColonProtectionRAIIObject ColonProtection(*this); + + SourceLocation StartLoc = Tok.getLocation(); + if (TryAnnotateTypeOrScopeToken()) + return true; + if (Tok.isSimpleTypeSpecifier(getLangOpts())) + return false; + CXXScopeSpec SS; + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr, + /*ObjectHasErrors=*/false, + /*EnteringContext=*/false); + ExprResult Result = tryParseCXXIdExpression(SS, /*isAddressOfOperand=*/false); + + if (!AnnotateInvalid && Result.isInvalid()) + return true; + + SourceLocation EndLoc = Tok.getLocation(); + if (PP.isBacktrackEnabled()) { + PP.RevertCachedTokens(1); + // if (Result.isInvalid()) + EndLoc = PP.getLastCachedTokenLocation(); + } else { + PP.EnterToken(Tok, /*IsReinject=*/true); + } + + Tok.setKind(tok::annot_primary_expr); + setExprAnnotation(Tok, Result); + Tok.setLocation(StartLoc); + Tok.setAnnotationEndLoc(EndLoc); + PP.AnnotateCachedTokens(Tok); + return Result.isInvalid(); +} + bool Parser::checkPotentialAngleBracketDelimiter( const AngleBracketTracker::Loc &LAngle, const Token &OpToken) { // If a comma in an expression context is followed by a type that can be a @@ -1814,6 +1850,16 @@ bool Parser::checkPotentialAngleBracketDelimiter( return true; } + if (OpToken.is(tok::greater) && Tok.is(tok::coloncolon) && + !NextToken().isOneOf(tok::kw_new, tok::kw_delete) && + isMissingTemplateKeywordBeforeScope(/*AnnotateInvalid=*/true)) { + Actions.diagnoseExprIntendedAsTemplateName( + getCurScope(), LAngle.TemplateName, LAngle.LessLoc, + OpToken.getLocation()); + AngleBrackets.clear(*this); + return true; + } + // After a '>' (etc), we're no longer potentially in a construct that's // intended to be treated as a template-id. if (OpToken.is(tok::greater) || diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index ff27ef70944a47..d2a54d844ddd7b 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -13,6 +13,7 @@ #include "clang/Basic/DiagnosticParse.h" #include "clang/Parse/Parser.h" +#include "clang/Parse/RAIIObjectsForParser.h" #include "clang/Sema/ParsedTemplate.h" using namespace clang; @@ -954,7 +955,7 @@ Parser::TPResult Parser::TryParsePtrOperatorSeq() { /// literal-operator-id: /// 'operator' string-literal identifier /// 'operator' user-defined-string-literal -Parser::TPResult Parser::TryParseOperatorId() { +Parser::TPResult Parser::TryParseNonConversionOperatorId() { assert(Tok.is(tok::kw_operator)); ConsumeToken(); @@ -1012,6 +1013,12 @@ Parser::TPResult Parser::TryParseOperatorId() { return TPResult::True; } + return TPResult::False; +} +Parser::TPResult Parser::TryParseOperatorId() { + if (TPResult TPR = TryParseNonConversionOperatorId(); TPR != TPResult::False) + return TPR; + // Maybe this is a conversion-function-id. bool AnyDeclSpecifiers = false; while (true) { @@ -2306,7 +2313,8 @@ Parser::TPResult Parser::TryParseBracketDeclarator() { /// of a template-id or simple-template-id, rather than a less-than comparison. /// This will often fail and produce an ambiguity, but should never be wrong /// if it returns True or False. -Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) { +Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip, + TemplateNameKind TNK) { if (!TokensToSkip) { if (Tok.isNot(tok::less)) return TPResult::False; @@ -2324,6 +2332,183 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) { if (!TryConsumeToken(tok::less)) return TPResult::False; + auto TrySkipTemplateArgument = [&]() { + bool NextIsTemplateId = false; + unsigned TemplateDepth = 0; + while (true) { + switch (Tok.getKind()) { + case tok::eof: + case tok::annot_module_begin: + case tok::annot_module_end: + case tok::annot_module_include: + case tok::annot_repl_input_end: + case tok::semi: + return TPResult::False; + + case tok::greatergreatergreater: + if (TemplateDepth) + --TemplateDepth; + [[fallthrough]]; + case tok::greatergreater: + if (TemplateDepth) + --TemplateDepth; + [[fallthrough]]; + case tok::greater: + if (TemplateDepth) { + ConsumeToken(); + --TemplateDepth; + break; + } + [[fallthrough]]; + case tok::comma: + return TPResult::True; + + case tok::l_paren: + ConsumeParen(); + if (!SkipUntil(tok::r_paren, StopAtSemi)) + return TPResult::Error; + break; + + case tok::l_brace: + ConsumeBrace(); + if (!SkipUntil(tok::r_brace, StopAtSemi)) + return TPResult::Error; + break; + + case tok::l_square: + ConsumeBracket(); + if (!SkipUntil(tok::r_square, StopAtSemi)) + return TPResult::Error; + break; + + case tok::question: + ConsumeToken(); + if (!SkipUntil(tok::colon, StopAtSemi)) + return TPResult::Error; + break; + + case tok::kw_template: + ConsumeToken(); + NextIsTemplateId = true; + continue; + + case tok::identifier: + ConsumeToken(); + if (Tok.is(tok::less)) { + if (!NextIsTemplateId) + return TPResult::Ambiguous; + ConsumeToken(); + ++TemplateDepth; + } + break; + + case tok::kw_operator: + if (TPResult TPR = TryParseNonConversionOperatorId(); + TPR == TPResult::Error) { + return TPResult::Error; + } else if (TPR == TPResult::True) { + if (Tok.is(tok::less)) { + if (!NextIsTemplateId) + return TPResult::Ambiguous; + ConsumeToken(); + ++TemplateDepth; + } + } + break; + + case tok::kw_const_cast: + case tok::kw_dynamic_cast: + case tok::kw_reinterpret_cast: + case tok::kw_static_cast: { + ConsumeToken(); + if (!TryConsumeToken(tok::less)) + return TPResult::Error; + bool MayHaveTrailingReturnType = Tok.is(tok::kw_auto); + + while (true) { + TPResult TPR = + isCXXDeclarationSpecifier(ImplicitTypenameContext::Yes); + if (TPR == TPResult::False) + break; + if (TPR == TPResult::Error || + TryConsumeDeclarationSpecifier() == TPResult::Error) + return TPResult::Error; + } + + if (TryParseDeclarator( + /*mayBeAbstract=*/true, + /*mayHaveIdentifier=*/false, + /*mayHaveDirectInit=*/false, + /*mayHaveTrailingReturnType=*/MayHaveTrailingReturnType) == + TPResult::Error) + return TPResult::Error; + + if (!TryConsumeToken(tok::greater)) + return TPResult::Error; + break; + } + default: + ConsumeAnyToken(); + break; + } + NextIsTemplateId = false; + } + }; + + bool IsNestedTemplateArgumentList = !GreaterThanIsOperator; + GreaterThanIsOperatorScope G(GreaterThanIsOperator, false); + + while (true) { + // An expression cannot be followed by a braced-init-list unless + // its the right operand of an assignment operator. + if (Tok.is(tok::l_brace)) + return TPResult::True; + + if (TryAnnotateOptionalCXXScopeToken()) + return TPResult::Error; + + bool InvalidAsTemplateArgumentList = false; + TPResult TPR = + isCXXDeclarationSpecifier(ImplicitTypenameContext::No, + /*BracedCastResult=*/TPResult::Ambiguous, + &InvalidAsTemplateArgumentList); + if (InvalidAsTemplateArgumentList) + return TPResult::False; + + if (TPR == TPResult::True) + return TPResult::True; + + if (IsNestedTemplateArgumentList || TNK != TNK_Non_template) + break; + + if (TPR == TPResult::Ambiguous) + TryConsumeDeclarationSpecifier(); + + TPR = TrySkipTemplateArgument(); + + if (TPR == TPResult::Error) + return TPResult::Error; + else if (TPR == TPResult::False) + return TPResult::False; + + if (!TryConsumeToken(tok::comma)) { + if (Tok.is(tok::less)) + break; + if (TryConsumeToken(tok::greater) && Tok.is(tok::coloncolon) && + !NextToken().isOneOf(tok::kw_new, tok::kw_delete)) { + TentativeParsingAction TPA(*this, /*Unannotated=*/true); + Sema::TentativeAnalysisScope TAS(Actions); + if (isMissingTemplateKeywordBeforeScope(/*AnnotateInvalid=*/false)) { + TPA.Revert(); + return TPResult::True; + } + TPA.Commit(); + } + return TPResult::Ambiguous; + } + } + +#if 0 // We can't do much to tell an expression apart from a template-argument, // but one good distinguishing factor is that a "decl-specifier" not // followed by '(' or '{' can't appear in an expression. @@ -2334,6 +2519,7 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) { return TPResult::True; if (InvalidAsTemplateArgumentList) return TPResult::False; +#endif // FIXME: In many contexts, X can only be a // template-argument-list. But that's not true in general: diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 36e56a92c3092e..4874c80c63c8e2 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1821,7 +1821,7 @@ Parser::TryAnnotateName(CorrectionCandidateCallback *CCC, // we interpret this as a template-id if it can be, but if it can't be, then // this is an error recovery case. if (Classification.getKind() == Sema::NC_UndeclaredTemplate && - isTemplateArgumentList(1) == TPResult::False) { + isTemplateArgumentList(1, TNK_Undeclared_template) == TPResult::False) { // It's not a template-id; re-classify without the '<' as a hint. Token FakeNext = Next; FakeNext.setKind(tok::unknown); @@ -2190,7 +2190,7 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec( // Only annotate an undeclared template name as a template-id if the // following tokens have the form of a template argument list. if (TNK != TNK_Undeclared_template || - isTemplateArgumentList(1) != TPResult::False) { + isTemplateArgumentList(1, TNK) != TPResult::False) { // Consume the identifier. ConsumeToken(); if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(), diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index f04f7f9929442c..3a0f82d59c2417 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -355,29 +355,41 @@ bool Sema::isAcceptableNestedNameSpecifier(const NamedDecl *SD, return false; } -NamedDecl *Sema::FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS) { - if (!S || !NNS) - return nullptr; +/// If the given nested-name-specifier begins with a bare identifier +/// (e.g., Base::), perform name lookup for that identifier as a +/// nested-name-specifier within the given scope, and return the result of that +/// name lookup. +bool Sema::LookupFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS, + UnresolvedSetImpl &R) { + if (!S) + return false; while (NNS->getPrefix()) NNS = NNS->getPrefix(); - if (NNS->getKind() != NestedNameSpecifier::Identifier) - return nullptr; - - LookupResult Found(*this, NNS->getAsIdentifier(), SourceLocation(), - LookupNestedNameSpecifierName); + // FIXME: This is a rather nasty hack! Ideally we should get the results + // from LookupTemplateName/BuildCXXNestedNameSpecifier. + const IdentifierInfo *II = NNS->getAsIdentifier(); + if (!II) { + if (const auto *DTST = + dyn_cast_if_present( + NNS->getAsType())) + II = DTST->getIdentifier(); + else + return false; + } + assert(II && "Missing first qualifier in scope"); + LookupResult Found(*this, II, SourceLocation(), + NNS->getAsIdentifier() ? LookupNestedNameSpecifierName + : LookupOrdinaryName); LookupName(Found, S); - assert(!Found.isAmbiguous() && "Cannot handle ambiguities here yet"); - if (!Found.isSingleResult()) - return nullptr; - - NamedDecl *Result = Found.getFoundDecl(); - if (isAcceptableNestedNameSpecifier(Result)) - return Result; + if (Found.empty()) + return false; - return nullptr; + R.addAllDecls(Found.asUnresolvedSet().pairs()); + Found.suppressDiagnostics(); + return true; } namespace { @@ -406,112 +418,82 @@ class NestedNameSpecifierValidatorCCC final bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, bool EnteringContext, CXXScopeSpec &SS, - NamedDecl *ScopeLookupResult, bool ErrorRecoveryLookup, bool *IsCorrectedToColon, bool OnlyNamespace) { if (IdInfo.Identifier->isEditorPlaceholder()) return true; + if (IsCorrectedToColon) + *IsCorrectedToColon = false; + + QualType ObjectType = GetTypeFromParser(IdInfo.ObjectType); LookupResult Found(*this, IdInfo.Identifier, IdInfo.IdentifierLoc, OnlyNamespace ? LookupNamespaceName : LookupNestedNameSpecifierName); - QualType ObjectType = GetTypeFromParser(IdInfo.ObjectType); - // Determine where to perform name lookup - DeclContext *LookupCtx = nullptr; - bool isDependent = false; - if (IsCorrectedToColon) - *IsCorrectedToColon = false; - if (!ObjectType.isNull()) { - // This nested-name-specifier occurs in a member access expression, e.g., - // x->B::f, and we are looking into the type of the object. - assert(!SS.isSet() && "ObjectType and scope specifier cannot coexist"); - LookupCtx = computeDeclContext(ObjectType); - isDependent = ObjectType->isDependentType(); - } else if (SS.isSet()) { - // This nested-name-specifier occurs after another nested-name-specifier, - // so look into the context associated with the prior nested-name-specifier. - LookupCtx = computeDeclContext(SS, EnteringContext); - isDependent = isDependentScopeSpecifier(SS); - Found.setContextRange(SS.getRange()); + // C++ [basic.lookup.qual.general]p3: + // Qualified name lookup in a class, namespace, or enumeration performs a + // search of the scope associated with it except as specified below. + LookupParsedName(Found, S, &SS, ObjectType, + /*AllowBuiltinCreation=*/false, EnteringContext); + + // C++ [basic.lookup.qual.general]p3: + // [...] Unless otherwise specified, a qualified name undergoes qualified + // name lookup in its lookup context from the point where it appears unless + // the lookup context either is dependent and is not the current + // instantiation or is not a class or class template. + if (Found.wasNotFoundInCurrentInstantiation()) { + // Don't speculate if we're just trying to improve error recovery. + if (ErrorRecoveryLookup) + return true; + + // The lookup context is dependent and either: + // - it is not the current instantiation, or + // - it is the current instantiation, it has at least one dependent base + // class, and qualified lookup found nothing. + // Build a dependent nested-name-specifier. We will lookup the name again + // during instantiation. + SS.Extend(Context, IdInfo.Identifier, IdInfo.IdentifierLoc, IdInfo.CCLoc); + return false; } bool ObjectTypeSearchedInScope = false; - if (LookupCtx) { - // Perform "qualified" name lookup into the declaration context we - // computed, which is either the type of the base of a member access - // expression or the declaration context associated with a prior - // nested-name-specifier. - - // The declaration context must be complete. - if (!LookupCtx->isDependentContext() && - RequireCompleteDeclContext(SS, LookupCtx)) - return true; - - LookupQualifiedName(Found, LookupCtx); - if (!ObjectType.isNull() && Found.empty()) { - // C++ [basic.lookup.classref]p4: - // If the id-expression in a class member access is a qualified-id of - // the form - // - // class-name-or-namespace-name::... - // - // the class-name-or-namespace-name following the . or -> operator is - // looked up both in the context of the entire postfix-expression and in - // the scope of the class of the object expression. If the name is found - // only in the scope of the class of the object expression, the name - // shall refer to a class-name. If the name is found only in the - // context of the entire postfix-expression, the name shall refer to a - // class-name or namespace-name. [...] - // - // Qualified name lookup into a class will not find a namespace-name, - // so we do not need to diagnose that case specifically. However, - // this qualified name lookup may find nothing. In that case, perform - // unqualified name lookup in the given scope (if available) or - // reconstruct the result from when name lookup was performed at template - // definition time. - if (S) - LookupName(Found, S); - else if (ScopeLookupResult) - Found.addDecl(ScopeLookupResult); - - ObjectTypeSearchedInScope = true; + // C++ [basic.lookup.qual.general]p2: + // A member-qualified name is the (unique) component name, if any, of + // - an unqualified-id or + // - a nested-name-specifier of the form type-name :: or namespace-name :: + // in the id-expression of a class member access expression. + // + // C++ [basic.lookup.qual.general]p3: + // [...] If nothing is found by qualified lookup for a member-qualified + // name that is the terminal name of a nested-name-specifier and is not + // dependent, it undergoes unqualified lookup. + // + // In 'x.A::B::y', 'A' will undergo unqualified lookup if qualified lookup + // in the type of 'x' finds nothing. If the lookup context is dependent, + // we perform the unqualified lookup in the template definition context + // and store the results so we can replicate the lookup during instantiation. + if (Found.empty() && !ObjectType.isNull()) { + if (S) { + LookupName(Found, S); + } else if (!SS.getUnqualifiedLookups().empty()) { + Found.addAllDecls(SS.getUnqualifiedLookups()); + Found.resolveKind(); } - } else if (!isDependent) { - // Perform unqualified name lookup in the current scope. - LookupName(Found, S); + ObjectTypeSearchedInScope = true; } if (Found.isAmbiguous()) return true; - // If we performed lookup into a dependent context and did not find anything, - // that's fine: just build a dependent nested-name-specifier. - if (Found.empty() && isDependent && - !(LookupCtx && LookupCtx->isRecord() && - (!cast(LookupCtx)->hasDefinition() || - !cast(LookupCtx)->hasAnyDependentBases()))) { - // Don't speculate if we're just trying to improve error recovery. - if (ErrorRecoveryLookup) - return true; - - // We were not able to compute the declaration context for a dependent - // base object type or prior nested-name-specifier, so this - // nested-name-specifier refers to an unknown specialization. Just build - // a dependent nested-name-specifier. - SS.Extend(Context, IdInfo.Identifier, IdInfo.IdentifierLoc, IdInfo.CCLoc); - return false; - } - if (Found.empty() && !ErrorRecoveryLookup) { // If identifier is not found as class-name-or-namespace-name, but is found // as other entity, don't look for typos. LookupResult R(*this, Found.getLookupNameInfo(), LookupOrdinaryName); - if (LookupCtx) - LookupQualifiedName(R, LookupCtx); - else if (S && !isDependent) - LookupName(R, S); + LookupParsedName(R, S, &SS, ObjectType, + /*AllowBuiltinCreation=*/false, EnteringContext); + if (!R.empty()) { // Don't diagnose problems with this speculative lookup. R.suppressDiagnostics(); @@ -538,6 +520,11 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, } } + DeclContext *LookupCtx = + SS.isSet() + ? computeDeclContext(SS, EnteringContext) + : (!ObjectType.isNull() ? computeDeclContext(ObjectType) : nullptr); + if (Found.empty() && !ErrorRecoveryLookup && !getLangOpts().MSVCCompat) { // We haven't found anything, and we're not recovering from a // different kind of error, so look for typos. @@ -593,14 +580,14 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, // scope, reconstruct the result from the template instantiation itself. // // Note that C++11 does *not* perform this redundant lookup. - NamedDecl *OuterDecl; + NamedDecl *OuterDecl = nullptr; if (S) { LookupResult FoundOuter(*this, IdInfo.Identifier, IdInfo.IdentifierLoc, LookupNestedNameSpecifierName); LookupName(FoundOuter, S); OuterDecl = FoundOuter.getAsSingle(); - } else - OuterDecl = ScopeLookupResult; + } else if (!SS.getUnqualifiedLookups().empty()) + OuterDecl = SS.getUnqualifiedLookups().front().getDecl(); if (isAcceptableNestedNameSpecifier(OuterDecl) && OuterDecl->getCanonicalDecl() != SD->getCanonicalDecl() && @@ -697,14 +684,6 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, if (ErrorRecoveryLookup) return true; - // If we didn't find anything during our lookup, try again with - // ordinary name lookup, which can help us produce better error - // messages. - if (Found.empty()) { - Found.clear(LookupOrdinaryName); - LookupName(Found, S); - } - // In Microsoft mode, if we are within a templated function and we can't // resolve Identifier, then extend the SS with Identifier. This will have // the effect of resolving Identifier during template instantiation. @@ -778,7 +757,7 @@ bool Sema::ActOnCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, return true; return BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS, - /*ScopeLookupResult=*/nullptr, false, + /*ErrorRecoveryLookup=*/false, IsCorrectedToColon, OnlyNamespace); } @@ -839,7 +818,7 @@ bool Sema::IsInvalidUnlessNestedName(Scope *S, CXXScopeSpec &SS, return false; return !BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS, - /*ScopeLookupResult=*/nullptr, true); + /*ErrorRecoveryLookup=*/true); } bool Sema::ActOnCXXNestedNameSpecifier(Scope *S, diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp index 3a22097152df56..98a0b3214db2ce 100644 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -304,8 +304,8 @@ static ExprResult buildMemberCall(Sema &S, Expr *Base, SourceLocation Loc, // FIXME: Fix BuildMemberReferenceExpr to take a const CXXScopeSpec&. CXXScopeSpec SS; ExprResult Result = S.BuildMemberReferenceExpr( - Base, Base->getType(), Loc, /*IsPtr=*/false, SS, - SourceLocation(), nullptr, NameInfo, /*TemplateArgs=*/nullptr, + Base, Base->getType(), Loc, /*IsPtr=*/false, SS, SourceLocation(), + NameInfo, /*TemplateArgs=*/nullptr, /*Scope=*/nullptr); if (Result.isInvalid()) return ExprError(); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 26041e53de5061..a1e9f5cb9c16f5 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -1272,9 +1272,11 @@ static bool checkTupleLikeDecomposition(Sema &S, if (UseMemberGet) { // if [lookup of member get] finds at least one declaration, the // initializer is e.get(). - E = S.BuildMemberReferenceExpr(E.get(), DecompType, Loc, false, - CXXScopeSpec(), SourceLocation(), nullptr, - MemberGet, &Args, nullptr); + E = S.BuildMemberReferenceExpr(E.get(), DecompType, Loc, + /*IsArrow=*/false, + /*SS=*/CXXScopeSpec(), + /*TemplateKWLoc=*/SourceLocation(), + MemberGet, &Args, /*S=*/nullptr); if (E.isInvalid()) return true; @@ -4894,16 +4896,12 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, MemberLookup.addDecl(Indirect ? cast(Indirect) : cast(Field), AS_public); MemberLookup.resolveKind(); - ExprResult CtorArg - = SemaRef.BuildMemberReferenceExpr(MemberExprBase, - ParamType, Loc, - /*IsArrow=*/false, - SS, - /*TemplateKWLoc=*/SourceLocation(), - /*FirstQualifierInScope=*/nullptr, - MemberLookup, - /*TemplateArgs=*/nullptr, - /*S*/nullptr); + ExprResult CtorArg = SemaRef.BuildMemberReferenceExpr( + MemberExprBase, ParamType, Loc, + /*IsArrow=*/false, SS, + /*TemplateKWLoc=*/SourceLocation(), MemberLookup, + /*TemplateArgs=*/nullptr, + /*S=*/nullptr); if (CtorArg.isInvalid()) return true; @@ -14394,8 +14392,10 @@ class MemberBuilder: public ExprBuilder { public: Expr *build(Sema &S, SourceLocation Loc) const override { return assertNotNull(S.BuildMemberReferenceExpr( - Builder.build(S, Loc), Type, Loc, IsArrow, SS, SourceLocation(), - nullptr, MemberLookup, nullptr, nullptr).get()); + Builder.build(S, Loc), Type, Loc, IsArrow, SS, + /*TemplateKwLoc=*/SourceLocation(), MemberLookup, + /*TemplateArgs=*/nullptr, /*S=*/nullptr) + .get()); } MemberBuilder(const ExprBuilder &Builder, QualType Type, bool IsArrow, @@ -14601,13 +14601,11 @@ buildSingleCopyAssignRecursively(Sema &S, SourceLocation Loc, QualType T, Loc); // Create the reference to operator=. - ExprResult OpEqualRef - = S.BuildMemberReferenceExpr(To.build(S, Loc), T, Loc, /*IsArrow=*/false, - SS, /*TemplateKWLoc=*/SourceLocation(), - /*FirstQualifierInScope=*/nullptr, - OpLookup, - /*TemplateArgs=*/nullptr, /*S*/nullptr, - /*SuppressQualifierCheck=*/true); + ExprResult OpEqualRef = S.BuildMemberReferenceExpr( + To.build(S, Loc), T, Loc, /*IsArrow=*/false, SS, + /*TemplateKWLoc=*/SourceLocation(), OpLookup, + /*TemplateArgs=*/nullptr, /*S*/ nullptr, + /*SuppressQualifierCheck=*/true); if (OpEqualRef.isInvalid()) return StmtError(); @@ -17213,8 +17211,9 @@ bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message, auto BuildExpr = [&](LookupResult &LR) { ExprResult Res = BuildMemberReferenceExpr( - Message, Message->getType(), Message->getBeginLoc(), false, - CXXScopeSpec(), SourceLocation(), nullptr, LR, nullptr, nullptr); + Message, Message->getType(), Message->getBeginLoc(), /*IsArrow=*/false, + /*SS=*/CXXScopeSpec(), /*TemplateKWLoc=*/SourceLocation(), LR, + /*TemplateArgs=*/nullptr, /*S=*/nullptr); if (Res.isInvalid()) return ExprError(); Res = BuildCallExpr(nullptr, Res.get(), Loc, {}, Loc, nullptr, false, true); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index dcf495b700540f..2e7a8e9121d8ff 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -2652,7 +2652,7 @@ recoverFromMSUnqualifiedLookup(Sema &S, ASTContext &Context, return CXXDependentScopeMemberExpr::Create( Context, /*This=*/nullptr, ThisType, /*IsArrow=*/true, /*Op=*/SourceLocation(), NestedNameSpecifierLoc(), TemplateKWLoc, - /*FirstQualifierFoundInScope=*/nullptr, NameInfo, TemplateArgs); + /*UnqualifiedLookups=*/std::nullopt, NameInfo, TemplateArgs); } // Synthesize a fake NNS that points to the derived class. This will diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index c32df607692813..b858214acc3c02 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -551,11 +551,9 @@ static Decl *FindGetterSetterNameDecl(const ObjCObjectPointerType *QIdTy, } ExprResult -Sema::ActOnDependentMemberExpr(Expr *BaseExpr, QualType BaseType, - bool IsArrow, SourceLocation OpLoc, - const CXXScopeSpec &SS, +Sema::ActOnDependentMemberExpr(Expr *BaseExpr, QualType BaseType, bool IsArrow, + SourceLocation OpLoc, const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, - NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo, const TemplateArgumentListInfo *TemplateArgs) { // Even in dependent contexts, try to diagnose base expressions with @@ -589,8 +587,8 @@ Sema::ActOnDependentMemberExpr(Expr *BaseExpr, QualType BaseType, // must have pointer type, and the accessed type is the pointee. return CXXDependentScopeMemberExpr::Create( Context, BaseExpr, BaseType, IsArrow, OpLoc, - SS.getWithLocInContext(Context), TemplateKWLoc, FirstQualifierInScope, - NameInfo, TemplateArgs); + SS.getWithLocInContext(Context), TemplateKWLoc, + SS.getUnqualifiedLookups(), NameInfo, TemplateArgs); } /// We know that the given qualified member reference points only to @@ -766,8 +764,9 @@ static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, R.addDecl(ND); R.resolveKind(); return SemaRef.BuildMemberReferenceExpr( - BaseExpr, BaseExpr->getType(), OpLoc, IsArrow, SS, SourceLocation(), - nullptr, R, nullptr, nullptr); + BaseExpr, BaseExpr->getType(), OpLoc, IsArrow, SS, + /*TemplateKWLoc=*/SourceLocation(), R, /*TemplateArgs=*/nullptr, + /*S=*/nullptr); }, Sema::CTK_ErrorRecovery, DC); @@ -783,7 +782,7 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, ExprResult Sema::BuildMemberReferenceExpr( Expr *Base, QualType BaseType, SourceLocation OpLoc, bool IsArrow, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, - NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo, + const DeclarationNameInfo &NameInfo, const TemplateArgumentListInfo *TemplateArgs, const Scope *S, ActOnMemberAccessExtraArgs *ExtraArgs) { LookupResult R(*this, NameInfo, LookupMemberName); @@ -827,10 +826,9 @@ ExprResult Sema::BuildMemberReferenceExpr( if (SS.isInvalid()) return ExprError(); - return BuildMemberReferenceExpr(Base, BaseType, - OpLoc, IsArrow, SS, TemplateKWLoc, - FirstQualifierInScope, R, TemplateArgs, S, - false, ExtraArgs); + return BuildMemberReferenceExpr(Base, BaseType, OpLoc, IsArrow, SS, + TemplateKWLoc, R, TemplateArgs, S, + /*SuppressQualifierCheck=*/false, ExtraArgs); } ExprResult @@ -968,17 +966,11 @@ static bool IsInFnTryBlockHandler(const Scope *S) { return false; } -ExprResult -Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, - SourceLocation OpLoc, bool IsArrow, - const CXXScopeSpec &SS, - SourceLocation TemplateKWLoc, - NamedDecl *FirstQualifierInScope, - LookupResult &R, - const TemplateArgumentListInfo *TemplateArgs, - const Scope *S, - bool SuppressQualifierCheck, - ActOnMemberAccessExtraArgs *ExtraArgs) { +ExprResult Sema::BuildMemberReferenceExpr( + Expr *BaseExpr, QualType BaseExprType, SourceLocation OpLoc, bool IsArrow, + const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, LookupResult &R, + const TemplateArgumentListInfo *TemplateArgs, const Scope *S, + bool SuppressQualifierCheck, ActOnMemberAccessExtraArgs *ExtraArgs) { assert(!SS.isInvalid() && "nested-name-specifier cannot be invalid"); // If the member wasn't found in the current instantiation, or if the // arrow operator was used with a dependent non-pointer object expression, @@ -988,8 +980,8 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, (SS.isSet() ? SS.getScopeRep()->isDependent() : BaseExprType->isDependentType()))) return ActOnDependentMemberExpr(BaseExpr, BaseExprType, IsArrow, OpLoc, SS, - TemplateKWLoc, FirstQualifierInScope, - R.getLookupNameInfo(), TemplateArgs); + TemplateKWLoc, R.getLookupNameInfo(), + TemplateArgs); QualType BaseType = BaseExprType; if (IsArrow) { @@ -1194,9 +1186,9 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, // Non-dependent member, but dependent template arguments. if (!VDecl.get()) - return ActOnDependentMemberExpr( - BaseExpr, BaseExpr->getType(), IsArrow, OpLoc, SS, TemplateKWLoc, - FirstQualifierInScope, MemberNameInfo, TemplateArgs); + return ActOnDependentMemberExpr(BaseExpr, BaseExpr->getType(), IsArrow, + OpLoc, SS, TemplateKWLoc, MemberNameInfo, + TemplateArgs); VarDecl *Var = cast(VDecl.get()); if (!Var->getTemplateSpecializationKind()) @@ -1760,17 +1752,17 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base, // Decompose the name into its component parts. DeclarationNameInfo NameInfo; const TemplateArgumentListInfo *TemplateArgs; - DecomposeUnqualifiedId(Id, TemplateArgsBuffer, - NameInfo, TemplateArgs); - + DecomposeUnqualifiedId(Id, TemplateArgsBuffer, NameInfo, TemplateArgs); bool IsArrow = (OpKind == tok::arrow); if (getLangOpts().HLSL && IsArrow) return ExprError(Diag(OpLoc, diag::err_hlsl_operator_unsupported) << 2); - NamedDecl *FirstQualifierInScope - = (!SS.isSet() ? nullptr : FindFirstQualifierInScope(S, SS.getScopeRep())); - + UnresolvedSet<4> UnqualifiedLookups; + if (SS.isValid() && + LookupFirstQualifierInScope(S, SS.getScopeRep(), UnqualifiedLookups)) { + SS.setUnqualifiedLookups(UnqualifiedLookups.pairs()); + } // This is a postfix expression, so get rid of ParenListExprs. ExprResult Result = MaybeConvertParenListExprToParenExpr(S, Base); if (Result.isInvalid()) return ExprError(); @@ -1778,8 +1770,8 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base, ActOnMemberAccessExtraArgs ExtraArgs = {S, Id, ObjCImpDecl}; ExprResult Res = BuildMemberReferenceExpr( - Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc, - FirstQualifierInScope, NameInfo, TemplateArgs, S, &ExtraArgs); + Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc, NameInfo, + TemplateArgs, S, &ExtraArgs); if (!Res.isInvalid() && isa(Res.get())) CheckMemberAccessOfNoDeref(cast(Res.get())); @@ -1923,9 +1915,8 @@ Sema::BuildImplicitMemberExpr(const CXXScopeSpec &SS, baseExpr = BuildCXXThisExpr(loc, ThisTy, /*IsImplicit=*/true); } - return BuildMemberReferenceExpr( - baseExpr, ThisTy, - /*OpLoc=*/SourceLocation(), - /*IsArrow=*/!getLangOpts().HLSL, SS, TemplateKWLoc, - /*FirstQualifierInScope=*/nullptr, R, TemplateArgs, S); + return BuildMemberReferenceExpr(baseExpr, ThisTy, + /*OpLoc=*/SourceLocation(), + /*IsArrow=*/!getLangOpts().HLSL, SS, + TemplateKWLoc, R, TemplateArgs, S); } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index a239f2c6e88e4b..f9b67a187ea145 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -16135,13 +16135,11 @@ Sema::BuildForRangeBeginEndCall(SourceLocation Loc, CandidateSet->clear(OverloadCandidateSet::CSK_Normal); if (!MemberLookup.empty()) { - ExprResult MemberRef = - BuildMemberReferenceExpr(Range, Range->getType(), Loc, - /*IsPtr=*/false, CXXScopeSpec(), - /*TemplateKWLoc=*/SourceLocation(), - /*FirstQualifierInScope=*/nullptr, - MemberLookup, - /*TemplateArgs=*/nullptr, S); + ExprResult MemberRef = BuildMemberReferenceExpr( + Range, Range->getType(), Loc, + /*IsPtr=*/false, /*SS=*/CXXScopeSpec(), + /*TemplateKWLoc=*/SourceLocation(), MemberLookup, + /*TemplateArgs=*/nullptr, S); if (MemberRef.isInvalid()) { *CallExpr = ExprError(); return FRS_DiagnosticIssued; diff --git a/clang/lib/Sema/SemaStmtAsm.cpp b/clang/lib/Sema/SemaStmtAsm.cpp index 0b272b806391c4..de989023d68a8c 100644 --- a/clang/lib/Sema/SemaStmtAsm.cpp +++ b/clang/lib/Sema/SemaStmtAsm.cpp @@ -893,7 +893,8 @@ Sema::LookupInlineAsmVarDeclField(Expr *E, StringRef Member, return CXXDependentScopeMemberExpr::Create( Context, E, T, /*IsArrow=*/false, AsmLoc, NestedNameSpecifierLoc(), SourceLocation(), - /*FirstQualifierFoundInScope=*/nullptr, NameInfo, /*TemplateArgs=*/nullptr); + /*UnqualifiedLookups=*/std::nullopt, NameInfo, + /*TemplateArgs=*/nullptr); } const RecordType *RT = T->getAs(); @@ -916,8 +917,9 @@ Sema::LookupInlineAsmVarDeclField(Expr *E, StringRef Member, // Make an Expr to thread through OpDecl. ExprResult Result = BuildMemberReferenceExpr( - E, E->getType(), AsmLoc, /*IsArrow=*/false, CXXScopeSpec(), - SourceLocation(), nullptr, FieldResult, nullptr, nullptr); + E, E->getType(), AsmLoc, /*IsArrow=*/false, /*SS=*/CXXScopeSpec(), + /*TemplateKWLoc*/ SourceLocation(), FieldResult, + /*TemplateArgs=*/nullptr, /*S=*/nullptr); return Result; } diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index f6ca06fefb4912..737b9ee7d91e95 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -170,15 +170,12 @@ bool Sema::hasAnyAcceptableTemplateNames(LookupResult &R, return false; } -TemplateNameKind Sema::isTemplateName(Scope *S, - CXXScopeSpec &SS, - bool hasTemplateKeyword, - const UnqualifiedId &Name, - ParsedType ObjectTypePtr, - bool EnteringContext, - TemplateTy &TemplateResult, - bool &MemberOfUnknownSpecialization, - bool Disambiguation) { +TemplateNameKind +Sema::isTemplateName(Scope *S, CXXScopeSpec &SS, bool hasTemplateKeyword, + const UnqualifiedId &Name, ParsedType ObjectTypePtr, + bool EnteringContext, TemplateTy &TemplateResult, + bool &MemberOfUnknownSpecialization, bool Disambiguation, + bool MayBeNNS) { assert(getLangOpts().CPlusPlus && "No template names in C!"); DeclarationName TName; @@ -209,7 +206,7 @@ TemplateNameKind Sema::isTemplateName(Scope *S, if (LookupTemplateName(R, S, SS, ObjectType, EnteringContext, /*RequiredTemplate=*/SourceLocation(), &AssumedTemplate, - /*AllowTypoCorrection=*/!Disambiguation)) + /*AllowTypoCorrection=*/!Disambiguation, MayBeNNS)) return TNK_Non_template; MemberOfUnknownSpecialization = R.wasNotFoundInCurrentInstantiation(); @@ -376,7 +373,7 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS, QualType ObjectType, bool EnteringContext, RequiredTemplateKind RequiredTemplate, AssumedTemplateKind *ATK, - bool AllowTypoCorrection) { + bool AllowTypoCorrection, bool MayBeNNS) { if (ATK) *ATK = AssumedTemplateKind::None; @@ -385,92 +382,89 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS, Found.setTemplateNameLookup(true); - // Determine where to perform name lookup - DeclContext *LookupCtx = nullptr; - bool IsDependent = false; - if (!ObjectType.isNull()) { - // This nested-name-specifier occurs in a member access expression, e.g., - // x->B::f, and we are looking into the type of the object. - assert(SS.isEmpty() && "ObjectType and scope specifier cannot coexist"); - LookupCtx = computeDeclContext(ObjectType); - IsDependent = !LookupCtx && ObjectType->isDependentType(); - assert((IsDependent || !ObjectType->isIncompleteType() || - !ObjectType->getAs() || - ObjectType->castAs()->isBeingDefined()) && - "Caller should have completed object type"); - - // Template names cannot appear inside an Objective-C class or object type - // or a vector type. - // - // FIXME: This is wrong. For example: - // - // template using Vec = T __attribute__((ext_vector_type(4))); - // Vec vi; - // vi.Vec::~Vec(); - // - // ... should be accepted but we will not treat 'Vec' as a template name - // here. The right thing to do would be to check if the name is a valid - // vector component name, and look up a template name if not. And similarly - // for lookups into Objective-C class and object types, where the same - // problem can arise. - if (ObjectType->isObjCObjectOrInterfaceType() || - ObjectType->isVectorType()) { - Found.clear(); - return false; - } - } else if (SS.isNotEmpty()) { - // This nested-name-specifier occurs after another nested-name-specifier, - // so long into the context associated with the prior nested-name-specifier. - LookupCtx = computeDeclContext(SS, EnteringContext); - IsDependent = !LookupCtx && isDependentScopeSpecifier(SS); - - // The declaration context must be complete. - if (LookupCtx && RequireCompleteDeclContext(SS, LookupCtx)) - return true; + // Template names cannot appear inside an Objective-C class or object type + // or a vector type. + // + // FIXME: This is wrong. For example: + // + // template using Vec = T __attribute__((ext_vector_type(4))); + // Vec vi; + // vi.Vec::~Vec(); + // + // ... should be accepted but we will not treat 'Vec' as a template name + // here. The right thing to do would be to check if the name is a valid + // vector component name, and look up a template name if not. And similarly + // for lookups into Objective-C class and object types, where the same + // problem can arise. + if (!ObjectType.isNull() && (ObjectType->isVectorType() || + ObjectType->isObjCObjectOrInterfaceType())) { + Found.clear(); + return false; } + LookupParsedName(Found, S, &SS, ObjectType, + /*AllowBuiltinCreation=*/false, EnteringContext); + + // C++ [basic.lookup.qual.general]p3: + // [...] Unless otherwise specified, a qualified name undergoes qualified + // name lookup in its lookup context from the point where it appears unless + // the lookup context either is dependent and is not the current + // instantiation or is not a class or class template. + // + // The lookup context is dependent and either: + // - it is not the current instantiation, or + // - it is the current instantiation, it has at least one dependent base + // class, and qualified lookup found nothing. + // + // If this is a member-qualified name that is the terminal name of a + // nested-name-specifier, we perform unqualified lookup and store the results + // so we can replicate the lookup during instantiation. The results of the + // unqualified loookup are *not* used to determine whether '<' is interpreted + // as the delimiter of a template-argument-list. + // + // For example: + // + // template + // struct A { + // int x; + // }; + // + // template + // using B = A; + // + // template + // void f(A a, A b) { + // a.B::x; // error: missing 'template' before 'B' + // b.B::x; // ok, lookup context is not dependent + // } + if (Found.wasNotFoundInCurrentInstantiation()) + return false; + bool ObjectTypeSearchedInScope = false; - bool AllowFunctionTemplatesInLookup = true; - if (LookupCtx) { - // Perform "qualified" name lookup into the declaration context we - // computed, which is either the type of the base of a member access - // expression or the declaration context associated with a prior - // nested-name-specifier. - LookupQualifiedName(Found, LookupCtx); - - // FIXME: The C++ standard does not clearly specify what happens in the - // case where the object type is dependent, and implementations vary. In - // Clang, we treat a name after a . or -> as a template-name if lookup - // finds a non-dependent member or member of the current instantiation that - // is a type template, or finds no such members and lookup in the context - // of the postfix-expression finds a type template. In the latter case, the - // name is nonetheless dependent, and we may resolve it to a member of an - // unknown specialization when we come to instantiate the template. - IsDependent |= Found.wasNotFoundInCurrentInstantiation(); - } - - if (SS.isEmpty() && (ObjectType.isNull() || Found.empty())) { - // C++ [basic.lookup.classref]p1: - // In a class member access expression (5.2.5), if the . or -> token is - // immediately followed by an identifier followed by a <, the - // identifier must be looked up to determine whether the < is the - // beginning of a template argument list (14.2) or a less-than operator. - // The identifier is first looked up in the class of the object - // expression. If the identifier is not found, it is then looked up in - // the context of the entire postfix-expression and shall name a class - // template. - if (S) - LookupName(Found, S); - if (!ObjectType.isNull()) { - // FIXME: We should filter out all non-type templates here, particularly - // variable templates and concepts. But the exclusion of alias templates - // and template template parameters is a wording defect. - AllowFunctionTemplatesInLookup = false; - ObjectTypeSearchedInScope = true; + // C++ [basic.lookup.qual.general]p2: + // A member-qualified name is the (unique) component name, if any, of + // - an unqualified-id or + // - a nested-name-specifier of the form type-name :: or namespace-name :: + // in the id-expression of a class member access expression. + // + // C++ [basic.lookup.qual.general]p3: + // [...] If nothing is found by qualified lookup for a member-qualified + // name that is the terminal name of a nested-name-specifier and is not + // dependent, it undergoes unqualified lookup. + // + // In 'x.A::B::y', 'A' will undergo unqualified lookup if qualified lookup + // in the type of 'x' finds nothing. If the lookup context is dependent, + // we perform the unqualified lookup in the template definition context + // and store the results so we can replicate the lookup during instantiation. + if (MayBeNNS && Found.empty() && !ObjectType.isNull()) { + if (S) { + LookupName(Found, S); + } else if (!SS.getUnqualifiedLookups().empty()) { + Found.addAllDecls(SS.getUnqualifiedLookups()); + Found.resolveKind(); } - - IsDependent |= Found.wasNotFoundInCurrentInstantiation(); + ObjectTypeSearchedInScope = true; } if (Found.isAmbiguous()) @@ -490,7 +484,7 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS, getLangOpts().CPlusPlus20 && llvm::all_of(Found, [](NamedDecl *ND) { return isa(ND->getUnderlyingDecl()); }); - if (AllFunctions || (Found.empty() && !IsDependent)) { + if (AllFunctions || Found.empty()) { // If lookup found any functions, or if this is a name that can only be // used for a function, then strongly assume this is a function // template-id. @@ -502,11 +496,15 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS, } } - if (Found.empty() && !IsDependent && AllowTypoCorrection) { + if (Found.empty() && AllowTypoCorrection) { // If we did not find any names, and this is not a disambiguation, attempt // to correct any typos. DeclarationName Name = Found.getLookupName(); Found.clear(); + DeclContext *LookupCtx = + SS.isSet() + ? computeDeclContext(SS, EnteringContext) + : (!ObjectType.isNull() ? computeDeclContext(ObjectType) : nullptr); // Simple filter callback that, for keywords, only accepts the C++ *_cast DefaultFilterCCC FilterCCC{}; FilterCCC.WantTypeSpecifiers = false; @@ -539,16 +537,11 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS, NamedDecl *ExampleLookupResult = Found.empty() ? nullptr : Found.getRepresentativeDecl(); - FilterAcceptableTemplateNames(Found, AllowFunctionTemplatesInLookup); + FilterAcceptableTemplateNames(Found); if (Found.empty()) { - if (IsDependent) { - Found.setNotFoundInCurrentInstantiation(); - return false; - } - - // If a 'template' keyword was used, a lookup that finds only non-template - // names is an error. if (ExampleLookupResult && RequiredTemplate) { + // If a 'template' keyword was used, a lookup that finds only non-template + // names is an error. Diag(Found.getNameLoc(), diag::err_template_kw_refers_to_non_template) << Found.getLookupName() << SS.getRange() << RequiredTemplate.hasTemplateKeyword() @@ -737,7 +730,7 @@ Sema::ActOnDependentIdExpression(const CXXScopeSpec &SS, /*IsArrow=*/!Context.getLangOpts().HLSL, /*OperatorLoc=*/SourceLocation(), /*QualifierLoc=*/NestedNameSpecifierLoc(), TemplateKWLoc, - /*FirstQualifierFoundInScope=*/nullptr, NameInfo, TemplateArgs); + /*UnqualifiedLookups=*/std::nullopt, NameInfo, TemplateArgs); } return BuildDependentDeclRefExpr(SS, TemplateKWLoc, NameInfo, TemplateArgs); } @@ -4676,14 +4669,10 @@ ExprResult Sema::BuildQualifiedTemplateIdExpr( return BuildTemplateIdExpr(SS, TemplateKWLoc, R, /*ADL=*/false, TemplateArgs); } -TemplateNameKind Sema::ActOnTemplateName(Scope *S, - CXXScopeSpec &SS, - SourceLocation TemplateKWLoc, - const UnqualifiedId &Name, - ParsedType ObjectType, - bool EnteringContext, - TemplateTy &Result, - bool AllowInjectedClassName) { +TemplateNameKind Sema::ActOnTemplateName( + Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, + const UnqualifiedId &Name, ParsedType ObjectType, bool EnteringContext, + TemplateTy &Result, bool AllowInjectedClassName, bool MayBeNNS) { if (TemplateKWLoc.isValid() && S && !S->getTemplateParamParent()) Diag(TemplateKWLoc, getLangOpts().CPlusPlus11 ? @@ -4718,9 +4707,10 @@ TemplateNameKind Sema::ActOnTemplateName(Scope *S, // "template" keyword is now permitted). We follow the C++0x // rules, even in C++03 mode with a warning, retroactively applying the DR. bool MemberOfUnknownSpecialization; - TemplateNameKind TNK = isTemplateName(S, SS, TemplateKWLoc.isValid(), Name, - ObjectType, EnteringContext, Result, - MemberOfUnknownSpecialization); + TemplateNameKind TNK = + isTemplateName(S, SS, TemplateKWLoc.isValid(), Name, ObjectType, + EnteringContext, Result, MemberOfUnknownSpecialization, + /*Disambiguation=*/false, MayBeNNS); if (TNK != TNK_Non_template) { // We resolved this to a (non-dependent) template name. Return it. auto *LookupRD = dyn_cast_or_null(LookupCtx); @@ -4759,7 +4749,8 @@ TemplateNameKind Sema::ActOnTemplateName(Scope *S, ? RequiredTemplateKind(TemplateKWLoc) : TemplateNameIsRequired; if (!LookupTemplateName(R, S, SS, ObjectType.get(), EnteringContext, RTK, - /*ATK=*/nullptr, /*AllowTypoCorrection=*/false) && + /*ATK=*/nullptr, /*AllowTypoCorrection=*/false, + MayBeNNS) && !R.isAmbiguous()) { if (LookupCtx) Diag(Name.getBeginLoc(), diag::err_no_member) diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 4d3d1c5a85bb63..eb8112dcbfc019 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1530,12 +1530,11 @@ namespace { NestedNameSpecifierLoc QualifierLoc, QualType T); - TemplateName - TransformTemplateName(CXXScopeSpec &SS, TemplateName Name, - SourceLocation NameLoc, - QualType ObjectType = QualType(), - NamedDecl *FirstQualifierInScope = nullptr, - bool AllowInjectedClassName = false); + TemplateName TransformTemplateName(CXXScopeSpec &SS, TemplateName Name, + SourceLocation NameLoc, + QualType ObjectType = QualType(), + bool AllowInjectedClassName = false, + bool MayBeNNS = false); const AnnotateAttr *TransformAnnotateAttr(const AnnotateAttr *AA); const CXXAssumeAttr *TransformCXXAssumeAttr(const CXXAssumeAttr *AA); @@ -2027,8 +2026,7 @@ TemplateInstantiator::RebuildElaboratedType(SourceLocation KeywordLoc, TemplateName TemplateInstantiator::TransformTemplateName( CXXScopeSpec &SS, TemplateName Name, SourceLocation NameLoc, - QualType ObjectType, NamedDecl *FirstQualifierInScope, - bool AllowInjectedClassName) { + QualType ObjectType, bool AllowInjectedClassName, bool MayBeNNS) { if (TemplateTemplateParmDecl *TTP = dyn_cast_or_null(Name.getAsTemplateDecl())) { if (TTP->getDepth() < TemplateArgs.getNumLevels()) { @@ -2102,8 +2100,7 @@ TemplateName TemplateInstantiator::TransformTemplateName( } return inherited::TransformTemplateName(SS, Name, NameLoc, ObjectType, - FirstQualifierInScope, - AllowInjectedClassName); + AllowInjectedClassName, MayBeNNS); } ExprResult diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 1465bba87724b9..1d6d6704404826 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -545,10 +545,9 @@ class TreeTransform { /// By default, transforms all of the types and declarations within the /// nested-name-specifier. Subclasses may override this function to provide /// alternate behavior. - NestedNameSpecifierLoc - TransformNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS, - QualType ObjectType = QualType(), - NamedDecl *FirstQualifierInScope = nullptr); + NestedNameSpecifierLoc TransformNestedNameSpecifierLoc( + NestedNameSpecifierLoc NNS, QualType ObjectType = QualType(), + ArrayRef UnqualifiedLookups = std::nullopt); /// Transform the given declaration name. /// @@ -589,12 +588,11 @@ class TreeTransform { /// By default, transforms the template name by transforming the declarations /// and nested-name-specifiers that occur within the template name. /// Subclasses may override this function to provide alternate behavior. - TemplateName - TransformTemplateName(CXXScopeSpec &SS, TemplateName Name, - SourceLocation NameLoc, - QualType ObjectType = QualType(), - NamedDecl *FirstQualifierInScope = nullptr, - bool AllowInjectedClassName = false); + TemplateName TransformTemplateName(CXXScopeSpec &SS, TemplateName Name, + SourceLocation NameLoc, + QualType ObjectType = QualType(), + bool AllowInjectedClassName = false, + bool MayBeNNS = false); /// Transform the given template argument. /// @@ -1142,8 +1140,8 @@ class TreeTransform { CXXScopeSpec SS; SS.Adopt(QualifierLoc); TemplateName InstName = getDerived().RebuildTemplateName( - SS, TemplateKWLoc, *Name, NameLoc, QualType(), nullptr, - AllowInjectedClassName); + SS, TemplateKWLoc, *Name, NameLoc, QualType(), AllowInjectedClassName, + /*MayBeNNS=*/false); if (InstName.isNull()) return QualType(); @@ -1314,8 +1312,7 @@ class TreeTransform { SourceLocation TemplateKWLoc, const IdentifierInfo &Name, SourceLocation NameLoc, QualType ObjectType, - NamedDecl *FirstQualifierInScope, - bool AllowInjectedClassName); + bool AllowInjectedClassName, bool MayBeNNS); /// Build a new template name given a nested name specifier and the /// overloaded operator name that is referred to as a template. @@ -2883,15 +2880,14 @@ class TreeTransform { /// /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. - ExprResult RebuildMemberExpr(Expr *Base, SourceLocation OpLoc, - bool isArrow, - NestedNameSpecifierLoc QualifierLoc, - SourceLocation TemplateKWLoc, - const DeclarationNameInfo &MemberNameInfo, - ValueDecl *Member, - NamedDecl *FoundDecl, - const TemplateArgumentListInfo *ExplicitTemplateArgs, - NamedDecl *FirstQualifierInScope) { + ExprResult + RebuildMemberExpr(Expr *Base, SourceLocation OpLoc, bool isArrow, + NestedNameSpecifierLoc QualifierLoc, + SourceLocation TemplateKWLoc, + const DeclarationNameInfo &MemberNameInfo, + ValueDecl *Member, NamedDecl *FoundDecl, + const TemplateArgumentListInfo *ExplicitTemplateArgs, + ArrayRef UnqualifiedLookups) { ExprResult BaseResult = getSema().PerformMemberExprBaseConversion(Base, isArrow); if (!Member->getDeclName()) { @@ -2928,6 +2924,7 @@ class TreeTransform { CXXScopeSpec SS; SS.Adopt(QualifierLoc); + SS.setUnqualifiedLookups(UnqualifiedLookups); Base = BaseResult.get(); if (Base->containsErrors()) @@ -2960,10 +2957,9 @@ class TreeTransform { } return getSema().BuildMemberReferenceExpr(Base, BaseType, OpLoc, isArrow, - SS, TemplateKWLoc, - FirstQualifierInScope, - R, ExplicitTemplateArgs, - /*S*/nullptr); + SS, TemplateKWLoc, R, + ExplicitTemplateArgs, + /*S=*/nullptr); } /// Build a new binary operator expression. @@ -3036,10 +3032,9 @@ class TreeTransform { CXXScopeSpec SS; DeclarationNameInfo NameInfo(&Accessor, AccessorLoc); return getSema().BuildMemberReferenceExpr( - Base, Base->getType(), OpLoc, IsArrow, SS, SourceLocation(), - /*FirstQualifierInScope*/ nullptr, NameInfo, - /* TemplateArgs */ nullptr, - /*S*/ nullptr); + Base, Base->getType(), OpLoc, IsArrow, SS, + /*TemplateKWLoc=*/SourceLocation(), NameInfo, + /*TemplateArgs=*/nullptr, /*S=*/nullptr); } /// Build a new initializer list expression. @@ -3606,46 +3601,37 @@ class TreeTransform { /// /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. - ExprResult RebuildCXXDependentScopeMemberExpr(Expr *BaseE, - QualType BaseType, - bool IsArrow, - SourceLocation OperatorLoc, - NestedNameSpecifierLoc QualifierLoc, - SourceLocation TemplateKWLoc, - NamedDecl *FirstQualifierInScope, - const DeclarationNameInfo &MemberNameInfo, - const TemplateArgumentListInfo *TemplateArgs) { + ExprResult RebuildCXXDependentScopeMemberExpr( + Expr *BaseE, QualType BaseType, bool IsArrow, SourceLocation OperatorLoc, + NestedNameSpecifierLoc QualifierLoc, SourceLocation TemplateKWLoc, + ArrayRef UnqualifiedLookups, + const DeclarationNameInfo &MemberNameInfo, + const TemplateArgumentListInfo *TemplateArgs) { CXXScopeSpec SS; SS.Adopt(QualifierLoc); + SS.setUnqualifiedLookups(UnqualifiedLookups); - return SemaRef.BuildMemberReferenceExpr(BaseE, BaseType, - OperatorLoc, IsArrow, - SS, TemplateKWLoc, - FirstQualifierInScope, - MemberNameInfo, - TemplateArgs, /*S*/nullptr); + return SemaRef.BuildMemberReferenceExpr( + BaseE, BaseType, OperatorLoc, IsArrow, SS, TemplateKWLoc, + MemberNameInfo, TemplateArgs, /*S=*/nullptr); } /// Build a new member reference expression. /// /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. - ExprResult RebuildUnresolvedMemberExpr(Expr *BaseE, QualType BaseType, - SourceLocation OperatorLoc, - bool IsArrow, - NestedNameSpecifierLoc QualifierLoc, - SourceLocation TemplateKWLoc, - NamedDecl *FirstQualifierInScope, - LookupResult &R, - const TemplateArgumentListInfo *TemplateArgs) { + ExprResult RebuildUnresolvedMemberExpr( + Expr *BaseE, QualType BaseType, SourceLocation OperatorLoc, bool IsArrow, + NestedNameSpecifierLoc QualifierLoc, SourceLocation TemplateKWLoc, + ArrayRef UnqualifiedLookups, LookupResult &R, + const TemplateArgumentListInfo *TemplateArgs) { CXXScopeSpec SS; SS.Adopt(QualifierLoc); + SS.setUnqualifiedLookups(UnqualifiedLookups); - return SemaRef.BuildMemberReferenceExpr(BaseE, BaseType, - OperatorLoc, IsArrow, - SS, TemplateKWLoc, - FirstQualifierInScope, - R, TemplateArgs, /*S*/nullptr); + return SemaRef.BuildMemberReferenceExpr(BaseE, BaseType, OperatorLoc, + IsArrow, SS, TemplateKWLoc, R, + TemplateArgs, /*S=*/nullptr); } /// Build a new noexcept expression. @@ -3864,10 +3850,8 @@ class TreeTransform { DeclarationNameInfo NameInfo(Ivar->getDeclName(), IvarLoc); ExprResult Result = getSema().BuildMemberReferenceExpr( BaseArg, BaseArg->getType(), - /*FIXME:*/ IvarLoc, IsArrow, SS, SourceLocation(), - /*FirstQualifierInScope=*/nullptr, NameInfo, - /*TemplateArgs=*/nullptr, - /*S=*/nullptr); + /*FIXME:*/ IvarLoc, IsArrow, SS, /*TemplateKWLoc=*/SourceLocation(), + NameInfo, /*TemplateArgs=*/nullptr, /*S=*/nullptr); if (IsFreeIvar && Result.isUsable()) cast(Result.get())->setIsFreeIvar(IsFreeIvar); return Result; @@ -3882,14 +3866,12 @@ class TreeTransform { SourceLocation PropertyLoc) { CXXScopeSpec SS; DeclarationNameInfo NameInfo(Property->getDeclName(), PropertyLoc); - return getSema().BuildMemberReferenceExpr(BaseArg, BaseArg->getType(), - /*FIXME:*/PropertyLoc, - /*IsArrow=*/false, - SS, SourceLocation(), - /*FirstQualifierInScope=*/nullptr, - NameInfo, - /*TemplateArgs=*/nullptr, - /*S=*/nullptr); + return getSema().BuildMemberReferenceExpr( + BaseArg, BaseArg->getType(), + /*FIXME:*/ PropertyLoc, + /*IsArrow=*/false, SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo, + /*TemplateArgs=*/nullptr, + /*S=*/nullptr); } /// Build a new Objective-C property reference expression. @@ -3916,13 +3898,11 @@ class TreeTransform { SourceLocation OpLoc, bool IsArrow) { CXXScopeSpec SS; DeclarationNameInfo NameInfo(&getSema().Context.Idents.get("isa"), IsaLoc); - return getSema().BuildMemberReferenceExpr(BaseArg, BaseArg->getType(), - OpLoc, IsArrow, - SS, SourceLocation(), - /*FirstQualifierInScope=*/nullptr, - NameInfo, - /*TemplateArgs=*/nullptr, - /*S=*/nullptr); + return getSema().BuildMemberReferenceExpr( + BaseArg, BaseArg->getType(), OpLoc, IsArrow, SS, + /*TemplateKWLoc=*/SourceLocation(), NameInfo, + /*TemplateArgs=*/nullptr, + /*S=*/nullptr); } /// Build a new shuffle vector expression. @@ -4115,18 +4095,14 @@ class TreeTransform { } private: - TypeLoc TransformTypeInObjectScope(TypeLoc TL, - QualType ObjectType, - NamedDecl *FirstQualifierInScope, + TypeLoc TransformTypeInObjectScope(TypeLoc TL, QualType ObjectType, CXXScopeSpec &SS); TypeSourceInfo *TransformTypeInObjectScope(TypeSourceInfo *TSInfo, QualType ObjectType, - NamedDecl *FirstQualifierInScope, CXXScopeSpec &SS); TypeSourceInfo *TransformTSIInObjectScope(TypeLoc TL, QualType ObjectType, - NamedDecl *FirstQualifierInScope, CXXScopeSpec &SS); QualType TransformDependentNameType(TypeLocBuilder &TLB, @@ -4448,7 +4424,7 @@ Sema::ConditionResult TreeTransform::TransformCondition( template NestedNameSpecifierLoc TreeTransform::TransformNestedNameSpecifierLoc( NestedNameSpecifierLoc NNS, QualType ObjectType, - NamedDecl *FirstQualifierInScope) { + ArrayRef UnqualifiedLookups) { SmallVector Qualifiers; auto insertNNS = [&Qualifiers](NestedNameSpecifierLoc NNS) { @@ -4459,6 +4435,8 @@ NestedNameSpecifierLoc TreeTransform::TransformNestedNameSpecifierLoc( insertNNS(NNS); CXXScopeSpec SS; + SS.setUnqualifiedLookups(UnqualifiedLookups); + while (!Qualifiers.empty()) { NestedNameSpecifierLoc Q = Qualifiers.pop_back_val(); NestedNameSpecifier *QNNS = Q.getNestedNameSpecifier(); @@ -4468,8 +4446,9 @@ NestedNameSpecifierLoc TreeTransform::TransformNestedNameSpecifierLoc( Sema::NestedNameSpecInfo IdInfo(QNNS->getAsIdentifier(), Q.getLocalBeginLoc(), Q.getLocalEndLoc(), ObjectType); - if (SemaRef.BuildCXXNestedNameSpecifier(/*Scope=*/nullptr, IdInfo, false, - SS, FirstQualifierInScope, false)) + if (SemaRef.BuildCXXNestedNameSpecifier(/*Scope=*/nullptr, IdInfo, + /*EnteringContext=*/false, SS, + /*ErrorRecoveryLookup=*/false)) return NestedNameSpecifierLoc(); break; } @@ -4507,8 +4486,7 @@ NestedNameSpecifierLoc TreeTransform::TransformNestedNameSpecifierLoc( case NestedNameSpecifier::TypeSpecWithTemplate: case NestedNameSpecifier::TypeSpec: { - TypeLoc TL = TransformTypeInObjectScope(Q.getTypeLoc(), ObjectType, - FirstQualifierInScope, SS); + TypeLoc TL = TransformTypeInObjectScope(Q.getTypeLoc(), ObjectType, SS); if (!TL) return NestedNameSpecifierLoc(); @@ -4541,7 +4519,7 @@ NestedNameSpecifierLoc TreeTransform::TransformNestedNameSpecifierLoc( } // The qualifier-in-scope and object type only apply to the leftmost entity. - FirstQualifierInScope = nullptr; + SS.setUnqualifiedLookups(std::nullopt); ObjectType = QualType(); } @@ -4624,14 +4602,10 @@ ::TransformDeclarationNameInfo(const DeclarationNameInfo &NameInfo) { llvm_unreachable("Unknown name kind."); } -template -TemplateName -TreeTransform::TransformTemplateName(CXXScopeSpec &SS, - TemplateName Name, - SourceLocation NameLoc, - QualType ObjectType, - NamedDecl *FirstQualifierInScope, - bool AllowInjectedClassName) { +template +TemplateName TreeTransform::TransformTemplateName( + CXXScopeSpec &SS, TemplateName Name, SourceLocation NameLoc, + QualType ObjectType, bool AllowInjectedClassName, bool MayBeNNS) { if (QualifiedTemplateName *QTN = Name.getAsQualifiedTemplateName()) { TemplateDecl *Template = QTN->getUnderlyingTemplate().getAsTemplateDecl(); assert(Template && "qualified template name must refer to a template"); @@ -4655,7 +4629,7 @@ TreeTransform::TransformTemplateName(CXXScopeSpec &SS, if (SS.getScopeRep()) { // These apply to the scope specifier, not the template. ObjectType = QualType(); - FirstQualifierInScope = nullptr; + SS.setUnqualifiedLookups(std::nullopt); } if (!getDerived().AlwaysRebuild() && @@ -4667,13 +4641,9 @@ TreeTransform::TransformTemplateName(CXXScopeSpec &SS, SourceLocation TemplateKWLoc = NameLoc; if (DTN->isIdentifier()) { - return getDerived().RebuildTemplateName(SS, - TemplateKWLoc, - *DTN->getIdentifier(), - NameLoc, - ObjectType, - FirstQualifierInScope, - AllowInjectedClassName); + return getDerived().RebuildTemplateName( + SS, TemplateKWLoc, *DTN->getIdentifier(), NameLoc, ObjectType, + AllowInjectedClassName, MayBeNNS); } return getDerived().RebuildTemplateName(SS, TemplateKWLoc, @@ -5217,39 +5187,31 @@ QualType TreeTransform::RebuildQualifiedType(QualType T, return SemaRef.BuildQualifiedType(T, Loc, Quals); } -template -TypeLoc -TreeTransform::TransformTypeInObjectScope(TypeLoc TL, - QualType ObjectType, - NamedDecl *UnqualLookup, - CXXScopeSpec &SS) { +template +TypeLoc TreeTransform::TransformTypeInObjectScope(TypeLoc TL, + QualType ObjectType, + CXXScopeSpec &SS) { if (getDerived().AlreadyTransformed(TL.getType())) return TL; - TypeSourceInfo *TSI = - TransformTSIInObjectScope(TL, ObjectType, UnqualLookup, SS); + TypeSourceInfo *TSI = TransformTSIInObjectScope(TL, ObjectType, SS); if (TSI) return TSI->getTypeLoc(); return TypeLoc(); } -template -TypeSourceInfo * -TreeTransform::TransformTypeInObjectScope(TypeSourceInfo *TSInfo, - QualType ObjectType, - NamedDecl *UnqualLookup, - CXXScopeSpec &SS) { +template +TypeSourceInfo *TreeTransform::TransformTypeInObjectScope( + TypeSourceInfo *TSInfo, QualType ObjectType, CXXScopeSpec &SS) { if (getDerived().AlreadyTransformed(TSInfo->getType())) return TSInfo; - return TransformTSIInObjectScope(TSInfo->getTypeLoc(), ObjectType, - UnqualLookup, SS); + return TransformTSIInObjectScope(TSInfo->getTypeLoc(), ObjectType, SS); } template TypeSourceInfo *TreeTransform::TransformTSIInObjectScope( - TypeLoc TL, QualType ObjectType, NamedDecl *UnqualLookup, - CXXScopeSpec &SS) { + TypeLoc TL, QualType ObjectType, CXXScopeSpec &SS) { QualType T = TL.getType(); assert(!getDerived().AlreadyTransformed(T)); @@ -5262,7 +5224,7 @@ TypeSourceInfo *TreeTransform::TransformTSIInObjectScope( TemplateName Template = getDerived().TransformTemplateName( SS, SpecTL.getTypePtr()->getTemplateName(), SpecTL.getTemplateNameLoc(), - ObjectType, UnqualLookup, /*AllowInjectedClassName*/true); + ObjectType, /*AllowInjectedClassName=*/true, /*MayBeNNS=*/true); if (Template.isNull()) return nullptr; @@ -5272,13 +5234,11 @@ TypeSourceInfo *TreeTransform::TransformTSIInObjectScope( DependentTemplateSpecializationTypeLoc SpecTL = TL.castAs(); - TemplateName Template - = getDerived().RebuildTemplateName(SS, - SpecTL.getTemplateKeywordLoc(), - *SpecTL.getTypePtr()->getIdentifier(), - SpecTL.getTemplateNameLoc(), - ObjectType, UnqualLookup, - /*AllowInjectedClassName*/true); + TemplateName Template = getDerived().RebuildTemplateName( + SS, SpecTL.getTemplateKeywordLoc(), + *SpecTL.getTypePtr()->getIdentifier(), SpecTL.getTemplateNameLoc(), + ObjectType, + /*AllowInjectedClassName=*/true, /*MayBeNNS=*/true); if (Template.isNull()) return nullptr; @@ -12842,7 +12802,8 @@ TreeTransform::TransformMemberExpr(MemberExpr *E) { // first-qualifier-in-scope here, just in case we had a dependent // base (and therefore couldn't do the check) and a // nested-name-qualifier (and therefore could do the lookup). - NamedDecl *FirstQualifierInScope = nullptr; + ArrayRef UnqualifiedLookups; + DeclarationNameInfo MemberNameInfo = E->getMemberNameInfo(); if (MemberNameInfo.getName()) { MemberNameInfo = getDerived().TransformDeclarationNameInfo(MemberNameInfo); @@ -12850,16 +12811,11 @@ TreeTransform::TransformMemberExpr(MemberExpr *E) { return ExprError(); } - return getDerived().RebuildMemberExpr(Base.get(), FakeOperatorLoc, - E->isArrow(), - QualifierLoc, - TemplateKWLoc, - MemberNameInfo, - Member, - FoundDecl, - (E->hasExplicitTemplateArgs() - ? &TransArgs : nullptr), - FirstQualifierInScope); + return getDerived().RebuildMemberExpr( + Base.get(), FakeOperatorLoc, E->isArrow(), QualifierLoc, TemplateKWLoc, + MemberNameInfo, Member, FoundDecl, + (E->hasExplicitTemplateArgs() ? &TransArgs : nullptr), + UnqualifiedLookups); } template @@ -13986,9 +13942,8 @@ TreeTransform::TransformCXXPseudoDestructorExpr( PseudoDestructorTypeStorage Destroyed; if (E->getDestroyedTypeInfo()) { - TypeSourceInfo *DestroyedTypeInfo - = getDerived().TransformTypeInObjectScope(E->getDestroyedTypeInfo(), - ObjectType, nullptr, SS); + TypeSourceInfo *DestroyedTypeInfo = getDerived().TransformTypeInObjectScope( + E->getDestroyedTypeInfo(), ObjectType, SS); if (!DestroyedTypeInfo) return ExprError(); Destroyed = DestroyedTypeInfo; @@ -14014,7 +13969,7 @@ TreeTransform::TransformCXXPseudoDestructorExpr( if (E->getScopeTypeInfo()) { CXXScopeSpec EmptySS; ScopeTypeInfo = getDerived().TransformTypeInObjectScope( - E->getScopeTypeInfo(), ObjectType, nullptr, EmptySS); + E->getScopeTypeInfo(), ObjectType, EmptySS); if (!ScopeTypeInfo) return ExprError(); } @@ -15261,19 +15216,17 @@ TreeTransform::TransformCXXDependentScopeMemberExpr( ObjectType = BaseType->castAs()->getPointeeType(); } - // Transform the first part of the nested-name-specifier that qualifies - // the member name. - NamedDecl *FirstQualifierInScope - = getDerived().TransformFirstQualifierInScope( - E->getFirstQualifierFoundInScope(), - E->getQualifierLoc().getBeginLoc()); + UnresolvedSet<4> UnqualifiedLookups; + for (auto D : E->unqualified_lookups()) { + if (NamedDecl *InstD = getDerived().TransformFirstQualifierInScope( + D.getDecl(), E->getQualifierLoc().getBeginLoc())) + UnqualifiedLookups.addDecl(InstD); + } NestedNameSpecifierLoc QualifierLoc; if (E->getQualifier()) { - QualifierLoc - = getDerived().TransformNestedNameSpecifierLoc(E->getQualifierLoc(), - ObjectType, - FirstQualifierInScope); + QualifierLoc = getDerived().TransformNestedNameSpecifierLoc( + E->getQualifierLoc(), ObjectType, UnqualifiedLookups.pairs()); if (!QualifierLoc) return ExprError(); } @@ -15292,23 +15245,16 @@ TreeTransform::TransformCXXDependentScopeMemberExpr( if (!E->hasExplicitTemplateArgs()) { // This is a reference to a member without an explicitly-specified // template argument list. Optimize for this common case. - if (!getDerived().AlwaysRebuild() && - Base.get() == OldBase && - BaseType == E->getBaseType() && - QualifierLoc == E->getQualifierLoc() && + if (!getDerived().AlwaysRebuild() && Base.get() == OldBase && + BaseType == E->getBaseType() && QualifierLoc == E->getQualifierLoc() && NameInfo.getName() == E->getMember() && - FirstQualifierInScope == E->getFirstQualifierFoundInScope()) + UnqualifiedLookups.pairs() == E->unqualified_lookups()) return E; - return getDerived().RebuildCXXDependentScopeMemberExpr(Base.get(), - BaseType, - E->isArrow(), - E->getOperatorLoc(), - QualifierLoc, - TemplateKWLoc, - FirstQualifierInScope, - NameInfo, - /*TemplateArgs*/nullptr); + return getDerived().RebuildCXXDependentScopeMemberExpr( + Base.get(), BaseType, E->isArrow(), E->getOperatorLoc(), QualifierLoc, + TemplateKWLoc, UnqualifiedLookups.pairs(), NameInfo, + /*TemplateArgs*/ nullptr); } TemplateArgumentListInfo TransArgs(E->getLAngleLoc(), E->getRAngleLoc()); @@ -15317,15 +15263,9 @@ TreeTransform::TransformCXXDependentScopeMemberExpr( TransArgs)) return ExprError(); - return getDerived().RebuildCXXDependentScopeMemberExpr(Base.get(), - BaseType, - E->isArrow(), - E->getOperatorLoc(), - QualifierLoc, - TemplateKWLoc, - FirstQualifierInScope, - NameInfo, - &TransArgs); + return getDerived().RebuildCXXDependentScopeMemberExpr( + Base.get(), BaseType, E->isArrow(), E->getOperatorLoc(), QualifierLoc, + TemplateKWLoc, UnqualifiedLookups.pairs(), NameInfo, &TransArgs); } template @@ -15386,11 +15326,11 @@ ExprResult TreeTransform::TransformUnresolvedMemberExpr( // first-qualifier-in-scope here, just in case we had a dependent // base (and therefore couldn't do the check) and a // nested-name-qualifier (and therefore could do the lookup). - NamedDecl *FirstQualifierInScope = nullptr; + ArrayRef UnqualifiedLookups; return getDerived().RebuildUnresolvedMemberExpr( Base.get(), BaseType, Old->getOperatorLoc(), Old->isArrow(), QualifierLoc, - TemplateKWLoc, FirstQualifierInScope, R, + TemplateKWLoc, UnqualifiedLookups, R, (Old->hasExplicitTemplateArgs() ? &TransArgs : nullptr)); } @@ -16754,22 +16694,18 @@ TreeTransform::RebuildTemplateName(CXXScopeSpec &SS, TemplateName(Template)); } -template -TemplateName -TreeTransform::RebuildTemplateName(CXXScopeSpec &SS, - SourceLocation TemplateKWLoc, - const IdentifierInfo &Name, - SourceLocation NameLoc, - QualType ObjectType, - NamedDecl *FirstQualifierInScope, - bool AllowInjectedClassName) { +template +TemplateName TreeTransform::RebuildTemplateName( + CXXScopeSpec &SS, SourceLocation TemplateKWLoc, const IdentifierInfo &Name, + SourceLocation NameLoc, QualType ObjectType, bool AllowInjectedClassName, + bool MayBeNNS) { UnqualifiedId TemplateName; TemplateName.setIdentifier(&Name, NameLoc); Sema::TemplateTy Template; getSema().ActOnTemplateName(/*Scope=*/nullptr, SS, TemplateKWLoc, TemplateName, ParsedType::make(ObjectType), /*EnteringContext=*/false, Template, - AllowInjectedClassName); + AllowInjectedClassName, MayBeNNS); return Template.get(); } @@ -16917,13 +16853,10 @@ TreeTransform::RebuildCXXPseudoDestructorExpr(Expr *Base, } SourceLocation TemplateKWLoc; // FIXME: retrieve it from caller. - return getSema().BuildMemberReferenceExpr(Base, BaseType, - OperatorLoc, isArrow, - SS, TemplateKWLoc, - /*FIXME: FirstQualifier*/ nullptr, - NameInfo, - /*TemplateArgs*/ nullptr, - /*S*/nullptr); + return getSema().BuildMemberReferenceExpr( + Base, BaseType, OperatorLoc, isArrow, SS, TemplateKWLoc, NameInfo, + /*TemplateArgs=*/nullptr, + /*S=*/nullptr); } template diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index c39a1950a6cf24..227eac39096ad4 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1997,42 +1997,43 @@ void ASTStmtReader::VisitCXXDependentScopeMemberExpr( CXXDependentScopeMemberExpr *E) { VisitExpr(E); - unsigned NumTemplateArgs = Record.readInt(); CurrentUnpackingBits.emplace(Record.readInt()); - bool HasTemplateKWAndArgsInfo = CurrentUnpackingBits->getNextBit(); - bool HasFirstQualifierFoundInScope = CurrentUnpackingBits->getNextBit(); - - assert((HasTemplateKWAndArgsInfo == E->hasTemplateKWAndArgsInfo()) && - "Wrong HasTemplateKWAndArgsInfo!"); - assert( - (HasFirstQualifierFoundInScope == E->hasFirstQualifierFoundInScope()) && - "Wrong HasFirstQualifierFoundInScope!"); - - if (HasTemplateKWAndArgsInfo) - ReadTemplateKWAndArgsInfo( - *E->getTrailingObjects(), - E->getTrailingObjects(), NumTemplateArgs); - - assert((NumTemplateArgs == E->getNumTemplateArgs()) && - "Wrong NumTemplateArgs!"); + bool HasQualifier = CurrentUnpackingBits->getNextBit(); + bool HasTemplateInfo = CurrentUnpackingBits->getNextBit(); + unsigned NumUnqualifiedLookups = Record.readInt(); + unsigned NumTemplateArgs = Record.readInt(); + E->CXXDependentScopeMemberExprBits.HasQualifier = HasQualifier; + E->CXXDependentScopeMemberExprBits.NumUnqualifiedLookups = + NumUnqualifiedLookups; + E->CXXDependentScopeMemberExprBits.HasTemplateKWAndArgsInfo = HasTemplateInfo; + E->BaseType = Record.readType(); E->CXXDependentScopeMemberExprBits.IsArrow = CurrentUnpackingBits->getNextBit(); - E->BaseType = Record.readType(); - E->QualifierLoc = Record.readNestedNameSpecifierLoc(); - // not ImplicitAccess if (CurrentUnpackingBits->getNextBit()) E->Base = Record.readSubExpr(); else E->Base = nullptr; - E->CXXDependentScopeMemberExprBits.OperatorLoc = readSourceLocation(); + E->OperatorLoc = Record.readSourceLocation(); + E->MemberNameInfo = Record.readDeclarationNameInfo(); - if (HasFirstQualifierFoundInScope) - *E->getTrailingObjects() = readDeclAs(); + if (HasQualifier) + new (E->getTrailingObjects()) + NestedNameSpecifierLoc(Record.readNestedNameSpecifierLoc()); - E->MemberNameInfo = Record.readDeclarationNameInfo(); + for (unsigned I = 0; I != NumUnqualifiedLookups; ++I) { + auto *FoundD = Record.readDeclAs(); + auto AS = (AccessSpecifier)Record.readInt(); + E->getTrailingObjects()[I] = + DeclAccessPair::make(FoundD, AS); + } + + if (HasTemplateInfo) + ReadTemplateKWAndArgsInfo( + *E->getTrailingObjects(), + E->getTrailingObjects(), NumTemplateArgs); } void @@ -4134,16 +4135,16 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { break; case EXPR_CXX_DEPENDENT_SCOPE_MEMBER: { - unsigned NumTemplateArgs = Record[ASTStmtReader::NumExprFields]; BitsUnpacker DependentScopeMemberBits( - Record[ASTStmtReader::NumExprFields + 1]); - bool HasTemplateKWAndArgsInfo = DependentScopeMemberBits.getNextBit(); + Record[ASTStmtReader::NumExprFields]); + bool HasQualifier = DependentScopeMemberBits.getNextBit(); + bool HasTemplateInfo = DependentScopeMemberBits.getNextBit(); + unsigned NumUnqualifiedLookups = Record[ASTStmtReader::NumExprFields + 1]; + unsigned NumTemplateArgs = Record[ASTStmtReader::NumExprFields + 2]; - bool HasFirstQualifierFoundInScope = - DependentScopeMemberBits.getNextBit(); S = CXXDependentScopeMemberExpr::CreateEmpty( - Context, HasTemplateKWAndArgsInfo, NumTemplateArgs, - HasFirstQualifierFoundInScope); + Context, HasQualifier, NumUnqualifiedLookups, HasTemplateInfo, + NumTemplateArgs); break; } diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index e7f567ff59a8ad..4c301ccf5753c6 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1995,34 +1995,41 @@ void ASTStmtWriter::VisitCXXDependentScopeMemberExpr( CXXDependentScopeMemberExpr *E) { VisitExpr(E); - // Don't emit anything here (or if you do you will have to update - // the corresponding deserialization function). - Record.push_back(E->getNumTemplateArgs()); - CurrentPackingBits.updateBits(); - CurrentPackingBits.addBit(E->hasTemplateKWAndArgsInfo()); - CurrentPackingBits.addBit(E->hasFirstQualifierFoundInScope()); - - if (E->hasTemplateKWAndArgsInfo()) { - const ASTTemplateKWAndArgsInfo &ArgInfo = - *E->getTrailingObjects(); - AddTemplateKWAndArgsInfo(ArgInfo, - E->getTrailingObjects()); - } + bool HasQualifier = E->hasQualifier(); + unsigned NumUnqualifiedLookups = E->getNumUnqualifiedLookups(); + bool HasTemplateInfo = E->hasTemplateKWAndArgsInfo(); + unsigned NumTemplateArgs = E->getNumTemplateArgs(); - CurrentPackingBits.addBit(E->isArrow()); + // Write these first for easy access when deserializing, as they affect the + // size of the CXXDependentScopeMemberExpr. + CurrentPackingBits.updateBits(); + CurrentPackingBits.addBit(HasQualifier); + CurrentPackingBits.addBit(HasTemplateInfo); + Record.push_back(NumUnqualifiedLookups); + Record.push_back(NumTemplateArgs); Record.AddTypeRef(E->getBaseType()); - Record.AddNestedNameSpecifierLoc(E->getQualifierLoc()); + CurrentPackingBits.addBit(E->isArrow()); CurrentPackingBits.addBit(!E->isImplicitAccess()); if (!E->isImplicitAccess()) Record.AddStmt(E->getBase()); Record.AddSourceLocation(E->getOperatorLoc()); - if (E->hasFirstQualifierFoundInScope()) - Record.AddDeclRef(E->getFirstQualifierFoundInScope()); - Record.AddDeclarationNameInfo(E->MemberNameInfo); + + if (HasQualifier) + Record.AddNestedNameSpecifierLoc(E->getQualifierLoc()); + + for (DeclAccessPair D : E->unqualified_lookups()) { + Record.AddDeclRef(D.getDecl()); + Record.push_back(D.getAccess()); + } + + if (HasTemplateInfo) + AddTemplateKWAndArgsInfo(*E->getTrailingObjects(), + E->getTrailingObjects()); + Code = serialization::EXPR_CXX_DEPENDENT_SCOPE_MEMBER; } diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.classref/p1-cxx11.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.classref/p1-cxx11.cpp index 1afea99e8895c7..11eb67fb4f159f 100644 --- a/clang/test/CXX/basic/basic.lookup/basic.lookup.classref/p1-cxx11.cpp +++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.classref/p1-cxx11.cpp @@ -55,15 +55,19 @@ namespace PR11856 { template T *end(T*); - class X { }; + struct X { }; + struct Y { + int end; + }; template void Foo2() { T it1; - if (it1->end < it1->end) { - } + if (it1->end < it1->end) { } X *x; - if (x->end < 7) { // expected-error{{no member named 'end' in 'PR11856::X'}} - } + if (x->end < 7) { } // expected-error{{no member named 'end' in 'PR11856::X'}} + + Y *y; + if (y->end < 7) { } } } diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.classref/p1.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.classref/p1.cpp index e3599db18350bf..5221b883f046c5 100644 --- a/clang/test/CXX/basic/basic.lookup/basic.lookup.classref/p1.cpp +++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.classref/p1.cpp @@ -86,15 +86,19 @@ namespace PR11856 { template T *end(T*); - class X { }; + struct X { }; + struct Y { + int end; + }; template void Foo2() { T it1; - if (it1->end < it1->end) { - } + if (it1->end < it1->end) { } X *x; - if (x->end < 7) { // expected-error{{no member named 'end' in 'PR11856::X'}} - } + if (x->end < 7) { } // expected-error{{no member named 'end' in 'PR11856::X'}} + + Y *y; + if (y->end < 7) { } } } diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p3-example3.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p3-example3.cpp new file mode 100644 index 00000000000000..423eacd21d441e --- /dev/null +++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p3-example3.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -std=c++23 %s -verify + +int f(); + +struct A { + int B, C; // expected-note {{declared as a non-template here}} + template using D = void; + using T = void; + void f(); +}; + +using B = A; +template using C = A; +template using D = A; +template using X = A; + +template +void g(T *p) { + p->X<0>::f(); // expected-error {{no member named 'X' in 'A'}} + p->template X<0>::f(); + p->B::f(); + p->template C<0>::f(); // expected-error {{'C' following the 'template' keyword does not refer to a template}} + p->template D<0>::f(); // expected-error {{type 'template D<0>' (aka 'void') cannot be used prior to '::' because it has no members}} + p->T::f(); // expected-error {{'A::T' (aka 'void') is not a class, namespace, or enumeration}} +} + +template void g(A*); // expected-note {{in instantiation of}} diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p3.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p3.cpp new file mode 100644 index 00000000000000..7d843649c3f300 --- /dev/null +++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p3.cpp @@ -0,0 +1,98 @@ +// RUN: %clang_cc1 -std=c++23 -Wno-unused %s -verify + +namespace Unambiguous { + struct A { + int x; + + template + using C = A; + }; + + using B = A; + + template + using D = A; + + using E = void; + + struct F : A { + void non_template() { + this->x; + this->A::x; + this->B::x; + this->C::x; + this->D::x; + this->E::x; // expected-error {{'Unambiguous::E' (aka 'void') is not a class, namespace, or enumeration}} + } + }; + + template + void not_instantiated(T t) { + t.x; + t.A::x; + t.B::x; + t.C::x; // expected-warning {{use 'template' keyword to treat 'C' as a dependent template name}} + t.template C::x; + t.D::x; // expected-warning {{use 'template' keyword to treat 'D' as a dependent template name}} + t.template D::x; + t.E::x; + } + + template + void instantiated_valid(T t) { + t.x; + t.A::x; + t.B::x; + t.template C::x; + t.template D::x; + t.E::x; + } + + template + void instantiated_invalid(T t) { + t.x; + t.A::x; + t.B::x; // expected-error {{'Unambiguous::Invalid::B' (aka 'void') is not a class, namespace, or enumeration}} + t.template C::x; + t.template D::x; // expected-error {{'D' following the 'template' keyword does not refer to a template}} + t.E::x; // expected-error {{'Unambiguous::E' (aka 'void') is not a class, namespace, or enumeration}} + } + + struct Valid : A { + using E = A; + }; + + template void instantiated_valid(Valid); + + struct Invalid : A { + using B = void; + using D = A; // expected-note {{declared as a non-template here}} + }; + + template void instantiated_invalid(Invalid); // expected-note {{in instantiation of}} +} // namespace Unambiguous + +namespace Ambiguous { + inline namespace N { + struct A { }; // expected-note {{candidate found by name lookup is 'Ambiguous::N::A'}} + } + + struct A { }; // expected-note {{candidate found by name lookup is 'Ambiguous::A'}} + + template + void f(T t) { + t.A::x; // expected-error {{reference to 'A' is ambiguous}} + } + + struct B { + using A = B; + + int x; + }; + + struct C { }; + + template void f(B); + template void f(C); // expected-note {{in instantiation of}} + +} // namespace Ambiguous diff --git a/clang/test/CXX/class.derived/class.member.lookup/p8.cpp b/clang/test/CXX/class.derived/class.member.lookup/p8.cpp index 78e83c0ab4566c..19fc0644be1b09 100644 --- a/clang/test/CXX/class.derived/class.member.lookup/p8.cpp +++ b/clang/test/CXX/class.derived/class.member.lookup/p8.cpp @@ -47,8 +47,8 @@ template void DerivedT::Inner() { Derived1T::Foo(); Derived2T::Member = 42; - this->Derived1T::Foo(); - this->Derived2T::Member = 42; + this->Derived1T::Foo(); // expected-warning {{use 'template' keyword to treat 'Derived1T' as a dependent template name}} + this->Derived2T::Member = 42; // expected-warning {{use 'template' keyword to treat 'Derived2T' as a dependent template name}} this->Foo(); // expected-error{{non-static member 'Foo' found in multiple base-class subobjects of type 'BaseT'}} } diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.mptr/p2.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.mptr/p2.cpp index a06b1077555968..33fb2b5fa82d85 100644 --- a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.mptr/p2.cpp +++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.mptr/p2.cpp @@ -36,7 +36,7 @@ template int A0::template C2::D1::* f6(); template -int A0::template C0::E0::* f7(); // expected-error {{use 'template' keyword to treat 'E0' as a dependent template name}} +int A0::template C0::E0::* f7(); // expected-warning {{use 'template' keyword to treat 'E0' as a dependent template name}} // expected-error@-1 {{expected unqualified-id}} template diff --git a/clang/test/CXX/drs/cwg1xx.cpp b/clang/test/CXX/drs/cwg1xx.cpp index 6aec8b65c91f12..4d3d1cb37bfe69 100644 --- a/clang/test/CXX/drs/cwg1xx.cpp +++ b/clang/test/CXX/drs/cwg1xx.cpp @@ -703,10 +703,8 @@ namespace cwg141 { // cwg141: 3.1 // cxx98-note@#cwg141-S {{lookup from the current scope refers here}} // expected-error@#cwg141-a {{no member named 'n' in 'cwg141::A::S'; did you mean '::cwg141::S::n'?}} // expected-note@#cwg141-S {{'::cwg141::S::n' declared here}} - // FIXME: we issue a useful diagnostic first, then some bogus ones. b.f(); // expected-error@-1 {{no member named 'f' in 'cwg141::B'}} - // expected-error@-2 +{{}} (void)b.S::n; } template struct C { @@ -716,10 +714,12 @@ namespace cwg141 { // cwg141: 3.1 // expected-error@-1 {{use 'template' keyword to treat 'f' as a dependent template name}} } void h() { - (void)t.S::n; // ok + (void)t.S::n; + // expected-error@-1 {{use 'template' keyword to treat 'S' as a dependent template name}} } void i() { - (void)t.S(); // ok! + (void)t.S(); + // expected-error@-1 {{use 'template' keyword to treat 'S' as a dependent template name}} } }; void h() { C().h(); } // ok diff --git a/clang/test/CXX/temp/temp.names/p3-23.cpp b/clang/test/CXX/temp/temp.names/p3-23.cpp new file mode 100644 index 00000000000000..27c24e1d61706e --- /dev/null +++ b/clang/test/CXX/temp/temp.names/p3-23.cpp @@ -0,0 +1,237 @@ +// RUN: %clang_cc1 -std=c++23 -Wno-unused %s -verify + +namespace FoundNothing { + template + void f0(T &t) { + t.x<0; + t.x<0>; // expected-error {{expected expression}} + t.x<0>1; + } + + template + struct A { + void f1() { + this->x<0; // expected-error {{no member named 'x' in 'A'}} + this->x<0>; // expected-error {{no member named 'x' in 'A'}} + // expected-error@-1 {{expected expression}} + this->x<0>1; // expected-error {{no member named 'x' in 'A'}} + } + }; +} // namespace FoundNothing + +namespace FoundSingleNonTemplate { + void f0(); + + struct A0; + + template + void g0(T &t) { + t.f0<0; + t.f0<0>; // expected-error {{expected expression}} + t.f0<0>1; + + t.A0<0; + t.A0<0>; // expected-error {{expected expression}} + t.A0<0>1; + } + + template + struct B { + void f1(); + + struct A1; // expected-note 3{{member 'A1' declared here}} + + void g1() { + this->f0<0; // expected-error {{no member named 'f0' in 'B'}} + this->f0<0>; // expected-error {{no member named 'f0' in 'B'}} + // expected-error@-1 {{expected expression}} + this->f0<0>1; // expected-error {{no member named 'f0' in 'B'}} + + this->A0<0; // expected-error {{no member named 'A0' in 'B'}} + this->A0<0>; // expected-error {{no member named 'A0' in 'B'}} + // expected-error@-1 {{expected expression}} + this->A0<0>1; // expected-error {{no member named 'A0' in 'B'}} + + this->f1<0; // expected-error {{reference to non-static member function must be called}} + this->f1<0>; // expected-error {{reference to non-static member function must be called}} + // expected-error@-1 {{expected expression}} + this->f1<0>1; // expected-error {{reference to non-static member function must be called}} + + this->A1<0; // expected-error {{cannot refer to type member 'A1' in 'B' with '->'}} + this->A1<0>; // expected-error {{cannot refer to type member 'A1' in 'B' with '->'}} + // expected-error@-1 {{expected expression}} + this->A1<0>1; // expected-error {{cannot refer to type member 'A1' in 'B' with '->'}} + } + }; +} // namespace FoundSingleNonTemplate + +namespace FoundSingleTemplate { + template + void f0(); + + template + struct A0; + + template + void g0(T &t) { + t.f0<0; + t.f0<0>; // expected-error {{expected expression}} + t.f0<0>1; + + t.A0<0; + t.A0<0>; // expected-error {{expected expression}} + t.A0<0>1; + } + + template + struct B { + template + void f1(); // expected-note 2{{possible target for call}} + + template + struct A1; // expected-note 2{{member 'A1' declared here}} + + void g1() { + this->f0<0; // expected-error {{no member named 'f0' in 'B'}} + this->f0<0>; // expected-error {{no member named 'f0' in 'B'}} + this->f0<0>1; // expected-error {{no member named 'f0' in 'B'}} + // expected-error@-1 {{expected ';' after expression}} + + this->A0<0; // expected-error {{no member named 'A0' in 'B'}} + this->A0<0>; // expected-error {{no member named 'A0' in 'B'}} + this->A0<0>1; // expected-error {{no member named 'A0' in 'B'}} + // expected-error@-1 {{expected ';' after expression}} + + + this->f1<0; // expected-error {{expected '>'}} + // expected-note@-1 {{to match this '<'}} + this->f1<0>; // expected-error {{reference to non-static member function must be called}} + this->f1<0>1; // expected-error {{reference to non-static member function must be called}} + // expected-error@-1 {{expected ';' after expression}} + + this->A1<0; // expected-error {{expected '>'}} + // expected-note@-1 {{to match this '<'}} + this->A1<0>; // expected-error {{cannot refer to member 'A1' in 'B' with '->'}} + this->A1<0>1; // expected-error {{cannot refer to member 'A1' in 'B' with '->'}} + // expected-error@-1 {{expected ';' after expression}} + } + }; +} // namespace FoundSingleTemplate + +namespace FoundAmbiguousNonTemplate { + inline namespace N { + int f0; + + struct A0; + } // namespace N + + void f0(); + + struct A0; + + template + void g0(T &t) { + t.f0<0; + t.f0<0>; // expected-error {{expected expression}} + t.f0<0>1; + + t.A0<0; + t.A0<0>; // expected-error {{expected expression}} + t.A0<0>1; + } + + template + struct B { + void f1(); + + struct A1; // expected-note 3{{member 'A1' declared here}} + + void g1() { + this->f0<0; // expected-error {{no member named 'f0' in 'B'}} + this->f0<0>; // expected-error {{no member named 'f0' in 'B'}} + // expected-error@-1 {{expected expression}} + this->f0<0>1; // expected-error {{no member named 'f0' in 'B'}} + + this->A0<0; // expected-error {{no member named 'A0' in 'B'}} + this->A0<0>; // expected-error {{no member named 'A0' in 'B'}} + // expected-error@-1 {{expected expression}} + this->A0<0>1; // expected-error {{no member named 'A0' in 'B'}} + + this->f1<0; // expected-error {{reference to non-static member function must be called}} + this->f1<0>; // expected-error {{reference to non-static member function must be called}} + // expected-error@-1 {{expected expression}} + this->f1<0>1; // expected-error {{reference to non-static member function must be called}} + + this->A1<0; // expected-error {{cannot refer to type member 'A1' in 'B' with '->'}} + this->A1<0>; // expected-error {{cannot refer to type member 'A1' in 'B' with '->'}} + // expected-error@-1 {{expected expression}} + this->A1<0>1; // expected-error {{cannot refer to type member 'A1' in 'B' with '->'}} + } + }; +} // namespace FoundAmbiguousNonTemplates + +namespace FoundAmbiguousTemplate { + inline namespace N { + template + int f0; // expected-note 3{{candidate found by name lookup is 'FoundAmbiguousTemplate::N::f0'}} + + template + struct A0; // expected-note 3{{candidate found by name lookup is 'FoundAmbiguousTemplate::N::A0'}} + } // namespace N + + template + void f0(); // expected-note 3{{candidate found by name lookup is 'FoundAmbiguousTemplate::f0'}} + + template + struct A0; // expected-note 3{{candidate found by name lookup is 'FoundAmbiguousTemplate::A0'}} + + template + void g0(T &t) { + t.f0<0; + t.f0<0>; // expected-error {{expected expression}} + t.f0<0>1; + + t.A0<0; + t.A0<0>; // expected-error {{expected expression}} + t.A0<0>1; + } + + template + struct B { + template + void f1(); // expected-note 2{{possible target for call}} + + template + struct A1; // expected-note 2{{member 'A1' declared here}} + + void g1() { + this->f0<0; // expected-error {{no member named 'f0' in 'B'}} + // expected-error@-1 {{reference to 'f0' is ambiguous}} + this->f0<0>; // expected-error {{no member named 'f0' in 'B'}} + // expected-error@-1 {{reference to 'f0' is ambiguous}} + this->f0<0>1; // expected-error {{no member named 'f0' in 'B'}} + // expected-error@-1 {{expected ';' after expression}} + // expected-error@-2 {{reference to 'f0' is ambiguous}} + + this->A0<0; // expected-error {{no member named 'A0' in 'B'}} + // expected-error@-1 {{reference to 'A0' is ambiguous}} + this->A0<0>; // expected-error {{no member named 'A0' in 'B'}} + // expected-error@-1 {{reference to 'A0' is ambiguous}} + this->A0<0>1; // expected-error {{no member named 'A0' in 'B'}} + // expected-error@-1 {{expected ';' after expression}} + // expected-error@-2 {{reference to 'A0' is ambiguous}} + + this->f1<0; // expected-error {{expected '>'}} + // expected-note@-1 {{to match this '<'}} + this->f1<0>; // expected-error {{reference to non-static member function must be called}} + this->f1<0>1; // expected-error {{reference to non-static member function must be called}} + // expected-error@-1 {{expected ';' after expression}} + + this->A1<0; // expected-error {{expected '>'}} + // expected-note@-1 {{to match this '<'}} + this->A1<0>; // expected-error {{cannot refer to member 'A1' in 'B' with '->'}} + this->A1<0>1; // expected-error {{cannot refer to member 'A1' in 'B' with '->'}} + // expected-error@-1 {{expected ';' after expression}} + } + }; +} // namespace FoundAmbiguousTemplate diff --git a/clang/test/CXX/temp/temp.res/p3.cpp b/clang/test/CXX/temp/temp.res/p3.cpp index 1eda967523a59c..c144acff369b11 100644 --- a/clang/test/CXX/temp/temp.res/p3.cpp +++ b/clang/test/CXX/temp/temp.res/p3.cpp @@ -28,6 +28,6 @@ template int A::template C::*f5() {} template template struct A::B { friend A::C f6(); // ok, same as 'friend T f6();' - friend A::C f7(); // expected-error {{use 'template' keyword to treat 'C' as a dependent template name}} expected-warning {{missing 'typename'}} + friend A::C f7(); // expected-warning {{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/FixIt/fixit.cpp b/clang/test/FixIt/fixit.cpp index 605c2d0bd02355..144eefb3ae4bd0 100644 --- a/clang/test/FixIt/fixit.cpp +++ b/clang/test/FixIt/fixit.cpp @@ -158,12 +158,12 @@ class F1 { template class F2 { - typename F1:: /*template*/ Iterator<0> Mypos; // expected-error {{use 'template' keyword to treat 'Iterator' as a dependent template name}} + typename F1:: /*template*/ Iterator<0> Mypos; // expected-warning {{use 'template' keyword to treat 'Iterator' as a dependent template name}} }; template void f(){ - typename F1:: /*template*/ Iterator<0> Mypos; // expected-error {{use 'template' keyword to treat 'Iterator' as a dependent template name}} + typename F1:: /*template*/ Iterator<0> Mypos; // expected-warning {{use 'template' keyword to treat 'Iterator' as a dependent template name}} } // Tests for &/* fixits diff --git a/clang/test/Misc/warning-flags.c b/clang/test/Misc/warning-flags.c index 1fd02440833359..64f78176d73307 100644 --- a/clang/test/Misc/warning-flags.c +++ b/clang/test/Misc/warning-flags.c @@ -21,6 +21,7 @@ The list of warnings below should NEVER grow. It should gradually shrink to 0. CHECK: Warnings without flags (61): CHECK-NEXT: ext_expected_semi_decl_list +CHECK-NEXT: ext_missing_dependent_template_keyword CHECK-NEXT: ext_missing_whitespace_after_macro_name CHECK-NEXT: ext_new_paren_array_nonconst CHECK-NEXT: ext_plain_complex @@ -58,7 +59,6 @@ CHECK-NEXT: warn_invalid_cpu_supports CHECK-NEXT: warn_maynot_respond CHECK-NEXT: warn_method_param_redefinition CHECK-NEXT: warn_missing_case_for_condition -CHECK-NEXT: warn_missing_dependent_template_keyword CHECK-NEXT: warn_missing_whitespace_after_macro_name CHECK-NEXT: warn_mt_message CHECK-NEXT: warn_no_constructor_for_refconst diff --git a/clang/test/Parser/cxx2a-concepts-requires-expr.cpp b/clang/test/Parser/cxx2a-concepts-requires-expr.cpp index 5755844a323d2c..0c7f453b5c48d8 100644 --- a/clang/test/Parser/cxx2a-concepts-requires-expr.cpp +++ b/clang/test/Parser/cxx2a-concepts-requires-expr.cpp @@ -78,7 +78,7 @@ bool r22 = requires { typename s::~s; }; template bool r23 = requires { typename identity::temp; }; -// expected-error@-1 {{use 'template' keyword to treat 'temp' as a dependent template name}} +// expected-warning@-1 {{use 'template' keyword to treat 'temp' as a dependent template name}} template bool r24 = requires { diff --git a/clang/test/SemaCXX/cxx0x-noexcept-expression.cpp b/clang/test/SemaCXX/cxx0x-noexcept-expression.cpp index b3c102830f3595..a01edc77e02aff 100644 --- a/clang/test/SemaCXX/cxx0x-noexcept-expression.cpp +++ b/clang/test/SemaCXX/cxx0x-noexcept-expression.cpp @@ -127,7 +127,7 @@ void f1() { // `dependent` should be type-dependent because the noexcept-expression should be value-dependent // (it is true if T is int*, false if T is Polymorphic* for example) dependent.f(); // This should need to be `.template f` to parse as a template - // expected-error@-1 {{use 'template' keyword to treat 'f' as a dependent template name}} + // expected-warning@-1 {{use 'template' keyword to treat 'f' as a dependent template name}} } template void f2() { @@ -135,14 +135,14 @@ void f2() { // X when T...[0] is a type with some operator&& which returns int* // X when sizeof...(T) == 0 dependent.f(); - // expected-error@-1 {{use 'template' keyword to treat 'f' as a dependent template name}} + // expected-warning@-1 {{use 'template' keyword to treat 'f' as a dependent template name}} } template void f3() { X(nullptr)))> dependent; // X when T is int, X when T is Polymorphic dependent.f(); - // expected-error@-1 {{use 'template' keyword to treat 'f' as a dependent template name}} + // expected-warning@-1 {{use 'template' keyword to treat 'f' as a dependent template name}} } template void f4() { diff --git a/clang/test/SemaCXX/nested-name-spec.cpp b/clang/test/SemaCXX/nested-name-spec.cpp index 981152dda23f54..fc45b65297defc 100644 --- a/clang/test/SemaCXX/nested-name-spec.cpp +++ b/clang/test/SemaCXX/nested-name-spec.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -std=c++98 -verify -fblocks %s +// RUN: %clang_cc1 -fsyntax-only -std=c++98 -verify -fblocks %s namespace A { struct C { static int cx; @@ -111,7 +111,7 @@ void A2::CC::NC::m(); // expected-error{{out-of-line declaration of a member mus namespace E { int X = 5; - + namespace Nested { enum E { X = 0 @@ -146,7 +146,7 @@ Operators::operator bool() { namespace A { void g(int&); // expected-note{{type of 1st parameter of member declaration does not match definition ('int &' vs 'const int &')}} -} +} void A::f() {} // expected-error-re{{out-of-line definition of 'f' does not match any declaration in namespace 'A'{{$}}}} @@ -462,7 +462,7 @@ namespace A { class B { typedef C D; // expected-error{{unknown type name 'C'}} A::D::F; - // expected-error@-1{{'PR30619::A::B::D' (aka 'int') is not a class, namespace, or enumeration}} + // expected-error@-1{{no member named 'D' in namespace 'PR30619::A'}} }; } } diff --git a/clang/test/SemaCXX/pseudo-destructors.cpp b/clang/test/SemaCXX/pseudo-destructors.cpp index 55a96002be2abd..c474a2c4351b81 100644 --- a/clang/test/SemaCXX/pseudo-destructors.cpp +++ b/clang/test/SemaCXX/pseudo-destructors.cpp @@ -22,21 +22,21 @@ void cv_test(const volatile T* cvt) { void f(A* a, Foo *f, int *i, double *d, int ii) { a->~A(); a->A::~A(); - + a->~foo(); // expected-error{{undeclared identifier 'foo' in destructor name}} - + a->~Bar(); // expected-error{{destructor type 'Bar' (aka 'Foo') in object destruction expression does not match the type 'A' of the object being destroyed}} - + f->~Bar(); f->~Foo(); i->~Bar(); // expected-error{{does not match}} - + g().~Bar(); // expected-error{{non-scalar}} - + f->::~Bar(); // expected-error {{not a structure or union}} f->::Bar::~Bar(); f->N::~Wibble(); // expected-error{{'N' does not refer to a type}} expected-error{{'Wibble' does not refer to a type}} - + f->Bar::~Bar(17, 42); // expected-error{{cannot have any arguments}} i->~Integer(); @@ -148,12 +148,12 @@ namespace TwoPhaseLookup { namespace Template { template struct Y {}; template using G = Y; - template void f(T *p) { p->~G(); } // expected-error {{no member named '~Y'}} + template void f(T *p) { p->~G(); } // expected-error {{no member named 'G'}} void h1(Y *p) { p->~G(); } - void h2(Y *p) { f(p); } + void h2(Y *p) { f(p); } // expected-note {{in instantiation of}} namespace N { template struct G {}; } void h3(N::G *p) { p->~G(); } - void h4(N::G *p) { f(p); } // expected-note {{instantiation of}} + void h4(N::G *p) { f(p); } } namespace TemplateUndeclared { @@ -172,7 +172,10 @@ namespace TwoPhaseLookup { namespace TemplateNamesNonTemplate { int A; // expected-note 2{{non-template here}} - template int B; // expected-note 2{{variable template 'B' declared here}} expected-warning {{extension}} + template int B; // expected-note 2{{variable template 'B' declared here}} + #if __cplusplus < 201402L + // expected-warning@-2 {{extension}} + #endif using C = int; // expected-note 2{{non-template here}} template void f1(int *p) { p->~A(); } // expected-error {{'A' does not refer to a template}} diff --git a/clang/test/SemaCXX/static-assert-cxx17.cpp b/clang/test/SemaCXX/static-assert-cxx17.cpp index 41a7b025d0eb75..2ac52a767773fa 100644 --- a/clang/test/SemaCXX/static-assert-cxx17.cpp +++ b/clang/test/SemaCXX/static-assert-cxx17.cpp @@ -98,7 +98,8 @@ void foo6() { // expected-error@-1{{static assertion failed due to requirement '(const X[0]){} == nullptr'}} static_assert(sizeof(X().X::~X())>) == 0); // expected-error@-1{{static assertion failed due to requirement 'sizeof(X) == 0'}} \ - // expected-note@-1 {{evaluates to '8 == 0'}} + // expected-note@-1 {{evaluates to '8 == 0'}} \ + // expected-warning@-1 {{use 'template' keyword to treat 'X' as a dependent template name}} static_assert(constexpr_return_false()); // expected-error@-1{{static assertion failed due to requirement 'constexpr_return_false()'}} } diff --git a/clang/test/SemaTemplate/dependent-base-classes.cpp b/clang/test/SemaTemplate/dependent-base-classes.cpp index 92a37efaa7e73f..4cb88a5b4070a1 100644 --- a/clang/test/SemaTemplate/dependent-base-classes.cpp +++ b/clang/test/SemaTemplate/dependent-base-classes.cpp @@ -1,12 +1,12 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s template -struct X0 : T::template apply { +struct X0 : T::template apply { X0(U u) : T::template apply(u) { } }; template -struct X1 : T::apply { }; // expected-error{{use 'template' keyword to treat 'apply' as a dependent template name}} +struct X1 : T::apply { }; // expected-warning{{use 'template' keyword to treat 'apply' as a dependent template name}} template struct X2 : vector { }; // expected-error{{no template named 'vector'}} @@ -85,7 +85,7 @@ namespace PR6081 { struct A { }; template - class B : public A + class B : public A { public: template< class X > @@ -109,9 +109,9 @@ namespace PR6081 { namespace PR6413 { template class Base_A { }; - + class Base_B { }; - + template class Derived : public virtual Base_A @@ -120,12 +120,12 @@ namespace PR6413 { } namespace PR5812 { - template struct Base { - Base* p; - }; + template struct Base { + Base* p; + }; - template struct Derived: public Base { - typename Derived::Base* p; // meaning Derived::Base + template struct Derived: public Base { + typename Derived::Base* p; // meaning Derived::Base }; Derived di; diff --git a/clang/test/SemaTemplate/dependent-template-recover.cpp b/clang/test/SemaTemplate/dependent-template-recover.cpp index c7e27e8da25f16..1a41e5e9ac79fe 100644 --- a/clang/test/SemaTemplate/dependent-template-recover.cpp +++ b/clang/test/SemaTemplate/dependent-template-recover.cpp @@ -2,20 +2,20 @@ template struct X { void f(T* t) { - t->f0(); // expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}} - t->f0(); // expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}} + t->f0(); // expected-warning{{use 'template' keyword to treat 'f0' as a dependent template name}} + t->f0(); // expected-warning{{use 'template' keyword to treat 'f0' as a dependent template name}} - t->operator+(1); // expected-error{{use 'template' keyword to treat 'operator +' as a dependent template name}} - t->f1(1); // expected-error{{use 'template' keyword to treat 'f1' as a dependent template name}} - t->f1<3, int const>(1); // expected-error{{missing 'template' keyword prior to dependent template name 'f1'}} + t->operator+(1); // expected-warning{{use 'template' keyword to treat 'operator +' as a dependent template name}} + t->f1(1); // expected-warning{{use 'template' keyword to treat 'f1' as a dependent template name}} + t->f1<3, int const>(1); // expected-warning{{use 'template' keyword to treat 'f1' as a dependent template name}} - T::getAs(); // expected-error{{use 'template' keyword to treat 'getAs' as a dependent template name}} - t->T::getAs(); // expected-error{{use 'template' keyword to treat 'getAs' as a dependent template name}} + T::getAs(); // expected-warning{{use 'template' keyword to treat 'getAs' as a dependent template name}} + t->T::getAs(); // expected-warning{{use 'template' keyword to treat 'getAs' as a dependent template name}} (*t).f2(); // expected-error{{missing 'template' keyword prior to dependent template name 'f2'}} (*t).f2<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f2'}} T::f2<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f2'}} - T::f2<0, int>(0); // expected-error{{missing 'template' keyword prior to dependent template name 'f2'}} + T::f2<0, int>(0); // expected-warning{{use 'template' keyword to treat 'f2' as a dependent template name}} T::foo= 4>(); // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}} @@ -83,12 +83,14 @@ template void f(T t) { T::g(0); // ... but this one must be a template-id. - T::g(0); // expected-error {{missing 'template' keyword prior to dependent template name 'g'}} + T::g(0); // expected-warning {{use 'template' keyword to treat 'g' as a dependent template name}} + // expected-error@-1 {{no matching function for call to 'g'}} } struct Y { template void f(int); template static void g(int); // expected-warning 0-1{{extension}} + // expected-note@-1 {{candidate template ignored: invalid explicitly-specified argument for 1st template parameter}} }; void q() { void (*p)(int) = Y::g; } template void f<0>(Y); // expected-note {{in instantiation of}} diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp index ad73daa8e214c3..7768d2f03ac5ba 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp @@ -115,7 +115,7 @@ namespace CopyCounting { static_assert(f(X()) == 0); template struct Y { void f(); }; - template void g(Y y) { y.Y::f(); } + template void g(Y y) { y.template Y::f(); } void h() { constexpr A a; g(Y{}); } template struct Z { diff --git a/clang/test/SemaTemplate/template-id-expr.cpp b/clang/test/SemaTemplate/template-id-expr.cpp index dc12823ae307fb..760d6c5852403d 100644 --- a/clang/test/SemaTemplate/template-id-expr.cpp +++ b/clang/test/SemaTemplate/template-id-expr.cpp @@ -19,7 +19,7 @@ template struct X0 { template void f1(); - + template void f2(U) { f1(); @@ -39,9 +39,9 @@ struct Y { template struct X { X(int, int); - void f() { - Y >(X(0, 0)); - Y >(::X(0, 0)); + void f() { + Y >(X(0, 0)); + Y >(::X(0, 0)); } }; @@ -149,11 +149,11 @@ struct Y2 : Y1 { int x; x = Y1::f4(0); - x = Y1::f4(0); // expected-error {{use 'template'}} expected-error {{assigning to 'int' from incompatible type 'void'}} + x = Y1::f4(0); // expected-warning {{use 'template'}} expected-error {{assigning to 'int' from incompatible type 'void'}} x = Y1::template f4(0); // expected-error {{assigning to 'int' from incompatible type 'void'}} expected-error {{a template argument list is expected after a name prefixed by the template keyword}} x = p->f4(0); - x = p->f4(0); // expected-error {{assigning to 'int' from incompatible type 'void'}} expected-error {{use 'template'}} + x = p->f4(0); // expected-error {{assigning to 'int' from incompatible type 'void'}} expected-warning {{use 'template'}} x = p->template f4(0); // expected-error {{assigning to 'int' from incompatible type 'void'}} expected-error {{a template argument list is expected after a name prefixed by the template keyword}} } }; @@ -184,7 +184,7 @@ class E { #if __cplusplus <= 199711L // expected-warning@+2 {{extension}} #endif -template using D = int; // expected-note {{declared here}} +template using D = int; // expected-note {{declared here}} E ed; // expected-note {{instantiation of}} namespace non_functions { diff --git a/clang/test/SemaTemplate/typename-specifier-3.cpp b/clang/test/SemaTemplate/typename-specifier-3.cpp index 714830f0032d28..a9010969322e55 100644 --- a/clang/test/SemaTemplate/typename-specifier-3.cpp +++ b/clang/test/SemaTemplate/typename-specifier-3.cpp @@ -46,7 +46,7 @@ namespace PR12884_half_fixed { typedef int arg; }; struct C { - typedef typename B::X x; // expected-error {{use 'template'}} expected-error {{refers to non-type}} + typedef typename B::X x; // expected-warning {{use 'template'}} expected-error {{refers to non-type}} }; }; diff --git a/libcxx/include/regex b/libcxx/include/regex index bd75e8de7db438..b62b4343dab77f 100644 --- a/libcxx/include/regex +++ b/libcxx/include/regex @@ -4214,7 +4214,7 @@ public: _LIBCPP_HIDE_FROM_ABI int compare(const value_type* __s) const { return str().compare(__s); } _LIBCPP_HIDE_FROM_ABI void swap(sub_match& __s) _NOEXCEPT_(__is_nothrow_swappable_v<_BidirectionalIterator>) { - this->pair<_BidirectionalIterator, _BidirectionalIterator>::swap(__s); + this->template pair<_BidirectionalIterator, _BidirectionalIterator>::swap(__s); std::swap(matched, __s.matched); } };