diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index d5446f7fd92cc6..be2f06766a7558 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -310,6 +310,9 @@ Resolutions to C++ Defect Reports - Clang now considers ``noexcept(typeid(expr))`` more carefully, instead of always assuming that ``std::bad_typeid`` can be thrown. (`CWG2191: Incorrect result for noexcept(typeid(v)) `_). +- 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 c2feac525c1ea6..edaea6fe27cc3d 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -3676,9 +3676,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; @@ -3691,17 +3691,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: // @@ -3721,8 +3719,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 { @@ -3733,33 +3739,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 @@ -3784,34 +3789,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 9cd7a364cd3f1d..257a61c97c9c6d 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -1020,18 +1020,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 12aab09f285567..0bd2e35bf2e317 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -895,9 +895,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 6880fa4bb0b03a..b4f5270a359569 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3368,15 +3368,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 425b6e2a0b30c9..9c22c35535ede7 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 57994f4033922c..6be6f6725e5b75 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2802,7 +2802,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. /// @@ -2842,9 +2843,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 '::' -> ':' @@ -2853,11 +2851,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 @@ -2866,7 +2859,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); @@ -8566,11 +8558,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 @@ -8626,15 +8619,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); @@ -11122,15 +11114,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. /// @@ -11459,12 +11450,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, diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 4e1b3a5a94de76..9bb035c07b8ae1 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -8439,8 +8439,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); @@ -8474,7 +8480,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 8d2a1b5611ccc6..9d2883a8debb72 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1489,19 +1489,27 @@ 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_n(UnqualifiedLookups.data(), + UnqualifiedLookups.size(), + getTrailingObjects()); if (TemplateArgs) { auto Deps = TemplateArgumentDependence::None; @@ -1513,54 +1521,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 5444dcf027fe2f..f14c4762bee6ff 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -594,11 +594,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, @@ -4496,14 +4495,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) @@ -4985,11 +4981,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; } @@ -4997,10 +4991,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; } @@ -5010,10 +5003,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 2dd2e1b83548bc..b98eb70a848e1a 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -2343,10 +2343,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 1d364f77a81464..17be090dea3fd1 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; @@ -405,7 +407,6 @@ bool Parser::ParseOptionalCXXScopeSpecifier( : TemplateId->TemplateNameLoc; SS.SetInvalid(SourceRange(StartLoc, CCLoc)); } - continue; } @@ -528,18 +529,19 @@ bool Parser::ParseOptionalCXXScopeSpecifier( UnqualifiedId TemplateName; TemplateName.setIdentifier(&II, Tok.getLocation()); bool MemberOfUnknownSpecialization; - if (TemplateNameKind TNK = Actions.isTemplateName(getCurScope(), SS, - /*hasTemplateKeyword=*/false, - TemplateName, - ObjectType, - EnteringContext, - Template, - MemberOfUnknownSpecialization)) { + if (TemplateNameKind TNK = Actions.isTemplateName( + getCurScope(), SS, + /*hasTemplateKeyword=*/false, TemplateName, ObjectType, + EnteringContext, Template, MemberOfUnknownSpecialization, + /*Disambiguation=*/false, + /*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 && + if (!IsTypename && + (TNK == TNK_Undeclared_template || + (!HasScopeSpecifier && ObjectType)) && isTemplateArgumentList(1) == TPResult::False) break; @@ -566,11 +568,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 "); } @@ -1920,12 +1918,12 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc, // argument list. This affects examples such as // 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)) + // example, so we accept it anyway + 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, @@ -2532,8 +2530,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; @@ -2547,13 +2546,14 @@ 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. @@ -2580,13 +2580,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; } @@ -2611,14 +2611,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) @@ -2680,7 +2682,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; } @@ -3028,8 +3030,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 = @@ -3124,13 +3127,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; @@ -3220,8 +3225,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/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index 5b2d65247e72e5..dd61bb22e3dfa7 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -356,29 +356,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 { @@ -407,112 +419,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(); @@ -539,6 +521,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. @@ -594,14 +581,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() && @@ -779,7 +766,7 @@ bool Sema::ActOnCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, return true; return BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS, - /*ScopeLookupResult=*/nullptr, false, + /*ErrorRecoveryLookup=*/false, IsCorrectedToColon, OnlyNamespace); } @@ -840,7 +827,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 81334c817b2af2..fa0eb1d2afbeee 100644 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -306,8 +306,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 09c2d35dab4570..a7da5285ad0e40 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -1275,9 +1275,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; @@ -4901,16 +4903,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; @@ -14336,8 +14334,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, @@ -14543,13 +14543,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(); @@ -17155,8 +17153,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, std::nullopt, Loc, nullptr, diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index d6b85cbcaf56b5..9940f8cb629554 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -2624,7 +2624,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 2070f3b7bb3a2f..8519618bacfee6 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -552,11 +552,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 @@ -590,8 +588,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 @@ -767,8 +765,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); @@ -784,7 +783,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); @@ -828,10 +827,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 @@ -969,17 +967,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, @@ -989,8 +981,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) { @@ -1195,9 +1187,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()) @@ -1763,15 +1755,16 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base, const TemplateArgumentListInfo *TemplateArgs; DecomposeUnqualifiedId(Id, TemplateArgsBuffer, NameInfo, TemplateArgs); - - bool IsArrow = (OpKind == tok::arrow); + 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(); @@ -1779,8 +1772,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())); @@ -1924,9 +1917,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 5ea6b06121c7cd..f49635aa445a63 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -16043,13 +16043,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 32d42f3c3f3bb7..da2e99b6bc00c7 100644 --- a/clang/lib/Sema/SemaStmtAsm.cpp +++ b/clang/lib/Sema/SemaStmtAsm.cpp @@ -900,7 +900,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(); @@ -923,8 +924,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 29d668e4fd8d75..dfc861195d0a9e 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -174,15 +174,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; @@ -213,8 +210,9 @@ 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(); if (AssumedTemplate != AssumedTemplateKind::None) { @@ -380,7 +378,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; @@ -389,92 +387,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()) @@ -494,7 +489,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. @@ -506,11 +501,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; @@ -543,13 +542,8 @@ 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) { @@ -741,7 +735,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); } @@ -5855,14 +5849,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 ? @@ -5897,9 +5887,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); @@ -5938,7 +5929,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 a7bc6749c58520..64c5a16b2e4c38 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1515,12 +1515,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 CXXAssumeAttr *TransformCXXAssumeAttr(const CXXAssumeAttr *AA); const LoopHintAttr *TransformLoopHintAttr(const LoopHintAttr *LH); @@ -1952,8 +1951,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()) { @@ -2025,8 +2023,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 655f248d113837..f6021440cda69f 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -541,10 +541,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. /// @@ -585,12 +584,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. /// @@ -1140,8 +1138,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(); @@ -1312,8 +1310,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. @@ -2849,15 +2846,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()) { @@ -2894,6 +2890,7 @@ class TreeTransform { CXXScopeSpec SS; SS.Adopt(QualifierLoc); + SS.setUnqualifiedLookups(UnqualifiedLookups); Base = BaseResult.get(); if (Base->containsErrors()) @@ -2926,10 +2923,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. @@ -3002,10 +2998,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. @@ -3573,46 +3568,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. @@ -3831,10 +3817,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; @@ -3849,14 +3833,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. @@ -3883,13 +3865,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. @@ -4054,18 +4034,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, @@ -4384,7 +4360,7 @@ Sema::ConditionResult TreeTransform::TransformCondition( template NestedNameSpecifierLoc TreeTransform::TransformNestedNameSpecifierLoc( NestedNameSpecifierLoc NNS, QualType ObjectType, - NamedDecl *FirstQualifierInScope) { + ArrayRef UnqualifiedLookups) { SmallVector Qualifiers; auto insertNNS = [&Qualifiers](NestedNameSpecifierLoc NNS) { @@ -4395,6 +4371,8 @@ NestedNameSpecifierLoc TreeTransform::TransformNestedNameSpecifierLoc( insertNNS(NNS); CXXScopeSpec SS; + SS.setUnqualifiedLookups(UnqualifiedLookups); + while (!Qualifiers.empty()) { NestedNameSpecifierLoc Q = Qualifiers.pop_back_val(); NestedNameSpecifier *QNNS = Q.getNestedNameSpecifier(); @@ -4404,8 +4382,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; } @@ -4443,8 +4422,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(); @@ -4477,7 +4455,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(); } @@ -4560,14 +4538,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"); @@ -4591,7 +4565,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() && @@ -4603,13 +4577,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, @@ -5153,39 +5123,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)); @@ -5198,7 +5160,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; @@ -5208,13 +5170,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; @@ -12358,7 +12318,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); @@ -12366,16 +12327,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 @@ -13502,9 +13458,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; @@ -13530,7 +13485,7 @@ TreeTransform::TransformCXXPseudoDestructorExpr( if (E->getScopeTypeInfo()) { CXXScopeSpec EmptySS; ScopeTypeInfo = getDerived().TransformTypeInObjectScope( - E->getScopeTypeInfo(), ObjectType, nullptr, EmptySS); + E->getScopeTypeInfo(), ObjectType, EmptySS); if (!ScopeTypeInfo) return ExprError(); } @@ -14791,19 +14746,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(); } @@ -14822,23 +14775,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()); @@ -14847,15 +14793,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 @@ -14916,11 +14856,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)); } @@ -16277,22 +16217,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(); } @@ -16440,13 +16376,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 e23ceffb10bfe6..5a5d7be69a2c31 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1993,42 +1993,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 @@ -4075,16 +4076,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 ea499019c9d16c..30c5b1bc56f479 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1988,34 +1988,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..97d3587881bbc1 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/drs/cwg1xx.cpp b/clang/test/CXX/drs/cwg1xx.cpp index e7dddd1ea9278f..6bca4608184254 100644 --- a/clang/test/CXX/drs/cwg1xx.cpp +++ b/clang/test/CXX/drs/cwg1xx.cpp @@ -615,10 +615,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 { @@ -628,10 +626,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 37ab93559e3690..a4d735e05e9b83 100644 --- a/clang/test/CXX/temp/temp.res/p3.cpp +++ b/clang/test/CXX/temp/temp.res/p3.cpp @@ -30,6 +30,6 @@ template int A::template C::*f5() {} // expected-error {{has 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 cdbe1e95cba965..651a86fb6e2265 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 (65): 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 @@ -61,7 +62,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 c616a77f36619a..b9aeffbf034b2f 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/pseudo-destructors.cpp b/clang/test/SemaCXX/pseudo-destructors.cpp index 55a96002be2abd..44dc9ce8b15208 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 {{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 { diff --git a/clang/test/SemaCXX/static-assert-cxx17.cpp b/clang/test/SemaCXX/static-assert-cxx17.cpp index 41a7b025d0eb75..754f4ae5f1d388 100644 --- a/clang/test/SemaCXX/static-assert-cxx17.cpp +++ b/clang/test/SemaCXX/static-assert-cxx17.cpp @@ -96,7 +96,7 @@ void foo6() { // expected-error@-1{{static assertion failed due to requirement 'static_cast *>(nullptr)'}} static_assert((const X[]){} == nullptr); // expected-error@-1{{static assertion failed due to requirement '(const X[0]){} == nullptr'}} - static_assert(sizeof(X().X::~X())>) == 0); + static_assert(sizeof(X().template X::~X())>) == 0); // expected-error@-1{{static assertion failed due to requirement 'sizeof(X) == 0'}} \ // expected-note@-1 {{evaluates to '8 == 0'}} static_assert(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..c763989e6dadb2 100644 --- a/clang/test/SemaTemplate/dependent-template-recover.cpp +++ b/clang/test/SemaTemplate/dependent-template-recover.cpp @@ -2,15 +2,15 @@ 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->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-error{{missing 'template' keyword prior to dependent template name 'f1'}} - 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'}} 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 b8141351213212..17ad0cf5b2aea7 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); } }; diff --git a/llvm/include/llvm/ADT/ArrayRef.h b/llvm/include/llvm/ADT/ArrayRef.h index ac40ec4a6b2404..1c6799f1c56eda 100644 --- a/llvm/include/llvm/ADT/ArrayRef.h +++ b/llvm/include/llvm/ADT/ArrayRef.h @@ -460,8 +460,11 @@ namespace llvm { OwningArrayRef &operator=(OwningArrayRef &&Other) { delete[] this->data(); - this->MutableArrayRef::operator=(Other); - Other.MutableArrayRef::operator=(MutableArrayRef()); + using Base = MutableArrayRef; + // GCC versions prior to 11.1 incorrectly reject if the 'template' keyword + // is used prior to the nested-name-specifier here. + this->Base::operator=(Other); + Other.Base::operator=(Base()); return *this; }