diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 83a1353f3c40..882277efb211 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -313,10 +313,6 @@ 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 < `_). - The warning can be disabled via `-Wno-missing-dependent-template-keyword`. - C Language Changes ------------------ diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index edaea6fe27cc..c2feac525c1e 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -3676,9 +3676,9 @@ public: /// an implicit access if a qualifier is provided. class CXXDependentScopeMemberExpr final : public Expr, - private llvm::TrailingObjects< - CXXDependentScopeMemberExpr, NestedNameSpecifierLoc, DeclAccessPair, - ASTTemplateKWAndArgsInfo, TemplateArgumentLoc> { + private llvm::TrailingObjects { friend class ASTStmtReader; friend class ASTStmtWriter; friend TrailingObjects; @@ -3691,15 +3691,17 @@ 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: // @@ -3719,16 +3721,8 @@ class CXXDependentScopeMemberExpr final return CXXDependentScopeMemberExprBits.HasTemplateKWAndArgsInfo; } - unsigned getNumUnqualifiedLookups() const { - return CXXDependentScopeMemberExprBits.NumUnqualifiedLookups; - } - - unsigned numTrailingObjects(OverloadToken) const { - return hasQualifier(); - } - - unsigned numTrailingObjects(OverloadToken) const { - return getNumUnqualifiedLookups(); + bool hasFirstQualifierFoundInScope() const { + return CXXDependentScopeMemberExprBits.HasFirstQualifierFoundInScope; } unsigned numTrailingObjects(OverloadToken) const { @@ -3739,32 +3733,33 @@ 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, - ArrayRef UnqualifiedLookups, + NamedDecl *FirstQualifierFoundInScope, DeclarationNameInfo MemberNameInfo, const TemplateArgumentListInfo *TemplateArgs); - CXXDependentScopeMemberExpr(EmptyShell Empty, bool HasQualifier, - unsigned NumUnqualifiedLookups, - bool HasTemplateKWAndArgsInfo); + CXXDependentScopeMemberExpr(EmptyShell Empty, bool HasTemplateKWAndArgsInfo, + bool HasFirstQualifierFoundInScope); public: static CXXDependentScopeMemberExpr * Create(const ASTContext &Ctx, Expr *Base, QualType BaseType, bool IsArrow, SourceLocation OperatorLoc, NestedNameSpecifierLoc QualifierLoc, - SourceLocation TemplateKWLoc, - ArrayRef UnqualifiedLookups, + SourceLocation TemplateKWLoc, NamedDecl *FirstQualifierFoundInScope, DeclarationNameInfo MemberNameInfo, const TemplateArgumentListInfo *TemplateArgs); static CXXDependentScopeMemberExpr * - CreateEmpty(const ASTContext &Ctx, bool HasQualifier, - unsigned NumUnqualifiedLookups, bool HasTemplateKWAndArgsInfo, - unsigned NumTemplateArgs); + CreateEmpty(const ASTContext &Ctx, bool HasTemplateKWAndArgsInfo, + unsigned NumTemplateArgs, bool HasFirstQualifierFoundInScope); /// True if this is an implicit access, i.e. one in which the /// member being accessed was not written in the source. The source @@ -3789,35 +3784,34 @@ public: bool isArrow() const { return CXXDependentScopeMemberExprBits.IsArrow; } /// Retrieve the location of the '->' or '.' operator. - 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; + SourceLocation getOperatorLoc() const { + return CXXDependentScopeMemberExprBits.OperatorLoc; } - /// 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(); - } - - /// If the member name was qualified, retrieves the - /// nested-name-specifier that precedes the member name. Otherwise, returns - /// NULL. + /// Retrieve the nested-name-specifier that qualifies the member name. NestedNameSpecifier *getQualifier() const { - return getQualifierLoc().getNestedNameSpecifier(); + return QualifierLoc.getNestedNameSpecifier(); } - /// 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 nested-name-specifier that qualifies the member + /// name, with source location information. + NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; } + + /// 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 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 257a61c97c9c..9cd7a364cd3f 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -1020,19 +1020,18 @@ protected: 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; - /// Number of declarations found by unqualified lookup for the - /// first component name of the nested-name-specifier. - unsigned NumUnqualifiedLookups; + /// See getFirstQualifierFoundInScope() and the comment listing + /// the trailing objects. + LLVM_PREFERRED_TYPE(bool) + unsigned HasFirstQualifierFoundInScope : 1; + + /// The location of the '->' or '.' operator. + SourceLocation OperatorLoc; }; class OverloadExprBitfields { diff --git a/clang/include/clang/AST/UnresolvedSet.h b/clang/include/clang/AST/UnresolvedSet.h index ef44499ce592..1369725ab4e9 100644 --- a/clang/include/clang/AST/UnresolvedSet.h +++ b/clang/include/clang/AST/UnresolvedSet.h @@ -97,10 +97,6 @@ public: 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 e00cd47411cb..12aab09f2855 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -895,9 +895,10 @@ def missing_template_arg_list_after_template_kw : Extension< "keyword">, InGroup>, DefaultError; -def ext_missing_dependent_template_keyword : ExtWarn< - "use 'template' keyword to treat '%0' as a dependent template name">, - InGroup>; +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< + "use 'template' keyword to treat '%0' as a dependent template name">; def ext_extern_template : Extension< "extern templates are a C++11 extension">, InGroup; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index bb3f08aef037..126fea0aef2a 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3372,11 +3372,15 @@ private: BaseResult ParseBaseSpecifier(Decl *ClassDecl); AccessSpecifier getAccessSpecifierIfPresent() const; - bool ParseUnqualifiedIdTemplateId( - CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors, - SourceLocation TemplateKWLoc, SourceLocation TildeLoc, - IdentifierInfo *Name, SourceLocation NameLoc, bool EnteringContext, - UnqualifiedId &Id, bool AssumeTemplateId); + bool ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, + ParsedType ObjectType, + bool ObjectHadErrors, + SourceLocation TemplateKWLoc, + 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 9c22c35535ed..425b6e2a0b30 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -75,7 +75,6 @@ class CXXScopeSpec { SourceRange Range; NestedNameSpecifierLocBuilder Builder; ArrayRef TemplateParamLists; - ArrayRef UnqualifiedLookups; public: SourceRange getRange() const { return Range; } @@ -92,13 +91,6 @@ public: 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 6b765ef3c980..b0a08a05ac6a 100644 --- a/clang/include/clang/Sema/Lookup.h +++ b/clang/include/clang/Sema/Lookup.h @@ -483,15 +483,11 @@ public: 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) { - addAllDecls(Other.Decls.pairs()); + Decls.append(Other.Decls.begin(), Other.Decls.end()); + ResultKind = Found; } /// 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 a0821d5298bc..48dff1b76cc5 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2803,8 +2803,7 @@ public: /// (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 LookupFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS, - UnresolvedSetImpl &R); + NamedDecl *FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS); /// Keeps information about an identifier in a nested-name-spec. /// @@ -2844,6 +2843,9 @@ public: /// \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 '::' -> ':' @@ -2852,6 +2854,11 @@ public: /// 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 @@ -2860,6 +2867,7 @@ public: /// specifier. bool BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, bool EnteringContext, CXXScopeSpec &SS, + NamedDecl *ScopeLookupResult, bool ErrorRecoveryLookup, bool *IsCorrectedToColon = nullptr, bool OnlyNamespace = false); @@ -8559,12 +8567,11 @@ public: const TemplateArgumentListInfo *TemplateArgs, bool IsDefiniteInstance, const Scope *S); - ExprResult - ActOnDependentMemberExpr(Expr *Base, QualType BaseType, bool IsArrow, - SourceLocation OpLoc, const CXXScopeSpec &SS, - SourceLocation TemplateKWLoc, - const DeclarationNameInfo &NameInfo, - const TemplateArgumentListInfo *TemplateArgs); + ExprResult ActOnDependentMemberExpr( + Expr *Base, QualType BaseType, bool IsArrow, SourceLocation OpLoc, + const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, + NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo, + const TemplateArgumentListInfo *TemplateArgs); /// The main callback when the parser finds something like /// expression . [nested-name-specifier] identifier @@ -8620,14 +8627,15 @@ public: ExprResult BuildMemberReferenceExpr( Expr *Base, QualType BaseType, SourceLocation OpLoc, bool IsArrow, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, - const DeclarationNameInfo &NameInfo, + NamedDecl *FirstQualifierInScope, 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, LookupResult &R, + SourceLocation TemplateKWLoc, + NamedDecl *FirstQualifierInScope, LookupResult &R, const TemplateArgumentListInfo *TemplateArgs, const Scope *S, bool SuppressQualifierCheck = false, ActOnMemberAccessExtraArgs *ExtraArgs = nullptr); @@ -11115,14 +11123,15 @@ public: QualType ObjectType, bool EnteringContext, RequiredTemplateKind RequiredTemplate = SourceLocation(), AssumedTemplateKind *ATK = nullptr, - bool AllowTypoCorrection = true, bool MayBeNNS = false); + bool AllowTypoCorrection = true); - 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); + TemplateNameKind isTemplateName(Scope *S, CXXScopeSpec &SS, + bool hasTemplateKeyword, + const UnqualifiedId &Name, + ParsedType ObjectType, bool EnteringContext, + TemplateTy &Template, + bool &MemberOfUnknownSpecialization, + bool Disambiguation = false); /// Try to resolve an undeclared template name as a type template. /// @@ -11455,11 +11464,12 @@ public: /// 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, bool MayBeNNS = false); + TemplateNameKind ActOnTemplateName(Scope *S, CXXScopeSpec &SS, + SourceLocation TemplateKWLoc, + const UnqualifiedId &Name, + ParsedType ObjectType, + bool EnteringContext, TemplateTy &Template, + bool AllowInjectedClassName = 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 9bb035c07b8a..4e1b3a5a94de 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -8439,14 +8439,8 @@ ExpectedStmt ASTNodeImporter::VisitCXXDependentScopeMemberExpr( auto ToOperatorLoc = importChecked(Err, E->getOperatorLoc()); auto ToQualifierLoc = importChecked(Err, E->getQualifierLoc()); auto ToTemplateKeywordLoc = importChecked(Err, E->getTemplateKeywordLoc()); - - UnresolvedSet<8> ToUnqualifiedLookups; - for (auto D : E->unqualified_lookups()) - if (auto ToDOrErr = import(D.getDecl())) - ToUnqualifiedLookups.addDecl(*ToDOrErr); - else - return ToDOrErr.takeError(); - + auto ToFirstQualifierFoundInScope = + importChecked(Err, E->getFirstQualifierFoundInScope()); if (Err) return std::move(Err); @@ -8480,7 +8474,7 @@ ExpectedStmt ASTNodeImporter::VisitCXXDependentScopeMemberExpr( return CXXDependentScopeMemberExpr::Create( Importer.getToContext(), ToBase, ToType, E->isArrow(), ToOperatorLoc, - ToQualifierLoc, ToTemplateKeywordLoc, ToUnqualifiedLookups.pairs(), + ToQualifierLoc, ToTemplateKeywordLoc, ToFirstQualifierFoundInScope, ToMemberNameInfo, ResInfo); } diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index 9d2883a8debb..8d2a1b5611cc 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1489,27 +1489,19 @@ SourceLocation CXXUnresolvedConstructExpr::getBeginLoc() const { CXXDependentScopeMemberExpr::CXXDependentScopeMemberExpr( const ASTContext &Ctx, Expr *Base, QualType BaseType, bool IsArrow, SourceLocation OperatorLoc, NestedNameSpecifierLoc QualifierLoc, - SourceLocation TemplateKWLoc, ArrayRef UnqualifiedLookups, + SourceLocation TemplateKWLoc, NamedDecl *FirstQualifierFoundInScope, DeclarationNameInfo MemberNameInfo, const TemplateArgumentListInfo *TemplateArgs) : Expr(CXXDependentScopeMemberExprClass, Ctx.DependentTy, VK_LValue, OK_Ordinary), - Base(Base), BaseType(BaseType), MemberNameInfo(MemberNameInfo), - OperatorLoc(OperatorLoc) { + Base(Base), BaseType(BaseType), QualifierLoc(QualifierLoc), + MemberNameInfo(MemberNameInfo) { CXXDependentScopeMemberExprBits.IsArrow = IsArrow; - CXXDependentScopeMemberExprBits.HasQualifier = QualifierLoc.hasQualifier(); - CXXDependentScopeMemberExprBits.NumUnqualifiedLookups = - UnqualifiedLookups.size(); CXXDependentScopeMemberExprBits.HasTemplateKWAndArgsInfo = (TemplateArgs != nullptr) || TemplateKWLoc.isValid(); - - if (hasQualifier()) - new (getTrailingObjects()) - NestedNameSpecifierLoc(QualifierLoc); - - std::uninitialized_copy_n(UnqualifiedLookups.data(), - UnqualifiedLookups.size(), - getTrailingObjects()); + CXXDependentScopeMemberExprBits.HasFirstQualifierFoundInScope = + FirstQualifierFoundInScope != nullptr; + CXXDependentScopeMemberExprBits.OperatorLoc = OperatorLoc; if (TemplateArgs) { auto Deps = TemplateArgumentDependence::None; @@ -1521,59 +1513,54 @@ CXXDependentScopeMemberExpr::CXXDependentScopeMemberExpr( TemplateKWLoc); } + if (hasFirstQualifierFoundInScope()) + *getTrailingObjects() = FirstQualifierFoundInScope; setDependence(computeDependence(this)); } CXXDependentScopeMemberExpr::CXXDependentScopeMemberExpr( - EmptyShell Empty, bool HasQualifier, unsigned NumUnqualifiedLookups, - bool HasTemplateKWAndArgsInfo) + EmptyShell Empty, bool HasTemplateKWAndArgsInfo, + bool HasFirstQualifierFoundInScope) : 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, ArrayRef UnqualifiedLookups, + SourceLocation TemplateKWLoc, NamedDecl *FirstQualifierFoundInScope, 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; - unsigned Size = - totalSizeToAlloc( - HasQualifier, NumUnqualifiedLookups, HasTemplateKWAndArgsInfo, - NumTemplateArgs); + bool HasFirstQualifierFoundInScope = FirstQualifierFoundInScope != nullptr; + + unsigned Size = totalSizeToAlloc( + HasTemplateKWAndArgsInfo, NumTemplateArgs, HasFirstQualifierFoundInScope); void *Mem = Ctx.Allocate(Size, alignof(CXXDependentScopeMemberExpr)); return new (Mem) CXXDependentScopeMemberExpr( Ctx, Base, BaseType, IsArrow, OperatorLoc, QualifierLoc, TemplateKWLoc, - UnqualifiedLookups, MemberNameInfo, TemplateArgs); + FirstQualifierFoundInScope, MemberNameInfo, TemplateArgs); } CXXDependentScopeMemberExpr *CXXDependentScopeMemberExpr::CreateEmpty( - const ASTContext &Ctx, bool HasQualifier, unsigned NumUnqualifiedLookups, - bool HasTemplateKWAndArgsInfo, unsigned NumTemplateArgs) { - assert(!NumTemplateArgs || HasTemplateKWAndArgsInfo); - assert(!NumUnqualifiedLookups || HasQualifier); + const ASTContext &Ctx, bool HasTemplateKWAndArgsInfo, + unsigned NumTemplateArgs, bool HasFirstQualifierFoundInScope) { + assert(NumTemplateArgs == 0 || HasTemplateKWAndArgsInfo); - unsigned Size = - totalSizeToAlloc( - HasQualifier, NumUnqualifiedLookups, HasTemplateKWAndArgsInfo, - NumTemplateArgs); + unsigned Size = totalSizeToAlloc( + HasTemplateKWAndArgsInfo, NumTemplateArgs, HasFirstQualifierFoundInScope); void *Mem = Ctx.Allocate(Size, alignof(CXXDependentScopeMemberExpr)); - return new (Mem) CXXDependentScopeMemberExpr(EmptyShell(), HasQualifier, - NumUnqualifiedLookups, - HasTemplateKWAndArgsInfo); + return new (Mem) CXXDependentScopeMemberExpr( + EmptyShell(), HasTemplateKWAndArgsInfo, HasFirstQualifierFoundInScope); } CXXThisExpr *CXXThisExpr::Create(const ASTContext &Ctx, SourceLocation L, diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index f14c4762bee6..5444dcf027fe 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -594,10 +594,11 @@ private: void mangleMemberExprBase(const Expr *base, bool isArrow); void mangleMemberExpr(const Expr *base, bool isArrow, NestedNameSpecifier *qualifier, - ArrayRef UnqualifiedLookups, + NamedDecl *firstQualifierLookup, 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, @@ -4495,11 +4496,14 @@ void CXXNameMangler::mangleMemberExprBase(const Expr *Base, bool IsArrow) { } /// Mangles a member expression. -void CXXNameMangler::mangleMemberExpr( - const Expr *base, bool isArrow, NestedNameSpecifier *qualifier, - ArrayRef UnqualifiedLookups, DeclarationName member, - const TemplateArgumentLoc *TemplateArgs, unsigned NumTemplateArgs, - unsigned arity) { +void CXXNameMangler::mangleMemberExpr(const Expr *base, + bool isArrow, + NestedNameSpecifier *qualifier, + NamedDecl *firstQualifierLookup, + DeclarationName member, + const TemplateArgumentLoc *TemplateArgs, + unsigned NumTemplateArgs, + unsigned arity) { // ::= dt // ::= pt if (base) @@ -4981,9 +4985,11 @@ recurse: case Expr::MemberExprClass: { NotPrimaryExpr(); const MemberExpr *ME = cast(E); - mangleMemberExpr(ME->getBase(), ME->isArrow(), ME->getQualifier(), - std::nullopt, ME->getMemberDecl()->getDeclName(), - ME->getTemplateArgs(), ME->getNumTemplateArgs(), Arity); + mangleMemberExpr(ME->getBase(), ME->isArrow(), + ME->getQualifier(), nullptr, + ME->getMemberDecl()->getDeclName(), + ME->getTemplateArgs(), ME->getNumTemplateArgs(), + Arity); break; } @@ -4991,9 +4997,10 @@ recurse: NotPrimaryExpr(); const UnresolvedMemberExpr *ME = cast(E); mangleMemberExpr(ME->isImplicitAccess() ? nullptr : ME->getBase(), - ME->isArrow(), ME->getQualifier(), std::nullopt, - ME->getMemberName(), ME->getTemplateArgs(), - ME->getNumTemplateArgs(), Arity); + ME->isArrow(), ME->getQualifier(), nullptr, + ME->getMemberName(), + ME->getTemplateArgs(), ME->getNumTemplateArgs(), + Arity); break; } @@ -5003,8 +5010,10 @@ recurse: = cast(E); mangleMemberExpr(ME->isImplicitAccess() ? nullptr : ME->getBase(), ME->isArrow(), ME->getQualifier(), - ME->unqualified_lookups(), ME->getMember(), - ME->getTemplateArgs(), ME->getNumTemplateArgs(), Arity); + ME->getFirstQualifierFoundInScope(), + ME->getMember(), + ME->getTemplateArgs(), ME->getNumTemplateArgs(), + Arity); break; } diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 310a38b2cd78..259bfa1d77cd 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -2348,9 +2348,10 @@ 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 17be090dea3f..1d364f77a814 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -100,8 +100,7 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType, bool MemberOfUnknownSpecialization; if (!Actions.isTemplateName(getCurScope(), SS, /*hasTemplateKeyword=*/false, TemplateName, ObjectType, EnteringContext, - Template, MemberOfUnknownSpecialization, - /*Disambiguation=*/false, /*MayBeNNS=*/true)) + Template, MemberOfUnknownSpecialization)) return; FixDigraph(*this, PP, Next, SecondToken, tok::unknown, @@ -354,8 +353,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier( TemplateTy Template; TemplateNameKind TNK = Actions.ActOnTemplateName( getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType, - EnteringContext, Template, /*AllowInjectedClassName*/ true, - /*MayBeNNS=*/true); + EnteringContext, Template, /*AllowInjectedClassName*/ true); if (AnnotateTemplateIdToken(Template, TNK, SS, TemplateKWLoc, TemplateName, false)) return true; @@ -407,6 +405,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier( : TemplateId->TemplateNameLoc; SS.SetInvalid(SourceRange(StartLoc, CCLoc)); } + continue; } @@ -529,19 +528,18 @@ 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, - /*Disambiguation=*/false, - /*MayBeNNS=*/true)) { + if (TemplateNameKind TNK = Actions.isTemplateName(getCurScope(), SS, + /*hasTemplateKeyword=*/false, + TemplateName, + ObjectType, + EnteringContext, + Template, + MemberOfUnknownSpecialization)) { // 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 || - (!HasScopeSpecifier && ObjectType)) && + if (!IsTypename && TNK == TNK_Undeclared_template && isTemplateArgumentList(1) == TPResult::False) break; @@ -568,7 +566,11 @@ 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. - Diag(Tok.getLocation(), diag::ext_missing_dependent_template_keyword) + unsigned DiagID = diag::err_missing_dependent_template_keyword; + if (getLangOpts().MicrosoftExt) + DiagID = diag::warn_missing_dependent_template_keyword; + + Diag(Tok.getLocation(), DiagID) << II.getName() << FixItHint::CreateInsertion(Tok.getLocation(), "template "); } @@ -1918,12 +1920,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(), - /*TemplateKWLoc=*/SourceLocation(), TildeLoc, - Name, NameLoc, false, SecondTypeName, - /*AssumeTemplateId=*/true)) + // example, so we accept it anyway. + if (Tok.is(tok::less) && + ParseUnqualifiedIdTemplateId( + SS, ObjectType, Base && Base->containsErrors(), SourceLocation(), + Name, NameLoc, false, SecondTypeName, + /*AssumeTemplateId=*/true)) return ExprError(); return Actions.ActOnPseudoDestructorExpr(getCurScope(), Base, OpLoc, OpKind, @@ -2530,9 +2532,8 @@ 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, SourceLocation TildeLoc, IdentifierInfo *Name, - SourceLocation NameLoc, bool EnteringContext, UnqualifiedId &Id, - bool AssumeTemplateId) { + SourceLocation TemplateKWLoc, IdentifierInfo *Name, SourceLocation NameLoc, + bool EnteringContext, UnqualifiedId &Id, bool AssumeTemplateId) { assert(Tok.is(tok::less) && "Expected '<' to finish parsing a template-id"); TemplateTy Template; @@ -2546,14 +2547,13 @@ 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, - TildeLoc.isValid()); + /*AllowInjectedClassName*/ true); } else { bool MemberOfUnknownSpecialization; - TNK = Actions.isTemplateName( - getCurScope(), SS, TemplateKWLoc.isValid(), Id, ObjectType, - EnteringContext, Template, MemberOfUnknownSpecialization, - /*Disambiguation=*/false, TildeLoc.isValid()); + TNK = Actions.isTemplateName(getCurScope(), SS, + TemplateKWLoc.isValid(), Id, + ObjectType, EnteringContext, Template, + MemberOfUnknownSpecialization); // 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::ext_missing_dependent_template_keyword) + Diag(Id.StartLocation, diag::err_missing_dependent_template_keyword) << Name << FixItHint::CreateInsertion(Id.StartLocation, "template "); } TNK = Actions.ActOnTemplateName( getCurScope(), SS, TemplateKWLoc, Id, ObjectType, EnteringContext, - Template, /*AllowInjectedClassName=*/true, TildeLoc.isValid()); + Template, /*AllowInjectedClassName*/ true); } else if (TNK == TNK_Non_template) { return false; } @@ -2611,16 +2611,14 @@ bool Parser::ParseUnqualifiedIdTemplateId( bool MemberOfUnknownSpecialization; TemplateName.setIdentifier(Name, NameLoc); if (ObjectType) { - TNK = Actions.ActOnTemplateName(getCurScope(), SS, TemplateKWLoc, - TemplateName, ObjectType, EnteringContext, - Template, /*AllowInjectedClassName=*/true, - /*MayBeNNS=*/true); + TNK = Actions.ActOnTemplateName( + getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType, + EnteringContext, Template, /*AllowInjectedClassName*/ true); } else { TNK = Actions.isTemplateName(getCurScope(), SS, TemplateKWLoc.isValid(), - TemplateName, ObjectType, EnteringContext, - Template, MemberOfUnknownSpecialization, - /*Disambiguation=*/false, - /*MayBeNNS=*/true); + TemplateName, ObjectType, + EnteringContext, Template, + MemberOfUnknownSpecialization); if (TNK == TNK_Non_template && !Id.DestructorName.get()) { Diag(NameLoc, diag::err_destructor_template_id) @@ -2682,7 +2680,7 @@ bool Parser::ParseUnqualifiedIdTemplateId( if (Id.getKind() == UnqualifiedIdKind::IK_ConstructorName) Id.setConstructorName(Type.get(), NameLoc, RAngleLoc); else - Id.setDestructorName(TildeLoc, Type.get(), RAngleLoc); + Id.setDestructorName(Id.StartLocation, Type.get(), RAngleLoc); return false; } @@ -3030,9 +3028,8 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType, if (Tok.is(tok::less)) return ParseUnqualifiedIdTemplateId( SS, ObjectType, ObjectHadErrors, - TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), - /*TildeLoc=*/SourceLocation(), Id, IdLoc, EnteringContext, Result, - TemplateSpecified); + TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), Id, IdLoc, + EnteringContext, Result, TemplateSpecified); if (TemplateSpecified) { TemplateNameKind TNK = @@ -3127,15 +3124,13 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType, Tok.is(tok::less)) return ParseUnqualifiedIdTemplateId( SS, ObjectType, ObjectHadErrors, - TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), - /*TildeLoc=*/SourceLocation(), /*Name=*/nullptr, - /*NameLoc=*/SourceLocation(), EnteringContext, Result, - TemplateSpecified); + TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), nullptr, + 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; @@ -3225,8 +3220,8 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType, Result.setDestructorName(TildeLoc, nullptr, ClassNameLoc); return ParseUnqualifiedIdTemplateId( SS, ObjectType, ObjectHadErrors, - TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), TildeLoc, - ClassName, ClassNameLoc, EnteringContext, Result, TemplateSpecified); + TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), 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 dd61bb22e3df..5b2d65247e72 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -356,41 +356,29 @@ bool Sema::isAcceptableNestedNameSpecifier(const NamedDecl *SD, return false; } -/// 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; +NamedDecl *Sema::FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS) { + if (!S || !NNS) + return nullptr; while (NNS->getPrefix()) NNS = NNS->getPrefix(); - // 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); + if (NNS->getKind() != NestedNameSpecifier::Identifier) + return nullptr; + + LookupResult Found(*this, NNS->getAsIdentifier(), SourceLocation(), + LookupNestedNameSpecifierName); LookupName(Found, S); + assert(!Found.isAmbiguous() && "Cannot handle ambiguities here yet"); - if (Found.empty()) - return false; + if (!Found.isSingleResult()) + return nullptr; - R.addAllDecls(Found.asUnresolvedSet().pairs()); - Found.suppressDiagnostics(); - return true; + NamedDecl *Result = Found.getFoundDecl(); + if (isAcceptableNestedNameSpecifier(Result)) + return Result; + + return nullptr; } namespace { @@ -419,82 +407,112 @@ public: 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); - // 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; + // 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()); } 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. - // 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(); + // 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; } - ObjectTypeSearchedInScope = true; + } else if (!isDependent) { + // Perform unqualified name lookup in the current scope. + LookupName(Found, S); } 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); - LookupParsedName(R, S, &SS, ObjectType, - /*AllowBuiltinCreation=*/false, EnteringContext); - + if (LookupCtx) + LookupQualifiedName(R, LookupCtx); + else if (S && !isDependent) + LookupName(R, S); if (!R.empty()) { // Don't diagnose problems with this speculative lookup. R.suppressDiagnostics(); @@ -521,11 +539,6 @@ 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. @@ -581,14 +594,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 = nullptr; + NamedDecl *OuterDecl; if (S) { LookupResult FoundOuter(*this, IdInfo.Identifier, IdInfo.IdentifierLoc, LookupNestedNameSpecifierName); LookupName(FoundOuter, S); OuterDecl = FoundOuter.getAsSingle(); - } else if (!SS.getUnqualifiedLookups().empty()) - OuterDecl = SS.getUnqualifiedLookups().front().getDecl(); + } else + OuterDecl = ScopeLookupResult; if (isAcceptableNestedNameSpecifier(OuterDecl) && OuterDecl->getCanonicalDecl() != SD->getCanonicalDecl() && @@ -766,7 +779,7 @@ bool Sema::ActOnCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo, return true; return BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS, - /*ErrorRecoveryLookup=*/false, + /*ScopeLookupResult=*/nullptr, false, IsCorrectedToColon, OnlyNamespace); } @@ -827,7 +840,7 @@ bool Sema::IsInvalidUnlessNestedName(Scope *S, CXXScopeSpec &SS, return false; return !BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS, - /*ErrorRecoveryLookup=*/true); + /*ScopeLookupResult=*/nullptr, true); } bool Sema::ActOnCXXNestedNameSpecifier(Scope *S, diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp index fa0eb1d2afbe..81334c817b2a 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(), - NameInfo, /*TemplateArgs=*/nullptr, + Base, Base->getType(), Loc, /*IsPtr=*/false, SS, + SourceLocation(), nullptr, 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 76db539b5726..17c4ba99c03f 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -1275,11 +1275,9 @@ 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, - /*IsArrow=*/false, - /*SS=*/CXXScopeSpec(), - /*TemplateKWLoc=*/SourceLocation(), - MemberGet, &Args, /*S=*/nullptr); + E = S.BuildMemberReferenceExpr(E.get(), DecompType, Loc, false, + CXXScopeSpec(), SourceLocation(), nullptr, + MemberGet, &Args, nullptr); if (E.isInvalid()) return true; @@ -4903,12 +4901,16 @@ 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(), MemberLookup, - /*TemplateArgs=*/nullptr, - /*S=*/nullptr); + ExprResult CtorArg + = SemaRef.BuildMemberReferenceExpr(MemberExprBase, + ParamType, Loc, + /*IsArrow=*/false, + SS, + /*TemplateKWLoc=*/SourceLocation(), + /*FirstQualifierInScope=*/nullptr, + MemberLookup, + /*TemplateArgs=*/nullptr, + /*S*/nullptr); if (CtorArg.isInvalid()) return true; @@ -14337,10 +14339,8 @@ 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, - /*TemplateKwLoc=*/SourceLocation(), MemberLookup, - /*TemplateArgs=*/nullptr, /*S=*/nullptr) - .get()); + Builder.build(S, Loc), Type, Loc, IsArrow, SS, SourceLocation(), + nullptr, MemberLookup, nullptr, nullptr).get()); } MemberBuilder(const ExprBuilder &Builder, QualType Type, bool IsArrow, @@ -14546,11 +14546,13 @@ 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(), OpLookup, - /*TemplateArgs=*/nullptr, /*S*/ nullptr, - /*SuppressQualifierCheck=*/true); + ExprResult OpEqualRef + = S.BuildMemberReferenceExpr(To.build(S, Loc), T, Loc, /*IsArrow=*/false, + SS, /*TemplateKWLoc=*/SourceLocation(), + /*FirstQualifierInScope=*/nullptr, + OpLookup, + /*TemplateArgs=*/nullptr, /*S*/nullptr, + /*SuppressQualifierCheck=*/true); if (OpEqualRef.isInvalid()) return StmtError(); @@ -17156,9 +17158,8 @@ bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message, auto BuildExpr = [&](LookupResult &LR) { ExprResult Res = BuildMemberReferenceExpr( - Message, Message->getType(), Message->getBeginLoc(), /*IsArrow=*/false, - /*SS=*/CXXScopeSpec(), /*TemplateKWLoc=*/SourceLocation(), LR, - /*TemplateArgs=*/nullptr, /*S=*/nullptr); + Message, Message->getType(), Message->getBeginLoc(), false, + CXXScopeSpec(), SourceLocation(), nullptr, LR, nullptr, 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 9940f8cb6295..d6b85cbcaf56 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, - /*UnqualifiedLookups=*/std::nullopt, NameInfo, TemplateArgs); + /*FirstQualifierFoundInScope=*/nullptr, 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 8519618bacfe..2070f3b7bb3a 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -552,9 +552,11 @@ 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 @@ -588,8 +590,8 @@ Sema::ActOnDependentMemberExpr(Expr *BaseExpr, QualType BaseType, bool IsArrow, // must have pointer type, and the accessed type is the pointee. return CXXDependentScopeMemberExpr::Create( Context, BaseExpr, BaseType, IsArrow, OpLoc, - SS.getWithLocInContext(Context), TemplateKWLoc, - SS.getUnqualifiedLookups(), NameInfo, TemplateArgs); + SS.getWithLocInContext(Context), TemplateKWLoc, FirstQualifierInScope, + NameInfo, TemplateArgs); } /// We know that the given qualified member reference points only to @@ -765,9 +767,8 @@ static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, R.addDecl(ND); R.resolveKind(); return SemaRef.BuildMemberReferenceExpr( - BaseExpr, BaseExpr->getType(), OpLoc, IsArrow, SS, - /*TemplateKWLoc=*/SourceLocation(), R, /*TemplateArgs=*/nullptr, - /*S=*/nullptr); + BaseExpr, BaseExpr->getType(), OpLoc, IsArrow, SS, SourceLocation(), + nullptr, R, nullptr, nullptr); }, Sema::CTK_ErrorRecovery, DC); @@ -783,7 +784,7 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, ExprResult Sema::BuildMemberReferenceExpr( Expr *Base, QualType BaseType, SourceLocation OpLoc, bool IsArrow, CXXScopeSpec &SS, SourceLocation TemplateKWLoc, - const DeclarationNameInfo &NameInfo, + NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo, const TemplateArgumentListInfo *TemplateArgs, const Scope *S, ActOnMemberAccessExtraArgs *ExtraArgs) { LookupResult R(*this, NameInfo, LookupMemberName); @@ -827,9 +828,10 @@ ExprResult Sema::BuildMemberReferenceExpr( if (SS.isInvalid()) return ExprError(); - return BuildMemberReferenceExpr(Base, BaseType, OpLoc, IsArrow, SS, - TemplateKWLoc, R, TemplateArgs, S, - /*SuppressQualifierCheck=*/false, ExtraArgs); + return BuildMemberReferenceExpr(Base, BaseType, + OpLoc, IsArrow, SS, TemplateKWLoc, + FirstQualifierInScope, R, TemplateArgs, S, + false, ExtraArgs); } ExprResult @@ -967,11 +969,17 @@ static bool IsInFnTryBlockHandler(const Scope *S) { return false; } -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) { +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) { 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, @@ -981,8 +989,8 @@ ExprResult Sema::BuildMemberReferenceExpr( (SS.isSet() ? SS.getScopeRep()->isDependent() : BaseExprType->isDependentType()))) return ActOnDependentMemberExpr(BaseExpr, BaseExprType, IsArrow, OpLoc, SS, - TemplateKWLoc, R.getLookupNameInfo(), - TemplateArgs); + TemplateKWLoc, FirstQualifierInScope, + R.getLookupNameInfo(), TemplateArgs); QualType BaseType = BaseExprType; if (IsArrow) { @@ -1187,9 +1195,9 @@ ExprResult Sema::BuildMemberReferenceExpr( // Non-dependent member, but dependent template arguments. if (!VDecl.get()) - return ActOnDependentMemberExpr(BaseExpr, BaseExpr->getType(), IsArrow, - OpLoc, SS, TemplateKWLoc, MemberNameInfo, - TemplateArgs); + return ActOnDependentMemberExpr( + BaseExpr, BaseExpr->getType(), IsArrow, OpLoc, SS, TemplateKWLoc, + FirstQualifierInScope, MemberNameInfo, TemplateArgs); VarDecl *Var = cast(VDecl.get()); if (!Var->getTemplateSpecializationKind()) @@ -1755,16 +1763,15 @@ 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); - UnresolvedSet<4> UnqualifiedLookups; - if (SS.isValid() && - LookupFirstQualifierInScope(S, SS.getScopeRep(), UnqualifiedLookups)) { - SS.setUnqualifiedLookups(UnqualifiedLookups.pairs()); - } + NamedDecl *FirstQualifierInScope + = (!SS.isSet() ? nullptr : FindFirstQualifierInScope(S, SS.getScopeRep())); + // This is a postfix expression, so get rid of ParenListExprs. ExprResult Result = MaybeConvertParenListExprToParenExpr(S, Base); if (Result.isInvalid()) return ExprError(); @@ -1772,8 +1779,8 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base, ActOnMemberAccessExtraArgs ExtraArgs = {S, Id, ObjCImpDecl}; ExprResult Res = BuildMemberReferenceExpr( - Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc, NameInfo, - TemplateArgs, S, &ExtraArgs); + Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc, + FirstQualifierInScope, NameInfo, TemplateArgs, S, &ExtraArgs); if (!Res.isInvalid() && isa(Res.get())) CheckMemberAccessOfNoDeref(cast(Res.get())); @@ -1917,8 +1924,9 @@ Sema::BuildImplicitMemberExpr(const CXXScopeSpec &SS, baseExpr = BuildCXXThisExpr(loc, ThisTy, /*IsImplicit=*/true); } - return BuildMemberReferenceExpr(baseExpr, ThisTy, - /*OpLoc=*/SourceLocation(), - /*IsArrow=*/!getLangOpts().HLSL, SS, - TemplateKWLoc, R, TemplateArgs, S); + return BuildMemberReferenceExpr( + baseExpr, ThisTy, + /*OpLoc=*/SourceLocation(), + /*IsArrow=*/!getLangOpts().HLSL, SS, TemplateKWLoc, + /*FirstQualifierInScope=*/nullptr, R, TemplateArgs, S); } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index d4a48858ec41..074062ebbb59 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -16036,11 +16036,13 @@ Sema::BuildForRangeBeginEndCall(SourceLocation Loc, CandidateSet->clear(OverloadCandidateSet::CSK_Normal); if (!MemberLookup.empty()) { - ExprResult MemberRef = BuildMemberReferenceExpr( - Range, Range->getType(), Loc, - /*IsPtr=*/false, /*SS=*/CXXScopeSpec(), - /*TemplateKWLoc=*/SourceLocation(), MemberLookup, - /*TemplateArgs=*/nullptr, S); + ExprResult MemberRef = + BuildMemberReferenceExpr(Range, Range->getType(), Loc, + /*IsPtr=*/false, CXXScopeSpec(), + /*TemplateKWLoc=*/SourceLocation(), + /*FirstQualifierInScope=*/nullptr, + 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 da2e99b6bc00..32d42f3c3f3b 100644 --- a/clang/lib/Sema/SemaStmtAsm.cpp +++ b/clang/lib/Sema/SemaStmtAsm.cpp @@ -900,8 +900,7 @@ Sema::LookupInlineAsmVarDeclField(Expr *E, StringRef Member, return CXXDependentScopeMemberExpr::Create( Context, E, T, /*IsArrow=*/false, AsmLoc, NestedNameSpecifierLoc(), SourceLocation(), - /*UnqualifiedLookups=*/std::nullopt, NameInfo, - /*TemplateArgs=*/nullptr); + /*FirstQualifierFoundInScope=*/nullptr, NameInfo, /*TemplateArgs=*/nullptr); } const RecordType *RT = T->getAs(); @@ -924,9 +923,8 @@ Sema::LookupInlineAsmVarDeclField(Expr *E, StringRef Member, // Make an Expr to thread through OpDecl. ExprResult Result = BuildMemberReferenceExpr( - E, E->getType(), AsmLoc, /*IsArrow=*/false, /*SS=*/CXXScopeSpec(), - /*TemplateKWLoc*/ SourceLocation(), FieldResult, - /*TemplateArgs=*/nullptr, /*S=*/nullptr); + E, E->getType(), AsmLoc, /*IsArrow=*/false, CXXScopeSpec(), + SourceLocation(), nullptr, FieldResult, nullptr, nullptr); return Result; } diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index d80e50deef69..9d9620162538 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -174,12 +174,15 @@ 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, - bool MayBeNNS) { +TemplateNameKind Sema::isTemplateName(Scope *S, + CXXScopeSpec &SS, + bool hasTemplateKeyword, + const UnqualifiedId &Name, + ParsedType ObjectTypePtr, + bool EnteringContext, + TemplateTy &TemplateResult, + bool &MemberOfUnknownSpecialization, + bool Disambiguation) { assert(getLangOpts().CPlusPlus && "No template names in C!"); DeclarationName TName; @@ -210,9 +213,8 @@ Sema::isTemplateName(Scope *S, CXXScopeSpec &SS, bool hasTemplateKeyword, if (LookupTemplateName(R, S, SS, ObjectType, EnteringContext, /*RequiredTemplate=*/SourceLocation(), &AssumedTemplate, - /*AllowTypoCorrection=*/!Disambiguation, MayBeNNS)) + /*AllowTypoCorrection=*/!Disambiguation)) return TNK_Non_template; - MemberOfUnknownSpecialization = R.wasNotFoundInCurrentInstantiation(); if (AssumedTemplate != AssumedTemplateKind::None) { @@ -378,7 +380,7 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS, QualType ObjectType, bool EnteringContext, RequiredTemplateKind RequiredTemplate, AssumedTemplateKind *ATK, - bool AllowTypoCorrection, bool MayBeNNS) { + bool AllowTypoCorrection) { if (ATK) *ATK = AssumedTemplateKind::None; @@ -387,89 +389,92 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS, Found.setTemplateNameLookup(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; + // 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; } - 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); - // 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) { + // 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); - } else if (!SS.getUnqualifiedLookups().empty()) { - Found.addAllDecls(SS.getUnqualifiedLookups()); - Found.resolveKind(); + + 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; } - ObjectTypeSearchedInScope = true; + + IsDependent |= Found.wasNotFoundInCurrentInstantiation(); } if (Found.isAmbiguous()) @@ -489,7 +494,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()) { + if (AllFunctions || (Found.empty() && !IsDependent)) { // 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. @@ -501,15 +506,11 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS, } } - if (Found.empty() && AllowTypoCorrection) { + if (Found.empty() && !IsDependent && 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; @@ -542,8 +543,13 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS, NamedDecl *ExampleLookupResult = Found.empty() ? nullptr : Found.getRepresentativeDecl(); - FilterAcceptableTemplateNames(Found); + FilterAcceptableTemplateNames(Found, AllowFunctionTemplatesInLookup); 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) { @@ -735,7 +741,7 @@ Sema::ActOnDependentIdExpression(const CXXScopeSpec &SS, /*IsArrow=*/!Context.getLangOpts().HLSL, /*OperatorLoc=*/SourceLocation(), /*QualifierLoc=*/NestedNameSpecifierLoc(), TemplateKWLoc, - /*UnqualifiedLookups=*/std::nullopt, NameInfo, TemplateArgs); + /*FirstQualifierFoundInScope=*/nullptr, NameInfo, TemplateArgs); } return BuildDependentDeclRefExpr(SS, TemplateKWLoc, NameInfo, TemplateArgs); } @@ -4470,10 +4476,14 @@ 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, bool MayBeNNS) { +TemplateNameKind Sema::ActOnTemplateName(Scope *S, + CXXScopeSpec &SS, + SourceLocation TemplateKWLoc, + const UnqualifiedId &Name, + ParsedType ObjectType, + bool EnteringContext, + TemplateTy &Result, + bool AllowInjectedClassName) { if (TemplateKWLoc.isValid() && S && !S->getTemplateParamParent()) Diag(TemplateKWLoc, getLangOpts().CPlusPlus11 ? @@ -4508,10 +4518,9 @@ TemplateNameKind Sema::ActOnTemplateName( // "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, - /*Disambiguation=*/false, MayBeNNS); + TemplateNameKind TNK = isTemplateName(S, SS, TemplateKWLoc.isValid(), Name, + ObjectType, EnteringContext, Result, + MemberOfUnknownSpecialization); if (TNK != TNK_Non_template) { // We resolved this to a (non-dependent) template name. Return it. auto *LookupRD = dyn_cast_or_null(LookupCtx); @@ -4550,8 +4559,7 @@ TemplateNameKind Sema::ActOnTemplateName( ? RequiredTemplateKind(TemplateKWLoc) : TemplateNameIsRequired; if (!LookupTemplateName(R, S, SS, ObjectType.get(), EnteringContext, RTK, - /*ATK=*/nullptr, /*AllowTypoCorrection=*/false, - MayBeNNS) && + /*ATK=*/nullptr, /*AllowTypoCorrection=*/false) && !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 64c5a16b2e4c..a7bc6749c585 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1515,11 +1515,12 @@ namespace { NestedNameSpecifierLoc QualifierLoc, QualType T); - TemplateName TransformTemplateName(CXXScopeSpec &SS, TemplateName Name, - SourceLocation NameLoc, - QualType ObjectType = QualType(), - bool AllowInjectedClassName = false, - bool MayBeNNS = false); + TemplateName + TransformTemplateName(CXXScopeSpec &SS, TemplateName Name, + SourceLocation NameLoc, + QualType ObjectType = QualType(), + NamedDecl *FirstQualifierInScope = nullptr, + bool AllowInjectedClassName = false); const CXXAssumeAttr *TransformCXXAssumeAttr(const CXXAssumeAttr *AA); const LoopHintAttr *TransformLoopHintAttr(const LoopHintAttr *LH); @@ -1951,7 +1952,8 @@ TemplateInstantiator::RebuildElaboratedType(SourceLocation KeywordLoc, TemplateName TemplateInstantiator::TransformTemplateName( CXXScopeSpec &SS, TemplateName Name, SourceLocation NameLoc, - QualType ObjectType, bool AllowInjectedClassName, bool MayBeNNS) { + QualType ObjectType, NamedDecl *FirstQualifierInScope, + bool AllowInjectedClassName) { if (TemplateTemplateParmDecl *TTP = dyn_cast_or_null(Name.getAsTemplateDecl())) { if (TTP->getDepth() < TemplateArgs.getNumLevels()) { @@ -2023,7 +2025,8 @@ TemplateName TemplateInstantiator::TransformTemplateName( } return inherited::TransformTemplateName(SS, Name, NameLoc, ObjectType, - AllowInjectedClassName, MayBeNNS); + FirstQualifierInScope, + AllowInjectedClassName); } ExprResult diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index f6021440cda6..655f248d1138 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -541,9 +541,10 @@ public: /// 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(), - ArrayRef UnqualifiedLookups = std::nullopt); + NestedNameSpecifierLoc + TransformNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS, + QualType ObjectType = QualType(), + NamedDecl *FirstQualifierInScope = nullptr); /// Transform the given declaration name. /// @@ -584,11 +585,12 @@ public: /// 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(), - bool AllowInjectedClassName = false, - bool MayBeNNS = false); + TemplateName + TransformTemplateName(CXXScopeSpec &SS, TemplateName Name, + SourceLocation NameLoc, + QualType ObjectType = QualType(), + NamedDecl *FirstQualifierInScope = nullptr, + bool AllowInjectedClassName = false); /// Transform the given template argument. /// @@ -1138,8 +1140,8 @@ public: CXXScopeSpec SS; SS.Adopt(QualifierLoc); TemplateName InstName = getDerived().RebuildTemplateName( - SS, TemplateKWLoc, *Name, NameLoc, QualType(), AllowInjectedClassName, - /*MayBeNNS=*/false); + SS, TemplateKWLoc, *Name, NameLoc, QualType(), nullptr, + AllowInjectedClassName); if (InstName.isNull()) return QualType(); @@ -1310,7 +1312,8 @@ public: SourceLocation TemplateKWLoc, const IdentifierInfo &Name, SourceLocation NameLoc, QualType ObjectType, - bool AllowInjectedClassName, bool MayBeNNS); + NamedDecl *FirstQualifierInScope, + bool AllowInjectedClassName); /// Build a new template name given a nested name specifier and the /// overloaded operator name that is referred to as a template. @@ -2846,14 +2849,15 @@ public: /// /// 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, - ArrayRef UnqualifiedLookups) { + 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 BaseResult = getSema().PerformMemberExprBaseConversion(Base, isArrow); if (!Member->getDeclName()) { @@ -2890,7 +2894,6 @@ public: CXXScopeSpec SS; SS.Adopt(QualifierLoc); - SS.setUnqualifiedLookups(UnqualifiedLookups); Base = BaseResult.get(); if (Base->containsErrors()) @@ -2923,9 +2926,10 @@ public: } return getSema().BuildMemberReferenceExpr(Base, BaseType, OpLoc, isArrow, - SS, TemplateKWLoc, R, - ExplicitTemplateArgs, - /*S=*/nullptr); + SS, TemplateKWLoc, + FirstQualifierInScope, + R, ExplicitTemplateArgs, + /*S*/nullptr); } /// Build a new binary operator expression. @@ -2998,9 +3002,10 @@ public: CXXScopeSpec SS; DeclarationNameInfo NameInfo(&Accessor, AccessorLoc); return getSema().BuildMemberReferenceExpr( - Base, Base->getType(), OpLoc, IsArrow, SS, - /*TemplateKWLoc=*/SourceLocation(), NameInfo, - /*TemplateArgs=*/nullptr, /*S=*/nullptr); + Base, Base->getType(), OpLoc, IsArrow, SS, SourceLocation(), + /*FirstQualifierInScope*/ nullptr, NameInfo, + /* TemplateArgs */ nullptr, + /*S*/ nullptr); } /// Build a new initializer list expression. @@ -3568,37 +3573,46 @@ public: /// /// 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, - ArrayRef UnqualifiedLookups, - const DeclarationNameInfo &MemberNameInfo, - const TemplateArgumentListInfo *TemplateArgs) { + ExprResult RebuildCXXDependentScopeMemberExpr(Expr *BaseE, + QualType BaseType, + bool IsArrow, + SourceLocation OperatorLoc, + NestedNameSpecifierLoc QualifierLoc, + SourceLocation TemplateKWLoc, + NamedDecl *FirstQualifierInScope, + const DeclarationNameInfo &MemberNameInfo, + const TemplateArgumentListInfo *TemplateArgs) { CXXScopeSpec SS; SS.Adopt(QualifierLoc); - SS.setUnqualifiedLookups(UnqualifiedLookups); - return SemaRef.BuildMemberReferenceExpr( - BaseE, BaseType, OperatorLoc, IsArrow, SS, TemplateKWLoc, - MemberNameInfo, TemplateArgs, /*S=*/nullptr); + return SemaRef.BuildMemberReferenceExpr(BaseE, BaseType, + OperatorLoc, IsArrow, + SS, TemplateKWLoc, + FirstQualifierInScope, + 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, - ArrayRef UnqualifiedLookups, LookupResult &R, - const TemplateArgumentListInfo *TemplateArgs) { + ExprResult RebuildUnresolvedMemberExpr(Expr *BaseE, QualType BaseType, + SourceLocation OperatorLoc, + bool IsArrow, + NestedNameSpecifierLoc QualifierLoc, + SourceLocation TemplateKWLoc, + NamedDecl *FirstQualifierInScope, + LookupResult &R, + const TemplateArgumentListInfo *TemplateArgs) { CXXScopeSpec SS; SS.Adopt(QualifierLoc); - SS.setUnqualifiedLookups(UnqualifiedLookups); - return SemaRef.BuildMemberReferenceExpr(BaseE, BaseType, OperatorLoc, - IsArrow, SS, TemplateKWLoc, R, - TemplateArgs, /*S=*/nullptr); + return SemaRef.BuildMemberReferenceExpr(BaseE, BaseType, + OperatorLoc, IsArrow, + SS, TemplateKWLoc, + FirstQualifierInScope, + R, TemplateArgs, /*S*/nullptr); } /// Build a new noexcept expression. @@ -3817,8 +3831,10 @@ public: DeclarationNameInfo NameInfo(Ivar->getDeclName(), IvarLoc); ExprResult Result = getSema().BuildMemberReferenceExpr( BaseArg, BaseArg->getType(), - /*FIXME:*/ IvarLoc, IsArrow, SS, /*TemplateKWLoc=*/SourceLocation(), - NameInfo, /*TemplateArgs=*/nullptr, /*S=*/nullptr); + /*FIXME:*/ IvarLoc, IsArrow, SS, SourceLocation(), + /*FirstQualifierInScope=*/nullptr, NameInfo, + /*TemplateArgs=*/nullptr, + /*S=*/nullptr); if (IsFreeIvar && Result.isUsable()) cast(Result.get())->setIsFreeIvar(IsFreeIvar); return Result; @@ -3833,12 +3849,14 @@ public: SourceLocation PropertyLoc) { CXXScopeSpec SS; DeclarationNameInfo NameInfo(Property->getDeclName(), PropertyLoc); - return getSema().BuildMemberReferenceExpr( - BaseArg, BaseArg->getType(), - /*FIXME:*/ PropertyLoc, - /*IsArrow=*/false, SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo, - /*TemplateArgs=*/nullptr, - /*S=*/nullptr); + return getSema().BuildMemberReferenceExpr(BaseArg, BaseArg->getType(), + /*FIXME:*/PropertyLoc, + /*IsArrow=*/false, + SS, SourceLocation(), + /*FirstQualifierInScope=*/nullptr, + NameInfo, + /*TemplateArgs=*/nullptr, + /*S=*/nullptr); } /// Build a new Objective-C property reference expression. @@ -3865,11 +3883,13 @@ public: SourceLocation OpLoc, bool IsArrow) { CXXScopeSpec SS; DeclarationNameInfo NameInfo(&getSema().Context.Idents.get("isa"), IsaLoc); - return getSema().BuildMemberReferenceExpr( - BaseArg, BaseArg->getType(), OpLoc, IsArrow, SS, - /*TemplateKWLoc=*/SourceLocation(), NameInfo, - /*TemplateArgs=*/nullptr, - /*S=*/nullptr); + return getSema().BuildMemberReferenceExpr(BaseArg, BaseArg->getType(), + OpLoc, IsArrow, + SS, SourceLocation(), + /*FirstQualifierInScope=*/nullptr, + NameInfo, + /*TemplateArgs=*/nullptr, + /*S=*/nullptr); } /// Build a new shuffle vector expression. @@ -4034,14 +4054,18 @@ public: } private: - TypeLoc TransformTypeInObjectScope(TypeLoc TL, QualType ObjectType, + TypeLoc TransformTypeInObjectScope(TypeLoc TL, + QualType ObjectType, + NamedDecl *FirstQualifierInScope, 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, @@ -4360,7 +4384,7 @@ Sema::ConditionResult TreeTransform::TransformCondition( template NestedNameSpecifierLoc TreeTransform::TransformNestedNameSpecifierLoc( NestedNameSpecifierLoc NNS, QualType ObjectType, - ArrayRef UnqualifiedLookups) { + NamedDecl *FirstQualifierInScope) { SmallVector Qualifiers; auto insertNNS = [&Qualifiers](NestedNameSpecifierLoc NNS) { @@ -4371,8 +4395,6 @@ NestedNameSpecifierLoc TreeTransform::TransformNestedNameSpecifierLoc( insertNNS(NNS); CXXScopeSpec SS; - SS.setUnqualifiedLookups(UnqualifiedLookups); - while (!Qualifiers.empty()) { NestedNameSpecifierLoc Q = Qualifiers.pop_back_val(); NestedNameSpecifier *QNNS = Q.getNestedNameSpecifier(); @@ -4382,9 +4404,8 @@ NestedNameSpecifierLoc TreeTransform::TransformNestedNameSpecifierLoc( Sema::NestedNameSpecInfo IdInfo(QNNS->getAsIdentifier(), Q.getLocalBeginLoc(), Q.getLocalEndLoc(), ObjectType); - if (SemaRef.BuildCXXNestedNameSpecifier(/*Scope=*/nullptr, IdInfo, - /*EnteringContext=*/false, SS, - /*ErrorRecoveryLookup=*/false)) + if (SemaRef.BuildCXXNestedNameSpecifier(/*Scope=*/nullptr, IdInfo, false, + SS, FirstQualifierInScope, false)) return NestedNameSpecifierLoc(); break; } @@ -4422,7 +4443,8 @@ NestedNameSpecifierLoc TreeTransform::TransformNestedNameSpecifierLoc( case NestedNameSpecifier::TypeSpecWithTemplate: case NestedNameSpecifier::TypeSpec: { - TypeLoc TL = TransformTypeInObjectScope(Q.getTypeLoc(), ObjectType, SS); + TypeLoc TL = TransformTypeInObjectScope(Q.getTypeLoc(), ObjectType, + FirstQualifierInScope, SS); if (!TL) return NestedNameSpecifierLoc(); @@ -4455,7 +4477,7 @@ NestedNameSpecifierLoc TreeTransform::TransformNestedNameSpecifierLoc( } // The qualifier-in-scope and object type only apply to the leftmost entity. - SS.setUnqualifiedLookups(std::nullopt); + FirstQualifierInScope = nullptr; ObjectType = QualType(); } @@ -4538,10 +4560,14 @@ TreeTransform llvm_unreachable("Unknown name kind."); } -template -TemplateName TreeTransform::TransformTemplateName( - CXXScopeSpec &SS, TemplateName Name, SourceLocation NameLoc, - QualType ObjectType, bool AllowInjectedClassName, bool MayBeNNS) { +template +TemplateName +TreeTransform::TransformTemplateName(CXXScopeSpec &SS, + TemplateName Name, + SourceLocation NameLoc, + QualType ObjectType, + NamedDecl *FirstQualifierInScope, + bool AllowInjectedClassName) { if (QualifiedTemplateName *QTN = Name.getAsQualifiedTemplateName()) { TemplateDecl *Template = QTN->getUnderlyingTemplate().getAsTemplateDecl(); assert(Template && "qualified template name must refer to a template"); @@ -4565,7 +4591,7 @@ TemplateName TreeTransform::TransformTemplateName( if (SS.getScopeRep()) { // These apply to the scope specifier, not the template. ObjectType = QualType(); - SS.setUnqualifiedLookups(std::nullopt); + FirstQualifierInScope = nullptr; } if (!getDerived().AlwaysRebuild() && @@ -4577,9 +4603,13 @@ TemplateName TreeTransform::TransformTemplateName( SourceLocation TemplateKWLoc = NameLoc; if (DTN->isIdentifier()) { - return getDerived().RebuildTemplateName( - SS, TemplateKWLoc, *DTN->getIdentifier(), NameLoc, ObjectType, - AllowInjectedClassName, MayBeNNS); + return getDerived().RebuildTemplateName(SS, + TemplateKWLoc, + *DTN->getIdentifier(), + NameLoc, + ObjectType, + FirstQualifierInScope, + AllowInjectedClassName); } return getDerived().RebuildTemplateName(SS, TemplateKWLoc, @@ -5123,31 +5153,39 @@ QualType TreeTransform::RebuildQualifiedType(QualType T, return SemaRef.BuildQualifiedType(T, Loc, Quals); } -template -TypeLoc TreeTransform::TransformTypeInObjectScope(TypeLoc TL, - QualType ObjectType, - CXXScopeSpec &SS) { +template +TypeLoc +TreeTransform::TransformTypeInObjectScope(TypeLoc TL, + QualType ObjectType, + NamedDecl *UnqualLookup, + CXXScopeSpec &SS) { if (getDerived().AlreadyTransformed(TL.getType())) return TL; - TypeSourceInfo *TSI = TransformTSIInObjectScope(TL, ObjectType, SS); + TypeSourceInfo *TSI = + TransformTSIInObjectScope(TL, ObjectType, UnqualLookup, SS); if (TSI) return TSI->getTypeLoc(); return TypeLoc(); } -template -TypeSourceInfo *TreeTransform::TransformTypeInObjectScope( - TypeSourceInfo *TSInfo, QualType ObjectType, CXXScopeSpec &SS) { +template +TypeSourceInfo * +TreeTransform::TransformTypeInObjectScope(TypeSourceInfo *TSInfo, + QualType ObjectType, + NamedDecl *UnqualLookup, + CXXScopeSpec &SS) { if (getDerived().AlreadyTransformed(TSInfo->getType())) return TSInfo; - return TransformTSIInObjectScope(TSInfo->getTypeLoc(), ObjectType, SS); + return TransformTSIInObjectScope(TSInfo->getTypeLoc(), ObjectType, + UnqualLookup, SS); } template TypeSourceInfo *TreeTransform::TransformTSIInObjectScope( - TypeLoc TL, QualType ObjectType, CXXScopeSpec &SS) { + TypeLoc TL, QualType ObjectType, NamedDecl *UnqualLookup, + CXXScopeSpec &SS) { QualType T = TL.getType(); assert(!getDerived().AlreadyTransformed(T)); @@ -5160,7 +5198,7 @@ TypeSourceInfo *TreeTransform::TransformTSIInObjectScope( TemplateName Template = getDerived().TransformTemplateName( SS, SpecTL.getTypePtr()->getTemplateName(), SpecTL.getTemplateNameLoc(), - ObjectType, /*AllowInjectedClassName=*/true, /*MayBeNNS=*/true); + ObjectType, UnqualLookup, /*AllowInjectedClassName*/true); if (Template.isNull()) return nullptr; @@ -5170,11 +5208,13 @@ TypeSourceInfo *TreeTransform::TransformTSIInObjectScope( DependentTemplateSpecializationTypeLoc SpecTL = TL.castAs(); - TemplateName Template = getDerived().RebuildTemplateName( - SS, SpecTL.getTemplateKeywordLoc(), - *SpecTL.getTypePtr()->getIdentifier(), SpecTL.getTemplateNameLoc(), - ObjectType, - /*AllowInjectedClassName=*/true, /*MayBeNNS=*/true); + TemplateName Template + = getDerived().RebuildTemplateName(SS, + SpecTL.getTemplateKeywordLoc(), + *SpecTL.getTypePtr()->getIdentifier(), + SpecTL.getTemplateNameLoc(), + ObjectType, UnqualLookup, + /*AllowInjectedClassName*/true); if (Template.isNull()) return nullptr; @@ -12318,8 +12358,7 @@ 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). - ArrayRef UnqualifiedLookups; - + NamedDecl *FirstQualifierInScope = nullptr; DeclarationNameInfo MemberNameInfo = E->getMemberNameInfo(); if (MemberNameInfo.getName()) { MemberNameInfo = getDerived().TransformDeclarationNameInfo(MemberNameInfo); @@ -12327,11 +12366,16 @@ TreeTransform::TransformMemberExpr(MemberExpr *E) { return ExprError(); } - return getDerived().RebuildMemberExpr( - Base.get(), FakeOperatorLoc, E->isArrow(), QualifierLoc, TemplateKWLoc, - MemberNameInfo, Member, FoundDecl, - (E->hasExplicitTemplateArgs() ? &TransArgs : nullptr), - UnqualifiedLookups); + return getDerived().RebuildMemberExpr(Base.get(), FakeOperatorLoc, + E->isArrow(), + QualifierLoc, + TemplateKWLoc, + MemberNameInfo, + Member, + FoundDecl, + (E->hasExplicitTemplateArgs() + ? &TransArgs : nullptr), + FirstQualifierInScope); } template @@ -13458,8 +13502,9 @@ TreeTransform::TransformCXXPseudoDestructorExpr( PseudoDestructorTypeStorage Destroyed; if (E->getDestroyedTypeInfo()) { - TypeSourceInfo *DestroyedTypeInfo = getDerived().TransformTypeInObjectScope( - E->getDestroyedTypeInfo(), ObjectType, SS); + TypeSourceInfo *DestroyedTypeInfo + = getDerived().TransformTypeInObjectScope(E->getDestroyedTypeInfo(), + ObjectType, nullptr, SS); if (!DestroyedTypeInfo) return ExprError(); Destroyed = DestroyedTypeInfo; @@ -13485,7 +13530,7 @@ TreeTransform::TransformCXXPseudoDestructorExpr( if (E->getScopeTypeInfo()) { CXXScopeSpec EmptySS; ScopeTypeInfo = getDerived().TransformTypeInObjectScope( - E->getScopeTypeInfo(), ObjectType, EmptySS); + E->getScopeTypeInfo(), ObjectType, nullptr, EmptySS); if (!ScopeTypeInfo) return ExprError(); } @@ -14746,17 +14791,19 @@ TreeTransform::TransformCXXDependentScopeMemberExpr( ObjectType = BaseType->castAs()->getPointeeType(); } - UnresolvedSet<4> UnqualifiedLookups; - for (auto D : E->unqualified_lookups()) { - if (NamedDecl *InstD = getDerived().TransformFirstQualifierInScope( - D.getDecl(), E->getQualifierLoc().getBeginLoc())) - UnqualifiedLookups.addDecl(InstD); - } + // Transform the first part of the nested-name-specifier that qualifies + // the member name. + NamedDecl *FirstQualifierInScope + = getDerived().TransformFirstQualifierInScope( + E->getFirstQualifierFoundInScope(), + E->getQualifierLoc().getBeginLoc()); NestedNameSpecifierLoc QualifierLoc; if (E->getQualifier()) { - QualifierLoc = getDerived().TransformNestedNameSpecifierLoc( - E->getQualifierLoc(), ObjectType, UnqualifiedLookups.pairs()); + QualifierLoc + = getDerived().TransformNestedNameSpecifierLoc(E->getQualifierLoc(), + ObjectType, + FirstQualifierInScope); if (!QualifierLoc) return ExprError(); } @@ -14775,16 +14822,23 @@ 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() && - UnqualifiedLookups.pairs() == E->unqualified_lookups()) + FirstQualifierInScope == E->getFirstQualifierFoundInScope()) return E; - return getDerived().RebuildCXXDependentScopeMemberExpr( - Base.get(), BaseType, E->isArrow(), E->getOperatorLoc(), QualifierLoc, - TemplateKWLoc, UnqualifiedLookups.pairs(), NameInfo, - /*TemplateArgs*/ nullptr); + return getDerived().RebuildCXXDependentScopeMemberExpr(Base.get(), + BaseType, + E->isArrow(), + E->getOperatorLoc(), + QualifierLoc, + TemplateKWLoc, + FirstQualifierInScope, + NameInfo, + /*TemplateArgs*/nullptr); } TemplateArgumentListInfo TransArgs(E->getLAngleLoc(), E->getRAngleLoc()); @@ -14793,9 +14847,15 @@ TreeTransform::TransformCXXDependentScopeMemberExpr( TransArgs)) return ExprError(); - return getDerived().RebuildCXXDependentScopeMemberExpr( - Base.get(), BaseType, E->isArrow(), E->getOperatorLoc(), QualifierLoc, - TemplateKWLoc, UnqualifiedLookups.pairs(), NameInfo, &TransArgs); + return getDerived().RebuildCXXDependentScopeMemberExpr(Base.get(), + BaseType, + E->isArrow(), + E->getOperatorLoc(), + QualifierLoc, + TemplateKWLoc, + FirstQualifierInScope, + NameInfo, + &TransArgs); } template @@ -14856,11 +14916,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). - ArrayRef UnqualifiedLookups; + NamedDecl *FirstQualifierInScope = nullptr; return getDerived().RebuildUnresolvedMemberExpr( Base.get(), BaseType, Old->getOperatorLoc(), Old->isArrow(), QualifierLoc, - TemplateKWLoc, UnqualifiedLookups, R, + TemplateKWLoc, FirstQualifierInScope, R, (Old->hasExplicitTemplateArgs() ? &TransArgs : nullptr)); } @@ -16217,18 +16277,22 @@ TreeTransform::RebuildTemplateName(CXXScopeSpec &SS, TemplateName(Template)); } -template -TemplateName TreeTransform::RebuildTemplateName( - CXXScopeSpec &SS, SourceLocation TemplateKWLoc, const IdentifierInfo &Name, - SourceLocation NameLoc, QualType ObjectType, bool AllowInjectedClassName, - bool MayBeNNS) { +template +TemplateName +TreeTransform::RebuildTemplateName(CXXScopeSpec &SS, + SourceLocation TemplateKWLoc, + const IdentifierInfo &Name, + SourceLocation NameLoc, + QualType ObjectType, + NamedDecl *FirstQualifierInScope, + bool AllowInjectedClassName) { UnqualifiedId TemplateName; TemplateName.setIdentifier(&Name, NameLoc); Sema::TemplateTy Template; getSema().ActOnTemplateName(/*Scope=*/nullptr, SS, TemplateKWLoc, TemplateName, ParsedType::make(ObjectType), /*EnteringContext=*/false, Template, - AllowInjectedClassName, MayBeNNS); + AllowInjectedClassName); return Template.get(); } @@ -16376,10 +16440,13 @@ TreeTransform::RebuildCXXPseudoDestructorExpr(Expr *Base, } SourceLocation TemplateKWLoc; // FIXME: retrieve it from caller. - return getSema().BuildMemberReferenceExpr( - Base, BaseType, OperatorLoc, isArrow, SS, TemplateKWLoc, NameInfo, - /*TemplateArgs=*/nullptr, - /*S=*/nullptr); + return getSema().BuildMemberReferenceExpr(Base, BaseType, + OperatorLoc, isArrow, + SS, TemplateKWLoc, + /*FIXME: FirstQualifier*/ nullptr, + NameInfo, + /*TemplateArgs*/ nullptr, + /*S*/nullptr); } template diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 6ccb4b01a036..37f5c7f2bf20 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1992,43 +1992,42 @@ void ASTStmtReader::VisitCXXDependentScopeMemberExpr( CXXDependentScopeMemberExpr *E) { VisitExpr(E); - CurrentUnpackingBits.emplace(Record.readInt()); - 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; + 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!"); - 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->OperatorLoc = Record.readSourceLocation(); + E->CXXDependentScopeMemberExprBits.OperatorLoc = readSourceLocation(); + + if (HasFirstQualifierFoundInScope) + *E->getTrailingObjects() = readDeclAs(); + E->MemberNameInfo = Record.readDeclarationNameInfo(); - - if (HasQualifier) - new (E->getTrailingObjects()) - NestedNameSpecifierLoc(Record.readNestedNameSpecifierLoc()); - - 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 +4074,16 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { break; case EXPR_CXX_DEPENDENT_SCOPE_MEMBER: { + unsigned NumTemplateArgs = Record[ASTStmtReader::NumExprFields]; BitsUnpacker DependentScopeMemberBits( - Record[ASTStmtReader::NumExprFields]); - bool HasQualifier = DependentScopeMemberBits.getNextBit(); - bool HasTemplateInfo = DependentScopeMemberBits.getNextBit(); - unsigned NumUnqualifiedLookups = Record[ASTStmtReader::NumExprFields + 1]; - unsigned NumTemplateArgs = Record[ASTStmtReader::NumExprFields + 2]; + Record[ASTStmtReader::NumExprFields + 1]); + bool HasTemplateKWAndArgsInfo = DependentScopeMemberBits.getNextBit(); + bool HasFirstQualifierFoundInScope = + DependentScopeMemberBits.getNextBit(); S = CXXDependentScopeMemberExpr::CreateEmpty( - Context, HasQualifier, NumUnqualifiedLookups, HasTemplateInfo, - NumTemplateArgs); + Context, HasTemplateKWAndArgsInfo, NumTemplateArgs, + HasFirstQualifierFoundInScope); break; } diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 98606bbf8c4a..d36f43fdaf26 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1986,41 +1986,34 @@ void ASTStmtWriter::VisitCXXDependentScopeMemberExpr( CXXDependentScopeMemberExpr *E) { VisitExpr(E); - bool HasQualifier = E->hasQualifier(); - unsigned NumUnqualifiedLookups = E->getNumUnqualifiedLookups(); - bool HasTemplateInfo = E->hasTemplateKWAndArgsInfo(); - unsigned NumTemplateArgs = E->getNumTemplateArgs(); - - // Write these first for easy access when deserializing, as they affect the - // size of the CXXDependentScopeMemberExpr. + // 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(HasQualifier); - CurrentPackingBits.addBit(HasTemplateInfo); - Record.push_back(NumUnqualifiedLookups); - Record.push_back(NumTemplateArgs); + CurrentPackingBits.addBit(E->hasTemplateKWAndArgsInfo()); + CurrentPackingBits.addBit(E->hasFirstQualifierFoundInScope()); + + if (E->hasTemplateKWAndArgsInfo()) { + const ASTTemplateKWAndArgsInfo &ArgInfo = + *E->getTrailingObjects(); + AddTemplateKWAndArgsInfo(ArgInfo, + E->getTrailingObjects()); + } + + CurrentPackingBits.addBit(E->isArrow()); Record.AddTypeRef(E->getBaseType()); - CurrentPackingBits.addBit(E->isArrow()); + Record.AddNestedNameSpecifierLoc(E->getQualifierLoc()); 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 11eb67fb4f15..1afea99e8895 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,19 +55,15 @@ namespace PR11856 { template T *end(T*); - struct X { }; - struct Y { - int end; - }; + class X { }; 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'}} - - Y *y; - if (y->end < 7) { } + if (x->end < 7) { // expected-error{{no member named 'end' in 'PR11856::X'}} + } } } 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 5221b883f046..e3599db18350 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,19 +86,15 @@ namespace PR11856 { template T *end(T*); - struct X { }; - struct Y { - int end; - }; + class X { }; 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'}} - - Y *y; - if (y->end < 7) { } + if (x->end < 7) { // expected-error{{no member named 'end' in 'PR11856::X'}} + } } } 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 deleted file mode 100644 index 423eacd21d44..000000000000 --- a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p3-example3.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// 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 deleted file mode 100644 index 7d843649c3f3..000000000000 --- a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p3.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// 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 97d3587881bb..78e83c0ab456 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(); // 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->Derived1T::Foo(); + this->Derived2T::Member = 42; 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 6bca46081842..e7dddd1ea927 100644 --- a/clang/test/CXX/drs/cwg1xx.cpp +++ b/clang/test/CXX/drs/cwg1xx.cpp @@ -615,8 +615,10 @@ 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 { @@ -626,12 +628,10 @@ 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; - // expected-error@-1 {{use 'template' keyword to treat 'S' as a dependent template name}} + (void)t.S::n; // ok } void i() { - (void)t.S(); - // expected-error@-1 {{use 'template' keyword to treat 'S' as a dependent template name}} + (void)t.S(); // ok! } }; 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 deleted file mode 100644 index 27c24e1d6170..000000000000 --- a/clang/test/CXX/temp/temp.names/p3-23.cpp +++ /dev/null @@ -1,237 +0,0 @@ -// 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 a4d735e05e9b..37ab93559e36 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-warning {{use 'template' keyword to treat 'C' as a dependent template name}} expected-warning {{missing 'typename'}} + friend A::C f7(); // expected-error {{use 'template' keyword to treat 'C' as a dependent template name}} expected-warning {{missing 'typename'}} friend A::template C f8(); // expected-warning {{missing 'typename'}} }; diff --git a/clang/test/FixIt/fixit.cpp b/clang/test/FixIt/fixit.cpp index 144eefb3ae4b..605c2d0bd023 100644 --- a/clang/test/FixIt/fixit.cpp +++ b/clang/test/FixIt/fixit.cpp @@ -158,12 +158,12 @@ public: template class F2 { - typename F1:: /*template*/ Iterator<0> Mypos; // expected-warning {{use 'template' keyword to treat 'Iterator' as a dependent template name}} + typename F1:: /*template*/ Iterator<0> Mypos; // expected-error {{use 'template' keyword to treat 'Iterator' as a dependent template name}} }; template void f(){ - typename F1:: /*template*/ Iterator<0> Mypos; // expected-warning {{use 'template' keyword to treat 'Iterator' as a dependent template name}} + typename F1:: /*template*/ Iterator<0> Mypos; // expected-error {{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 7b993f684936..cdbe1e95cba9 100644 --- a/clang/test/Misc/warning-flags.c +++ b/clang/test/Misc/warning-flags.c @@ -18,7 +18,7 @@ This test serves two purposes: The list of warnings below should NEVER grow. It should gradually shrink to 0. -CHECK: Warnings without flags (64): +CHECK: Warnings without flags (65): CHECK-NEXT: ext_expected_semi_decl_list CHECK-NEXT: ext_missing_whitespace_after_macro_name @@ -61,6 +61,7 @@ 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 0c7f453b5c48..5755844a323d 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-warning@-1 {{use 'template' keyword to treat 'temp' as a dependent template name}} +// expected-error@-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 a01edc77e02a..b3c102830f35 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-warning@-1 {{use 'template' keyword to treat 'f' as a dependent template name}} + // expected-error@-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-warning@-1 {{use 'template' keyword to treat 'f' as a dependent template name}} + // expected-error@-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-warning@-1 {{use 'template' keyword to treat 'f' as a dependent template name}} + // expected-error@-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 44dc9ce8b152..55a96002be2a 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 'G'}} + template void f(T *p) { p->~G(); } // expected-error {{no member named '~Y'}} void h1(Y *p) { p->~G(); } - void h2(Y *p) { f(p); } // expected-note {{instantiation of}} + void h2(Y *p) { f(p); } namespace N { template struct G {}; } void h3(N::G *p) { p->~G(); } - void h4(N::G *p) { f(p); } + void h4(N::G *p) { f(p); } // expected-note {{instantiation of}} } namespace TemplateUndeclared { diff --git a/clang/test/SemaCXX/static-assert-cxx17.cpp b/clang/test/SemaCXX/static-assert-cxx17.cpp index 754f4ae5f1d3..41a7b025d0eb 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().template X::~X())>) == 0); + static_assert(sizeof(X().X::~X())>) == 0); // expected-error@-1{{static assertion failed due to requirement 'sizeof(X) == 0'}} \ // expected-note@-1 {{evaluates to '8 == 0'}} static_assert(constexpr_return_false()); diff --git a/clang/test/SemaTemplate/dependent-base-classes.cpp b/clang/test/SemaTemplate/dependent-base-classes.cpp index 4cb88a5b4070..92a37efaa7e7 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-warning{{use 'template' keyword to treat 'apply' as a dependent template name}} +struct X1 : T::apply { }; // expected-error{{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 c763989e6dad..c7e27e8da25f 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-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->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->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->operator+(1); // expected-error{{use 'template' keyword to treat 'operator +' as a dependent template name}} + t->f1(1); // expected-error{{use 'template' keyword to treat 'f1' as a dependent template name}} t->f1<3, int const>(1); // expected-error{{missing 'template' keyword prior to dependent template name 'f1'}} - T::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::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).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 7768d2f03ac5..ad73daa8e214 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.template Y::f(); } + template void g(Y y) { y.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 760d6c585240..dc12823ae307 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-warning {{use 'template'}} expected-error {{assigning to 'int' from incompatible type 'void'}} + x = Y1::f4(0); // expected-error {{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-warning {{use 'template'}} + x = p->f4(0); // expected-error {{assigning to 'int' from incompatible type 'void'}} expected-error {{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 a9010969322e..714830f0032d 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-warning {{use 'template'}} expected-error {{refers to non-type}} + typedef typename B::X x; // expected-error {{use 'template'}} expected-error {{refers to non-type}} }; }; diff --git a/libcxx/include/regex b/libcxx/include/regex index 17ad0cf5b2ae..b81413512132 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->template pair<_BidirectionalIterator, _BidirectionalIterator>::swap(__s); + this->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 1c6799f1c56e..ac40ec4a6b24 100644 --- a/llvm/include/llvm/ADT/ArrayRef.h +++ b/llvm/include/llvm/ADT/ArrayRef.h @@ -460,11 +460,8 @@ namespace llvm { OwningArrayRef &operator=(OwningArrayRef &&Other) { delete[] this->data(); - 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()); + this->MutableArrayRef::operator=(Other); + Other.MutableArrayRef::operator=(MutableArrayRef()); return *this; }