[libcxx-commits] [clang] [libcxx] Reapply "[Clang] Implement resolution for CWG1835 (#92957, #98547)" (PR #100425)

Krystian Stasiowski via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jul 30 08:07:07 PDT 2024


https://github.com/sdkrystian updated https://github.com/llvm/llvm-project/pull/100425

>From 18c19b2f056cf93b095a9dbad85b24ff89519d6b Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Thu, 11 Jul 2024 16:21:41 -0400
Subject: [PATCH 01/15] Reapply "[Clang] Implement resolution for CWG1835
 (#92957)"

Reapplies #92957.
---
 clang/docs/ReleaseNotes.rst                   |   3 +
 clang/include/clang/AST/ExprCXX.h             |  92 ++---
 clang/include/clang/AST/Stmt.h                |  15 +-
 clang/include/clang/AST/UnresolvedSet.h       |   4 +
 .../clang/Basic/DiagnosticParseKinds.td       |   4 +-
 clang/include/clang/Parse/Parser.h            |  14 +-
 clang/include/clang/Sema/DeclSpec.h           |   8 +
 clang/include/clang/Sema/Lookup.h             |   8 +-
 clang/include/clang/Sema/Sema.h               |  54 ++-
 clang/lib/AST/ASTImporter.cpp                 |  12 +-
 clang/lib/AST/ExprCXX.cpp                     |  67 ++--
 clang/lib/AST/ItaniumMangle.cpp               |  39 +--
 clang/lib/Parse/ParseExpr.cpp                 |   7 +-
 clang/lib/Parse/ParseExprCXX.cpp              |  85 ++---
 clang/lib/Sema/SemaCXXScopeSpec.cpp           | 195 +++++------
 clang/lib/Sema/SemaCoroutine.cpp              |   4 +-
 clang/lib/Sema/SemaDeclCXX.cpp                |  47 ++-
 clang/lib/Sema/SemaExpr.cpp                   |   2 +-
 clang/lib/Sema/SemaExprMember.cpp             |  74 ++--
 clang/lib/Sema/SemaOverload.cpp               |  12 +-
 clang/lib/Sema/SemaStmtAsm.cpp                |   8 +-
 clang/lib/Sema/SemaTemplate.cpp               | 218 ++++++------
 clang/lib/Sema/SemaTemplateInstantiate.cpp    |  17 +-
 clang/lib/Sema/TreeTransform.h                | 329 +++++++-----------
 clang/lib/Serialization/ASTReaderStmt.cpp     |  63 ++--
 clang/lib/Serialization/ASTWriterStmt.cpp     |  43 ++-
 .../basic.lookup.classref/p1-cxx11.cpp        |  14 +-
 .../basic.lookup/basic.lookup.classref/p1.cpp |  14 +-
 .../basic.lookup.qual.general/p3-example3.cpp |  27 ++
 .../basic.lookup.qual.general/p3.cpp          |  98 ++++++
 .../class.derived/class.member.lookup/p8.cpp  |   4 +-
 clang/test/CXX/drs/cwg1xx.cpp                 |   8 +-
 clang/test/CXX/temp/temp.names/p3-23.cpp      | 237 +++++++++++++
 clang/test/CXX/temp/temp.res/p3.cpp           |   2 +-
 clang/test/FixIt/fixit.cpp                    |   4 +-
 clang/test/Misc/warning-flags.c               |   2 +-
 .../Parser/cxx2a-concepts-requires-expr.cpp   |   2 +-
 .../SemaCXX/cxx0x-noexcept-expression.cpp     |   6 +-
 clang/test/SemaCXX/pseudo-destructors.cpp     |  18 +-
 clang/test/SemaCXX/static-assert-cxx17.cpp    |   2 +-
 .../SemaTemplate/dependent-base-classes.cpp   |  20 +-
 .../dependent-template-recover.cpp            |  12 +-
 .../SemaTemplate/temp_arg_nontype_cxx20.cpp   |   2 +-
 clang/test/SemaTemplate/template-id-expr.cpp  |  14 +-
 .../SemaTemplate/typename-specifier-3.cpp     |   2 +-
 libcxx/include/regex                          |   2 +-
 46 files changed, 1109 insertions(+), 805 deletions(-)
 create mode 100644 clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p3-example3.cpp
 create mode 100644 clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p3.cpp
 create mode 100644 clang/test/CXX/temp/temp.names/p3-23.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 25a9f2836ba3d..6440300c3a59f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -87,6 +87,9 @@ C++2c Feature Support
 Resolutions to C++ Defect Reports
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+- Clang now correctly implements lookup for the terminal name of a member-qualified nested-name-specifier.
+  (`CWG1835: Dependent member lookup before < <https://cplusplus.github.io/CWG/issues/1835.html>`_).
+
 C Language Changes
 ------------------
 
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index f86f1818110e6..fde7b4b7d8fea 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -3676,9 +3676,9 @@ class CXXUnresolvedConstructExpr final
 /// an implicit access if a qualifier is provided.
 class CXXDependentScopeMemberExpr final
     : public Expr,
-      private llvm::TrailingObjects<CXXDependentScopeMemberExpr,
-                                    ASTTemplateKWAndArgsInfo,
-                                    TemplateArgumentLoc, NamedDecl *> {
+      private llvm::TrailingObjects<
+          CXXDependentScopeMemberExpr, NestedNameSpecifierLoc, DeclAccessPair,
+          ASTTemplateKWAndArgsInfo, TemplateArgumentLoc> {
   friend class ASTStmtReader;
   friend class ASTStmtWriter;
   friend TrailingObjects;
@@ -3691,17 +3691,15 @@ class CXXDependentScopeMemberExpr final
   /// implicit accesses.
   QualType BaseType;
 
-  /// The nested-name-specifier that precedes the member name, if any.
-  /// FIXME: This could be in principle store as a trailing object.
-  /// However the performance impact of doing so should be investigated first.
-  NestedNameSpecifierLoc QualifierLoc;
-
   /// The member to which this member expression refers, which
   /// can be name, overloaded operator, or destructor.
   ///
   /// FIXME: could also be a template-id
   DeclarationNameInfo MemberNameInfo;
 
+  /// The location of the '->' or '.' operator.
+  SourceLocation OperatorLoc;
+
   // CXXDependentScopeMemberExpr is followed by several trailing objects,
   // some of which optional. They are in order:
   //
@@ -3721,8 +3719,16 @@ class CXXDependentScopeMemberExpr final
     return CXXDependentScopeMemberExprBits.HasTemplateKWAndArgsInfo;
   }
 
-  bool hasFirstQualifierFoundInScope() const {
-    return CXXDependentScopeMemberExprBits.HasFirstQualifierFoundInScope;
+  unsigned getNumUnqualifiedLookups() const {
+    return CXXDependentScopeMemberExprBits.NumUnqualifiedLookups;
+  }
+
+  unsigned numTrailingObjects(OverloadToken<NestedNameSpecifierLoc>) const {
+    return hasQualifier();
+  }
+
+  unsigned numTrailingObjects(OverloadToken<DeclAccessPair>) const {
+    return getNumUnqualifiedLookups();
   }
 
   unsigned numTrailingObjects(OverloadToken<ASTTemplateKWAndArgsInfo>) const {
@@ -3733,33 +3739,32 @@ class CXXDependentScopeMemberExpr final
     return getNumTemplateArgs();
   }
 
-  unsigned numTrailingObjects(OverloadToken<NamedDecl *>) const {
-    return hasFirstQualifierFoundInScope();
-  }
-
   CXXDependentScopeMemberExpr(const ASTContext &Ctx, Expr *Base,
                               QualType BaseType, bool IsArrow,
                               SourceLocation OperatorLoc,
                               NestedNameSpecifierLoc QualifierLoc,
                               SourceLocation TemplateKWLoc,
-                              NamedDecl *FirstQualifierFoundInScope,
+                              ArrayRef<DeclAccessPair> UnqualifiedLookups,
                               DeclarationNameInfo MemberNameInfo,
                               const TemplateArgumentListInfo *TemplateArgs);
 
-  CXXDependentScopeMemberExpr(EmptyShell Empty, bool HasTemplateKWAndArgsInfo,
-                              bool HasFirstQualifierFoundInScope);
+  CXXDependentScopeMemberExpr(EmptyShell Empty, bool HasQualifier,
+                              unsigned NumUnqualifiedLookups,
+                              bool HasTemplateKWAndArgsInfo);
 
 public:
   static CXXDependentScopeMemberExpr *
   Create(const ASTContext &Ctx, Expr *Base, QualType BaseType, bool IsArrow,
          SourceLocation OperatorLoc, NestedNameSpecifierLoc QualifierLoc,
-         SourceLocation TemplateKWLoc, NamedDecl *FirstQualifierFoundInScope,
+         SourceLocation TemplateKWLoc,
+         ArrayRef<DeclAccessPair> UnqualifiedLookups,
          DeclarationNameInfo MemberNameInfo,
          const TemplateArgumentListInfo *TemplateArgs);
 
   static CXXDependentScopeMemberExpr *
-  CreateEmpty(const ASTContext &Ctx, bool HasTemplateKWAndArgsInfo,
-              unsigned NumTemplateArgs, bool HasFirstQualifierFoundInScope);
+  CreateEmpty(const ASTContext &Ctx, bool HasQualifier,
+              unsigned NumUnqualifiedLookups, bool HasTemplateKWAndArgsInfo,
+              unsigned NumTemplateArgs);
 
   /// True if this is an implicit access, i.e. one in which the
   /// member being accessed was not written in the source.  The source
@@ -3784,34 +3789,35 @@ class CXXDependentScopeMemberExpr final
   bool isArrow() const { return CXXDependentScopeMemberExprBits.IsArrow; }
 
   /// Retrieve the location of the '->' or '.' operator.
-  SourceLocation getOperatorLoc() const {
-    return CXXDependentScopeMemberExprBits.OperatorLoc;
+  SourceLocation getOperatorLoc() const { return OperatorLoc; }
+
+  /// Determines whether this member expression had a nested-name-specifier
+  /// prior to the name of the member, e.g., x->Base::foo.
+  bool hasQualifier() const {
+    return CXXDependentScopeMemberExprBits.HasQualifier;
   }
 
-  /// Retrieve the nested-name-specifier that qualifies the member name.
-  NestedNameSpecifier *getQualifier() const {
-    return QualifierLoc.getNestedNameSpecifier();
+  /// If the member name was qualified, retrieves the nested-name-specifier
+  /// that precedes the member name, with source-location information.
+  NestedNameSpecifierLoc getQualifierLoc() const {
+    if (!hasQualifier())
+      return NestedNameSpecifierLoc();
+    return *getTrailingObjects<NestedNameSpecifierLoc>();
   }
 
-  /// Retrieve the nested-name-specifier that qualifies the member
-  /// name, with source location information.
-  NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; }
+  /// If the member name was qualified, retrieves the
+  /// nested-name-specifier that precedes the member name. Otherwise, returns
+  /// NULL.
+  NestedNameSpecifier *getQualifier() const {
+    return getQualifierLoc().getNestedNameSpecifier();
+  }
 
-  /// Retrieve the first part of the nested-name-specifier that was
-  /// found in the scope of the member access expression when the member access
-  /// was initially parsed.
-  ///
-  /// This function only returns a useful result when member access expression
-  /// uses a qualified member name, e.g., "x.Base::f". Here, the declaration
-  /// returned by this function describes what was found by unqualified name
-  /// lookup for the identifier "Base" within the scope of the member access
-  /// expression itself. At template instantiation time, this information is
-  /// combined with the results of name lookup into the type of the object
-  /// expression itself (the class type of x).
-  NamedDecl *getFirstQualifierFoundInScope() const {
-    if (!hasFirstQualifierFoundInScope())
-      return nullptr;
-    return *getTrailingObjects<NamedDecl *>();
+  /// Retrieve the declarations found by unqualified lookup for the first
+  /// component name of the nested-name-specifier, if any.
+  ArrayRef<DeclAccessPair> unqualified_lookups() const {
+    if (!getNumUnqualifiedLookups())
+      return std::nullopt;
+    return {getTrailingObjects<DeclAccessPair>(), getNumUnqualifiedLookups()};
   }
 
   /// Retrieve the name of the member that this expression refers to.
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index bbd7634bcc3bf..d0593865ee445 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -1020,18 +1020,19 @@ class alignas(void *) Stmt {
     LLVM_PREFERRED_TYPE(bool)
     unsigned IsArrow : 1;
 
+    /// True if this member expression used a nested-name-specifier to
+    /// refer to the member, e.g., "x->Base::f".
+    LLVM_PREFERRED_TYPE(bool)
+    unsigned HasQualifier : 1;
+
     /// Whether this member expression has info for explicit template
     /// keyword and arguments.
     LLVM_PREFERRED_TYPE(bool)
     unsigned HasTemplateKWAndArgsInfo : 1;
 
-    /// See getFirstQualifierFoundInScope() and the comment listing
-    /// the trailing objects.
-    LLVM_PREFERRED_TYPE(bool)
-    unsigned HasFirstQualifierFoundInScope : 1;
-
-    /// The location of the '->' or '.' operator.
-    SourceLocation OperatorLoc;
+    /// Number of declarations found by unqualified lookup for the
+    /// first component name of the nested-name-specifier.
+    unsigned NumUnqualifiedLookups;
   };
 
   class OverloadExprBitfields {
diff --git a/clang/include/clang/AST/UnresolvedSet.h b/clang/include/clang/AST/UnresolvedSet.h
index 1369725ab4e96..ef44499ce5926 100644
--- a/clang/include/clang/AST/UnresolvedSet.h
+++ b/clang/include/clang/AST/UnresolvedSet.h
@@ -97,6 +97,10 @@ class UnresolvedSetImpl {
     decls().push_back(DeclAccessPair::make(D, AS));
   }
 
+  void addAllDecls(ArrayRef<DeclAccessPair> 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 f8d50d12bb935..1f78652e631b6 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -895,9 +895,7 @@ def missing_template_arg_list_after_template_kw : Extension<
   "keyword">, InGroup<DiagGroup<"missing-template-arg-list-after-template-kw">>,
   DefaultError;
 
-def err_missing_dependent_template_keyword : Error<
-  "use 'template' keyword to treat '%0' as a dependent template name">;
-def warn_missing_dependent_template_keyword : ExtWarn<
+def ext_missing_dependent_template_keyword : ExtWarn<
   "use 'template' keyword to treat '%0' as a dependent template name">;
 
 def ext_extern_template : Extension<
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index ba7d6866ebacd..99e6d8a343469 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3374,15 +3374,11 @@ class Parser : public CodeCompletionHandler {
   BaseResult ParseBaseSpecifier(Decl *ClassDecl);
   AccessSpecifier getAccessSpecifierIfPresent() const;
 
-  bool ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
-                                    ParsedType ObjectType,
-                                    bool ObjectHadErrors,
-                                    SourceLocation TemplateKWLoc,
-                                    IdentifierInfo *Name,
-                                    SourceLocation NameLoc,
-                                    bool EnteringContext,
-                                    UnqualifiedId &Id,
-                                    bool AssumeTemplateId);
+  bool ParseUnqualifiedIdTemplateId(
+      CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors,
+      SourceLocation TemplateKWLoc, SourceLocation TildeLoc,
+      IdentifierInfo *Name, SourceLocation NameLoc, bool EnteringContext,
+      UnqualifiedId &Id, bool AssumeTemplateId);
   bool ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext,
                                   ParsedType ObjectType,
                                   UnqualifiedId &Result);
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 425b6e2a0b30c..9c22c35535ede 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -75,6 +75,7 @@ class CXXScopeSpec {
   SourceRange Range;
   NestedNameSpecifierLocBuilder Builder;
   ArrayRef<TemplateParameterList *> TemplateParamLists;
+  ArrayRef<DeclAccessPair> UnqualifiedLookups;
 
 public:
   SourceRange getRange() const { return Range; }
@@ -91,6 +92,13 @@ class CXXScopeSpec {
     return TemplateParamLists;
   }
 
+  void setUnqualifiedLookups(ArrayRef<DeclAccessPair> Found) {
+    UnqualifiedLookups = Found;
+  }
+  ArrayRef<DeclAccessPair> 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 b0a08a05ac6a0..6b765ef3c980f 100644
--- a/clang/include/clang/Sema/Lookup.h
+++ b/clang/include/clang/Sema/Lookup.h
@@ -483,11 +483,15 @@ class LookupResult {
     ResultKind = Found;
   }
 
+  void addAllDecls(ArrayRef<DeclAccessPair> Other) {
+    Decls.addAllDecls(Other);
+    ResultKind = Found;
+  }
+
   /// Add all the declarations from another set of lookup
   /// results.
   void addAllDecls(const LookupResult &Other) {
-    Decls.append(Other.Decls.begin(), Other.Decls.end());
-    ResultKind = Found;
+    addAllDecls(Other.Decls.pairs());
   }
 
   /// Determine whether no result was found because we could not
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 2ec6367eccea0..3e897aec55391 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2804,7 +2804,8 @@ class Sema final : public SemaBase {
   /// (e.g., Base::), perform name lookup for that identifier as a
   /// nested-name-specifier within the given scope, and return the result of
   /// that name lookup.
-  NamedDecl *FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS);
+  bool LookupFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS,
+                                   UnresolvedSetImpl &R);
 
   /// Keeps information about an identifier in a nested-name-spec.
   ///
@@ -2844,9 +2845,6 @@ class Sema final : public SemaBase {
   /// \param EnteringContext If true, enter the context specified by the
   ///        nested-name-specifier.
   /// \param SS Optional nested name specifier preceding the identifier.
-  /// \param ScopeLookupResult Provides the result of name lookup within the
-  ///        scope of the nested-name-specifier that was computed at template
-  ///        definition time.
   /// \param ErrorRecoveryLookup Specifies if the method is called to improve
   ///        error recovery and what kind of recovery is performed.
   /// \param IsCorrectedToColon If not null, suggestion of replace '::' -> ':'
@@ -2855,11 +2853,6 @@ class Sema final : public SemaBase {
   ///        not '::'.
   /// \param OnlyNamespace If true, only considers namespaces in lookup.
   ///
-  /// This routine differs only slightly from ActOnCXXNestedNameSpecifier, in
-  /// that it contains an extra parameter \p ScopeLookupResult, which provides
-  /// the result of name lookup within the scope of the nested-name-specifier
-  /// that was computed at template definition time.
-  ///
   /// If ErrorRecoveryLookup is true, then this call is used to improve error
   /// recovery.  This means that it should not emit diagnostics, it should
   /// just return true on failure.  It also means it should only return a valid
@@ -2868,7 +2861,6 @@ class Sema final : public SemaBase {
   /// specifier.
   bool BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
                                    bool EnteringContext, CXXScopeSpec &SS,
-                                   NamedDecl *ScopeLookupResult,
                                    bool ErrorRecoveryLookup,
                                    bool *IsCorrectedToColon = nullptr,
                                    bool OnlyNamespace = false);
@@ -8570,11 +8562,12 @@ class Sema final : public SemaBase {
                           const TemplateArgumentListInfo *TemplateArgs,
                           bool IsDefiniteInstance, const Scope *S);
 
-  ExprResult ActOnDependentMemberExpr(
-      Expr *Base, QualType BaseType, bool IsArrow, SourceLocation OpLoc,
-      const CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
-      NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo,
-      const TemplateArgumentListInfo *TemplateArgs);
+  ExprResult
+  ActOnDependentMemberExpr(Expr *Base, QualType BaseType, bool IsArrow,
+                           SourceLocation OpLoc, const CXXScopeSpec &SS,
+                           SourceLocation TemplateKWLoc,
+                           const DeclarationNameInfo &NameInfo,
+                           const TemplateArgumentListInfo *TemplateArgs);
 
   /// The main callback when the parser finds something like
   ///   expression . [nested-name-specifier] identifier
@@ -8630,15 +8623,14 @@ class Sema final : public SemaBase {
   ExprResult BuildMemberReferenceExpr(
       Expr *Base, QualType BaseType, SourceLocation OpLoc, bool IsArrow,
       CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
-      NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo,
+      const DeclarationNameInfo &NameInfo,
       const TemplateArgumentListInfo *TemplateArgs, const Scope *S,
       ActOnMemberAccessExtraArgs *ExtraArgs = nullptr);
 
   ExprResult
   BuildMemberReferenceExpr(Expr *Base, QualType BaseType, SourceLocation OpLoc,
                            bool IsArrow, const CXXScopeSpec &SS,
-                           SourceLocation TemplateKWLoc,
-                           NamedDecl *FirstQualifierInScope, LookupResult &R,
+                           SourceLocation TemplateKWLoc, LookupResult &R,
                            const TemplateArgumentListInfo *TemplateArgs,
                            const Scope *S, bool SuppressQualifierCheck = false,
                            ActOnMemberAccessExtraArgs *ExtraArgs = nullptr);
@@ -11126,15 +11118,14 @@ class Sema final : public SemaBase {
                      QualType ObjectType, bool EnteringContext,
                      RequiredTemplateKind RequiredTemplate = SourceLocation(),
                      AssumedTemplateKind *ATK = nullptr,
-                     bool AllowTypoCorrection = true);
+                     bool AllowTypoCorrection = true, bool MayBeNNS = false);
 
-  TemplateNameKind isTemplateName(Scope *S, CXXScopeSpec &SS,
-                                  bool hasTemplateKeyword,
-                                  const UnqualifiedId &Name,
-                                  ParsedType ObjectType, bool EnteringContext,
-                                  TemplateTy &Template,
-                                  bool &MemberOfUnknownSpecialization,
-                                  bool Disambiguation = false);
+  TemplateNameKind
+  isTemplateName(Scope *S, CXXScopeSpec &SS, bool hasTemplateKeyword,
+                 const UnqualifiedId &Name, ParsedType ObjectType,
+                 bool EnteringContext, TemplateTy &Template,
+                 bool &MemberOfUnknownSpecialization,
+                 bool Disambiguation = false, bool MayBeNNS = false);
 
   /// Try to resolve an undeclared template name as a type template.
   ///
@@ -11467,12 +11458,11 @@ class Sema final : public SemaBase {
   /// For example, given "x.MetaFun::template apply", the scope specifier
   /// \p SS will be "MetaFun::", \p TemplateKWLoc contains the location
   /// of the "template" keyword, and "apply" is the \p Name.
-  TemplateNameKind ActOnTemplateName(Scope *S, CXXScopeSpec &SS,
-                                     SourceLocation TemplateKWLoc,
-                                     const UnqualifiedId &Name,
-                                     ParsedType ObjectType,
-                                     bool EnteringContext, TemplateTy &Template,
-                                     bool AllowInjectedClassName = false);
+  TemplateNameKind
+  ActOnTemplateName(Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
+                    const UnqualifiedId &Name, ParsedType ObjectType,
+                    bool EnteringContext, TemplateTy &Template,
+                    bool AllowInjectedClassName = false, bool MayBeNNS = false);
 
   DeclResult ActOnClassTemplateSpecialization(
       Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index da1981d8dd05f..e521f34a2b175 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -8438,8 +8438,14 @@ ExpectedStmt ASTNodeImporter::VisitCXXDependentScopeMemberExpr(
   auto ToOperatorLoc = importChecked(Err, E->getOperatorLoc());
   auto ToQualifierLoc = importChecked(Err, E->getQualifierLoc());
   auto ToTemplateKeywordLoc = importChecked(Err, E->getTemplateKeywordLoc());
-  auto ToFirstQualifierFoundInScope =
-      importChecked(Err, E->getFirstQualifierFoundInScope());
+
+  UnresolvedSet<8> ToUnqualifiedLookups;
+  for (auto D : E->unqualified_lookups())
+    if (auto ToDOrErr = import(D.getDecl()))
+      ToUnqualifiedLookups.addDecl(*ToDOrErr);
+    else
+      return ToDOrErr.takeError();
+
   if (Err)
     return std::move(Err);
 
@@ -8473,7 +8479,7 @@ ExpectedStmt ASTNodeImporter::VisitCXXDependentScopeMemberExpr(
 
   return CXXDependentScopeMemberExpr::Create(
       Importer.getToContext(), ToBase, ToType, E->isArrow(), ToOperatorLoc,
-      ToQualifierLoc, ToTemplateKeywordLoc, ToFirstQualifierFoundInScope,
+      ToQualifierLoc, ToTemplateKeywordLoc, ToUnqualifiedLookups.pairs(),
       ToMemberNameInfo, ResInfo);
 }
 
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index e2c9643151126..e97c80141be0a 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -1489,19 +1489,27 @@ SourceLocation CXXUnresolvedConstructExpr::getBeginLoc() const {
 CXXDependentScopeMemberExpr::CXXDependentScopeMemberExpr(
     const ASTContext &Ctx, Expr *Base, QualType BaseType, bool IsArrow,
     SourceLocation OperatorLoc, NestedNameSpecifierLoc QualifierLoc,
-    SourceLocation TemplateKWLoc, NamedDecl *FirstQualifierFoundInScope,
+    SourceLocation TemplateKWLoc, ArrayRef<DeclAccessPair> UnqualifiedLookups,
     DeclarationNameInfo MemberNameInfo,
     const TemplateArgumentListInfo *TemplateArgs)
     : Expr(CXXDependentScopeMemberExprClass, Ctx.DependentTy, VK_LValue,
            OK_Ordinary),
-      Base(Base), BaseType(BaseType), QualifierLoc(QualifierLoc),
-      MemberNameInfo(MemberNameInfo) {
+      Base(Base), BaseType(BaseType), MemberNameInfo(MemberNameInfo),
+      OperatorLoc(OperatorLoc) {
   CXXDependentScopeMemberExprBits.IsArrow = IsArrow;
+  CXXDependentScopeMemberExprBits.HasQualifier = QualifierLoc.hasQualifier();
+  CXXDependentScopeMemberExprBits.NumUnqualifiedLookups =
+      UnqualifiedLookups.size();
   CXXDependentScopeMemberExprBits.HasTemplateKWAndArgsInfo =
       (TemplateArgs != nullptr) || TemplateKWLoc.isValid();
-  CXXDependentScopeMemberExprBits.HasFirstQualifierFoundInScope =
-      FirstQualifierFoundInScope != nullptr;
-  CXXDependentScopeMemberExprBits.OperatorLoc = OperatorLoc;
+
+  if (hasQualifier())
+    new (getTrailingObjects<NestedNameSpecifierLoc>())
+        NestedNameSpecifierLoc(QualifierLoc);
+
+  std::uninitialized_copy_n(UnqualifiedLookups.data(),
+                            UnqualifiedLookups.size(),
+                            getTrailingObjects<DeclAccessPair>());
 
   if (TemplateArgs) {
     auto Deps = TemplateArgumentDependence::None;
@@ -1513,54 +1521,59 @@ CXXDependentScopeMemberExpr::CXXDependentScopeMemberExpr(
         TemplateKWLoc);
   }
 
-  if (hasFirstQualifierFoundInScope())
-    *getTrailingObjects<NamedDecl *>() = FirstQualifierFoundInScope;
   setDependence(computeDependence(this));
 }
 
 CXXDependentScopeMemberExpr::CXXDependentScopeMemberExpr(
-    EmptyShell Empty, bool HasTemplateKWAndArgsInfo,
-    bool HasFirstQualifierFoundInScope)
+    EmptyShell Empty, bool HasQualifier, unsigned NumUnqualifiedLookups,
+    bool HasTemplateKWAndArgsInfo)
     : Expr(CXXDependentScopeMemberExprClass, Empty) {
+  CXXDependentScopeMemberExprBits.HasQualifier = HasQualifier;
+  CXXDependentScopeMemberExprBits.NumUnqualifiedLookups = NumUnqualifiedLookups;
   CXXDependentScopeMemberExprBits.HasTemplateKWAndArgsInfo =
       HasTemplateKWAndArgsInfo;
-  CXXDependentScopeMemberExprBits.HasFirstQualifierFoundInScope =
-      HasFirstQualifierFoundInScope;
 }
 
 CXXDependentScopeMemberExpr *CXXDependentScopeMemberExpr::Create(
     const ASTContext &Ctx, Expr *Base, QualType BaseType, bool IsArrow,
     SourceLocation OperatorLoc, NestedNameSpecifierLoc QualifierLoc,
-    SourceLocation TemplateKWLoc, NamedDecl *FirstQualifierFoundInScope,
+    SourceLocation TemplateKWLoc, ArrayRef<DeclAccessPair> UnqualifiedLookups,
     DeclarationNameInfo MemberNameInfo,
     const TemplateArgumentListInfo *TemplateArgs) {
+  bool HasQualifier = QualifierLoc.hasQualifier();
+  unsigned NumUnqualifiedLookups = UnqualifiedLookups.size();
+  assert(!NumUnqualifiedLookups || HasQualifier);
   bool HasTemplateKWAndArgsInfo =
       (TemplateArgs != nullptr) || TemplateKWLoc.isValid();
   unsigned NumTemplateArgs = TemplateArgs ? TemplateArgs->size() : 0;
-  bool HasFirstQualifierFoundInScope = FirstQualifierFoundInScope != nullptr;
-
-  unsigned Size = totalSizeToAlloc<ASTTemplateKWAndArgsInfo,
-                                   TemplateArgumentLoc, NamedDecl *>(
-      HasTemplateKWAndArgsInfo, NumTemplateArgs, HasFirstQualifierFoundInScope);
+  unsigned Size =
+      totalSizeToAlloc<NestedNameSpecifierLoc, DeclAccessPair,
+                       ASTTemplateKWAndArgsInfo, TemplateArgumentLoc>(
+          HasQualifier, NumUnqualifiedLookups, HasTemplateKWAndArgsInfo,
+          NumTemplateArgs);
 
   void *Mem = Ctx.Allocate(Size, alignof(CXXDependentScopeMemberExpr));
   return new (Mem) CXXDependentScopeMemberExpr(
       Ctx, Base, BaseType, IsArrow, OperatorLoc, QualifierLoc, TemplateKWLoc,
-      FirstQualifierFoundInScope, MemberNameInfo, TemplateArgs);
+      UnqualifiedLookups, MemberNameInfo, TemplateArgs);
 }
 
 CXXDependentScopeMemberExpr *CXXDependentScopeMemberExpr::CreateEmpty(
-    const ASTContext &Ctx, bool HasTemplateKWAndArgsInfo,
-    unsigned NumTemplateArgs, bool HasFirstQualifierFoundInScope) {
-  assert(NumTemplateArgs == 0 || HasTemplateKWAndArgsInfo);
+    const ASTContext &Ctx, bool HasQualifier, unsigned NumUnqualifiedLookups,
+    bool HasTemplateKWAndArgsInfo, unsigned NumTemplateArgs) {
+  assert(!NumTemplateArgs || HasTemplateKWAndArgsInfo);
+  assert(!NumUnqualifiedLookups || HasQualifier);
 
-  unsigned Size = totalSizeToAlloc<ASTTemplateKWAndArgsInfo,
-                                   TemplateArgumentLoc, NamedDecl *>(
-      HasTemplateKWAndArgsInfo, NumTemplateArgs, HasFirstQualifierFoundInScope);
+  unsigned Size =
+      totalSizeToAlloc<NestedNameSpecifierLoc, DeclAccessPair,
+                       ASTTemplateKWAndArgsInfo, TemplateArgumentLoc>(
+          HasQualifier, NumUnqualifiedLookups, HasTemplateKWAndArgsInfo,
+          NumTemplateArgs);
 
   void *Mem = Ctx.Allocate(Size, alignof(CXXDependentScopeMemberExpr));
-  return new (Mem) CXXDependentScopeMemberExpr(
-      EmptyShell(), HasTemplateKWAndArgsInfo, HasFirstQualifierFoundInScope);
+  return new (Mem) CXXDependentScopeMemberExpr(EmptyShell(), HasQualifier,
+                                               NumUnqualifiedLookups,
+                                               HasTemplateKWAndArgsInfo);
 }
 
 CXXThisExpr *CXXThisExpr::Create(const ASTContext &Ctx, SourceLocation L,
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index d46d621d4c7d4..eea51199aa302 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -594,11 +594,10 @@ class CXXNameMangler {
   void mangleMemberExprBase(const Expr *base, bool isArrow);
   void mangleMemberExpr(const Expr *base, bool isArrow,
                         NestedNameSpecifier *qualifier,
-                        NamedDecl *firstQualifierLookup,
+                        ArrayRef<DeclAccessPair> UnqualifiedLookups,
                         DeclarationName name,
                         const TemplateArgumentLoc *TemplateArgs,
-                        unsigned NumTemplateArgs,
-                        unsigned knownArity);
+                        unsigned NumTemplateArgs, unsigned knownArity);
   void mangleCastExpression(const Expr *E, StringRef CastEncoding);
   void mangleInitListElements(const InitListExpr *InitList);
   void mangleRequirement(SourceLocation RequiresExprLoc,
@@ -4496,14 +4495,11 @@ void CXXNameMangler::mangleMemberExprBase(const Expr *Base, bool IsArrow) {
 }
 
 /// Mangles a member expression.
-void CXXNameMangler::mangleMemberExpr(const Expr *base,
-                                      bool isArrow,
-                                      NestedNameSpecifier *qualifier,
-                                      NamedDecl *firstQualifierLookup,
-                                      DeclarationName member,
-                                      const TemplateArgumentLoc *TemplateArgs,
-                                      unsigned NumTemplateArgs,
-                                      unsigned arity) {
+void CXXNameMangler::mangleMemberExpr(
+    const Expr *base, bool isArrow, NestedNameSpecifier *qualifier,
+    ArrayRef<DeclAccessPair> UnqualifiedLookups, DeclarationName member,
+    const TemplateArgumentLoc *TemplateArgs, unsigned NumTemplateArgs,
+    unsigned arity) {
   // <expression> ::= dt <expression> <unresolved-name>
   //              ::= pt <expression> <unresolved-name>
   if (base)
@@ -4985,11 +4981,9 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
   case Expr::MemberExprClass: {
     NotPrimaryExpr();
     const MemberExpr *ME = cast<MemberExpr>(E);
-    mangleMemberExpr(ME->getBase(), ME->isArrow(),
-                     ME->getQualifier(), nullptr,
-                     ME->getMemberDecl()->getDeclName(),
-                     ME->getTemplateArgs(), ME->getNumTemplateArgs(),
-                     Arity);
+    mangleMemberExpr(ME->getBase(), ME->isArrow(), ME->getQualifier(),
+                     std::nullopt, ME->getMemberDecl()->getDeclName(),
+                     ME->getTemplateArgs(), ME->getNumTemplateArgs(), Arity);
     break;
   }
 
@@ -4997,10 +4991,9 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
     NotPrimaryExpr();
     const UnresolvedMemberExpr *ME = cast<UnresolvedMemberExpr>(E);
     mangleMemberExpr(ME->isImplicitAccess() ? nullptr : ME->getBase(),
-                     ME->isArrow(), ME->getQualifier(), nullptr,
-                     ME->getMemberName(),
-                     ME->getTemplateArgs(), ME->getNumTemplateArgs(),
-                     Arity);
+                     ME->isArrow(), ME->getQualifier(), std::nullopt,
+                     ME->getMemberName(), ME->getTemplateArgs(),
+                     ME->getNumTemplateArgs(), Arity);
     break;
   }
 
@@ -5010,10 +5003,8 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
       = cast<CXXDependentScopeMemberExpr>(E);
     mangleMemberExpr(ME->isImplicitAccess() ? nullptr : ME->getBase(),
                      ME->isArrow(), ME->getQualifier(),
-                     ME->getFirstQualifierFoundInScope(),
-                     ME->getMember(),
-                     ME->getTemplateArgs(), ME->getNumTemplateArgs(),
-                     Arity);
+                     ME->unqualified_lookups(), ME->getMember(),
+                     ME->getTemplateArgs(), ME->getNumTemplateArgs(), Arity);
     break;
   }
 
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index e82b565272831..5953717e75d97 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -2365,10 +2365,9 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
       }
 
       if (!LHS.isInvalid())
-        LHS = Actions.ActOnMemberAccessExpr(getCurScope(), LHS.get(), OpLoc,
-                                            OpKind, SS, TemplateKWLoc, Name,
-                                 CurParsedObjCImpl ? CurParsedObjCImpl->Dcl
-                                                   : nullptr);
+        LHS = Actions.ActOnMemberAccessExpr(
+            getCurScope(), LHS.get(), OpLoc, OpKind, SS, TemplateKWLoc, Name,
+            CurParsedObjCImpl ? CurParsedObjCImpl->Dcl : nullptr);
       if (!LHS.isInvalid()) {
         if (Tok.is(tok::less))
           checkPotentialAngleBracket(LHS);
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index c0eae73927cdd..ad0758a616380 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -100,7 +100,8 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType,
   bool MemberOfUnknownSpecialization;
   if (!Actions.isTemplateName(getCurScope(), SS, /*hasTemplateKeyword=*/false,
                               TemplateName, ObjectType, EnteringContext,
-                              Template, MemberOfUnknownSpecialization))
+                              Template, MemberOfUnknownSpecialization,
+                              /*Disambiguation=*/false, /*MayBeNNS=*/true))
     return;
 
   FixDigraph(*this, PP, Next, SecondToken, tok::unknown,
@@ -353,7 +354,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
       TemplateTy Template;
       TemplateNameKind TNK = Actions.ActOnTemplateName(
           getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType,
-          EnteringContext, Template, /*AllowInjectedClassName*/ true);
+          EnteringContext, Template, /*AllowInjectedClassName*/ true,
+          /*MayBeNNS=*/true);
       if (AnnotateTemplateIdToken(Template, TNK, SS, TemplateKWLoc,
                                   TemplateName, false))
         return true;
@@ -405,7 +407,6 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
                                       : TemplateId->TemplateNameLoc;
         SS.SetInvalid(SourceRange(StartLoc, CCLoc));
       }
-
       continue;
     }
 
@@ -532,12 +533,15 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
               getCurScope(), SS,
               /*hasTemplateKeyword=*/false, TemplateName, ObjectType,
               EnteringContext, Template, MemberOfUnknownSpecialization,
-              Disambiguation)) {
+              Disambiguation,
+              /*MayBeNNS=*/true)) {
         // If lookup didn't find anything, we treat the name as a template-name
         // anyway. C++20 requires this, and in prior language modes it improves
         // error recovery. But before we commit to this, check that we actually
         // have something that looks like a template-argument-list next.
-        if (!IsTypename && TNK == TNK_Undeclared_template &&
+        if (!IsTypename &&
+            (TNK == TNK_Undeclared_template ||
+             (!HasScopeSpecifier && ObjectType)) &&
             isTemplateArgumentList(1) == TPResult::False)
           break;
 
@@ -565,11 +569,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
           // member of an unknown specialization. However, this will only
           // parse correctly as a template, so suggest the keyword 'template'
           // before 'getAs' and treat this as a dependent template name.
-          unsigned DiagID = diag::err_missing_dependent_template_keyword;
-          if (getLangOpts().MicrosoftExt)
-            DiagID = diag::warn_missing_dependent_template_keyword;
-
-          Diag(Tok.getLocation(), DiagID)
+          Diag(Tok.getLocation(), diag::ext_missing_dependent_template_keyword)
               << II.getName()
               << FixItHint::CreateInsertion(Tok.getLocation(), "template ");
         }
@@ -1919,12 +1919,12 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc,
   // argument list. This affects examples such as
   //   void f(auto *p) { p->~X<int>(); }
   // ... but there's no ambiguity, and nowhere to write 'template' in such an
-  // example, so we accept it anyway.
-  if (Tok.is(tok::less) &&
-      ParseUnqualifiedIdTemplateId(
-          SS, ObjectType, Base && Base->containsErrors(), SourceLocation(),
-          Name, NameLoc, false, SecondTypeName,
-          /*AssumeTemplateId=*/true))
+  // example, so we accept it anyway
+  if (Tok.is(tok::less) && ParseUnqualifiedIdTemplateId(
+                               SS, ObjectType, Base && Base->containsErrors(),
+                               /*TemplateKWLoc=*/SourceLocation(), TildeLoc,
+                               Name, NameLoc, false, SecondTypeName,
+                               /*AssumeTemplateId=*/true))
     return ExprError();
 
   return Actions.ActOnPseudoDestructorExpr(getCurScope(), Base, OpLoc, OpKind,
@@ -2531,8 +2531,9 @@ bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS, DeclaratorContext Context) {
 /// \returns true if a parse error occurred, false otherwise.
 bool Parser::ParseUnqualifiedIdTemplateId(
     CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors,
-    SourceLocation TemplateKWLoc, IdentifierInfo *Name, SourceLocation NameLoc,
-    bool EnteringContext, UnqualifiedId &Id, bool AssumeTemplateId) {
+    SourceLocation TemplateKWLoc, SourceLocation TildeLoc, IdentifierInfo *Name,
+    SourceLocation NameLoc, bool EnteringContext, UnqualifiedId &Id,
+    bool AssumeTemplateId) {
   assert(Tok.is(tok::less) && "Expected '<' to finish parsing a template-id");
 
   TemplateTy Template;
@@ -2546,13 +2547,14 @@ bool Parser::ParseUnqualifiedIdTemplateId(
       // this template-id is used to form a nested-name-specifier or not.
       TNK = Actions.ActOnTemplateName(getCurScope(), SS, TemplateKWLoc, Id,
                                       ObjectType, EnteringContext, Template,
-                                      /*AllowInjectedClassName*/ true);
+                                      /*AllowInjectedClassName=*/true,
+                                      TildeLoc.isValid());
     } else {
       bool MemberOfUnknownSpecialization;
-      TNK = Actions.isTemplateName(getCurScope(), SS,
-                                   TemplateKWLoc.isValid(), Id,
-                                   ObjectType, EnteringContext, Template,
-                                   MemberOfUnknownSpecialization);
+      TNK = Actions.isTemplateName(
+          getCurScope(), SS, TemplateKWLoc.isValid(), Id, ObjectType,
+          EnteringContext, Template, MemberOfUnknownSpecialization,
+          /*Disambiguation=*/false, TildeLoc.isValid());
       // If lookup found nothing but we're assuming that this is a template
       // name, double-check that makes sense syntactically before committing
       // to it.
@@ -2579,13 +2581,13 @@ bool Parser::ParseUnqualifiedIdTemplateId(
             else
               Name += Id.Identifier->getName();
           }
-          Diag(Id.StartLocation, diag::err_missing_dependent_template_keyword)
+          Diag(Id.StartLocation, diag::ext_missing_dependent_template_keyword)
               << Name
               << FixItHint::CreateInsertion(Id.StartLocation, "template ");
         }
         TNK = Actions.ActOnTemplateName(
             getCurScope(), SS, TemplateKWLoc, Id, ObjectType, EnteringContext,
-            Template, /*AllowInjectedClassName*/ true);
+            Template, /*AllowInjectedClassName=*/true, TildeLoc.isValid());
       } else if (TNK == TNK_Non_template) {
         return false;
       }
@@ -2610,14 +2612,16 @@ bool Parser::ParseUnqualifiedIdTemplateId(
     bool MemberOfUnknownSpecialization;
     TemplateName.setIdentifier(Name, NameLoc);
     if (ObjectType) {
-      TNK = Actions.ActOnTemplateName(
-          getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType,
-          EnteringContext, Template, /*AllowInjectedClassName*/ true);
+      TNK = Actions.ActOnTemplateName(getCurScope(), SS, TemplateKWLoc,
+                                      TemplateName, ObjectType, EnteringContext,
+                                      Template, /*AllowInjectedClassName=*/true,
+                                      /*MayBeNNS=*/true);
     } else {
       TNK = Actions.isTemplateName(getCurScope(), SS, TemplateKWLoc.isValid(),
-                                   TemplateName, ObjectType,
-                                   EnteringContext, Template,
-                                   MemberOfUnknownSpecialization);
+                                   TemplateName, ObjectType, EnteringContext,
+                                   Template, MemberOfUnknownSpecialization,
+                                   /*Disambiguation=*/false,
+                                   /*MayBeNNS=*/true);
 
       if (TNK == TNK_Non_template && !Id.DestructorName.get()) {
         Diag(NameLoc, diag::err_destructor_template_id)
@@ -2679,7 +2683,7 @@ bool Parser::ParseUnqualifiedIdTemplateId(
   if (Id.getKind() == UnqualifiedIdKind::IK_ConstructorName)
     Id.setConstructorName(Type.get(), NameLoc, RAngleLoc);
   else
-    Id.setDestructorName(Id.StartLocation, Type.get(), RAngleLoc);
+    Id.setDestructorName(TildeLoc, Type.get(), RAngleLoc);
 
   return false;
 }
@@ -3027,8 +3031,9 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
     if (Tok.is(tok::less))
       return ParseUnqualifiedIdTemplateId(
           SS, ObjectType, ObjectHadErrors,
-          TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), Id, IdLoc,
-          EnteringContext, Result, TemplateSpecified);
+          TemplateKWLoc ? *TemplateKWLoc : SourceLocation(),
+          /*TildeLoc=*/SourceLocation(), Id, IdLoc, EnteringContext, Result,
+          TemplateSpecified);
 
     if (TemplateSpecified) {
       TemplateNameKind TNK =
@@ -3123,13 +3128,15 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
         Tok.is(tok::less))
       return ParseUnqualifiedIdTemplateId(
           SS, ObjectType, ObjectHadErrors,
-          TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), nullptr,
-          SourceLocation(), EnteringContext, Result, TemplateSpecified);
+          TemplateKWLoc ? *TemplateKWLoc : SourceLocation(),
+          /*TildeLoc=*/SourceLocation(), /*Name=*/nullptr,
+          /*NameLoc=*/SourceLocation(), EnteringContext, Result,
+          TemplateSpecified);
     else if (TemplateSpecified &&
              Actions.ActOnTemplateName(
                  getCurScope(), SS, *TemplateKWLoc, Result, ObjectType,
                  EnteringContext, Template,
-                 /*AllowInjectedClassName*/ true) == TNK_Non_template)
+                 /*AllowInjectedClassName=*/true) == TNK_Non_template)
       return true;
 
     return false;
@@ -3219,8 +3226,8 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
       Result.setDestructorName(TildeLoc, nullptr, ClassNameLoc);
       return ParseUnqualifiedIdTemplateId(
           SS, ObjectType, ObjectHadErrors,
-          TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), ClassName,
-          ClassNameLoc, EnteringContext, Result, TemplateSpecified);
+          TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), TildeLoc,
+          ClassName, ClassNameLoc, EnteringContext, Result, TemplateSpecified);
     }
 
     // Note that this is a destructor name.
diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp
index 5b2d65247e72e..dd61bb22e3dfa 100644
--- a/clang/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp
@@ -356,29 +356,41 @@ bool Sema::isAcceptableNestedNameSpecifier(const NamedDecl *SD,
   return false;
 }
 
-NamedDecl *Sema::FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS) {
-  if (!S || !NNS)
-    return nullptr;
+/// If the given nested-name-specifier begins with a bare identifier
+/// (e.g., Base::), perform name lookup for that identifier as a
+/// nested-name-specifier within the given scope, and return the result of that
+/// name lookup.
+bool Sema::LookupFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS,
+                                       UnresolvedSetImpl &R) {
+  if (!S)
+    return false;
 
   while (NNS->getPrefix())
     NNS = NNS->getPrefix();
 
-  if (NNS->getKind() != NestedNameSpecifier::Identifier)
-    return nullptr;
-
-  LookupResult Found(*this, NNS->getAsIdentifier(), SourceLocation(),
-                     LookupNestedNameSpecifierName);
+  // FIXME: This is a rather nasty hack! Ideally we should get the results
+  // from LookupTemplateName/BuildCXXNestedNameSpecifier.
+  const IdentifierInfo *II = NNS->getAsIdentifier();
+  if (!II) {
+    if (const auto *DTST =
+            dyn_cast_if_present<DependentTemplateSpecializationType>(
+                NNS->getAsType()))
+      II = DTST->getIdentifier();
+    else
+      return false;
+  }
+  assert(II && "Missing first qualifier in scope");
+  LookupResult Found(*this, II, SourceLocation(),
+                     NNS->getAsIdentifier() ? LookupNestedNameSpecifierName
+                                            : LookupOrdinaryName);
   LookupName(Found, S);
-  assert(!Found.isAmbiguous() && "Cannot handle ambiguities here yet");
 
-  if (!Found.isSingleResult())
-    return nullptr;
-
-  NamedDecl *Result = Found.getFoundDecl();
-  if (isAcceptableNestedNameSpecifier(Result))
-    return Result;
+  if (Found.empty())
+    return false;
 
-  return nullptr;
+  R.addAllDecls(Found.asUnresolvedSet().pairs());
+  Found.suppressDiagnostics();
+  return true;
 }
 
 namespace {
@@ -407,112 +419,82 @@ class NestedNameSpecifierValidatorCCC final
 
 bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
                                        bool EnteringContext, CXXScopeSpec &SS,
-                                       NamedDecl *ScopeLookupResult,
                                        bool ErrorRecoveryLookup,
                                        bool *IsCorrectedToColon,
                                        bool OnlyNamespace) {
   if (IdInfo.Identifier->isEditorPlaceholder())
     return true;
+  if (IsCorrectedToColon)
+    *IsCorrectedToColon = false;
+
+  QualType ObjectType = GetTypeFromParser(IdInfo.ObjectType);
   LookupResult Found(*this, IdInfo.Identifier, IdInfo.IdentifierLoc,
                      OnlyNamespace ? LookupNamespaceName
                                    : LookupNestedNameSpecifierName);
-  QualType ObjectType = GetTypeFromParser(IdInfo.ObjectType);
 
-  // Determine where to perform name lookup
-  DeclContext *LookupCtx = nullptr;
-  bool isDependent = false;
-  if (IsCorrectedToColon)
-    *IsCorrectedToColon = false;
-  if (!ObjectType.isNull()) {
-    // This nested-name-specifier occurs in a member access expression, e.g.,
-    // x->B::f, and we are looking into the type of the object.
-    assert(!SS.isSet() && "ObjectType and scope specifier cannot coexist");
-    LookupCtx = computeDeclContext(ObjectType);
-    isDependent = ObjectType->isDependentType();
-  } else if (SS.isSet()) {
-    // This nested-name-specifier occurs after another nested-name-specifier,
-    // so look into the context associated with the prior nested-name-specifier.
-    LookupCtx = computeDeclContext(SS, EnteringContext);
-    isDependent = isDependentScopeSpecifier(SS);
-    Found.setContextRange(SS.getRange());
+  // C++ [basic.lookup.qual.general]p3:
+  //   Qualified name lookup in a class, namespace, or enumeration performs a
+  //   search of the scope associated with it except as specified below.
+  LookupParsedName(Found, S, &SS, ObjectType,
+                   /*AllowBuiltinCreation=*/false, EnteringContext);
+
+  // C++ [basic.lookup.qual.general]p3:
+  //   [...] Unless otherwise specified, a qualified name undergoes qualified
+  //   name lookup in its lookup context from the point where it appears unless
+  //   the lookup context either is dependent and is not the current
+  //   instantiation or is not a class or class template.
+  if (Found.wasNotFoundInCurrentInstantiation()) {
+    // Don't speculate if we're just trying to improve error recovery.
+    if (ErrorRecoveryLookup)
+      return true;
+
+    // The lookup context is dependent and either:
+    // - it is not the current instantiation, or
+    // - it is the current instantiation, it has at least one dependent base
+    //   class, and qualified lookup found nothing.
+    // Build a dependent nested-name-specifier. We will lookup the name again
+    // during instantiation.
+    SS.Extend(Context, IdInfo.Identifier, IdInfo.IdentifierLoc, IdInfo.CCLoc);
+    return false;
   }
 
   bool ObjectTypeSearchedInScope = false;
-  if (LookupCtx) {
-    // Perform "qualified" name lookup into the declaration context we
-    // computed, which is either the type of the base of a member access
-    // expression or the declaration context associated with a prior
-    // nested-name-specifier.
-
-    // The declaration context must be complete.
-    if (!LookupCtx->isDependentContext() &&
-        RequireCompleteDeclContext(SS, LookupCtx))
-      return true;
 
-    LookupQualifiedName(Found, LookupCtx);
-
-    if (!ObjectType.isNull() && Found.empty()) {
-      // C++ [basic.lookup.classref]p4:
-      //   If the id-expression in a class member access is a qualified-id of
-      //   the form
-      //
-      //        class-name-or-namespace-name::...
-      //
-      //   the class-name-or-namespace-name following the . or -> operator is
-      //   looked up both in the context of the entire postfix-expression and in
-      //   the scope of the class of the object expression. If the name is found
-      //   only in the scope of the class of the object expression, the name
-      //   shall refer to a class-name. If the name is found only in the
-      //   context of the entire postfix-expression, the name shall refer to a
-      //   class-name or namespace-name. [...]
-      //
-      // Qualified name lookup into a class will not find a namespace-name,
-      // so we do not need to diagnose that case specifically. However,
-      // this qualified name lookup may find nothing. In that case, perform
-      // unqualified name lookup in the given scope (if available) or
-      // reconstruct the result from when name lookup was performed at template
-      // definition time.
-      if (S)
-        LookupName(Found, S);
-      else if (ScopeLookupResult)
-        Found.addDecl(ScopeLookupResult);
-
-      ObjectTypeSearchedInScope = true;
+  // C++ [basic.lookup.qual.general]p2:
+  //   A member-qualified name is the (unique) component name, if any, of
+  //   - an unqualified-id or
+  //   - a nested-name-specifier of the form type-name :: or namespace-name ::
+  //   in the id-expression of a class member access expression.
+  //
+  // C++ [basic.lookup.qual.general]p3:
+  //   [...] If nothing is found by qualified lookup for a member-qualified
+  //   name that is the terminal name of a nested-name-specifier and is not
+  //   dependent, it undergoes unqualified lookup.
+  //
+  // In 'x.A::B::y', 'A' will undergo unqualified lookup if qualified lookup
+  // in the type of 'x' finds nothing. If the lookup context is dependent,
+  // we perform the unqualified lookup in the template definition context
+  // and store the results so we can replicate the lookup during instantiation.
+  if (Found.empty() && !ObjectType.isNull()) {
+    if (S) {
+      LookupName(Found, S);
+    } else if (!SS.getUnqualifiedLookups().empty()) {
+      Found.addAllDecls(SS.getUnqualifiedLookups());
+      Found.resolveKind();
     }
-  } else if (!isDependent) {
-    // Perform unqualified name lookup in the current scope.
-    LookupName(Found, S);
+    ObjectTypeSearchedInScope = true;
   }
 
   if (Found.isAmbiguous())
     return true;
 
-  // If we performed lookup into a dependent context and did not find anything,
-  // that's fine: just build a dependent nested-name-specifier.
-  if (Found.empty() && isDependent &&
-      !(LookupCtx && LookupCtx->isRecord() &&
-        (!cast<CXXRecordDecl>(LookupCtx)->hasDefinition() ||
-         !cast<CXXRecordDecl>(LookupCtx)->hasAnyDependentBases()))) {
-    // Don't speculate if we're just trying to improve error recovery.
-    if (ErrorRecoveryLookup)
-      return true;
-
-    // We were not able to compute the declaration context for a dependent
-    // base object type or prior nested-name-specifier, so this
-    // nested-name-specifier refers to an unknown specialization. Just build
-    // a dependent nested-name-specifier.
-    SS.Extend(Context, IdInfo.Identifier, IdInfo.IdentifierLoc, IdInfo.CCLoc);
-    return false;
-  }
-
   if (Found.empty() && !ErrorRecoveryLookup) {
     // If identifier is not found as class-name-or-namespace-name, but is found
     // as other entity, don't look for typos.
     LookupResult R(*this, Found.getLookupNameInfo(), LookupOrdinaryName);
-    if (LookupCtx)
-      LookupQualifiedName(R, LookupCtx);
-    else if (S && !isDependent)
-      LookupName(R, S);
+    LookupParsedName(R, S, &SS, ObjectType,
+                     /*AllowBuiltinCreation=*/false, EnteringContext);
+
     if (!R.empty()) {
       // Don't diagnose problems with this speculative lookup.
       R.suppressDiagnostics();
@@ -539,6 +521,11 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
     }
   }
 
+  DeclContext *LookupCtx =
+      SS.isSet()
+          ? computeDeclContext(SS, EnteringContext)
+          : (!ObjectType.isNull() ? computeDeclContext(ObjectType) : nullptr);
+
   if (Found.empty() && !ErrorRecoveryLookup && !getLangOpts().MSVCCompat) {
     // We haven't found anything, and we're not recovering from a
     // different kind of error, so look for typos.
@@ -594,14 +581,14 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
       // scope, reconstruct the result from the template instantiation itself.
       //
       // Note that C++11 does *not* perform this redundant lookup.
-      NamedDecl *OuterDecl;
+      NamedDecl *OuterDecl = nullptr;
       if (S) {
         LookupResult FoundOuter(*this, IdInfo.Identifier, IdInfo.IdentifierLoc,
                                 LookupNestedNameSpecifierName);
         LookupName(FoundOuter, S);
         OuterDecl = FoundOuter.getAsSingle<NamedDecl>();
-      } else
-        OuterDecl = ScopeLookupResult;
+      } else if (!SS.getUnqualifiedLookups().empty())
+        OuterDecl = SS.getUnqualifiedLookups().front().getDecl();
 
       if (isAcceptableNestedNameSpecifier(OuterDecl) &&
           OuterDecl->getCanonicalDecl() != SD->getCanonicalDecl() &&
@@ -779,7 +766,7 @@ bool Sema::ActOnCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
     return true;
 
   return BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS,
-                                     /*ScopeLookupResult=*/nullptr, false,
+                                     /*ErrorRecoveryLookup=*/false,
                                      IsCorrectedToColon, OnlyNamespace);
 }
 
@@ -840,7 +827,7 @@ bool Sema::IsInvalidUnlessNestedName(Scope *S, CXXScopeSpec &SS,
     return false;
 
   return !BuildCXXNestedNameSpecifier(S, IdInfo, EnteringContext, SS,
-                                      /*ScopeLookupResult=*/nullptr, true);
+                                      /*ErrorRecoveryLookup=*/true);
 }
 
 bool Sema::ActOnCXXNestedNameSpecifier(Scope *S,
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 81334c817b2af..fa0eb1d2afbee 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -306,8 +306,8 @@ static ExprResult buildMemberCall(Sema &S, Expr *Base, SourceLocation Loc,
   // FIXME: Fix BuildMemberReferenceExpr to take a const CXXScopeSpec&.
   CXXScopeSpec SS;
   ExprResult Result = S.BuildMemberReferenceExpr(
-      Base, Base->getType(), Loc, /*IsPtr=*/false, SS,
-      SourceLocation(), nullptr, NameInfo, /*TemplateArgs=*/nullptr,
+      Base, Base->getType(), Loc, /*IsPtr=*/false, SS, SourceLocation(),
+      NameInfo, /*TemplateArgs=*/nullptr,
       /*Scope=*/nullptr);
   if (Result.isInvalid())
     return ExprError();
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 5782daa041f32..42a3dcd657e20 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1275,9 +1275,11 @@ static bool checkTupleLikeDecomposition(Sema &S,
     if (UseMemberGet) {
       //   if [lookup of member get] finds at least one declaration, the
       //   initializer is e.get<i-1>().
-      E = S.BuildMemberReferenceExpr(E.get(), DecompType, Loc, false,
-                                     CXXScopeSpec(), SourceLocation(), nullptr,
-                                     MemberGet, &Args, nullptr);
+      E = S.BuildMemberReferenceExpr(E.get(), DecompType, Loc,
+                                     /*IsArrow=*/false,
+                                     /*SS=*/CXXScopeSpec(),
+                                     /*TemplateKWLoc=*/SourceLocation(),
+                                     MemberGet, &Args, /*S=*/nullptr);
       if (E.isInvalid())
         return true;
 
@@ -4899,16 +4901,12 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor,
     MemberLookup.addDecl(Indirect ? cast<ValueDecl>(Indirect)
                                   : cast<ValueDecl>(Field), AS_public);
     MemberLookup.resolveKind();
-    ExprResult CtorArg
-      = SemaRef.BuildMemberReferenceExpr(MemberExprBase,
-                                         ParamType, Loc,
-                                         /*IsArrow=*/false,
-                                         SS,
-                                         /*TemplateKWLoc=*/SourceLocation(),
-                                         /*FirstQualifierInScope=*/nullptr,
-                                         MemberLookup,
-                                         /*TemplateArgs=*/nullptr,
-                                         /*S*/nullptr);
+    ExprResult CtorArg = SemaRef.BuildMemberReferenceExpr(
+        MemberExprBase, ParamType, Loc,
+        /*IsArrow=*/false, SS,
+        /*TemplateKWLoc=*/SourceLocation(), MemberLookup,
+        /*TemplateArgs=*/nullptr,
+        /*S=*/nullptr);
     if (CtorArg.isInvalid())
       return true;
 
@@ -14362,8 +14360,10 @@ class MemberBuilder: public ExprBuilder {
 public:
   Expr *build(Sema &S, SourceLocation Loc) const override {
     return assertNotNull(S.BuildMemberReferenceExpr(
-        Builder.build(S, Loc), Type, Loc, IsArrow, SS, SourceLocation(),
-        nullptr, MemberLookup, nullptr, nullptr).get());
+                              Builder.build(S, Loc), Type, Loc, IsArrow, SS,
+                              /*TemplateKwLoc=*/SourceLocation(), MemberLookup,
+                              /*TemplateArgs=*/nullptr, /*S=*/nullptr)
+                             .get());
   }
 
   MemberBuilder(const ExprBuilder &Builder, QualType Type, bool IsArrow,
@@ -14569,13 +14569,11 @@ buildSingleCopyAssignRecursively(Sema &S, SourceLocation Loc, QualType T,
                    Loc);
 
     // Create the reference to operator=.
-    ExprResult OpEqualRef
-      = S.BuildMemberReferenceExpr(To.build(S, Loc), T, Loc, /*IsArrow=*/false,
-                                   SS, /*TemplateKWLoc=*/SourceLocation(),
-                                   /*FirstQualifierInScope=*/nullptr,
-                                   OpLookup,
-                                   /*TemplateArgs=*/nullptr, /*S*/nullptr,
-                                   /*SuppressQualifierCheck=*/true);
+    ExprResult OpEqualRef = S.BuildMemberReferenceExpr(
+        To.build(S, Loc), T, Loc, /*IsArrow=*/false, SS,
+        /*TemplateKWLoc=*/SourceLocation(), OpLookup,
+        /*TemplateArgs=*/nullptr, /*S*/ nullptr,
+        /*SuppressQualifierCheck=*/true);
     if (OpEqualRef.isInvalid())
       return StmtError();
 
@@ -17181,8 +17179,9 @@ bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message,
 
   auto BuildExpr = [&](LookupResult &LR) {
     ExprResult Res = BuildMemberReferenceExpr(
-        Message, Message->getType(), Message->getBeginLoc(), false,
-        CXXScopeSpec(), SourceLocation(), nullptr, LR, nullptr, nullptr);
+        Message, Message->getType(), Message->getBeginLoc(), /*IsArrow=*/false,
+        /*SS=*/CXXScopeSpec(), /*TemplateKWLoc=*/SourceLocation(), LR,
+        /*TemplateArgs=*/nullptr, /*S=*/nullptr);
     if (Res.isInvalid())
       return ExprError();
     Res = BuildCallExpr(nullptr, Res.get(), Loc, std::nullopt, Loc, nullptr,
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 74c0e01705905..e0db2e370928b 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -2632,7 +2632,7 @@ recoverFromMSUnqualifiedLookup(Sema &S, ASTContext &Context,
     return CXXDependentScopeMemberExpr::Create(
         Context, /*This=*/nullptr, ThisType, /*IsArrow=*/true,
         /*Op=*/SourceLocation(), NestedNameSpecifierLoc(), TemplateKWLoc,
-        /*FirstQualifierFoundInScope=*/nullptr, NameInfo, TemplateArgs);
+        /*UnqualifiedLookups=*/std::nullopt, NameInfo, TemplateArgs);
   }
 
   // Synthesize a fake NNS that points to the derived class.  This will
diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index 2070f3b7bb3a2..8519618bacfee 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -552,11 +552,9 @@ static Decl *FindGetterSetterNameDecl(const ObjCObjectPointerType *QIdTy,
 }
 
 ExprResult
-Sema::ActOnDependentMemberExpr(Expr *BaseExpr, QualType BaseType,
-                               bool IsArrow, SourceLocation OpLoc,
-                               const CXXScopeSpec &SS,
+Sema::ActOnDependentMemberExpr(Expr *BaseExpr, QualType BaseType, bool IsArrow,
+                               SourceLocation OpLoc, const CXXScopeSpec &SS,
                                SourceLocation TemplateKWLoc,
-                               NamedDecl *FirstQualifierInScope,
                                const DeclarationNameInfo &NameInfo,
                                const TemplateArgumentListInfo *TemplateArgs) {
   // Even in dependent contexts, try to diagnose base expressions with
@@ -590,8 +588,8 @@ Sema::ActOnDependentMemberExpr(Expr *BaseExpr, QualType BaseType,
   // must have pointer type, and the accessed type is the pointee.
   return CXXDependentScopeMemberExpr::Create(
       Context, BaseExpr, BaseType, IsArrow, OpLoc,
-      SS.getWithLocInContext(Context), TemplateKWLoc, FirstQualifierInScope,
-      NameInfo, TemplateArgs);
+      SS.getWithLocInContext(Context), TemplateKWLoc,
+      SS.getUnqualifiedLookups(), NameInfo, TemplateArgs);
 }
 
 /// We know that the given qualified member reference points only to
@@ -767,8 +765,9 @@ static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R,
           R.addDecl(ND);
         R.resolveKind();
         return SemaRef.BuildMemberReferenceExpr(
-            BaseExpr, BaseExpr->getType(), OpLoc, IsArrow, SS, SourceLocation(),
-            nullptr, R, nullptr, nullptr);
+            BaseExpr, BaseExpr->getType(), OpLoc, IsArrow, SS,
+            /*TemplateKWLoc=*/SourceLocation(), R, /*TemplateArgs=*/nullptr,
+            /*S=*/nullptr);
       },
       Sema::CTK_ErrorRecovery, DC);
 
@@ -784,7 +783,7 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R,
 ExprResult Sema::BuildMemberReferenceExpr(
     Expr *Base, QualType BaseType, SourceLocation OpLoc, bool IsArrow,
     CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
-    NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo,
+    const DeclarationNameInfo &NameInfo,
     const TemplateArgumentListInfo *TemplateArgs, const Scope *S,
     ActOnMemberAccessExtraArgs *ExtraArgs) {
   LookupResult R(*this, NameInfo, LookupMemberName);
@@ -828,10 +827,9 @@ ExprResult Sema::BuildMemberReferenceExpr(
   if (SS.isInvalid())
     return ExprError();
 
-  return BuildMemberReferenceExpr(Base, BaseType,
-                                  OpLoc, IsArrow, SS, TemplateKWLoc,
-                                  FirstQualifierInScope, R, TemplateArgs, S,
-                                  false, ExtraArgs);
+  return BuildMemberReferenceExpr(Base, BaseType, OpLoc, IsArrow, SS,
+                                  TemplateKWLoc, R, TemplateArgs, S,
+                                  /*SuppressQualifierCheck=*/false, ExtraArgs);
 }
 
 ExprResult
@@ -969,17 +967,11 @@ static bool IsInFnTryBlockHandler(const Scope *S) {
   return false;
 }
 
-ExprResult
-Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
-                               SourceLocation OpLoc, bool IsArrow,
-                               const CXXScopeSpec &SS,
-                               SourceLocation TemplateKWLoc,
-                               NamedDecl *FirstQualifierInScope,
-                               LookupResult &R,
-                               const TemplateArgumentListInfo *TemplateArgs,
-                               const Scope *S,
-                               bool SuppressQualifierCheck,
-                               ActOnMemberAccessExtraArgs *ExtraArgs) {
+ExprResult Sema::BuildMemberReferenceExpr(
+    Expr *BaseExpr, QualType BaseExprType, SourceLocation OpLoc, bool IsArrow,
+    const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, LookupResult &R,
+    const TemplateArgumentListInfo *TemplateArgs, const Scope *S,
+    bool SuppressQualifierCheck, ActOnMemberAccessExtraArgs *ExtraArgs) {
   assert(!SS.isInvalid() && "nested-name-specifier cannot be invalid");
   // If the member wasn't found in the current instantiation, or if the
   // arrow operator was used with a dependent non-pointer object expression,
@@ -989,8 +981,8 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
        (SS.isSet() ? SS.getScopeRep()->isDependent()
                    : BaseExprType->isDependentType())))
     return ActOnDependentMemberExpr(BaseExpr, BaseExprType, IsArrow, OpLoc, SS,
-                                    TemplateKWLoc, FirstQualifierInScope,
-                                    R.getLookupNameInfo(), TemplateArgs);
+                                    TemplateKWLoc, R.getLookupNameInfo(),
+                                    TemplateArgs);
 
   QualType BaseType = BaseExprType;
   if (IsArrow) {
@@ -1195,9 +1187,9 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
 
     // Non-dependent member, but dependent template arguments.
     if (!VDecl.get())
-      return ActOnDependentMemberExpr(
-          BaseExpr, BaseExpr->getType(), IsArrow, OpLoc, SS, TemplateKWLoc,
-          FirstQualifierInScope, MemberNameInfo, TemplateArgs);
+      return ActOnDependentMemberExpr(BaseExpr, BaseExpr->getType(), IsArrow,
+                                      OpLoc, SS, TemplateKWLoc, MemberNameInfo,
+                                      TemplateArgs);
 
     VarDecl *Var = cast<VarDecl>(VDecl.get());
     if (!Var->getTemplateSpecializationKind())
@@ -1763,15 +1755,16 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base,
   const TemplateArgumentListInfo *TemplateArgs;
   DecomposeUnqualifiedId(Id, TemplateArgsBuffer,
                          NameInfo, TemplateArgs);
-
-  bool IsArrow = (OpKind == tok::arrow);
+  bool IsArrow = OpKind == tok::arrow;
 
   if (getLangOpts().HLSL && IsArrow)
     return ExprError(Diag(OpLoc, diag::err_hlsl_operator_unsupported) << 2);
 
-  NamedDecl *FirstQualifierInScope
-    = (!SS.isSet() ? nullptr : FindFirstQualifierInScope(S, SS.getScopeRep()));
-
+  UnresolvedSet<4> UnqualifiedLookups;
+  if (SS.isValid() &&
+      LookupFirstQualifierInScope(S, SS.getScopeRep(), UnqualifiedLookups)) {
+    SS.setUnqualifiedLookups(UnqualifiedLookups.pairs());
+  }
   // This is a postfix expression, so get rid of ParenListExprs.
   ExprResult Result = MaybeConvertParenListExprToParenExpr(S, Base);
   if (Result.isInvalid()) return ExprError();
@@ -1779,8 +1772,8 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base,
 
   ActOnMemberAccessExtraArgs ExtraArgs = {S, Id, ObjCImpDecl};
   ExprResult Res = BuildMemberReferenceExpr(
-      Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc,
-      FirstQualifierInScope, NameInfo, TemplateArgs, S, &ExtraArgs);
+      Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc, NameInfo,
+      TemplateArgs, S, &ExtraArgs);
 
   if (!Res.isInvalid() && isa<MemberExpr>(Res.get()))
     CheckMemberAccessOfNoDeref(cast<MemberExpr>(Res.get()));
@@ -1924,9 +1917,8 @@ Sema::BuildImplicitMemberExpr(const CXXScopeSpec &SS,
     baseExpr = BuildCXXThisExpr(loc, ThisTy, /*IsImplicit=*/true);
   }
 
-  return BuildMemberReferenceExpr(
-      baseExpr, ThisTy,
-      /*OpLoc=*/SourceLocation(),
-      /*IsArrow=*/!getLangOpts().HLSL, SS, TemplateKWLoc,
-      /*FirstQualifierInScope=*/nullptr, R, TemplateArgs, S);
+  return BuildMemberReferenceExpr(baseExpr, ThisTy,
+                                  /*OpLoc=*/SourceLocation(),
+                                  /*IsArrow=*/!getLangOpts().HLSL, SS,
+                                  TemplateKWLoc, R, TemplateArgs, S);
 }
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index c5f56ac62b458..65349c7b6af72 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -16031,13 +16031,11 @@ Sema::BuildForRangeBeginEndCall(SourceLocation Loc,
 
   CandidateSet->clear(OverloadCandidateSet::CSK_Normal);
   if (!MemberLookup.empty()) {
-    ExprResult MemberRef =
-        BuildMemberReferenceExpr(Range, Range->getType(), Loc,
-                                 /*IsPtr=*/false, CXXScopeSpec(),
-                                 /*TemplateKWLoc=*/SourceLocation(),
-                                 /*FirstQualifierInScope=*/nullptr,
-                                 MemberLookup,
-                                 /*TemplateArgs=*/nullptr, S);
+    ExprResult MemberRef = BuildMemberReferenceExpr(
+        Range, Range->getType(), Loc,
+        /*IsPtr=*/false, /*SS=*/CXXScopeSpec(),
+        /*TemplateKWLoc=*/SourceLocation(), MemberLookup,
+        /*TemplateArgs=*/nullptr, S);
     if (MemberRef.isInvalid()) {
       *CallExpr = ExprError();
       return FRS_DiagnosticIssued;
diff --git a/clang/lib/Sema/SemaStmtAsm.cpp b/clang/lib/Sema/SemaStmtAsm.cpp
index 32d42f3c3f3bb..da2e99b6bc00c 100644
--- a/clang/lib/Sema/SemaStmtAsm.cpp
+++ b/clang/lib/Sema/SemaStmtAsm.cpp
@@ -900,7 +900,8 @@ Sema::LookupInlineAsmVarDeclField(Expr *E, StringRef Member,
     return CXXDependentScopeMemberExpr::Create(
         Context, E, T, /*IsArrow=*/false, AsmLoc, NestedNameSpecifierLoc(),
         SourceLocation(),
-        /*FirstQualifierFoundInScope=*/nullptr, NameInfo, /*TemplateArgs=*/nullptr);
+        /*UnqualifiedLookups=*/std::nullopt, NameInfo,
+        /*TemplateArgs=*/nullptr);
   }
 
   const RecordType *RT = T->getAs<RecordType>();
@@ -923,8 +924,9 @@ Sema::LookupInlineAsmVarDeclField(Expr *E, StringRef Member,
 
   // Make an Expr to thread through OpDecl.
   ExprResult Result = BuildMemberReferenceExpr(
-      E, E->getType(), AsmLoc, /*IsArrow=*/false, CXXScopeSpec(),
-      SourceLocation(), nullptr, FieldResult, nullptr, nullptr);
+      E, E->getType(), AsmLoc, /*IsArrow=*/false, /*SS=*/CXXScopeSpec(),
+      /*TemplateKWLoc*/ SourceLocation(), FieldResult,
+      /*TemplateArgs=*/nullptr, /*S=*/nullptr);
 
   return Result;
 }
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 87b1f98bbe5ac..91cc9881ceb8b 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -174,15 +174,12 @@ bool Sema::hasAnyAcceptableTemplateNames(LookupResult &R,
   return false;
 }
 
-TemplateNameKind Sema::isTemplateName(Scope *S,
-                                      CXXScopeSpec &SS,
-                                      bool hasTemplateKeyword,
-                                      const UnqualifiedId &Name,
-                                      ParsedType ObjectTypePtr,
-                                      bool EnteringContext,
-                                      TemplateTy &TemplateResult,
-                                      bool &MemberOfUnknownSpecialization,
-                                      bool Disambiguation) {
+TemplateNameKind
+Sema::isTemplateName(Scope *S, CXXScopeSpec &SS, bool hasTemplateKeyword,
+                     const UnqualifiedId &Name, ParsedType ObjectTypePtr,
+                     bool EnteringContext, TemplateTy &TemplateResult,
+                     bool &MemberOfUnknownSpecialization, bool Disambiguation,
+                     bool MayBeNNS) {
   assert(getLangOpts().CPlusPlus && "No template names in C!");
 
   DeclarationName TName;
@@ -213,8 +210,9 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
   if (LookupTemplateName(R, S, SS, ObjectType, EnteringContext,
                          /*RequiredTemplate=*/SourceLocation(),
                          &AssumedTemplate,
-                         /*AllowTypoCorrection=*/!Disambiguation))
+                         /*AllowTypoCorrection=*/!Disambiguation, MayBeNNS))
     return TNK_Non_template;
+
   MemberOfUnknownSpecialization = R.wasNotFoundInCurrentInstantiation();
 
   if (AssumedTemplate != AssumedTemplateKind::None) {
@@ -380,7 +378,7 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
                               QualType ObjectType, bool EnteringContext,
                               RequiredTemplateKind RequiredTemplate,
                               AssumedTemplateKind *ATK,
-                              bool AllowTypoCorrection) {
+                              bool AllowTypoCorrection, bool MayBeNNS) {
   if (ATK)
     *ATK = AssumedTemplateKind::None;
 
@@ -389,92 +387,89 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
 
   Found.setTemplateNameLookup(true);
 
-  // Determine where to perform name lookup
-  DeclContext *LookupCtx = nullptr;
-  bool IsDependent = false;
-  if (!ObjectType.isNull()) {
-    // This nested-name-specifier occurs in a member access expression, e.g.,
-    // x->B::f, and we are looking into the type of the object.
-    assert(SS.isEmpty() && "ObjectType and scope specifier cannot coexist");
-    LookupCtx = computeDeclContext(ObjectType);
-    IsDependent = !LookupCtx && ObjectType->isDependentType();
-    assert((IsDependent || !ObjectType->isIncompleteType() ||
-            !ObjectType->getAs<TagType>() ||
-            ObjectType->castAs<TagType>()->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<typename T> using Vec = T __attribute__((ext_vector_type(4)));
-    //   Vec<int> vi;
-    //   vi.Vec<int>::~Vec<int>();
-    //
-    // ... should be accepted but we will not treat 'Vec' as a template name
-    // here. The right thing to do would be to check if the name is a valid
-    // vector component name, and look up a template name if not. And similarly
-    // for lookups into Objective-C class and object types, where the same
-    // problem can arise.
-    if (ObjectType->isObjCObjectOrInterfaceType() ||
-        ObjectType->isVectorType()) {
-      Found.clear();
-      return false;
-    }
-  } else if (SS.isNotEmpty()) {
-    // This nested-name-specifier occurs after another nested-name-specifier,
-    // so long into the context associated with the prior nested-name-specifier.
-    LookupCtx = computeDeclContext(SS, EnteringContext);
-    IsDependent = !LookupCtx && isDependentScopeSpecifier(SS);
-
-    // The declaration context must be complete.
-    if (LookupCtx && RequireCompleteDeclContext(SS, LookupCtx))
-      return true;
+  // Template names cannot appear inside an Objective-C class or object type
+  // or a vector type.
+  //
+  // FIXME: This is wrong. For example:
+  //
+  //   template<typename T> using Vec = T __attribute__((ext_vector_type(4)));
+  //   Vec<int> vi;
+  //   vi.Vec<int>::~Vec<int>();
+  //
+  // ... should be accepted but we will not treat 'Vec' as a template name
+  // here. The right thing to do would be to check if the name is a valid
+  // vector component name, and look up a template name if not. And similarly
+  // for lookups into Objective-C class and object types, where the same
+  // problem can arise.
+  if (!ObjectType.isNull() && (ObjectType->isVectorType() ||
+                               ObjectType->isObjCObjectOrInterfaceType())) {
+    Found.clear();
+    return false;
   }
 
+  LookupParsedName(Found, S, &SS, ObjectType,
+                   /*AllowBuiltinCreation=*/false, EnteringContext);
+
+  // C++ [basic.lookup.qual.general]p3:
+  //   [...] Unless otherwise specified, a qualified name undergoes qualified
+  //   name lookup in its lookup context from the point where it appears unless
+  //   the lookup context either is dependent and is not the current
+  //   instantiation or is not a class or class template.
+  //
+  // The lookup context is dependent and either:
+  // - it is not the current instantiation, or
+  // - it is the current instantiation, it has at least one dependent base
+  //   class, and qualified lookup found nothing.
+  //
+  // If this is a member-qualified name that is the terminal name of a
+  // nested-name-specifier, we perform unqualified lookup and store the results
+  // so we can replicate the lookup during instantiation. The results of the
+  // unqualified loookup are *not* used to determine whether '<' is interpreted
+  // as the delimiter of a template-argument-list.
+  //
+  // For example:
+  //
+  //   template<typename T>
+  //   struct A {
+  //     int x;
+  //   };
+  //
+  //   template<typename T>
+  //   using B = A<T>;
+  //
+  //   template<typename T>
+  //   void f(A<T> a, A<int> b) {
+  //     a.B<T>::x; // error: missing 'template' before 'B'
+  //     b.B<int>::x; // ok, lookup context is not dependent
+  //   }
+  if (Found.wasNotFoundInCurrentInstantiation())
+    return false;
+
   bool ObjectTypeSearchedInScope = false;
-  bool AllowFunctionTemplatesInLookup = true;
-  if (LookupCtx) {
-    // Perform "qualified" name lookup into the declaration context we
-    // computed, which is either the type of the base of a member access
-    // expression or the declaration context associated with a prior
-    // nested-name-specifier.
-    LookupQualifiedName(Found, LookupCtx);
-
-    // FIXME: The C++ standard does not clearly specify what happens in the
-    // case where the object type is dependent, and implementations vary. In
-    // Clang, we treat a name after a . or -> as a template-name if lookup
-    // finds a non-dependent member or member of the current instantiation that
-    // is a type template, or finds no such members and lookup in the context
-    // of the postfix-expression finds a type template. In the latter case, the
-    // name is nonetheless dependent, and we may resolve it to a member of an
-    // unknown specialization when we come to instantiate the template.
-    IsDependent |= Found.wasNotFoundInCurrentInstantiation();
-  }
-
-  if (SS.isEmpty() && (ObjectType.isNull() || Found.empty())) {
-    // C++ [basic.lookup.classref]p1:
-    //   In a class member access expression (5.2.5), if the . or -> token is
-    //   immediately followed by an identifier followed by a <, the
-    //   identifier must be looked up to determine whether the < is the
-    //   beginning of a template argument list (14.2) or a less-than operator.
-    //   The identifier is first looked up in the class of the object
-    //   expression. If the identifier is not found, it is then looked up in
-    //   the context of the entire postfix-expression and shall name a class
-    //   template.
-    if (S)
-      LookupName(Found, S);
 
-    if (!ObjectType.isNull()) {
-      //  FIXME: We should filter out all non-type templates here, particularly
-      //  variable templates and concepts. But the exclusion of alias templates
-      //  and template template parameters is a wording defect.
-      AllowFunctionTemplatesInLookup = false;
-      ObjectTypeSearchedInScope = true;
+  // C++ [basic.lookup.qual.general]p2:
+  //   A member-qualified name is the (unique) component name, if any, of
+  //   - an unqualified-id or
+  //   - a nested-name-specifier of the form type-name :: or namespace-name ::
+  //   in the id-expression of a class member access expression.
+  //
+  // C++ [basic.lookup.qual.general]p3:
+  //   [...] If nothing is found by qualified lookup for a member-qualified
+  //   name that is the terminal name of a nested-name-specifier and is not
+  //   dependent, it undergoes unqualified lookup.
+  //
+  // In 'x.A::B::y', 'A' will undergo unqualified lookup if qualified lookup
+  // in the type of 'x' finds nothing. If the lookup context is dependent,
+  // we perform the unqualified lookup in the template definition context
+  // and store the results so we can replicate the lookup during instantiation.
+  if (MayBeNNS && Found.empty() && !ObjectType.isNull()) {
+    if (S) {
+      LookupName(Found, S);
+    } else if (!SS.getUnqualifiedLookups().empty()) {
+      Found.addAllDecls(SS.getUnqualifiedLookups());
+      Found.resolveKind();
     }
-
-    IsDependent |= Found.wasNotFoundInCurrentInstantiation();
+    ObjectTypeSearchedInScope = true;
   }
 
   if (Found.isAmbiguous())
@@ -494,7 +489,7 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
         getLangOpts().CPlusPlus20 && llvm::all_of(Found, [](NamedDecl *ND) {
           return isa<FunctionDecl>(ND->getUnderlyingDecl());
         });
-    if (AllFunctions || (Found.empty() && !IsDependent)) {
+    if (AllFunctions || Found.empty()) {
       // If lookup found any functions, or if this is a name that can only be
       // used for a function, then strongly assume this is a function
       // template-id.
@@ -506,11 +501,15 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
     }
   }
 
-  if (Found.empty() && !IsDependent && AllowTypoCorrection) {
+  if (Found.empty() && AllowTypoCorrection) {
     // If we did not find any names, and this is not a disambiguation, attempt
     // to correct any typos.
     DeclarationName Name = Found.getLookupName();
     Found.clear();
+    DeclContext *LookupCtx =
+        SS.isSet()
+            ? computeDeclContext(SS, EnteringContext)
+            : (!ObjectType.isNull() ? computeDeclContext(ObjectType) : nullptr);
     // Simple filter callback that, for keywords, only accepts the C++ *_cast
     DefaultFilterCCC FilterCCC{};
     FilterCCC.WantTypeSpecifiers = false;
@@ -543,13 +542,8 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
 
   NamedDecl *ExampleLookupResult =
       Found.empty() ? nullptr : Found.getRepresentativeDecl();
-  FilterAcceptableTemplateNames(Found, AllowFunctionTemplatesInLookup);
+  FilterAcceptableTemplateNames(Found);
   if (Found.empty()) {
-    if (IsDependent) {
-      Found.setNotFoundInCurrentInstantiation();
-      return false;
-    }
-
     // If a 'template' keyword was used, a lookup that finds only non-template
     // names is an error.
     if (ExampleLookupResult && RequiredTemplate) {
@@ -741,7 +735,7 @@ Sema::ActOnDependentIdExpression(const CXXScopeSpec &SS,
         /*IsArrow=*/!Context.getLangOpts().HLSL,
         /*OperatorLoc=*/SourceLocation(),
         /*QualifierLoc=*/NestedNameSpecifierLoc(), TemplateKWLoc,
-        /*FirstQualifierFoundInScope=*/nullptr, NameInfo, TemplateArgs);
+        /*UnqualifiedLookups=*/std::nullopt, NameInfo, TemplateArgs);
   }
   return BuildDependentDeclRefExpr(SS, TemplateKWLoc, NameInfo, TemplateArgs);
 }
@@ -4480,14 +4474,10 @@ ExprResult Sema::BuildQualifiedTemplateIdExpr(
   return BuildTemplateIdExpr(SS, TemplateKWLoc, R, /*ADL=*/false, TemplateArgs);
 }
 
-TemplateNameKind Sema::ActOnTemplateName(Scope *S,
-                                         CXXScopeSpec &SS,
-                                         SourceLocation TemplateKWLoc,
-                                         const UnqualifiedId &Name,
-                                         ParsedType ObjectType,
-                                         bool EnteringContext,
-                                         TemplateTy &Result,
-                                         bool AllowInjectedClassName) {
+TemplateNameKind Sema::ActOnTemplateName(
+    Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
+    const UnqualifiedId &Name, ParsedType ObjectType, bool EnteringContext,
+    TemplateTy &Result, bool AllowInjectedClassName, bool MayBeNNS) {
   if (TemplateKWLoc.isValid() && S && !S->getTemplateParamParent())
     Diag(TemplateKWLoc,
          getLangOpts().CPlusPlus11 ?
@@ -4522,9 +4512,10 @@ TemplateNameKind Sema::ActOnTemplateName(Scope *S,
   // "template" keyword is now permitted). We follow the C++0x
   // rules, even in C++03 mode with a warning, retroactively applying the DR.
   bool MemberOfUnknownSpecialization;
-  TemplateNameKind TNK = isTemplateName(S, SS, TemplateKWLoc.isValid(), Name,
-                                        ObjectType, EnteringContext, Result,
-                                        MemberOfUnknownSpecialization);
+  TemplateNameKind TNK =
+      isTemplateName(S, SS, TemplateKWLoc.isValid(), Name, ObjectType,
+                     EnteringContext, Result, MemberOfUnknownSpecialization,
+                     /*Disambiguation=*/false, MayBeNNS);
   if (TNK != TNK_Non_template) {
     // We resolved this to a (non-dependent) template name. Return it.
     auto *LookupRD = dyn_cast_or_null<CXXRecordDecl>(LookupCtx);
@@ -4563,7 +4554,8 @@ TemplateNameKind Sema::ActOnTemplateName(Scope *S,
                                    ? RequiredTemplateKind(TemplateKWLoc)
                                    : TemplateNameIsRequired;
     if (!LookupTemplateName(R, S, SS, ObjectType.get(), EnteringContext, RTK,
-                            /*ATK=*/nullptr, /*AllowTypoCorrection=*/false) &&
+                            /*ATK=*/nullptr, /*AllowTypoCorrection=*/false,
+                            MayBeNNS) &&
         !R.isAmbiguous()) {
       if (LookupCtx)
         Diag(Name.getBeginLoc(), diag::err_no_member)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 8995d461362d7..d989bedb78877 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1515,12 +1515,11 @@ namespace {
                                    NestedNameSpecifierLoc QualifierLoc,
                                    QualType T);
 
-    TemplateName
-    TransformTemplateName(CXXScopeSpec &SS, TemplateName Name,
-                          SourceLocation NameLoc,
-                          QualType ObjectType = QualType(),
-                          NamedDecl *FirstQualifierInScope = nullptr,
-                          bool AllowInjectedClassName = false);
+    TemplateName TransformTemplateName(CXXScopeSpec &SS, TemplateName Name,
+                                       SourceLocation NameLoc,
+                                       QualType ObjectType = QualType(),
+                                       bool AllowInjectedClassName = false,
+                                       bool MayBeNNS = false);
 
     const CXXAssumeAttr *TransformCXXAssumeAttr(const CXXAssumeAttr *AA);
     const LoopHintAttr *TransformLoopHintAttr(const LoopHintAttr *LH);
@@ -1952,8 +1951,7 @@ TemplateInstantiator::RebuildElaboratedType(SourceLocation KeywordLoc,
 
 TemplateName TemplateInstantiator::TransformTemplateName(
     CXXScopeSpec &SS, TemplateName Name, SourceLocation NameLoc,
-    QualType ObjectType, NamedDecl *FirstQualifierInScope,
-    bool AllowInjectedClassName) {
+    QualType ObjectType, bool AllowInjectedClassName, bool MayBeNNS) {
   if (TemplateTemplateParmDecl *TTP
        = dyn_cast_or_null<TemplateTemplateParmDecl>(Name.getAsTemplateDecl())) {
     if (TTP->getDepth() < TemplateArgs.getNumLevels()) {
@@ -2025,8 +2023,7 @@ TemplateName TemplateInstantiator::TransformTemplateName(
   }
 
   return inherited::TransformTemplateName(SS, Name, NameLoc, ObjectType,
-                                          FirstQualifierInScope,
-                                          AllowInjectedClassName);
+                                          AllowInjectedClassName, MayBeNNS);
 }
 
 ExprResult
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 4d68ebf0cc452..ac51b74731815 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -541,10 +541,9 @@ class TreeTransform {
   /// By default, transforms all of the types and declarations within the
   /// nested-name-specifier. Subclasses may override this function to provide
   /// alternate behavior.
-  NestedNameSpecifierLoc
-  TransformNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS,
-                                  QualType ObjectType = QualType(),
-                                  NamedDecl *FirstQualifierInScope = nullptr);
+  NestedNameSpecifierLoc TransformNestedNameSpecifierLoc(
+      NestedNameSpecifierLoc NNS, QualType ObjectType = QualType(),
+      ArrayRef<DeclAccessPair> UnqualifiedLookups = std::nullopt);
 
   /// Transform the given declaration name.
   ///
@@ -585,12 +584,11 @@ class TreeTransform {
   /// By default, transforms the template name by transforming the declarations
   /// and nested-name-specifiers that occur within the template name.
   /// Subclasses may override this function to provide alternate behavior.
-  TemplateName
-  TransformTemplateName(CXXScopeSpec &SS, TemplateName Name,
-                        SourceLocation NameLoc,
-                        QualType ObjectType = QualType(),
-                        NamedDecl *FirstQualifierInScope = nullptr,
-                        bool AllowInjectedClassName = false);
+  TemplateName TransformTemplateName(CXXScopeSpec &SS, TemplateName Name,
+                                     SourceLocation NameLoc,
+                                     QualType ObjectType = QualType(),
+                                     bool AllowInjectedClassName = false,
+                                     bool MayBeNNS = false);
 
   /// Transform the given template argument.
   ///
@@ -1140,8 +1138,8 @@ class TreeTransform {
     CXXScopeSpec SS;
     SS.Adopt(QualifierLoc);
     TemplateName InstName = getDerived().RebuildTemplateName(
-        SS, TemplateKWLoc, *Name, NameLoc, QualType(), nullptr,
-        AllowInjectedClassName);
+        SS, TemplateKWLoc, *Name, NameLoc, QualType(), AllowInjectedClassName,
+        /*MayBeNNS=*/false);
 
     if (InstName.isNull())
       return QualType();
@@ -1312,8 +1310,7 @@ class TreeTransform {
                                    SourceLocation TemplateKWLoc,
                                    const IdentifierInfo &Name,
                                    SourceLocation NameLoc, QualType ObjectType,
-                                   NamedDecl *FirstQualifierInScope,
-                                   bool AllowInjectedClassName);
+                                   bool AllowInjectedClassName, bool MayBeNNS);
 
   /// Build a new template name given a nested name specifier and the
   /// overloaded operator name that is referred to as a template.
@@ -2849,15 +2846,14 @@ class TreeTransform {
   ///
   /// By default, performs semantic analysis to build the new expression.
   /// Subclasses may override this routine to provide different behavior.
-  ExprResult RebuildMemberExpr(Expr *Base, SourceLocation OpLoc,
-                               bool isArrow,
-                               NestedNameSpecifierLoc QualifierLoc,
-                               SourceLocation TemplateKWLoc,
-                               const DeclarationNameInfo &MemberNameInfo,
-                               ValueDecl *Member,
-                               NamedDecl *FoundDecl,
-                        const TemplateArgumentListInfo *ExplicitTemplateArgs,
-                               NamedDecl *FirstQualifierInScope) {
+  ExprResult
+  RebuildMemberExpr(Expr *Base, SourceLocation OpLoc, bool isArrow,
+                    NestedNameSpecifierLoc QualifierLoc,
+                    SourceLocation TemplateKWLoc,
+                    const DeclarationNameInfo &MemberNameInfo,
+                    ValueDecl *Member, NamedDecl *FoundDecl,
+                    const TemplateArgumentListInfo *ExplicitTemplateArgs,
+                    ArrayRef<DeclAccessPair> UnqualifiedLookups) {
     ExprResult BaseResult = getSema().PerformMemberExprBaseConversion(Base,
                                                                       isArrow);
     if (!Member->getDeclName()) {
@@ -2894,6 +2890,7 @@ class TreeTransform {
 
     CXXScopeSpec SS;
     SS.Adopt(QualifierLoc);
+    SS.setUnqualifiedLookups(UnqualifiedLookups);
 
     Base = BaseResult.get();
     if (Base->containsErrors())
@@ -2926,10 +2923,9 @@ class TreeTransform {
     }
 
     return getSema().BuildMemberReferenceExpr(Base, BaseType, OpLoc, isArrow,
-                                              SS, TemplateKWLoc,
-                                              FirstQualifierInScope,
-                                              R, ExplicitTemplateArgs,
-                                              /*S*/nullptr);
+                                              SS, TemplateKWLoc, R,
+                                              ExplicitTemplateArgs,
+                                              /*S=*/nullptr);
   }
 
   /// Build a new binary operator expression.
@@ -3002,10 +2998,9 @@ class TreeTransform {
     CXXScopeSpec SS;
     DeclarationNameInfo NameInfo(&Accessor, AccessorLoc);
     return getSema().BuildMemberReferenceExpr(
-        Base, Base->getType(), OpLoc, IsArrow, SS, SourceLocation(),
-        /*FirstQualifierInScope*/ nullptr, NameInfo,
-        /* TemplateArgs */ nullptr,
-        /*S*/ nullptr);
+        Base, Base->getType(), OpLoc, IsArrow, SS,
+        /*TemplateKWLoc=*/SourceLocation(), NameInfo,
+        /*TemplateArgs=*/nullptr, /*S=*/nullptr);
   }
 
   /// Build a new initializer list expression.
@@ -3573,46 +3568,37 @@ class TreeTransform {
   ///
   /// By default, performs semantic analysis to build the new expression.
   /// Subclasses may override this routine to provide different behavior.
-  ExprResult RebuildCXXDependentScopeMemberExpr(Expr *BaseE,
-                                                QualType BaseType,
-                                                bool IsArrow,
-                                                SourceLocation OperatorLoc,
-                                          NestedNameSpecifierLoc QualifierLoc,
-                                                SourceLocation TemplateKWLoc,
-                                            NamedDecl *FirstQualifierInScope,
-                                   const DeclarationNameInfo &MemberNameInfo,
-                              const TemplateArgumentListInfo *TemplateArgs) {
+  ExprResult RebuildCXXDependentScopeMemberExpr(
+      Expr *BaseE, QualType BaseType, bool IsArrow, SourceLocation OperatorLoc,
+      NestedNameSpecifierLoc QualifierLoc, SourceLocation TemplateKWLoc,
+      ArrayRef<DeclAccessPair> UnqualifiedLookups,
+      const DeclarationNameInfo &MemberNameInfo,
+      const TemplateArgumentListInfo *TemplateArgs) {
     CXXScopeSpec SS;
     SS.Adopt(QualifierLoc);
+    SS.setUnqualifiedLookups(UnqualifiedLookups);
 
-    return SemaRef.BuildMemberReferenceExpr(BaseE, BaseType,
-                                            OperatorLoc, IsArrow,
-                                            SS, TemplateKWLoc,
-                                            FirstQualifierInScope,
-                                            MemberNameInfo,
-                                            TemplateArgs, /*S*/nullptr);
+    return SemaRef.BuildMemberReferenceExpr(
+        BaseE, BaseType, OperatorLoc, IsArrow, SS, TemplateKWLoc,
+        MemberNameInfo, TemplateArgs, /*S=*/nullptr);
   }
 
   /// Build a new member reference expression.
   ///
   /// By default, performs semantic analysis to build the new expression.
   /// Subclasses may override this routine to provide different behavior.
-  ExprResult RebuildUnresolvedMemberExpr(Expr *BaseE, QualType BaseType,
-                                         SourceLocation OperatorLoc,
-                                         bool IsArrow,
-                                         NestedNameSpecifierLoc QualifierLoc,
-                                         SourceLocation TemplateKWLoc,
-                                         NamedDecl *FirstQualifierInScope,
-                                         LookupResult &R,
-                                const TemplateArgumentListInfo *TemplateArgs) {
+  ExprResult RebuildUnresolvedMemberExpr(
+      Expr *BaseE, QualType BaseType, SourceLocation OperatorLoc, bool IsArrow,
+      NestedNameSpecifierLoc QualifierLoc, SourceLocation TemplateKWLoc,
+      ArrayRef<DeclAccessPair> UnqualifiedLookups, LookupResult &R,
+      const TemplateArgumentListInfo *TemplateArgs) {
     CXXScopeSpec SS;
     SS.Adopt(QualifierLoc);
+    SS.setUnqualifiedLookups(UnqualifiedLookups);
 
-    return SemaRef.BuildMemberReferenceExpr(BaseE, BaseType,
-                                            OperatorLoc, IsArrow,
-                                            SS, TemplateKWLoc,
-                                            FirstQualifierInScope,
-                                            R, TemplateArgs, /*S*/nullptr);
+    return SemaRef.BuildMemberReferenceExpr(BaseE, BaseType, OperatorLoc,
+                                            IsArrow, SS, TemplateKWLoc, R,
+                                            TemplateArgs, /*S=*/nullptr);
   }
 
   /// Build a new noexcept expression.
@@ -3831,10 +3817,8 @@ class TreeTransform {
     DeclarationNameInfo NameInfo(Ivar->getDeclName(), IvarLoc);
     ExprResult Result = getSema().BuildMemberReferenceExpr(
         BaseArg, BaseArg->getType(),
-        /*FIXME:*/ IvarLoc, IsArrow, SS, SourceLocation(),
-        /*FirstQualifierInScope=*/nullptr, NameInfo,
-        /*TemplateArgs=*/nullptr,
-        /*S=*/nullptr);
+        /*FIXME:*/ IvarLoc, IsArrow, SS, /*TemplateKWLoc=*/SourceLocation(),
+        NameInfo, /*TemplateArgs=*/nullptr, /*S=*/nullptr);
     if (IsFreeIvar && Result.isUsable())
       cast<ObjCIvarRefExpr>(Result.get())->setIsFreeIvar(IsFreeIvar);
     return Result;
@@ -3849,14 +3833,12 @@ class TreeTransform {
                                         SourceLocation PropertyLoc) {
     CXXScopeSpec SS;
     DeclarationNameInfo NameInfo(Property->getDeclName(), PropertyLoc);
-    return getSema().BuildMemberReferenceExpr(BaseArg, BaseArg->getType(),
-                                              /*FIXME:*/PropertyLoc,
-                                              /*IsArrow=*/false,
-                                              SS, SourceLocation(),
-                                              /*FirstQualifierInScope=*/nullptr,
-                                              NameInfo,
-                                              /*TemplateArgs=*/nullptr,
-                                              /*S=*/nullptr);
+    return getSema().BuildMemberReferenceExpr(
+        BaseArg, BaseArg->getType(),
+        /*FIXME:*/ PropertyLoc,
+        /*IsArrow=*/false, SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo,
+        /*TemplateArgs=*/nullptr,
+        /*S=*/nullptr);
   }
 
   /// Build a new Objective-C property reference expression.
@@ -3883,13 +3865,11 @@ class TreeTransform {
                                 SourceLocation OpLoc, bool IsArrow) {
     CXXScopeSpec SS;
     DeclarationNameInfo NameInfo(&getSema().Context.Idents.get("isa"), IsaLoc);
-    return getSema().BuildMemberReferenceExpr(BaseArg, BaseArg->getType(),
-                                              OpLoc, IsArrow,
-                                              SS, SourceLocation(),
-                                              /*FirstQualifierInScope=*/nullptr,
-                                              NameInfo,
-                                              /*TemplateArgs=*/nullptr,
-                                              /*S=*/nullptr);
+    return getSema().BuildMemberReferenceExpr(
+        BaseArg, BaseArg->getType(), OpLoc, IsArrow, SS,
+        /*TemplateKWLoc=*/SourceLocation(), NameInfo,
+        /*TemplateArgs=*/nullptr,
+        /*S=*/nullptr);
   }
 
   /// Build a new shuffle vector expression.
@@ -4054,18 +4034,14 @@ class TreeTransform {
   }
 
 private:
-  TypeLoc TransformTypeInObjectScope(TypeLoc TL,
-                                     QualType ObjectType,
-                                     NamedDecl *FirstQualifierInScope,
+  TypeLoc TransformTypeInObjectScope(TypeLoc TL, QualType ObjectType,
                                      CXXScopeSpec &SS);
 
   TypeSourceInfo *TransformTypeInObjectScope(TypeSourceInfo *TSInfo,
                                              QualType ObjectType,
-                                             NamedDecl *FirstQualifierInScope,
                                              CXXScopeSpec &SS);
 
   TypeSourceInfo *TransformTSIInObjectScope(TypeLoc TL, QualType ObjectType,
-                                            NamedDecl *FirstQualifierInScope,
                                             CXXScopeSpec &SS);
 
   QualType TransformDependentNameType(TypeLocBuilder &TLB,
@@ -4384,7 +4360,7 @@ Sema::ConditionResult TreeTransform<Derived>::TransformCondition(
 template <typename Derived>
 NestedNameSpecifierLoc TreeTransform<Derived>::TransformNestedNameSpecifierLoc(
     NestedNameSpecifierLoc NNS, QualType ObjectType,
-    NamedDecl *FirstQualifierInScope) {
+    ArrayRef<DeclAccessPair> UnqualifiedLookups) {
   SmallVector<NestedNameSpecifierLoc, 4> Qualifiers;
 
   auto insertNNS = [&Qualifiers](NestedNameSpecifierLoc NNS) {
@@ -4395,6 +4371,8 @@ NestedNameSpecifierLoc TreeTransform<Derived>::TransformNestedNameSpecifierLoc(
   insertNNS(NNS);
 
   CXXScopeSpec SS;
+  SS.setUnqualifiedLookups(UnqualifiedLookups);
+
   while (!Qualifiers.empty()) {
     NestedNameSpecifierLoc Q = Qualifiers.pop_back_val();
     NestedNameSpecifier *QNNS = Q.getNestedNameSpecifier();
@@ -4404,8 +4382,9 @@ NestedNameSpecifierLoc TreeTransform<Derived>::TransformNestedNameSpecifierLoc(
       Sema::NestedNameSpecInfo IdInfo(QNNS->getAsIdentifier(),
                                       Q.getLocalBeginLoc(), Q.getLocalEndLoc(),
                                       ObjectType);
-      if (SemaRef.BuildCXXNestedNameSpecifier(/*Scope=*/nullptr, IdInfo, false,
-                                              SS, FirstQualifierInScope, false))
+      if (SemaRef.BuildCXXNestedNameSpecifier(/*Scope=*/nullptr, IdInfo,
+                                              /*EnteringContext=*/false, SS,
+                                              /*ErrorRecoveryLookup=*/false))
         return NestedNameSpecifierLoc();
       break;
     }
@@ -4443,8 +4422,7 @@ NestedNameSpecifierLoc TreeTransform<Derived>::TransformNestedNameSpecifierLoc(
 
     case NestedNameSpecifier::TypeSpecWithTemplate:
     case NestedNameSpecifier::TypeSpec: {
-      TypeLoc TL = TransformTypeInObjectScope(Q.getTypeLoc(), ObjectType,
-                                              FirstQualifierInScope, SS);
+      TypeLoc TL = TransformTypeInObjectScope(Q.getTypeLoc(), ObjectType, SS);
 
       if (!TL)
         return NestedNameSpecifierLoc();
@@ -4477,7 +4455,7 @@ NestedNameSpecifierLoc TreeTransform<Derived>::TransformNestedNameSpecifierLoc(
     }
 
     // The qualifier-in-scope and object type only apply to the leftmost entity.
-    FirstQualifierInScope = nullptr;
+    SS.setUnqualifiedLookups(std::nullopt);
     ObjectType = QualType();
   }
 
@@ -4560,14 +4538,10 @@ ::TransformDeclarationNameInfo(const DeclarationNameInfo &NameInfo) {
   llvm_unreachable("Unknown name kind.");
 }
 
-template<typename Derived>
-TemplateName
-TreeTransform<Derived>::TransformTemplateName(CXXScopeSpec &SS,
-                                              TemplateName Name,
-                                              SourceLocation NameLoc,
-                                              QualType ObjectType,
-                                              NamedDecl *FirstQualifierInScope,
-                                              bool AllowInjectedClassName) {
+template <typename Derived>
+TemplateName TreeTransform<Derived>::TransformTemplateName(
+    CXXScopeSpec &SS, TemplateName Name, SourceLocation NameLoc,
+    QualType ObjectType, bool AllowInjectedClassName, bool MayBeNNS) {
   if (QualifiedTemplateName *QTN = Name.getAsQualifiedTemplateName()) {
     TemplateDecl *Template = QTN->getUnderlyingTemplate().getAsTemplateDecl();
     assert(Template && "qualified template name must refer to a template");
@@ -4591,7 +4565,7 @@ TreeTransform<Derived>::TransformTemplateName(CXXScopeSpec &SS,
     if (SS.getScopeRep()) {
       // These apply to the scope specifier, not the template.
       ObjectType = QualType();
-      FirstQualifierInScope = nullptr;
+      SS.setUnqualifiedLookups(std::nullopt);
     }
 
     if (!getDerived().AlwaysRebuild() &&
@@ -4603,13 +4577,9 @@ TreeTransform<Derived>::TransformTemplateName(CXXScopeSpec &SS,
     SourceLocation TemplateKWLoc = NameLoc;
 
     if (DTN->isIdentifier()) {
-      return getDerived().RebuildTemplateName(SS,
-                                              TemplateKWLoc,
-                                              *DTN->getIdentifier(),
-                                              NameLoc,
-                                              ObjectType,
-                                              FirstQualifierInScope,
-                                              AllowInjectedClassName);
+      return getDerived().RebuildTemplateName(
+          SS, TemplateKWLoc, *DTN->getIdentifier(), NameLoc, ObjectType,
+          AllowInjectedClassName, MayBeNNS);
     }
 
     return getDerived().RebuildTemplateName(SS, TemplateKWLoc,
@@ -5153,39 +5123,31 @@ QualType TreeTransform<Derived>::RebuildQualifiedType(QualType T,
   return SemaRef.BuildQualifiedType(T, Loc, Quals);
 }
 
-template<typename Derived>
-TypeLoc
-TreeTransform<Derived>::TransformTypeInObjectScope(TypeLoc TL,
-                                                   QualType ObjectType,
-                                                   NamedDecl *UnqualLookup,
-                                                   CXXScopeSpec &SS) {
+template <typename Derived>
+TypeLoc TreeTransform<Derived>::TransformTypeInObjectScope(TypeLoc TL,
+                                                           QualType ObjectType,
+                                                           CXXScopeSpec &SS) {
   if (getDerived().AlreadyTransformed(TL.getType()))
     return TL;
 
-  TypeSourceInfo *TSI =
-      TransformTSIInObjectScope(TL, ObjectType, UnqualLookup, SS);
+  TypeSourceInfo *TSI = TransformTSIInObjectScope(TL, ObjectType, SS);
   if (TSI)
     return TSI->getTypeLoc();
   return TypeLoc();
 }
 
-template<typename Derived>
-TypeSourceInfo *
-TreeTransform<Derived>::TransformTypeInObjectScope(TypeSourceInfo *TSInfo,
-                                                   QualType ObjectType,
-                                                   NamedDecl *UnqualLookup,
-                                                   CXXScopeSpec &SS) {
+template <typename Derived>
+TypeSourceInfo *TreeTransform<Derived>::TransformTypeInObjectScope(
+    TypeSourceInfo *TSInfo, QualType ObjectType, CXXScopeSpec &SS) {
   if (getDerived().AlreadyTransformed(TSInfo->getType()))
     return TSInfo;
 
-  return TransformTSIInObjectScope(TSInfo->getTypeLoc(), ObjectType,
-                                   UnqualLookup, SS);
+  return TransformTSIInObjectScope(TSInfo->getTypeLoc(), ObjectType, SS);
 }
 
 template <typename Derived>
 TypeSourceInfo *TreeTransform<Derived>::TransformTSIInObjectScope(
-    TypeLoc TL, QualType ObjectType, NamedDecl *UnqualLookup,
-    CXXScopeSpec &SS) {
+    TypeLoc TL, QualType ObjectType, CXXScopeSpec &SS) {
   QualType T = TL.getType();
   assert(!getDerived().AlreadyTransformed(T));
 
@@ -5198,7 +5160,7 @@ TypeSourceInfo *TreeTransform<Derived>::TransformTSIInObjectScope(
 
     TemplateName Template = getDerived().TransformTemplateName(
         SS, SpecTL.getTypePtr()->getTemplateName(), SpecTL.getTemplateNameLoc(),
-        ObjectType, UnqualLookup, /*AllowInjectedClassName*/true);
+        ObjectType, /*AllowInjectedClassName=*/true, /*MayBeNNS=*/true);
     if (Template.isNull())
       return nullptr;
 
@@ -5208,13 +5170,11 @@ TypeSourceInfo *TreeTransform<Derived>::TransformTSIInObjectScope(
     DependentTemplateSpecializationTypeLoc SpecTL =
         TL.castAs<DependentTemplateSpecializationTypeLoc>();
 
-    TemplateName Template
-      = getDerived().RebuildTemplateName(SS,
-                                         SpecTL.getTemplateKeywordLoc(),
-                                         *SpecTL.getTypePtr()->getIdentifier(),
-                                         SpecTL.getTemplateNameLoc(),
-                                         ObjectType, UnqualLookup,
-                                         /*AllowInjectedClassName*/true);
+    TemplateName Template = getDerived().RebuildTemplateName(
+        SS, SpecTL.getTemplateKeywordLoc(),
+        *SpecTL.getTypePtr()->getIdentifier(), SpecTL.getTemplateNameLoc(),
+        ObjectType,
+        /*AllowInjectedClassName=*/true, /*MayBeNNS=*/true);
     if (Template.isNull())
       return nullptr;
 
@@ -12384,7 +12344,8 @@ TreeTransform<Derived>::TransformMemberExpr(MemberExpr *E) {
   // first-qualifier-in-scope here, just in case we had a dependent
   // base (and therefore couldn't do the check) and a
   // nested-name-qualifier (and therefore could do the lookup).
-  NamedDecl *FirstQualifierInScope = nullptr;
+  ArrayRef<DeclAccessPair> UnqualifiedLookups;
+
   DeclarationNameInfo MemberNameInfo = E->getMemberNameInfo();
   if (MemberNameInfo.getName()) {
     MemberNameInfo = getDerived().TransformDeclarationNameInfo(MemberNameInfo);
@@ -12392,16 +12353,11 @@ TreeTransform<Derived>::TransformMemberExpr(MemberExpr *E) {
       return ExprError();
   }
 
-  return getDerived().RebuildMemberExpr(Base.get(), FakeOperatorLoc,
-                                        E->isArrow(),
-                                        QualifierLoc,
-                                        TemplateKWLoc,
-                                        MemberNameInfo,
-                                        Member,
-                                        FoundDecl,
-                                        (E->hasExplicitTemplateArgs()
-                                           ? &TransArgs : nullptr),
-                                        FirstQualifierInScope);
+  return getDerived().RebuildMemberExpr(
+      Base.get(), FakeOperatorLoc, E->isArrow(), QualifierLoc, TemplateKWLoc,
+      MemberNameInfo, Member, FoundDecl,
+      (E->hasExplicitTemplateArgs() ? &TransArgs : nullptr),
+      UnqualifiedLookups);
 }
 
 template<typename Derived>
@@ -13528,9 +13484,8 @@ TreeTransform<Derived>::TransformCXXPseudoDestructorExpr(
 
   PseudoDestructorTypeStorage Destroyed;
   if (E->getDestroyedTypeInfo()) {
-    TypeSourceInfo *DestroyedTypeInfo
-      = getDerived().TransformTypeInObjectScope(E->getDestroyedTypeInfo(),
-                                                ObjectType, nullptr, SS);
+    TypeSourceInfo *DestroyedTypeInfo = getDerived().TransformTypeInObjectScope(
+        E->getDestroyedTypeInfo(), ObjectType, SS);
     if (!DestroyedTypeInfo)
       return ExprError();
     Destroyed = DestroyedTypeInfo;
@@ -13556,7 +13511,7 @@ TreeTransform<Derived>::TransformCXXPseudoDestructorExpr(
   if (E->getScopeTypeInfo()) {
     CXXScopeSpec EmptySS;
     ScopeTypeInfo = getDerived().TransformTypeInObjectScope(
-                      E->getScopeTypeInfo(), ObjectType, nullptr, EmptySS);
+        E->getScopeTypeInfo(), ObjectType, EmptySS);
     if (!ScopeTypeInfo)
       return ExprError();
   }
@@ -14817,19 +14772,17 @@ TreeTransform<Derived>::TransformCXXDependentScopeMemberExpr(
     ObjectType = BaseType->castAs<PointerType>()->getPointeeType();
   }
 
-  // Transform the first part of the nested-name-specifier that qualifies
-  // the member name.
-  NamedDecl *FirstQualifierInScope
-    = getDerived().TransformFirstQualifierInScope(
-                                            E->getFirstQualifierFoundInScope(),
-                                            E->getQualifierLoc().getBeginLoc());
+  UnresolvedSet<4> UnqualifiedLookups;
+  for (auto D : E->unqualified_lookups()) {
+    if (NamedDecl *InstD = getDerived().TransformFirstQualifierInScope(
+            D.getDecl(), E->getQualifierLoc().getBeginLoc()))
+      UnqualifiedLookups.addDecl(InstD);
+  }
 
   NestedNameSpecifierLoc QualifierLoc;
   if (E->getQualifier()) {
-    QualifierLoc
-      = getDerived().TransformNestedNameSpecifierLoc(E->getQualifierLoc(),
-                                                     ObjectType,
-                                                     FirstQualifierInScope);
+    QualifierLoc = getDerived().TransformNestedNameSpecifierLoc(
+        E->getQualifierLoc(), ObjectType, UnqualifiedLookups.pairs());
     if (!QualifierLoc)
       return ExprError();
   }
@@ -14848,23 +14801,16 @@ TreeTransform<Derived>::TransformCXXDependentScopeMemberExpr(
   if (!E->hasExplicitTemplateArgs()) {
     // This is a reference to a member without an explicitly-specified
     // template argument list. Optimize for this common case.
-    if (!getDerived().AlwaysRebuild() &&
-        Base.get() == OldBase &&
-        BaseType == E->getBaseType() &&
-        QualifierLoc == E->getQualifierLoc() &&
+    if (!getDerived().AlwaysRebuild() && Base.get() == OldBase &&
+        BaseType == E->getBaseType() && QualifierLoc == E->getQualifierLoc() &&
         NameInfo.getName() == E->getMember() &&
-        FirstQualifierInScope == E->getFirstQualifierFoundInScope())
+        UnqualifiedLookups.pairs() == E->unqualified_lookups())
       return E;
 
-    return getDerived().RebuildCXXDependentScopeMemberExpr(Base.get(),
-                                                       BaseType,
-                                                       E->isArrow(),
-                                                       E->getOperatorLoc(),
-                                                       QualifierLoc,
-                                                       TemplateKWLoc,
-                                                       FirstQualifierInScope,
-                                                       NameInfo,
-                                                       /*TemplateArgs*/nullptr);
+    return getDerived().RebuildCXXDependentScopeMemberExpr(
+        Base.get(), BaseType, E->isArrow(), E->getOperatorLoc(), QualifierLoc,
+        TemplateKWLoc, UnqualifiedLookups.pairs(), NameInfo,
+        /*TemplateArgs*/ nullptr);
   }
 
   TemplateArgumentListInfo TransArgs(E->getLAngleLoc(), E->getRAngleLoc());
@@ -14873,15 +14819,9 @@ TreeTransform<Derived>::TransformCXXDependentScopeMemberExpr(
                                               TransArgs))
     return ExprError();
 
-  return getDerived().RebuildCXXDependentScopeMemberExpr(Base.get(),
-                                                     BaseType,
-                                                     E->isArrow(),
-                                                     E->getOperatorLoc(),
-                                                     QualifierLoc,
-                                                     TemplateKWLoc,
-                                                     FirstQualifierInScope,
-                                                     NameInfo,
-                                                     &TransArgs);
+  return getDerived().RebuildCXXDependentScopeMemberExpr(
+      Base.get(), BaseType, E->isArrow(), E->getOperatorLoc(), QualifierLoc,
+      TemplateKWLoc, UnqualifiedLookups.pairs(), NameInfo, &TransArgs);
 }
 
 template <typename Derived>
@@ -14942,11 +14882,11 @@ ExprResult TreeTransform<Derived>::TransformUnresolvedMemberExpr(
   // first-qualifier-in-scope here, just in case we had a dependent
   // base (and therefore couldn't do the check) and a
   // nested-name-qualifier (and therefore could do the lookup).
-  NamedDecl *FirstQualifierInScope = nullptr;
+  ArrayRef<DeclAccessPair> UnqualifiedLookups;
 
   return getDerived().RebuildUnresolvedMemberExpr(
       Base.get(), BaseType, Old->getOperatorLoc(), Old->isArrow(), QualifierLoc,
-      TemplateKWLoc, FirstQualifierInScope, R,
+      TemplateKWLoc, UnqualifiedLookups, R,
       (Old->hasExplicitTemplateArgs() ? &TransArgs : nullptr));
 }
 
@@ -16303,22 +16243,18 @@ TreeTransform<Derived>::RebuildTemplateName(CXXScopeSpec &SS,
                                                   TemplateName(Template));
 }
 
-template<typename Derived>
-TemplateName
-TreeTransform<Derived>::RebuildTemplateName(CXXScopeSpec &SS,
-                                            SourceLocation TemplateKWLoc,
-                                            const IdentifierInfo &Name,
-                                            SourceLocation NameLoc,
-                                            QualType ObjectType,
-                                            NamedDecl *FirstQualifierInScope,
-                                            bool AllowInjectedClassName) {
+template <typename Derived>
+TemplateName TreeTransform<Derived>::RebuildTemplateName(
+    CXXScopeSpec &SS, SourceLocation TemplateKWLoc, const IdentifierInfo &Name,
+    SourceLocation NameLoc, QualType ObjectType, bool AllowInjectedClassName,
+    bool MayBeNNS) {
   UnqualifiedId TemplateName;
   TemplateName.setIdentifier(&Name, NameLoc);
   Sema::TemplateTy Template;
   getSema().ActOnTemplateName(/*Scope=*/nullptr, SS, TemplateKWLoc,
                               TemplateName, ParsedType::make(ObjectType),
                               /*EnteringContext=*/false, Template,
-                              AllowInjectedClassName);
+                              AllowInjectedClassName, MayBeNNS);
   return Template.get();
 }
 
@@ -16466,13 +16402,10 @@ TreeTransform<Derived>::RebuildCXXPseudoDestructorExpr(Expr *Base,
   }
 
   SourceLocation TemplateKWLoc; // FIXME: retrieve it from caller.
-  return getSema().BuildMemberReferenceExpr(Base, BaseType,
-                                            OperatorLoc, isArrow,
-                                            SS, TemplateKWLoc,
-                                            /*FIXME: FirstQualifier*/ nullptr,
-                                            NameInfo,
-                                            /*TemplateArgs*/ nullptr,
-                                            /*S*/nullptr);
+  return getSema().BuildMemberReferenceExpr(
+      Base, BaseType, OperatorLoc, isArrow, SS, TemplateKWLoc, NameInfo,
+      /*TemplateArgs=*/nullptr,
+      /*S=*/nullptr);
 }
 
 template<typename Derived>
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index e1cba9e612be3..e7cad990d7352 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1990,42 +1990,43 @@ void ASTStmtReader::VisitCXXDependentScopeMemberExpr(
     CXXDependentScopeMemberExpr *E) {
   VisitExpr(E);
 
-  unsigned NumTemplateArgs = Record.readInt();
   CurrentUnpackingBits.emplace(Record.readInt());
-  bool HasTemplateKWAndArgsInfo = CurrentUnpackingBits->getNextBit();
-  bool HasFirstQualifierFoundInScope = CurrentUnpackingBits->getNextBit();
-
-  assert((HasTemplateKWAndArgsInfo == E->hasTemplateKWAndArgsInfo()) &&
-         "Wrong HasTemplateKWAndArgsInfo!");
-  assert(
-      (HasFirstQualifierFoundInScope == E->hasFirstQualifierFoundInScope()) &&
-      "Wrong HasFirstQualifierFoundInScope!");
-
-  if (HasTemplateKWAndArgsInfo)
-    ReadTemplateKWAndArgsInfo(
-        *E->getTrailingObjects<ASTTemplateKWAndArgsInfo>(),
-        E->getTrailingObjects<TemplateArgumentLoc>(), NumTemplateArgs);
-
-  assert((NumTemplateArgs == E->getNumTemplateArgs()) &&
-         "Wrong NumTemplateArgs!");
+  bool HasQualifier = CurrentUnpackingBits->getNextBit();
+  bool HasTemplateInfo = CurrentUnpackingBits->getNextBit();
+  unsigned NumUnqualifiedLookups = Record.readInt();
+  unsigned NumTemplateArgs = Record.readInt();
+  E->CXXDependentScopeMemberExprBits.HasQualifier = HasQualifier;
+  E->CXXDependentScopeMemberExprBits.NumUnqualifiedLookups =
+      NumUnqualifiedLookups;
+  E->CXXDependentScopeMemberExprBits.HasTemplateKWAndArgsInfo = HasTemplateInfo;
 
+  E->BaseType = Record.readType();
   E->CXXDependentScopeMemberExprBits.IsArrow =
       CurrentUnpackingBits->getNextBit();
 
-  E->BaseType = Record.readType();
-  E->QualifierLoc = Record.readNestedNameSpecifierLoc();
-  // not ImplicitAccess
   if (CurrentUnpackingBits->getNextBit())
     E->Base = Record.readSubExpr();
   else
     E->Base = nullptr;
 
-  E->CXXDependentScopeMemberExprBits.OperatorLoc = readSourceLocation();
+  E->OperatorLoc = Record.readSourceLocation();
+  E->MemberNameInfo = Record.readDeclarationNameInfo();
 
-  if (HasFirstQualifierFoundInScope)
-    *E->getTrailingObjects<NamedDecl *>() = readDeclAs<NamedDecl>();
+  if (HasQualifier)
+    new (E->getTrailingObjects<NestedNameSpecifierLoc>())
+        NestedNameSpecifierLoc(Record.readNestedNameSpecifierLoc());
 
-  E->MemberNameInfo = Record.readDeclarationNameInfo();
+  for (unsigned I = 0; I != NumUnqualifiedLookups; ++I) {
+    auto *FoundD = Record.readDeclAs<NamedDecl>();
+    auto AS = (AccessSpecifier)Record.readInt();
+    E->getTrailingObjects<DeclAccessPair>()[I] =
+        DeclAccessPair::make(FoundD, AS);
+  }
+
+  if (HasTemplateInfo)
+    ReadTemplateKWAndArgsInfo(
+        *E->getTrailingObjects<ASTTemplateKWAndArgsInfo>(),
+        E->getTrailingObjects<TemplateArgumentLoc>(), NumTemplateArgs);
 }
 
 void
@@ -4095,16 +4096,16 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
       break;
 
     case EXPR_CXX_DEPENDENT_SCOPE_MEMBER: {
-      unsigned NumTemplateArgs = Record[ASTStmtReader::NumExprFields];
       BitsUnpacker DependentScopeMemberBits(
-          Record[ASTStmtReader::NumExprFields + 1]);
-      bool HasTemplateKWAndArgsInfo = DependentScopeMemberBits.getNextBit();
+          Record[ASTStmtReader::NumExprFields]);
+      bool HasQualifier = DependentScopeMemberBits.getNextBit();
+      bool HasTemplateInfo = DependentScopeMemberBits.getNextBit();
+      unsigned NumUnqualifiedLookups = Record[ASTStmtReader::NumExprFields + 1];
+      unsigned NumTemplateArgs = Record[ASTStmtReader::NumExprFields + 2];
 
-      bool HasFirstQualifierFoundInScope =
-          DependentScopeMemberBits.getNextBit();
       S = CXXDependentScopeMemberExpr::CreateEmpty(
-          Context, HasTemplateKWAndArgsInfo, NumTemplateArgs,
-          HasFirstQualifierFoundInScope);
+          Context, HasQualifier, NumUnqualifiedLookups, HasTemplateInfo,
+          NumTemplateArgs);
       break;
     }
 
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index ec667b58337ff..8abe40772b0ee 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1986,34 +1986,41 @@ void ASTStmtWriter::VisitCXXDependentScopeMemberExpr(
     CXXDependentScopeMemberExpr *E) {
   VisitExpr(E);
 
-  // Don't emit anything here (or if you do you will have to update
-  // the corresponding deserialization function).
-  Record.push_back(E->getNumTemplateArgs());
-  CurrentPackingBits.updateBits();
-  CurrentPackingBits.addBit(E->hasTemplateKWAndArgsInfo());
-  CurrentPackingBits.addBit(E->hasFirstQualifierFoundInScope());
-
-  if (E->hasTemplateKWAndArgsInfo()) {
-    const ASTTemplateKWAndArgsInfo &ArgInfo =
-        *E->getTrailingObjects<ASTTemplateKWAndArgsInfo>();
-    AddTemplateKWAndArgsInfo(ArgInfo,
-                             E->getTrailingObjects<TemplateArgumentLoc>());
-  }
+  bool HasQualifier = E->hasQualifier();
+  unsigned NumUnqualifiedLookups = E->getNumUnqualifiedLookups();
+  bool HasTemplateInfo = E->hasTemplateKWAndArgsInfo();
+  unsigned NumTemplateArgs = E->getNumTemplateArgs();
 
-  CurrentPackingBits.addBit(E->isArrow());
+  // Write these first for easy access when deserializing, as they affect the
+  // size of the CXXDependentScopeMemberExpr.
+  CurrentPackingBits.updateBits();
+  CurrentPackingBits.addBit(HasQualifier);
+  CurrentPackingBits.addBit(HasTemplateInfo);
+  Record.push_back(NumUnqualifiedLookups);
+  Record.push_back(NumTemplateArgs);
 
   Record.AddTypeRef(E->getBaseType());
-  Record.AddNestedNameSpecifierLoc(E->getQualifierLoc());
+  CurrentPackingBits.addBit(E->isArrow());
   CurrentPackingBits.addBit(!E->isImplicitAccess());
   if (!E->isImplicitAccess())
     Record.AddStmt(E->getBase());
 
   Record.AddSourceLocation(E->getOperatorLoc());
 
-  if (E->hasFirstQualifierFoundInScope())
-    Record.AddDeclRef(E->getFirstQualifierFoundInScope());
-
   Record.AddDeclarationNameInfo(E->MemberNameInfo);
+
+  if (HasQualifier)
+    Record.AddNestedNameSpecifierLoc(E->getQualifierLoc());
+
+  for (DeclAccessPair D : E->unqualified_lookups()) {
+    Record.AddDeclRef(D.getDecl());
+    Record.push_back(D.getAccess());
+  }
+
+  if (HasTemplateInfo)
+    AddTemplateKWAndArgsInfo(*E->getTrailingObjects<ASTTemplateKWAndArgsInfo>(),
+                             E->getTrailingObjects<TemplateArgumentLoc>());
+
   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 1afea99e8895c..11eb67fb4f159 100644
--- a/clang/test/CXX/basic/basic.lookup/basic.lookup.classref/p1-cxx11.cpp
+++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.classref/p1-cxx11.cpp
@@ -55,15 +55,19 @@ namespace PR11856 {
 
   template<typename T> T *end(T*);
 
-  class X { };
+  struct X { };
+  struct Y {
+    int end;
+  };
   template <typename T>
   void Foo2() {
     T it1;
-    if (it1->end < it1->end) {
-    }
+    if (it1->end < it1->end) { }
 
     X *x;
-    if (x->end < 7) {  // expected-error{{no member named 'end' in 'PR11856::X'}}
-    }
+    if (x->end < 7) { } // expected-error{{no member named 'end' in 'PR11856::X'}}
+
+    Y *y;
+    if (y->end < 7) { }
   }
 }
diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.classref/p1.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.classref/p1.cpp
index e3599db18350b..5221b883f046c 100644
--- a/clang/test/CXX/basic/basic.lookup/basic.lookup.classref/p1.cpp
+++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.classref/p1.cpp
@@ -86,15 +86,19 @@ namespace PR11856 {
 
   template<typename T> T *end(T*);
 
-  class X { };
+  struct X { };
+  struct Y {
+    int end;
+  };
   template <typename T>
   void Foo2() {
     T it1;
-    if (it1->end < it1->end) {
-    }
+    if (it1->end < it1->end) { }
 
     X *x;
-    if (x->end < 7) {  // expected-error{{no member named 'end' in 'PR11856::X'}}
-    }
+    if (x->end < 7) { } // expected-error{{no member named 'end' in 'PR11856::X'}}
+
+    Y *y;
+    if (y->end < 7) { }
   }
 }
diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p3-example3.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p3-example3.cpp
new file mode 100644
index 0000000000000..423eacd21d441
--- /dev/null
+++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p3-example3.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -std=c++23 %s -verify
+
+int f();
+
+struct A {
+  int B, C; // expected-note {{declared as a non-template here}}
+  template<int> using D = void;
+  using T = void;
+  void f();
+};
+
+using B = A;
+template<int> using C = A;
+template<int> using D = A;
+template<int> using X = A;
+
+template<class T>
+void g(T *p) {
+  p->X<0>::f(); // expected-error {{no member named 'X' in 'A'}}
+  p->template X<0>::f();
+  p->B::f();
+  p->template C<0>::f(); // expected-error {{'C' following the 'template' keyword does not refer to a template}}
+  p->template D<0>::f(); // expected-error {{type 'template D<0>' (aka 'void') cannot be used prior to '::' because it has no members}}
+  p->T::f(); // expected-error {{'A::T' (aka 'void') is not a class, namespace, or enumeration}}
+}
+
+template void g(A*); // expected-note {{in instantiation of}}
diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p3.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p3.cpp
new file mode 100644
index 0000000000000..7d843649c3f30
--- /dev/null
+++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p3.cpp
@@ -0,0 +1,98 @@
+// RUN: %clang_cc1 -std=c++23 -Wno-unused %s -verify
+
+namespace Unambiguous {
+  struct A {
+    int x;
+
+    template<typename T>
+    using C = A;
+  };
+
+  using B = A;
+
+  template<typename T>
+  using D = A;
+
+  using E = void;
+
+  struct F : A {
+    void non_template() {
+      this->x;
+      this->A::x;
+      this->B::x;
+      this->C<int>::x;
+      this->D<int>::x;
+      this->E::x; // expected-error {{'Unambiguous::E' (aka 'void') is not a class, namespace, or enumeration}}
+    }
+  };
+
+  template<typename T>
+  void not_instantiated(T t) {
+    t.x;
+    t.A::x;
+    t.B::x;
+    t.C<int>::x; // expected-warning {{use 'template' keyword to treat 'C' as a dependent template name}}
+    t.template C<int>::x;
+    t.D<int>::x; // expected-warning {{use 'template' keyword to treat 'D' as a dependent template name}}
+    t.template D<int>::x;
+    t.E::x;
+  }
+
+  template<typename T>
+  void instantiated_valid(T t) {
+    t.x;
+    t.A::x;
+    t.B::x;
+    t.template C<int>::x;
+    t.template D<int>::x;
+    t.E::x;
+  }
+
+  template<typename T>
+  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<int>::x;
+    t.template D<int>::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<typename T>
+  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 78e83c0ab4566..97d3587881bbc 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<typename T>
 void DerivedT<T>::Inner() {
   Derived1T<T>::Foo();
   Derived2T<T>::Member = 42;
-  this->Derived1T<T>::Foo();
-  this->Derived2T<T>::Member = 42;
+  this->Derived1T<T>::Foo(); // expected-warning{{use 'template' keyword to treat 'Derived1T' as a dependent template name}}
+  this->Derived2T<T>::Member = 42; // expected-warning{{use 'template' keyword to treat 'Derived2T' as a dependent template name}}
   this->Foo(); // expected-error{{non-static member 'Foo' found in multiple base-class subobjects of type 'BaseT<int>'}}
 }
 
diff --git a/clang/test/CXX/drs/cwg1xx.cpp b/clang/test/CXX/drs/cwg1xx.cpp
index e7dddd1ea9278..6bca460818425 100644
--- a/clang/test/CXX/drs/cwg1xx.cpp
+++ b/clang/test/CXX/drs/cwg1xx.cpp
@@ -615,10 +615,8 @@ namespace cwg141 { // cwg141: 3.1
     //   cxx98-note@#cwg141-S {{lookup from the current scope refers here}}
     // expected-error@#cwg141-a {{no member named 'n' in 'cwg141::A::S<int>'; did you mean '::cwg141::S<int>::n'?}}
     //   expected-note@#cwg141-S {{'::cwg141::S<int>::n' declared here}}
-    // FIXME: we issue a useful diagnostic first, then some bogus ones.
     b.f<int>();
     // expected-error at -1 {{no member named 'f' in 'cwg141::B'}}
-    // expected-error at -2 +{{}}
     (void)b.S<int>::n;
   }
   template<typename T> struct C {
@@ -628,10 +626,12 @@ namespace cwg141 { // cwg141: 3.1
       // expected-error at -1 {{use 'template' keyword to treat 'f' as a dependent template name}}
     }
     void h() {
-      (void)t.S<int>::n; // ok
+      (void)t.S<int>::n;
+      // expected-error at -1 {{use 'template' keyword to treat 'S' as a dependent template name}}
     }
     void i() {
-      (void)t.S<int>(); // ok!
+      (void)t.S<int>();
+      // expected-error at -1 {{use 'template' keyword to treat 'S' as a dependent template name}}
     }
   };
   void h() { C<B>().h(); } // ok
diff --git a/clang/test/CXX/temp/temp.names/p3-23.cpp b/clang/test/CXX/temp/temp.names/p3-23.cpp
new file mode 100644
index 0000000000000..27c24e1d61706
--- /dev/null
+++ b/clang/test/CXX/temp/temp.names/p3-23.cpp
@@ -0,0 +1,237 @@
+// RUN: %clang_cc1 -std=c++23 -Wno-unused %s -verify
+
+namespace FoundNothing {
+  template<typename T>
+  void f0(T &t) {
+    t.x<0;
+    t.x<0>; // expected-error {{expected expression}}
+    t.x<0>1;
+  }
+
+  template<typename T>
+  struct A {
+    void f1() {
+      this->x<0; // expected-error {{no member named 'x' in 'A<T>'}}
+      this->x<0>; // expected-error {{no member named 'x' in 'A<T>'}}
+                  // expected-error at -1 {{expected expression}}
+      this->x<0>1; // expected-error {{no member named 'x' in 'A<T>'}}
+    }
+  };
+} // namespace FoundNothing
+
+namespace FoundSingleNonTemplate {
+  void f0();
+
+  struct A0;
+
+  template<typename T>
+  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<typename T>
+  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<T>'}}
+      this->f0<0>; // expected-error {{no member named 'f0' in 'B<T>'}}
+                   // expected-error at -1 {{expected expression}}
+      this->f0<0>1; // expected-error {{no member named 'f0' in 'B<T>'}}
+
+      this->A0<0; // expected-error {{no member named 'A0' in 'B<T>'}}
+      this->A0<0>; // expected-error {{no member named 'A0' in 'B<T>'}}
+                   // expected-error at -1 {{expected expression}}
+      this->A0<0>1; // expected-error {{no member named 'A0' in 'B<T>'}}
+
+      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 at -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<T>' with '->'}}
+      this->A1<0>; // expected-error {{cannot refer to type member 'A1' in 'B<T>' with '->'}}
+                   // expected-error at -1 {{expected expression}}
+      this->A1<0>1; // expected-error {{cannot refer to type member 'A1' in 'B<T>' with '->'}}
+    }
+  };
+} // namespace FoundSingleNonTemplate
+
+namespace FoundSingleTemplate {
+  template<int I>
+  void f0();
+
+  template<int I>
+  struct A0;
+
+  template<typename T>
+  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<typename T>
+  struct B {
+    template<int I>
+    void f1(); // expected-note 2{{possible target for call}}
+
+    template<int I>
+    struct A1; // expected-note 2{{member 'A1' declared here}}
+
+    void g1() {
+      this->f0<0; // expected-error {{no member named 'f0' in 'B<T>'}}
+      this->f0<0>; // expected-error {{no member named 'f0' in 'B<T>'}}
+      this->f0<0>1; // expected-error {{no member named 'f0' in 'B<T>'}}
+                    // expected-error at -1 {{expected ';' after expression}}
+
+      this->A0<0; // expected-error {{no member named 'A0' in 'B<T>'}}
+      this->A0<0>; // expected-error {{no member named 'A0' in 'B<T>'}}
+      this->A0<0>1; // expected-error {{no member named 'A0' in 'B<T>'}}
+                    // expected-error at -1 {{expected ';' after expression}}
+
+
+      this->f1<0; // expected-error {{expected '>'}}
+                  // expected-note at -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 at -1 {{expected ';' after expression}}
+
+      this->A1<0; // expected-error {{expected '>'}}
+                  // expected-note at -1 {{to match this '<'}}
+      this->A1<0>; // expected-error {{cannot refer to member 'A1' in 'B<T>' with '->'}}
+      this->A1<0>1; // expected-error {{cannot refer to member 'A1' in 'B<T>' with '->'}}
+                    // expected-error at -1 {{expected ';' after expression}}
+    }
+  };
+} // namespace FoundSingleTemplate
+
+namespace FoundAmbiguousNonTemplate {
+  inline namespace N {
+    int f0;
+
+    struct A0;
+  } // namespace N
+
+  void f0();
+
+  struct A0;
+
+  template<typename T>
+  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<typename T>
+  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<T>'}}
+      this->f0<0>; // expected-error {{no member named 'f0' in 'B<T>'}}
+                   // expected-error at -1 {{expected expression}}
+      this->f0<0>1; // expected-error {{no member named 'f0' in 'B<T>'}}
+
+      this->A0<0; // expected-error {{no member named 'A0' in 'B<T>'}}
+      this->A0<0>; // expected-error {{no member named 'A0' in 'B<T>'}}
+                   // expected-error at -1 {{expected expression}}
+      this->A0<0>1; // expected-error {{no member named 'A0' in 'B<T>'}}
+
+      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 at -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<T>' with '->'}}
+      this->A1<0>; // expected-error {{cannot refer to type member 'A1' in 'B<T>' with '->'}}
+                   // expected-error at -1 {{expected expression}}
+      this->A1<0>1; // expected-error {{cannot refer to type member 'A1' in 'B<T>' with '->'}}
+    }
+  };
+} // namespace FoundAmbiguousNonTemplates
+
+namespace FoundAmbiguousTemplate {
+  inline namespace N {
+    template<int I>
+    int f0; // expected-note 3{{candidate found by name lookup is 'FoundAmbiguousTemplate::N::f0'}}
+
+    template<int I>
+    struct A0; // expected-note 3{{candidate found by name lookup is 'FoundAmbiguousTemplate::N::A0'}}
+  } // namespace N
+
+  template<int I>
+  void f0(); // expected-note 3{{candidate found by name lookup is 'FoundAmbiguousTemplate::f0'}}
+
+  template<int I>
+  struct A0; // expected-note 3{{candidate found by name lookup is 'FoundAmbiguousTemplate::A0'}}
+
+  template<typename T>
+  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<typename T>
+  struct B {
+    template<int I>
+    void f1(); // expected-note 2{{possible target for call}}
+
+    template<int I>
+    struct A1; // expected-note 2{{member 'A1' declared here}}
+
+    void g1() {
+      this->f0<0; // expected-error {{no member named 'f0' in 'B<T>'}}
+                  // expected-error at -1 {{reference to 'f0' is ambiguous}}
+      this->f0<0>; // expected-error {{no member named 'f0' in 'B<T>'}}
+                   // expected-error at -1 {{reference to 'f0' is ambiguous}}
+      this->f0<0>1; // expected-error {{no member named 'f0' in 'B<T>'}}
+                    // expected-error at -1 {{expected ';' after expression}}
+                    // expected-error at -2 {{reference to 'f0' is ambiguous}}
+
+      this->A0<0; // expected-error {{no member named 'A0' in 'B<T>'}}
+                  // expected-error at -1 {{reference to 'A0' is ambiguous}}
+      this->A0<0>; // expected-error {{no member named 'A0' in 'B<T>'}}
+                   // expected-error at -1 {{reference to 'A0' is ambiguous}}
+      this->A0<0>1; // expected-error {{no member named 'A0' in 'B<T>'}}
+                    // expected-error at -1 {{expected ';' after expression}}
+                    // expected-error at -2 {{reference to 'A0' is ambiguous}}
+
+      this->f1<0; // expected-error {{expected '>'}}
+                  // expected-note at -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 at -1 {{expected ';' after expression}}
+
+      this->A1<0; // expected-error {{expected '>'}}
+                  // expected-note at -1 {{to match this '<'}}
+      this->A1<0>; // expected-error {{cannot refer to member 'A1' in 'B<T>' with '->'}}
+      this->A1<0>1; // expected-error {{cannot refer to member 'A1' in 'B<T>' with '->'}}
+                    // expected-error at -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 1eda967523a59..c144acff369b1 100644
--- a/clang/test/CXX/temp/temp.res/p3.cpp
+++ b/clang/test/CXX/temp/temp.res/p3.cpp
@@ -28,6 +28,6 @@ template<typename T> int A<T>::template C<int>::*f5() {}
 template<typename T> template<typename U> struct A<T>::B {
   friend A<T>::C<T> f6(); // ok, same as 'friend T f6();'
 
-  friend A<U>::C<T> f7(); // expected-error {{use 'template' keyword to treat 'C' as a dependent template name}} expected-warning {{missing 'typename'}}
+  friend A<U>::C<T> f7(); // expected-warning {{use 'template' keyword to treat 'C' as a dependent template name}} expected-warning {{missing 'typename'}}
   friend A<U>::template C<T> f8(); // expected-warning {{missing 'typename'}}
 };
diff --git a/clang/test/FixIt/fixit.cpp b/clang/test/FixIt/fixit.cpp
index 605c2d0bd0235..144eefb3ae4bd 100644
--- a/clang/test/FixIt/fixit.cpp
+++ b/clang/test/FixIt/fixit.cpp
@@ -158,12 +158,12 @@ class F1 {
  
 template<class T>
 class F2  {
-  typename F1<T>:: /*template*/  Iterator<0> Mypos; // expected-error {{use 'template' keyword to treat 'Iterator' as a dependent template name}}
+  typename F1<T>:: /*template*/  Iterator<0> Mypos; // expected-warning {{use 'template' keyword to treat 'Iterator' as a dependent template name}}
 };
 
 template <class T>
 void f(){
-  typename F1<T>:: /*template*/ Iterator<0> Mypos; // expected-error {{use 'template' keyword to treat 'Iterator' as a dependent template name}}
+  typename F1<T>:: /*template*/ Iterator<0> Mypos; // expected-warning {{use 'template' keyword to treat 'Iterator' as a dependent template name}}
 }
 
 // Tests for &/* fixits
diff --git a/clang/test/Misc/warning-flags.c b/clang/test/Misc/warning-flags.c
index cdbe1e95cba96..651a86fb6e226 100644
--- a/clang/test/Misc/warning-flags.c
+++ b/clang/test/Misc/warning-flags.c
@@ -21,6 +21,7 @@ The list of warnings below should NEVER grow.  It should gradually shrink to 0.
 CHECK: Warnings without flags (65):
 
 CHECK-NEXT:   ext_expected_semi_decl_list
+CHECK-NEXT:   ext_missing_dependent_template_keyword
 CHECK-NEXT:   ext_missing_whitespace_after_macro_name
 CHECK-NEXT:   ext_new_paren_array_nonconst
 CHECK-NEXT:   ext_plain_complex
@@ -61,7 +62,6 @@ CHECK-NEXT:   warn_invalid_cpu_supports
 CHECK-NEXT:   warn_maynot_respond
 CHECK-NEXT:   warn_method_param_redefinition
 CHECK-NEXT:   warn_missing_case_for_condition
-CHECK-NEXT:   warn_missing_dependent_template_keyword
 CHECK-NEXT:   warn_missing_whitespace_after_macro_name
 CHECK-NEXT:   warn_mt_message
 CHECK-NEXT:   warn_no_constructor_for_refconst
diff --git a/clang/test/Parser/cxx2a-concepts-requires-expr.cpp b/clang/test/Parser/cxx2a-concepts-requires-expr.cpp
index 5755844a323d2..0c7f453b5c48d 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<typename T>
 bool r23 = requires { typename identity<T>::temp<T>; };
-// expected-error at -1 {{use 'template' keyword to treat 'temp' as a dependent template name}}
+// expected-warning at -1 {{use 'template' keyword to treat 'temp' as a dependent template name}}
 
 template<typename T>
 bool r24 = requires {
diff --git a/clang/test/SemaCXX/cxx0x-noexcept-expression.cpp b/clang/test/SemaCXX/cxx0x-noexcept-expression.cpp
index b3c102830f359..a01edc77e02af 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<false, false>* for example)
   dependent.f<void>();  // This should need to be `.template f` to parse as a template
-  // expected-error at -1 {{use 'template' keyword to treat 'f' as a dependent template name}}
+  // expected-warning at -1 {{use 'template' keyword to treat 'f' as a dependent template name}}
 }
 template<typename... T>
 void f2() {
@@ -135,14 +135,14 @@ void f2() {
   // X<true> when T...[0] is a type with some operator&& which returns int*
   // X<false> when sizeof...(T) == 0
   dependent.f<void>();
-  // expected-error at -1 {{use 'template' keyword to treat 'f' as a dependent template name}}
+  // expected-warning at -1 {{use 'template' keyword to treat 'f' as a dependent template name}}
 }
 template<typename T>
 void f3() {
   X<noexcept(typeid(*static_cast<T*>(nullptr)))> dependent;
   // X<true> when T is int, X<false> when T is Polymorphic<false, false>
   dependent.f<void>();
-  // expected-error at -1 {{use 'template' keyword to treat 'f' as a dependent template name}}
+  // expected-warning at -1 {{use 'template' keyword to treat 'f' as a dependent template name}}
 }
 template<typename T>
 void f4() {
diff --git a/clang/test/SemaCXX/pseudo-destructors.cpp b/clang/test/SemaCXX/pseudo-destructors.cpp
index 55a96002be2ab..44dc9ce8b1520 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<typename T> struct Y {};
     template<class U> using G = Y<U>;
-    template<typename T> void f(T *p) { p->~G<int>(); } // expected-error {{no member named '~Y'}}
+    template<typename T> void f(T *p) { p->~G<int>(); } // expected-error {{no member named 'G'}}
     void h1(Y<int> *p) { p->~G<int>(); }
-    void h2(Y<int> *p) { f(p); }
+    void h2(Y<int> *p) { f(p); } // expected-note {{instantiation of}}
     namespace N { template<typename T> struct G {}; }
     void h3(N::G<int> *p) { p->~G<int>(); }
-    void h4(N::G<int> *p) { f(p); } // expected-note {{instantiation of}}
+    void h4(N::G<int> *p) { f(p); }
   }
 
   namespace TemplateUndeclared {
diff --git a/clang/test/SemaCXX/static-assert-cxx17.cpp b/clang/test/SemaCXX/static-assert-cxx17.cpp
index 41a7b025d0eb7..754f4ae5f1d38 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 at -1{{static assertion failed due to requirement 'static_cast<const X<int> *>(nullptr)'}}
   static_assert((const X<typename T::T>[]){} == nullptr);
   // expected-error at -1{{static assertion failed due to requirement '(const X<int>[0]){} == nullptr'}}
-  static_assert(sizeof(X<decltype(X<typename T::T>().X<typename T::T>::~X())>) == 0);
+  static_assert(sizeof(X<decltype(X<typename T::T>().template X<typename T::T>::~X())>) == 0);
   // expected-error at -1{{static assertion failed due to requirement 'sizeof(X<void>) == 0'}} \
   // expected-note at -1 {{evaluates to '8 == 0'}}
   static_assert(constexpr_return_false<typename T::T, typename T::U>());
diff --git a/clang/test/SemaTemplate/dependent-base-classes.cpp b/clang/test/SemaTemplate/dependent-base-classes.cpp
index 92a37efaa7e73..4cb88a5b4070a 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<typename T, typename U>
-struct X0 : T::template apply<U> { 
+struct X0 : T::template apply<U> {
   X0(U u) : T::template apply<U>(u) { }
 };
 
 template<typename T, typename U>
-struct X1 : T::apply<U> { }; // expected-error{{use 'template' keyword to treat 'apply' as a dependent template name}}
+struct X1 : T::apply<U> { }; // expected-warning{{use 'template' keyword to treat 'apply' as a dependent template name}}
 
 template<typename T>
 struct X2 : vector<T> { }; // expected-error{{no template named 'vector'}}
@@ -85,7 +85,7 @@ namespace PR6081 {
   struct A { };
 
   template<typename T>
-  class B : public A<T> 
+  class B : public A<T>
   {
   public:
     template< class X >
@@ -109,9 +109,9 @@ namespace PR6081 {
 
 namespace PR6413 {
   template <typename T> class Base_A { };
-  
+
   class Base_B { };
-  
+
   template <typename T>
   class Derived
     : public virtual Base_A<T>
@@ -120,12 +120,12 @@ namespace PR6413 {
 }
 
 namespace PR5812 {
-  template <class T> struct Base { 
-    Base* p; 
-  }; 
+  template <class T> struct Base {
+    Base* p;
+  };
 
-  template <class T> struct Derived: public Base<T> { 
-    typename Derived::Base* p; // meaning Derived::Base<T> 
+  template <class T> struct Derived: public Base<T> {
+    typename Derived::Base* p; // meaning Derived::Base<T>
   };
 
   Derived<int> di;
diff --git a/clang/test/SemaTemplate/dependent-template-recover.cpp b/clang/test/SemaTemplate/dependent-template-recover.cpp
index c7e27e8da25f1..c763989e6dadb 100644
--- a/clang/test/SemaTemplate/dependent-template-recover.cpp
+++ b/clang/test/SemaTemplate/dependent-template-recover.cpp
@@ -2,15 +2,15 @@
 template<typename T, typename U, int N>
 struct X {
   void f(T* t) {
-    t->f0<U>(); // expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}}
-    t->f0<int>(); // expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}}
+    t->f0<U>(); // expected-warning{{use 'template' keyword to treat 'f0' as a dependent template name}}
+    t->f0<int>(); // expected-warning{{use 'template' keyword to treat 'f0' as a dependent template name}}
 
-    t->operator+<U const, 1>(1); // expected-error{{use 'template' keyword to treat 'operator +' as a dependent template name}}
-    t->f1<int const, 2>(1); // expected-error{{use 'template' keyword to treat 'f1' as a dependent template name}}
+    t->operator+<U const, 1>(1); // expected-warning{{use 'template' keyword to treat 'operator +' as a dependent template name}}
+    t->f1<int const, 2>(1); // expected-warning{{use 'template' keyword to treat 'f1' as a dependent template name}}
     t->f1<3, int const>(1); // expected-error{{missing 'template' keyword prior to dependent template name 'f1'}}
 
-    T::getAs<U>(); // expected-error{{use 'template' keyword to treat 'getAs' as a dependent template name}}
-    t->T::getAs<U>(); // expected-error{{use 'template' keyword to treat 'getAs' as a dependent template name}}
+    T::getAs<U>(); // expected-warning{{use 'template' keyword to treat 'getAs' as a dependent template name}}
+    t->T::getAs<U>(); // expected-warning{{use 'template' keyword to treat 'getAs' as a dependent template name}}
 
     (*t).f2<N>(); // 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 ad73daa8e214c..7768d2f03ac5b 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<A{}>()) == 0);
 
   template<A a> struct Y { void f(); };
-  template<A a> void g(Y<a> y) { y.Y<a>::f(); }
+  template<A a> void g(Y<a> y) { y.template Y<a>::f(); }
   void h() { constexpr A a; g<a>(Y<a>{}); }
 
   template<A a> struct Z {
diff --git a/clang/test/SemaTemplate/template-id-expr.cpp b/clang/test/SemaTemplate/template-id-expr.cpp
index dc12823ae307f..760d6c5852403 100644
--- a/clang/test/SemaTemplate/template-id-expr.cpp
+++ b/clang/test/SemaTemplate/template-id-expr.cpp
@@ -19,7 +19,7 @@ template<typename T>
 struct X0 {
   template<typename U>
   void f1();
-  
+
   template<typename U>
   void f2(U) {
     f1<U>();
@@ -39,9 +39,9 @@ struct Y {
 template<int I>
 struct X {
   X(int, int);
-  void f() { 
-    Y<X<I> >(X<I>(0, 0)); 
-    Y<X<I> >(::X<I>(0, 0)); 
+  void f() {
+    Y<X<I> >(X<I>(0, 0));
+    Y<X<I> >(::X<I>(0, 0));
   }
 };
 
@@ -149,11 +149,11 @@ struct Y2 : Y1<T> {
 
     int x;
     x = Y1::f4(0);
-    x = Y1::f4<int>(0); // expected-error {{use 'template'}} expected-error {{assigning to 'int' from incompatible type 'void'}}
+    x = Y1::f4<int>(0); // expected-warning {{use 'template'}} expected-error {{assigning to 'int' from incompatible type 'void'}}
     x = Y1::template f4(0); // expected-error {{assigning to 'int' from incompatible type 'void'}} expected-error {{a template argument list is expected after a name prefixed by the template keyword}}
 
     x = p->f4(0);
-    x = p->f4<int>(0); // expected-error {{assigning to 'int' from incompatible type 'void'}} expected-error {{use 'template'}}
+    x = p->f4<int>(0); // expected-error {{assigning to 'int' from incompatible type 'void'}} expected-warning {{use 'template'}}
     x = p->template f4(0); // expected-error {{assigning to 'int' from incompatible type 'void'}} expected-error {{a template argument list is expected after a name prefixed by the template keyword}}
   }
 };
@@ -184,7 +184,7 @@ class E {
 #if __cplusplus <= 199711L
 // expected-warning at +2 {{extension}}
 #endif
-template<typename T> using D = int; // expected-note {{declared here}} 
+template<typename T> using D = int; // expected-note {{declared here}}
 E<D> 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 714830f0032d2..a9010969322e5 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<typename B::arg> x; // expected-error {{use 'template'}} expected-error {{refers to non-type}}
+      typedef typename B::X<typename B::arg> x; // expected-warning {{use 'template'}} expected-error {{refers to non-type}}
     };
   };
 
diff --git a/libcxx/include/regex b/libcxx/include/regex
index b814135121321..17ad0cf5b2aea 100644
--- a/libcxx/include/regex
+++ b/libcxx/include/regex
@@ -4214,7 +4214,7 @@ public:
   _LIBCPP_HIDE_FROM_ABI int compare(const value_type* __s) const { return str().compare(__s); }
 
   _LIBCPP_HIDE_FROM_ABI void swap(sub_match& __s) _NOEXCEPT_(__is_nothrow_swappable_v<_BidirectionalIterator>) {
-    this->pair<_BidirectionalIterator, _BidirectionalIterator>::swap(__s);
+    this->template pair<_BidirectionalIterator, _BidirectionalIterator>::swap(__s);
     std::swap(matched, __s.matched);
   }
 };

>From 160c464924326ab82caa59ce77491e67f1387e5c Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Thu, 18 Jul 2024 09:51:28 -0400
Subject: [PATCH 02/15] [FOLD] emit better missing 'template' keyword
 diagnostics for dependent class member access

---
 clang/include/clang/Parse/Parser.h |  2 +-
 clang/lib/Parse/ParseExpr.cpp      |  3 +--
 clang/lib/Parse/ParseExprCXX.cpp   |  8 ++++----
 clang/lib/Parse/ParseOpenMP.cpp    |  4 +---
 clang/lib/Parse/ParseTemplate.cpp  | 32 ++++++++++++++++++++++++++++++
 5 files changed, 39 insertions(+), 10 deletions(-)

diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 99e6d8a343469..66bd3790f9818 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1967,7 +1967,7 @@ class Parser : public CodeCompletionHandler {
   //===--------------------------------------------------------------------===//
   // C++ Expressions
   ExprResult tryParseCXXIdExpression(CXXScopeSpec &SS, bool isAddressOfOperand,
-                                     Token &Replacement);
+                                     Token *Replacement = nullptr);
 
   ExprResult tryParseCXXPackIndexingExpression(ExprResult PackIdExpression);
   ExprResult ParseCXXPackIndexingExpression(ExprResult PackIdExpression);
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 5953717e75d97..437b87acac35f 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -1163,8 +1163,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
   case tok::annot_non_type_dependent:
   case tok::annot_non_type_undeclared: {
     CXXScopeSpec SS;
-    Token Replacement;
-    Res = tryParseCXXIdExpression(SS, isAddressOfOperand, Replacement);
+    Res = tryParseCXXIdExpression(SS, isAddressOfOperand);
     assert(!Res.isUnset() &&
            "should not perform typo correction on annotation token");
     break;
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index ad0758a616380..cb607880eb860 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -603,7 +603,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
 
 ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS,
                                            bool isAddressOfOperand,
-                                           Token &Replacement) {
+                                           Token *Replacement) {
   ExprResult E;
 
   // We may have already annotated this id-expression.
@@ -657,7 +657,7 @@ ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS,
     E = Actions.ActOnIdExpression(
         getCurScope(), SS, TemplateKWLoc, Name, Tok.is(tok::l_paren),
         isAddressOfOperand, /*CCC=*/nullptr, /*IsInlineAsmIdentifier=*/false,
-        &Replacement);
+        Replacement);
     break;
   }
 
@@ -747,12 +747,12 @@ ExprResult Parser::ParseCXXIdExpression(bool isAddressOfOperand) {
 
   Token Replacement;
   ExprResult Result =
-      tryParseCXXIdExpression(SS, isAddressOfOperand, Replacement);
+      tryParseCXXIdExpression(SS, isAddressOfOperand, &Replacement);
   if (Result.isUnset()) {
     // If the ExprResult is valid but null, then typo correction suggested a
     // keyword replacement that needs to be reparsed.
     UnconsumeToken(Replacement);
-    Result = tryParseCXXIdExpression(SS, isAddressOfOperand, Replacement);
+    Result = tryParseCXXIdExpression(SS, isAddressOfOperand);
   }
   assert(!Result.isUnset() && "Typo correction suggested a keyword replacement "
                               "for a previous keyword suggestion");
diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp
index e975e96c5c7e4..94b966b5fa276 100644
--- a/clang/lib/Parse/ParseOpenMP.cpp
+++ b/clang/lib/Parse/ParseOpenMP.cpp
@@ -3016,12 +3016,10 @@ OMPClause *Parser::ParseOpenMPUsesAllocatorClause(OpenMPDirectiveKind DKind) {
   SmallVector<SemaOpenMP::UsesAllocatorsData, 4> Data;
   do {
     CXXScopeSpec SS;
-    Token Replacement;
     ExprResult Allocator =
         getLangOpts().CPlusPlus
             ? ParseCXXIdExpression()
-            : tryParseCXXIdExpression(SS, /*isAddressOfOperand=*/false,
-                                      Replacement);
+            : tryParseCXXIdExpression(SS, /*isAddressOfOperand=*/false);
     if (Allocator.isInvalid()) {
       SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openmp_end,
                 StopBeforeMatch);
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index a5130f56600e5..ac51052754c25 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -1802,6 +1802,38 @@ bool Parser::checkPotentialAngleBracketDelimiter(
     return true;
   }
 
+  if (OpToken.is(tok::greater) && Tok.is(tok::coloncolon)) {
+    SourceLocation StartLoc = Tok.getLocation();
+    CXXScopeSpec SS;
+    ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+                                   /*ObjectHasErrors=*/false,
+                                   /*EnteringContext=*/false);
+    ExprResult Result =
+        tryParseCXXIdExpression(SS, /*isAddressOfOperand=*/false);
+    bool Invalid = !Result.isUsable() || Result.get()->containsErrors();
+    Result = Actions.CorrectDelayedTyposInExpr(Result);
+
+    if (PP.isBacktrackEnabled())
+      PP.RevertCachedTokens(1);
+    else
+      PP.EnterToken(Tok, /*IsReinject=*/true);
+
+    SourceLocation EndLoc = Tok.getLocation();
+    Tok.setLocation(StartLoc);
+    Tok.setKind(tok::annot_primary_expr);
+    setExprAnnotation(Tok, Result);
+    Tok.setAnnotationEndLoc(EndLoc);
+    PP.AnnotateCachedTokens(Tok);
+
+    if (Invalid) {
+      Actions.diagnoseExprIntendedAsTemplateName(
+          getCurScope(), LAngle.TemplateName, LAngle.LessLoc,
+          OpToken.getLocation());
+      AngleBrackets.clear(*this);
+      return true;
+    }
+  }
+
   // After a '>' (etc), we're no longer potentially in a construct that's
   // intended to be treated as a template-id.
   if (OpToken.is(tok::greater) ||

>From 6fe4ee99712830f5f06690953fc1392351b5b693 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Thu, 18 Jul 2024 10:19:25 -0400
Subject: [PATCH 03/15] [FOLD] disable type correction when disambiguating

---
 clang/include/clang/Sema/Sema.h   | 26 ++++++++++++++++----------
 clang/lib/Parse/ParseTemplate.cpp |  6 +++---
 2 files changed, 19 insertions(+), 13 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 3e897aec55391..df07842dca573 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12103,25 +12103,31 @@ class Sema final : public SemaBase {
     }
   };
 
+  class DisableTypoCorrectionRAII {
+    Sema &SemaRef;
+    bool PrevDisableTypoCorrection;
+
+  public:
+    explicit DisableTypoCorrectionRAII(Sema &SemaRef)
+        : SemaRef(SemaRef), PrevDisableTypoCorrection(std::exchange(
+                                SemaRef.DisableTypoCorrection, true)) {}
+
+    ~DisableTypoCorrectionRAII() {
+      SemaRef.DisableTypoCorrection = PrevDisableTypoCorrection;
+    }
+  };
+
   /// RAII class used to indicate that we are performing provisional
   /// semantic analysis to determine the validity of a construct, so
   /// typo-correction and diagnostics in the immediate context (not within
   /// implicitly-instantiated templates) should be suppressed.
-  class TentativeAnalysisScope {
-    Sema &SemaRef;
+  class TentativeAnalysisScope : public DisableTypoCorrectionRAII {
     // FIXME: Using a SFINAETrap for this is a hack.
     SFINAETrap Trap;
-    bool PrevDisableTypoCorrection;
 
   public:
     explicit TentativeAnalysisScope(Sema &SemaRef)
-        : SemaRef(SemaRef), Trap(SemaRef, true),
-          PrevDisableTypoCorrection(SemaRef.DisableTypoCorrection) {
-      SemaRef.DisableTypoCorrection = true;
-    }
-    ~TentativeAnalysisScope() {
-      SemaRef.DisableTypoCorrection = PrevDisableTypoCorrection;
-    }
+        : DisableTypoCorrectionRAII(SemaRef), Trap(SemaRef, true) {}
   };
 
   /// For each declaration that involved template argument deduction, the
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index ac51052754c25..84ad04d6ca9f6 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -1803,6 +1803,8 @@ bool Parser::checkPotentialAngleBracketDelimiter(
   }
 
   if (OpToken.is(tok::greater) && Tok.is(tok::coloncolon)) {
+    Sema::DisableTypoCorrectionRAII DTC(Actions);
+
     SourceLocation StartLoc = Tok.getLocation();
     CXXScopeSpec SS;
     ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
@@ -1810,8 +1812,6 @@ bool Parser::checkPotentialAngleBracketDelimiter(
                                    /*EnteringContext=*/false);
     ExprResult Result =
         tryParseCXXIdExpression(SS, /*isAddressOfOperand=*/false);
-    bool Invalid = !Result.isUsable() || Result.get()->containsErrors();
-    Result = Actions.CorrectDelayedTyposInExpr(Result);
 
     if (PP.isBacktrackEnabled())
       PP.RevertCachedTokens(1);
@@ -1825,7 +1825,7 @@ bool Parser::checkPotentialAngleBracketDelimiter(
     Tok.setAnnotationEndLoc(EndLoc);
     PP.AnnotateCachedTokens(Tok);
 
-    if (Invalid) {
+    if (Result.isInvalid()) {
       Actions.diagnoseExprIntendedAsTemplateName(
           getCurScope(), LAngle.TemplateName, LAngle.LessLoc,
           OpToken.getLocation());

>From c7441a242fac30cdb1ebd87035c0558ec4e808ce Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Thu, 18 Jul 2024 12:00:11 -0400
Subject: [PATCH 04/15] [FOLD] only enable cwg1835 behavior in c++23 and up

---
 clang/lib/Sema/SemaTemplate.cpp               | 61 ++++++++++---------
 .../class.derived/class.member.lookup/p8.cpp  |  4 +-
 clang/test/CXX/drs/cwg13xx.cpp                | 13 ++--
 clang/test/CXX/drs/cwg1xx.cpp                 | 18 +++---
 clang/test/SemaCXX/pseudo-destructors.cpp     | 15 +++--
 5 files changed, 63 insertions(+), 48 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 91cc9881ceb8b..262618a0c6d27 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -410,6 +410,35 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
   LookupParsedName(Found, S, &SS, ObjectType,
                    /*AllowBuiltinCreation=*/false, EnteringContext);
 
+  bool ObjectTypeSearchedInScope = false;
+
+  // 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() &&
+      (!getLangOpts().CPlusPlus23 ||
+       !Found.wasNotFoundInCurrentInstantiation())) {
+    if (S) {
+      LookupName(Found, S);
+    } else if (!SS.getUnqualifiedLookups().empty()) {
+      Found.addAllDecls(SS.getUnqualifiedLookups());
+      Found.resolveKind();
+    }
+    ObjectTypeSearchedInScope = true;
+  }
+
   // 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
@@ -445,33 +474,6 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
   if (Found.wasNotFoundInCurrentInstantiation())
     return false;
 
-  bool ObjectTypeSearchedInScope = false;
-
-  // C++ [basic.lookup.qual.general]p2:
-  //   A member-qualified name is the (unique) component name, if any, of
-  //   - an unqualified-id or
-  //   - a nested-name-specifier of the form type-name :: or namespace-name ::
-  //   in the id-expression of a class member access expression.
-  //
-  // C++ [basic.lookup.qual.general]p3:
-  //   [...] If nothing is found by qualified lookup for a member-qualified
-  //   name that is the terminal name of a nested-name-specifier and is not
-  //   dependent, it undergoes unqualified lookup.
-  //
-  // In 'x.A::B::y', 'A' will undergo unqualified lookup if qualified lookup
-  // in the type of 'x' finds nothing. If the lookup context is dependent,
-  // we perform the unqualified lookup in the template definition context
-  // and store the results so we can replicate the lookup during instantiation.
-  if (MayBeNNS && Found.empty() && !ObjectType.isNull()) {
-    if (S) {
-      LookupName(Found, S);
-    } else if (!SS.getUnqualifiedLookups().empty()) {
-      Found.addAllDecls(SS.getUnqualifiedLookups());
-      Found.resolveKind();
-    }
-    ObjectTypeSearchedInScope = true;
-  }
-
   if (Found.isAmbiguous())
     return false;
 
@@ -542,7 +544,10 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
 
   NamedDecl *ExampleLookupResult =
       Found.empty() ? nullptr : Found.getRepresentativeDecl();
-  FilterAcceptableTemplateNames(Found);
+  FilterAcceptableTemplateNames(
+      Found,
+      /*AllowFunctionTemplates=*/getLangOpts().CPlusPlus23 ||
+          !ObjectTypeSearchedInScope);
   if (Found.empty()) {
     // If a 'template' keyword was used, a lookup that finds only non-template
     // names is an error.
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 97d3587881bbc..78e83c0ab4566 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<typename T>
 void DerivedT<T>::Inner() {
   Derived1T<T>::Foo();
   Derived2T<T>::Member = 42;
-  this->Derived1T<T>::Foo(); // expected-warning{{use 'template' keyword to treat 'Derived1T' as a dependent template name}}
-  this->Derived2T<T>::Member = 42; // expected-warning{{use 'template' keyword to treat 'Derived2T' as a dependent template name}}
+  this->Derived1T<T>::Foo();
+  this->Derived2T<T>::Member = 42;
   this->Foo(); // expected-error{{non-static member 'Foo' found in multiple base-class subobjects of type 'BaseT<int>'}}
 }
 
diff --git a/clang/test/CXX/drs/cwg13xx.cpp b/clang/test/CXX/drs/cwg13xx.cpp
index 416de7c536b1a..a19086b9dc90d 100644
--- a/clang/test/CXX/drs/cwg13xx.cpp
+++ b/clang/test/CXX/drs/cwg13xx.cpp
@@ -1,8 +1,8 @@
-// RUN: %clang_cc1 -std=c++98 %s -verify=expected,cxx98-14,cxx98 -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++11 %s -verify=expected,cxx11-20,cxx11-17,cxx11-14,cxx98-14,since-cxx11,cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++14 %s -verify=expected,cxx11-20,cxx11-17,cxx11-14,since-cxx14,cxx98-14,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++17 %s -verify=expected,cxx11-20,cxx11-17,since-cxx14,since-cxx17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++20 %s -verify=expected,cxx11-20,since-cxx14,since-cxx20,since-cxx17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++98 %s -verify=expected,cxx98-14,cxx98-20,cxx98 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++11 %s -verify=expected,cxx11-20,cxx11-17,cxx11-14,cxx98-14,cxx98-20,since-cxx11,cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++14 %s -verify=expected,cxx11-20,cxx11-17,cxx11-14,since-cxx14,cxx98-14,cxx98-20,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++17 %s -verify=expected,cxx11-20,cxx11-17,cxx98-20,since-cxx14,since-cxx17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++20 %s -verify=expected,cxx11-20,cxx98-20,since-cxx14,since-cxx20,since-cxx17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++23 %s -verify=expected,since-cxx14,since-cxx20,since-cxx17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++2c %s -verify=expected,since-cxx14,since-cxx20,since-cxx17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
 
@@ -136,6 +136,7 @@ namespace cwg1310 { // cwg1310: 5
     // expected-error at -1 {{ISO C++ specifies that qualified reference to 'W' is a constructor name rather than a template name in this context, despite preceding 'template' keyword}}
   }
   template<typename W>
+  // cxx98-20-note at -1 2{{declared as a non-template here}}
   void wt_test_good() {
     typename W::W::X w2ax;
     typename W::template W<int>::X w4x;
@@ -146,7 +147,9 @@ namespace cwg1310 { // cwg1310: 5
     (void)w.W::W::n;
     (void)w.W::template W<int>::n;
     (void)w.template W<int>::W::n;
+    // cxx98-20-error at -1 {{'W' following the 'template' keyword does not refer to a template}}
     (void)w.template W<int>::template W<int>::n;
+    // cxx98-20-error at -1 {{'W' following the 'template' keyword does not refer to a template}}
   }
   template void wt_test<W<int> >(); // #cwg1310-W-int
   template void wt_test_good<W<int> >();
diff --git a/clang/test/CXX/drs/cwg1xx.cpp b/clang/test/CXX/drs/cwg1xx.cpp
index 6bca460818425..86fe56a19b15f 100644
--- a/clang/test/CXX/drs/cwg1xx.cpp
+++ b/clang/test/CXX/drs/cwg1xx.cpp
@@ -1,9 +1,9 @@
-// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify=expected,cxx98,cxx98-11,cxx98-14,cxx98-17 -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,cxx98-11,cxx98-14,cxx98-17,cxx11-14 -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,cxx98-14,cxx98-17,cxx11-14 -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17,cxx98-17 -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify=expected,cxx98,cxx98-11,cxx98-14,cxx98-17,cxx98-20 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,cxx98-11,cxx98-14,cxx98-17,cxx98-20,cxx11-14 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,cxx98-14,cxx98-17,cxx98-20,cxx11-14 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17,cxx98-17,cxx98-20 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17,cxx98-20 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17,since-cxx23 -fexceptions -fcxx-exceptions -pedantic-errors
 
 #if __cplusplus == 199711L
 #define static_assert(...) __extension__ _Static_assert(__VA_ARGS__)
@@ -617,6 +617,8 @@ namespace cwg141 { // cwg141: 3.1
     //   expected-note@#cwg141-S {{'::cwg141::S<int>::n' declared here}}
     b.f<int>();
     // expected-error at -1 {{no member named 'f' in 'cwg141::B'}}
+    // cxx98-20-error at -2 {{expected '(' for function-style cast or type construction}}
+    // cxx98-20-error at -3 {{expected expression}}
     (void)b.S<int>::n;
   }
   template<typename T> struct C {
@@ -627,11 +629,11 @@ namespace cwg141 { // cwg141: 3.1
     }
     void h() {
       (void)t.S<int>::n;
-      // expected-error at -1 {{use 'template' keyword to treat 'S' as a dependent template name}}
+      // since-cxx23-error at -1 {{use 'template' keyword to treat 'S' as a dependent template name}}
     }
     void i() {
       (void)t.S<int>();
-      // expected-error at -1 {{use 'template' keyword to treat 'S' as a dependent template name}}
+      // since-cxx23-error at -1 {{use 'template' keyword to treat 'S' as a dependent template name}}
     }
   };
   void h() { C<B>().h(); } // ok
diff --git a/clang/test/SemaCXX/pseudo-destructors.cpp b/clang/test/SemaCXX/pseudo-destructors.cpp
index 44dc9ce8b1520..a630c54bd0bec 100644
--- a/clang/test/SemaCXX/pseudo-destructors.cpp
+++ b/clang/test/SemaCXX/pseudo-destructors.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -emit-llvm-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -emit-llvm-only -std=c++11 -verify=expected,cxx98-20  %s
+// RUN: %clang_cc1 -emit-llvm-only -std=c++23 -verify=expected,since-cxx23 %s
 struct A {};
 
 enum Foo { F };
@@ -148,12 +149,13 @@ namespace TwoPhaseLookup {
   namespace Template {
     template<typename T> struct Y {};
     template<class U> using G = Y<U>;
-    template<typename T> void f(T *p) { p->~G<int>(); } // expected-error {{no member named 'G'}}
+    template<typename T> void f(T *p) { p->~G<int>(); } // since-cxx23-error {{no member named 'G'}}
+                                                        // cxx98-20-error at -1 {{no member named '~Y' in 'TwoPhaseLookup::Template::N::G<int>'}}
     void h1(Y<int> *p) { p->~G<int>(); }
-    void h2(Y<int> *p) { f(p); } // expected-note {{instantiation of}}
+    void h2(Y<int> *p) { f(p); } // since-cxx23-note {{in instantiation of}}
     namespace N { template<typename T> struct G {}; }
     void h3(N::G<int> *p) { p->~G<int>(); }
-    void h4(N::G<int> *p) { f(p); }
+    void h4(N::G<int> *p) { f(p); } // cxx98-20-note {{in instantiation of}}
   }
 
   namespace TemplateUndeclared {
@@ -172,7 +174,10 @@ namespace TwoPhaseLookup {
 
   namespace TemplateNamesNonTemplate {
     int A; // expected-note 2{{non-template here}}
-    template<typename> int B; // expected-note 2{{variable template 'B' declared here}} expected-warning {{extension}}
+    template<typename> int B; // expected-note 2{{variable template 'B' declared here}}
+    #if __cplusplus < 201402L
+    // expected-warning at -2 {{extension}}
+    #endif
     using C = int; // expected-note 2{{non-template here}}
 
     template<typename T> void f1(int *p) { p->~A<int>(); } // expected-error {{'A' does not refer to a template}}

>From 236f28bdd16fca92a3774d104665f461aba2dded Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Thu, 18 Jul 2024 12:59:50 -0400
Subject: [PATCH 05/15] [FOLD] minor cleanup

---
 clang/lib/Parse/ParseExprCXX.cpp | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index cb607880eb860..8e8489461da2d 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -539,10 +539,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
         // 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)) &&
-            isTemplateArgumentList(1) == TPResult::False)
+        if (!IsTypename && (ObjectType || TNK == TNK_Undeclared_template) &&
+          isTemplateArgumentList(1) == TPResult::False)
           break;
 
         // We have found a template name, so annotate this token

>From d1d3f29f5158c3be48a522b1b1c421429280a20e Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Thu, 18 Jul 2024 16:51:49 -0400
Subject: [PATCH 06/15] [FOLD] don't annotate types as id-expressions

---
 clang/include/clang/Parse/Parser.h      |  1 +
 clang/lib/Parse/ParseTemplate.cpp       | 68 ++++++++++++++-----------
 clang/lib/Sema/SemaCXXScopeSpec.cpp     |  4 ++
 clang/test/SemaCXX/nested-name-spec.cpp |  8 +--
 4 files changed, 47 insertions(+), 34 deletions(-)

diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 66bd3790f9818..c02c9859b2cc5 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1907,6 +1907,7 @@ class Parser : public CodeCompletionHandler {
   }
 
   bool diagnoseUnknownTemplateId(ExprResult TemplateName, SourceLocation Less);
+  bool isMissingTemplateKeywordBeforeScope();
   void checkPotentialAngleBracket(ExprResult &PotentialTemplateName);
   bool checkPotentialAngleBracketDelimiter(const AngleBracketTracker::Loc &,
                                            const Token &OpToken);
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index 84ad04d6ca9f6..ac50cbbb56232 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -1779,6 +1779,36 @@ void Parser::checkPotentialAngleBracket(ExprResult &PotentialTemplateName) {
                     Priority);
 }
 
+bool Parser::isMissingTemplateKeywordBeforeScope() {
+  assert(Tok.is(tok::coloncolon));
+  Sema::DisableTypoCorrectionRAII DTC(Actions);
+  ColonProtectionRAIIObject ColonProtection(*this);
+
+  SourceLocation StartLoc = Tok.getLocation();
+  if (TryAnnotateTypeOrScopeToken())
+    return true;
+  if (Tok.isSimpleTypeSpecifier(getLangOpts()))
+    return false;
+  CXXScopeSpec SS;
+  ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
+                                 /*ObjectHasErrors=*/false,
+                                 /*EnteringContext=*/false);
+  ExprResult Result = tryParseCXXIdExpression(SS, /*isAddressOfOperand=*/false);
+
+  if (PP.isBacktrackEnabled())
+    PP.RevertCachedTokens(1);
+  else
+    PP.EnterToken(Tok, /*IsReinject=*/true);
+
+  SourceLocation EndLoc = Tok.getLocation();
+  Tok.setLocation(StartLoc);
+  Tok.setKind(tok::annot_primary_expr);
+  setExprAnnotation(Tok, Result);
+  Tok.setAnnotationEndLoc(EndLoc);
+  PP.AnnotateCachedTokens(Tok);
+  return Result.isInvalid();
+}
+
 bool Parser::checkPotentialAngleBracketDelimiter(
     const AngleBracketTracker::Loc &LAngle, const Token &OpToken) {
   // If a comma in an expression context is followed by a type that can be a
@@ -1802,36 +1832,14 @@ bool Parser::checkPotentialAngleBracketDelimiter(
     return true;
   }
 
-  if (OpToken.is(tok::greater) && Tok.is(tok::coloncolon)) {
-    Sema::DisableTypoCorrectionRAII DTC(Actions);
-
-    SourceLocation StartLoc = Tok.getLocation();
-    CXXScopeSpec SS;
-    ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
-                                   /*ObjectHasErrors=*/false,
-                                   /*EnteringContext=*/false);
-    ExprResult Result =
-        tryParseCXXIdExpression(SS, /*isAddressOfOperand=*/false);
-
-    if (PP.isBacktrackEnabled())
-      PP.RevertCachedTokens(1);
-    else
-      PP.EnterToken(Tok, /*IsReinject=*/true);
-
-    SourceLocation EndLoc = Tok.getLocation();
-    Tok.setLocation(StartLoc);
-    Tok.setKind(tok::annot_primary_expr);
-    setExprAnnotation(Tok, Result);
-    Tok.setAnnotationEndLoc(EndLoc);
-    PP.AnnotateCachedTokens(Tok);
-
-    if (Result.isInvalid()) {
-      Actions.diagnoseExprIntendedAsTemplateName(
-          getCurScope(), LAngle.TemplateName, LAngle.LessLoc,
-          OpToken.getLocation());
-      AngleBrackets.clear(*this);
-      return true;
-    }
+  if (OpToken.is(tok::greater) && Tok.is(tok::coloncolon) &&
+      !NextToken().isOneOf(tok::kw_new, tok::kw_delete) &&
+      isMissingTemplateKeywordBeforeScope()) {
+    Actions.diagnoseExprIntendedAsTemplateName(
+        getCurScope(), LAngle.TemplateName, LAngle.LessLoc,
+        OpToken.getLocation());
+    AngleBrackets.clear(*this);
+    return true;
   }
 
   // After a '>' (etc), we're no longer potentially in a construct that's
diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp
index dd61bb22e3dfa..8a74cd64be15d 100644
--- a/clang/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp
@@ -685,6 +685,7 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
   if (ErrorRecoveryLookup)
     return true;
 
+#if 0
   // If we didn't find anything during our lookup, try again with
   // ordinary name lookup, which can help us produce better error
   // messages.
@@ -692,6 +693,7 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
     Found.clear(LookupOrdinaryName);
     LookupName(Found, S);
   }
+#endif
 
   // In Microsoft mode, if we are within a templated function and we can't
   // resolve Identifier, then extend the SS with Identifier. This will have
@@ -733,6 +735,7 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
     }
   }
 
+#if 1
   if (!Found.empty()) {
     if (TypeDecl *TD = Found.getAsSingle<TypeDecl>()) {
       Diag(IdInfo.IdentifierLoc, diag::err_expected_class_or_namespace)
@@ -754,6 +757,7 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
   else
     Diag(IdInfo.IdentifierLoc, diag::err_undeclared_var_use)
         << IdInfo.Identifier;
+#endif
 
   return true;
 }
diff --git a/clang/test/SemaCXX/nested-name-spec.cpp b/clang/test/SemaCXX/nested-name-spec.cpp
index 920ef42bc1564..ce4af7a17c0ff 100644
--- a/clang/test/SemaCXX/nested-name-spec.cpp
+++ b/clang/test/SemaCXX/nested-name-spec.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -std=c++98 -verify -fblocks %s 
+// RUN: %clang_cc1 -fsyntax-only -std=c++98 -verify -fblocks %s
 namespace A {
   struct C {
     static int cx;
@@ -109,7 +109,7 @@ void A2::CC::NC::m(); // expected-error{{out-of-line declaration of a member mus
 
 namespace E {
   int X = 5;
-  
+
   namespace Nested {
     enum E {
       X = 0
@@ -143,7 +143,7 @@ Operators::operator bool() {
 
 namespace A {
   void g(int&); // expected-note{{type of 1st parameter of member declaration does not match definition ('int &' vs 'const int &')}}
-} 
+}
 
 void A::f() {} // expected-error-re{{out-of-line definition of 'f' does not match any declaration in namespace 'A'{{$}}}}
 
@@ -458,7 +458,7 @@ namespace A {
 class B {
   typedef C D; // expected-error{{unknown type name 'C'}}
   A::D::F;
-  // expected-error at -1{{'PR30619::A::B::D' (aka 'int') is not a class, namespace, or enumeration}}
+  // expected-error at -1{{no member named 'D' in namespace 'PR30619::A'}}
 };
 }
 }

>From 81a46e396789ee3a4a451f010a902916d0258a5f Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Fri, 19 Jul 2024 15:29:03 -0400
Subject: [PATCH 07/15] [FOLD] fix pre-c++23 compat

---
 clang/lib/Sema/SemaTemplate.cpp | 21 +++++++++++----------
 clang/test/CXX/drs/cwg13xx.cpp  | 13 +++++--------
 2 files changed, 16 insertions(+), 18 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 262618a0c6d27..17656c57bb986 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -410,6 +410,8 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
   LookupParsedName(Found, S, &SS, ObjectType,
                    /*AllowBuiltinCreation=*/false, EnteringContext);
 
+  bool IsDependent = Found.wasNotFoundInCurrentInstantiation();
+
   bool ObjectTypeSearchedInScope = false;
 
   // C++ [basic.lookup.qual.general]p2:
@@ -428,8 +430,7 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
   // 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() &&
-      (!getLangOpts().CPlusPlus23 ||
-       !Found.wasNotFoundInCurrentInstantiation())) {
+      (!getLangOpts().CPlusPlus23 || !IsDependent)) {
     if (S) {
       LookupName(Found, S);
     } else if (!SS.getUnqualifiedLookups().empty()) {
@@ -503,7 +504,7 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
     }
   }
 
-  if (Found.empty() && AllowTypoCorrection) {
+  if (Found.empty() && AllowTypoCorrection && !IsDependent) {
     // If we did not find any names, and this is not a disambiguation, attempt
     // to correct any typos.
     DeclarationName Name = Found.getLookupName();
@@ -544,14 +545,14 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
 
   NamedDecl *ExampleLookupResult =
       Found.empty() ? nullptr : Found.getRepresentativeDecl();
-  FilterAcceptableTemplateNames(
-      Found,
-      /*AllowFunctionTemplates=*/getLangOpts().CPlusPlus23 ||
-          !ObjectTypeSearchedInScope);
+  FilterAcceptableTemplateNames(Found, getLangOpts().CPlusPlus23 ||
+                                           !ObjectTypeSearchedInScope);
   if (Found.empty()) {
-    // If a 'template' keyword was used, a lookup that finds only non-template
-    // names is an error.
-    if (ExampleLookupResult && RequiredTemplate) {
+    if (IsDependent) {
+      Found.setNotFoundInCurrentInstantiation();
+    } else if (ExampleLookupResult && RequiredTemplate) {
+      // If a 'template' keyword was used, a lookup that finds only non-template
+      // names is an error.
       Diag(Found.getNameLoc(), diag::err_template_kw_refers_to_non_template)
           << Found.getLookupName() << SS.getRange()
           << RequiredTemplate.hasTemplateKeyword()
diff --git a/clang/test/CXX/drs/cwg13xx.cpp b/clang/test/CXX/drs/cwg13xx.cpp
index a19086b9dc90d..416de7c536b1a 100644
--- a/clang/test/CXX/drs/cwg13xx.cpp
+++ b/clang/test/CXX/drs/cwg13xx.cpp
@@ -1,8 +1,8 @@
-// RUN: %clang_cc1 -std=c++98 %s -verify=expected,cxx98-14,cxx98-20,cxx98 -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++11 %s -verify=expected,cxx11-20,cxx11-17,cxx11-14,cxx98-14,cxx98-20,since-cxx11,cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++14 %s -verify=expected,cxx11-20,cxx11-17,cxx11-14,since-cxx14,cxx98-14,cxx98-20,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++17 %s -verify=expected,cxx11-20,cxx11-17,cxx98-20,since-cxx14,since-cxx17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++20 %s -verify=expected,cxx11-20,cxx98-20,since-cxx14,since-cxx20,since-cxx17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++98 %s -verify=expected,cxx98-14,cxx98 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++11 %s -verify=expected,cxx11-20,cxx11-17,cxx11-14,cxx98-14,since-cxx11,cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++14 %s -verify=expected,cxx11-20,cxx11-17,cxx11-14,since-cxx14,cxx98-14,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++17 %s -verify=expected,cxx11-20,cxx11-17,since-cxx14,since-cxx17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++20 %s -verify=expected,cxx11-20,since-cxx14,since-cxx20,since-cxx17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++23 %s -verify=expected,since-cxx14,since-cxx20,since-cxx17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++2c %s -verify=expected,since-cxx14,since-cxx20,since-cxx17,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
 
@@ -136,7 +136,6 @@ namespace cwg1310 { // cwg1310: 5
     // expected-error at -1 {{ISO C++ specifies that qualified reference to 'W' is a constructor name rather than a template name in this context, despite preceding 'template' keyword}}
   }
   template<typename W>
-  // cxx98-20-note at -1 2{{declared as a non-template here}}
   void wt_test_good() {
     typename W::W::X w2ax;
     typename W::template W<int>::X w4x;
@@ -147,9 +146,7 @@ namespace cwg1310 { // cwg1310: 5
     (void)w.W::W::n;
     (void)w.W::template W<int>::n;
     (void)w.template W<int>::W::n;
-    // cxx98-20-error at -1 {{'W' following the 'template' keyword does not refer to a template}}
     (void)w.template W<int>::template W<int>::n;
-    // cxx98-20-error at -1 {{'W' following the 'template' keyword does not refer to a template}}
   }
   template void wt_test<W<int> >(); // #cwg1310-W-int
   template void wt_test_good<W<int> >();

>From a32df19340983eae3c01df692c7cbd740a7db271 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Mon, 22 Jul 2024 10:03:20 -0400
Subject: [PATCH 08/15] [FOLD] cleanups

---
 clang/lib/AST/ExprCXX.cpp                  |  5 ++---
 clang/lib/Parse/ParseExprCXX.cpp           | 15 ++++++++-------
 clang/lib/Sema/SemaExprMember.cpp          |  5 ++---
 clang/lib/Sema/SemaTemplate.cpp            |  1 -
 clang/test/SemaCXX/static-assert-cxx17.cpp |  2 +-
 5 files changed, 13 insertions(+), 15 deletions(-)

diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index e97c80141be0a..9a249a7d12ff0 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -1507,9 +1507,8 @@ CXXDependentScopeMemberExpr::CXXDependentScopeMemberExpr(
     new (getTrailingObjects<NestedNameSpecifierLoc>())
         NestedNameSpecifierLoc(QualifierLoc);
 
-  std::uninitialized_copy_n(UnqualifiedLookups.data(),
-                            UnqualifiedLookups.size(),
-                            getTrailingObjects<DeclAccessPair>());
+  std::uninitialized_copy(UnqualifiedLookups.begin(), UnqualifiedLookups.end(),
+                          getTrailingObjects<DeclAccessPair>());
 
   if (TemplateArgs) {
     auto Deps = TemplateArgumentDependence::None;
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 8e8489461da2d..95b4a08edafea 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -354,7 +354,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
       TemplateTy Template;
       TemplateNameKind TNK = Actions.ActOnTemplateName(
           getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType,
-          EnteringContext, Template, /*AllowInjectedClassName*/ true,
+          EnteringContext, Template, /*AllowInjectedClassName=*/true,
           /*MayBeNNS=*/true);
       if (AnnotateTemplateIdToken(Template, TNK, SS, TemplateKWLoc,
                                   TemplateName, false))
@@ -407,6 +407,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
                                       : TemplateId->TemplateNameLoc;
         SS.SetInvalid(SourceRange(StartLoc, CCLoc));
       }
+
       continue;
     }
 
@@ -540,7 +541,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
         // error recovery. But before we commit to this, check that we actually
         // have something that looks like a template-argument-list next.
         if (!IsTypename && (ObjectType || TNK == TNK_Undeclared_template) &&
-          isTemplateArgumentList(1) == TPResult::False)
+            isTemplateArgumentList(1) == TPResult::False)
           break;
 
         // We have found a template name, so annotate this token
@@ -652,10 +653,10 @@ ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS,
     if (isAddressOfOperand && isPostfixExpressionSuffixStart())
       isAddressOfOperand = false;
 
-    E = Actions.ActOnIdExpression(
-        getCurScope(), SS, TemplateKWLoc, Name, Tok.is(tok::l_paren),
-        isAddressOfOperand, /*CCC=*/nullptr, /*IsInlineAsmIdentifier=*/false,
-        Replacement);
+    E = Actions.ActOnIdExpression(getCurScope(), SS, TemplateKWLoc, Name,
+                                  Tok.is(tok::l_paren), isAddressOfOperand,
+                                  /*CCC=*/nullptr,
+                                  /*IsInlineAsmIdentifier=*/false, Replacement);
     break;
   }
 
@@ -1917,7 +1918,7 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc,
   // argument list. This affects examples such as
   //   void f(auto *p) { p->~X<int>(); }
   // ... but there's no ambiguity, and nowhere to write 'template' in such an
-  // example, so we accept it anyway
+  // example, so we accept it anyway.
   if (Tok.is(tok::less) && ParseUnqualifiedIdTemplateId(
                                SS, ObjectType, Base && Base->containsErrors(),
                                /*TemplateKWLoc=*/SourceLocation(), TildeLoc,
diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index 8519618bacfee..4750d5adc331d 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -1753,9 +1753,8 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base,
   // Decompose the name into its component parts.
   DeclarationNameInfo NameInfo;
   const TemplateArgumentListInfo *TemplateArgs;
-  DecomposeUnqualifiedId(Id, TemplateArgsBuffer,
-                         NameInfo, TemplateArgs);
-  bool IsArrow = OpKind == tok::arrow;
+  DecomposeUnqualifiedId(Id, TemplateArgsBuffer, NameInfo, TemplateArgs);
+  bool IsArrow = (OpKind == tok::arrow);
 
   if (getLangOpts().HLSL && IsArrow)
     return ExprError(Diag(OpLoc, diag::err_hlsl_operator_unsupported) << 2);
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 17656c57bb986..bad51237d5753 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -212,7 +212,6 @@ Sema::isTemplateName(Scope *S, CXXScopeSpec &SS, bool hasTemplateKeyword,
                          &AssumedTemplate,
                          /*AllowTypoCorrection=*/!Disambiguation, MayBeNNS))
     return TNK_Non_template;
-
   MemberOfUnknownSpecialization = R.wasNotFoundInCurrentInstantiation();
 
   if (AssumedTemplate != AssumedTemplateKind::None) {
diff --git a/clang/test/SemaCXX/static-assert-cxx17.cpp b/clang/test/SemaCXX/static-assert-cxx17.cpp
index 754f4ae5f1d38..41a7b025d0eb7 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 at -1{{static assertion failed due to requirement 'static_cast<const X<int> *>(nullptr)'}}
   static_assert((const X<typename T::T>[]){} == nullptr);
   // expected-error at -1{{static assertion failed due to requirement '(const X<int>[0]){} == nullptr'}}
-  static_assert(sizeof(X<decltype(X<typename T::T>().template X<typename T::T>::~X())>) == 0);
+  static_assert(sizeof(X<decltype(X<typename T::T>().X<typename T::T>::~X())>) == 0);
   // expected-error at -1{{static assertion failed due to requirement 'sizeof(X<void>) == 0'}} \
   // expected-note at -1 {{evaluates to '8 == 0'}}
   static_assert(constexpr_return_false<typename T::T, typename T::U>());

>From fb25bb5fe2f193fe4117ee647c85b532dbc18a02 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Tue, 23 Jul 2024 11:38:35 -0400
Subject: [PATCH 09/15] [FOLD] correct annotation end loc?

---
 clang/lib/Parse/ParseTemplate.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index ac50cbbb56232..fb66d77f552b2 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -1800,11 +1800,10 @@ bool Parser::isMissingTemplateKeywordBeforeScope() {
   else
     PP.EnterToken(Tok, /*IsReinject=*/true);
 
-  SourceLocation EndLoc = Tok.getLocation();
-  Tok.setLocation(StartLoc);
   Tok.setKind(tok::annot_primary_expr);
   setExprAnnotation(Tok, Result);
-  Tok.setAnnotationEndLoc(EndLoc);
+  Tok.setAnnotationEndLoc(Tok.getLocation());
+  Tok.setLocation(StartLoc);
   PP.AnnotateCachedTokens(Tok);
   return Result.isInvalid();
 }

>From 3554a8363a7d26c8401755d9c597129c8a6673a6 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Fri, 26 Jul 2024 08:39:01 -0400
Subject: [PATCH 10/15] [WIP] convert more missing 'template' errors to
 warnings

---
 clang/include/clang/Parse/Parser.h |  2 +-
 clang/lib/Parse/ParseTemplate.cpp  | 17 +++++++---
 clang/lib/Parse/ParseTentative.cpp | 50 ++++++++++++++++++++++++++++++
 3 files changed, 63 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index c02c9859b2cc5..4915bbfb8beec 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1907,7 +1907,7 @@ class Parser : public CodeCompletionHandler {
   }
 
   bool diagnoseUnknownTemplateId(ExprResult TemplateName, SourceLocation Less);
-  bool isMissingTemplateKeywordBeforeScope();
+  bool isMissingTemplateKeywordBeforeScope(bool AnnotateInvalid);
   void checkPotentialAngleBracket(ExprResult &PotentialTemplateName);
   bool checkPotentialAngleBracketDelimiter(const AngleBracketTracker::Loc &,
                                            const Token &OpToken);
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index fb66d77f552b2..a4f9b987cde68 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -1779,7 +1779,7 @@ void Parser::checkPotentialAngleBracket(ExprResult &PotentialTemplateName) {
                     Priority);
 }
 
-bool Parser::isMissingTemplateKeywordBeforeScope() {
+bool Parser::isMissingTemplateKeywordBeforeScope(bool AnnotateInvalid) {
   assert(Tok.is(tok::coloncolon));
   Sema::DisableTypoCorrectionRAII DTC(Actions);
   ColonProtectionRAIIObject ColonProtection(*this);
@@ -1795,14 +1795,21 @@ bool Parser::isMissingTemplateKeywordBeforeScope() {
                                  /*EnteringContext=*/false);
   ExprResult Result = tryParseCXXIdExpression(SS, /*isAddressOfOperand=*/false);
 
-  if (PP.isBacktrackEnabled())
+  if (!AnnotateInvalid && Result.isInvalid())
+    return true;
+
+  SourceLocation EndLoc = Tok.getLocation();
+  if (PP.isBacktrackEnabled()) {
     PP.RevertCachedTokens(1);
-  else
+    if (Result.isInvalid())
+      EndLoc = PP.getLastCachedTokenLocation();
+  } else {
     PP.EnterToken(Tok, /*IsReinject=*/true);
+  }
 
   Tok.setKind(tok::annot_primary_expr);
   setExprAnnotation(Tok, Result);
-  Tok.setAnnotationEndLoc(Tok.getLocation());
+  Tok.setAnnotationEndLoc(EndLoc);
   Tok.setLocation(StartLoc);
   PP.AnnotateCachedTokens(Tok);
   return Result.isInvalid();
@@ -1833,7 +1840,7 @@ bool Parser::checkPotentialAngleBracketDelimiter(
 
   if (OpToken.is(tok::greater) && Tok.is(tok::coloncolon) &&
       !NextToken().isOneOf(tok::kw_new, tok::kw_delete) &&
-      isMissingTemplateKeywordBeforeScope()) {
+      isMissingTemplateKeywordBeforeScope(/*AnnotateInvalid=*/true)) {
     Actions.diagnoseExprIntendedAsTemplateName(
         getCurScope(), LAngle.TemplateName, LAngle.LessLoc,
         OpToken.getLocation());
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 0142271b8e6d1..752469705a73f 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -2320,6 +2320,56 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) {
   if (!TryConsumeToken(tok::less))
     return TPResult::False;
 
+  while (true) {
+    // An expression cannot be followed by a braced-init-list unless
+    // its the right operand of an assignment operator.
+    if (Tok.is(tok::l_brace))
+      return TPResult::True;
+
+    bool InvalidAsTemplateArgumentList = false;
+    if (isCXXDeclarationSpecifier(ImplicitTypenameContext::No, TPResult::False,
+                                  &InvalidAsTemplateArgumentList) ==
+        TPResult::True)
+      return TPResult::True;
+    if (InvalidAsTemplateArgumentList)
+      return TPResult::False;
+
+    if (Tok.is(tok::l_paren)) {
+      // Skip the parens.
+      ConsumeParen();
+      if (!SkipUntil(tok::r_paren, StopAtSemi))
+        return TPResult::Error;
+    } else if (MightBeCXXScopeToken()) {
+      if (TryAnnotateCXXScopeToken())
+        return TPResult::Error;
+    }
+
+    if (!SkipUntil({tok::comma, tok::less,
+        tok::greater, tok::greatergreater, tok::greatergreatergreater},
+        StopAtSemi | StopBeforeMatch))
+      return TPResult::False;
+
+    if (Tok.isNot(tok::comma)) {
+      if (Tok.is(tok::less))
+        break;
+      if (TryConsumeToken(tok::greater) && Tok.is(tok::coloncolon)) {
+        TentativeParsingAction TPA(*this, /*Unannotated=*/true);
+        if (isMissingTemplateKeywordBeforeScope(/*AnnotateInvalid=*/false)) {
+          TPA.Revert();
+          return TPResult::True;
+        }
+        TPA.Commit();
+      }
+      #if 0
+      if (TryConsumeToken(tok::greater) && Tok.is(tok::coloncolon) &&
+          isMissingTemplateKeywordBeforeScope(/*AnnotateInvalid=*/false))
+        return TPResult::True;
+      #endif
+      return TPResult::Ambiguous;
+    }
+    ConsumeToken();
+  }
+
   // We can't do much to tell an expression apart from a template-argument,
   // but one good distinguishing factor is that a "decl-specifier" not
   // followed by '(' or '{' can't appear in an expression.

>From 41a3a6f2133f4eefb509783e2e7686ee45e02fc2 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Fri, 26 Jul 2024 12:39:08 -0400
Subject: [PATCH 11/15] [FOLD] improve detection of missing template

---
 clang/include/clang/Parse/Parser.h |  2 +-
 clang/lib/Parse/ParseExprCXX.cpp   |  8 ++++----
 clang/lib/Parse/ParseTemplate.cpp  |  9 ++++++---
 clang/lib/Parse/ParseTentative.cpp | 26 ++++++++++----------------
 clang/lib/Parse/Parser.cpp         |  4 ++--
 5 files changed, 23 insertions(+), 26 deletions(-)

diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 4915bbfb8beec..8b3953e092cc3 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -2743,7 +2743,7 @@ class Parser : public CodeCompletionHandler {
   /// Determine whether the current token sequence might be
   ///   '<' template-argument-list '>'
   /// rather than a less-than expression.
-  TPResult isTemplateArgumentList(unsigned TokensToSkip);
+  TPResult isTemplateArgumentList(unsigned TokensToSkip, TemplateNameKind TNK);
 
   /// Determine whether an '(' after an 'explicit' keyword is part of a C++20
   /// 'explicit(bool)' declaration, in earlier language modes where that is an
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 95b4a08edafea..f5a6520610075 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -541,7 +541,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
         // error recovery. But before we commit to this, check that we actually
         // have something that looks like a template-argument-list next.
         if (!IsTypename && (ObjectType || TNK == TNK_Undeclared_template) &&
-            isTemplateArgumentList(1) == TPResult::False)
+            isTemplateArgumentList(1, TNK) == TPResult::False)
           break;
 
         // We have found a template name, so annotate this token
@@ -560,7 +560,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
 
       if (MemberOfUnknownSpecialization && !Disambiguation &&
           (ObjectType || SS.isSet()) &&
-          (IsTypename || isTemplateArgumentList(1) == TPResult::True)) {
+          (IsTypename || isTemplateArgumentList(1, TNK_Non_template) == TPResult::True)) {
         // If we had errors before, ObjectType can be dependent even without any
         // templates. Do not report missing template keyword in that case.
         if (!ObjectHadErrors) {
@@ -2558,11 +2558,11 @@ bool Parser::ParseUnqualifiedIdTemplateId(
       // name, double-check that makes sense syntactically before committing
       // to it.
       if (TNK == TNK_Undeclared_template &&
-          isTemplateArgumentList(0) == TPResult::False)
+          isTemplateArgumentList(0, TNK) == TPResult::False)
         return false;
 
       if (TNK == TNK_Non_template && MemberOfUnknownSpecialization &&
-          ObjectType && isTemplateArgumentList(0) == TPResult::True) {
+          ObjectType && isTemplateArgumentList(0, TNK) == TPResult::True) {
         // If we had errors before, ObjectType can be dependent even without any
         // templates, do not report missing template keyword in that case.
         if (!ObjectHadErrors) {
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index a4f9b987cde68..6e2f8faad42e6 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -1783,6 +1783,9 @@ bool Parser::isMissingTemplateKeywordBeforeScope(bool AnnotateInvalid) {
   assert(Tok.is(tok::coloncolon));
   Sema::DisableTypoCorrectionRAII DTC(Actions);
   ColonProtectionRAIIObject ColonProtection(*this);
+  std::optional<Sema::TentativeAnalysisScope> TAS;
+  if (!AnnotateInvalid)
+    TAS.emplace(Actions);
 
   SourceLocation StartLoc = Tok.getLocation();
   if (TryAnnotateTypeOrScopeToken())
@@ -1801,16 +1804,16 @@ bool Parser::isMissingTemplateKeywordBeforeScope(bool AnnotateInvalid) {
   SourceLocation EndLoc = Tok.getLocation();
   if (PP.isBacktrackEnabled()) {
     PP.RevertCachedTokens(1);
-    if (Result.isInvalid())
-      EndLoc = PP.getLastCachedTokenLocation();
+    // if (Result.isInvalid())
+    EndLoc = PP.getLastCachedTokenLocation();
   } else {
     PP.EnterToken(Tok, /*IsReinject=*/true);
   }
 
   Tok.setKind(tok::annot_primary_expr);
   setExprAnnotation(Tok, Result);
-  Tok.setAnnotationEndLoc(EndLoc);
   Tok.setLocation(StartLoc);
+  Tok.setAnnotationEndLoc(EndLoc);
   PP.AnnotateCachedTokens(Tok);
   return Result.isInvalid();
 }
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 752469705a73f..f6e7ffae033f3 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -2302,7 +2302,7 @@ Parser::TPResult Parser::TryParseBracketDeclarator() {
 /// of a template-id or simple-template-id, rather than a less-than comparison.
 /// This will often fail and produce an ambiguity, but should never be wrong
 /// if it returns True or False.
-Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) {
+Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip, TemplateNameKind TNK) {
   if (!TokensToSkip) {
     if (Tok.isNot(tok::less))
       return TPResult::False;
@@ -2334,15 +2334,11 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) {
     if (InvalidAsTemplateArgumentList)
       return TPResult::False;
 
-    if (Tok.is(tok::l_paren)) {
-      // Skip the parens.
-      ConsumeParen();
-      if (!SkipUntil(tok::r_paren, StopAtSemi))
-        return TPResult::Error;
-    } else if (MightBeCXXScopeToken()) {
-      if (TryAnnotateCXXScopeToken())
-        return TPResult::Error;
-    }
+    if (!GreaterThanIsOperator || TNK != TNK_Non_template)
+      break;
+
+    if (TryAnnotateOptionalCXXScopeToken())
+      return TPResult::Error;
 
     if (!SkipUntil({tok::comma, tok::less,
         tok::greater, tok::greatergreater, tok::greatergreatergreater},
@@ -2352,7 +2348,8 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) {
     if (Tok.isNot(tok::comma)) {
       if (Tok.is(tok::less))
         break;
-      if (TryConsumeToken(tok::greater) && Tok.is(tok::coloncolon)) {
+      if (TryConsumeToken(tok::greater) && Tok.is(tok::coloncolon) &&
+          !NextToken().isOneOf(tok::kw_new, tok::kw_delete)) {
         TentativeParsingAction TPA(*this, /*Unannotated=*/true);
         if (isMissingTemplateKeywordBeforeScope(/*AnnotateInvalid=*/false)) {
           TPA.Revert();
@@ -2360,16 +2357,12 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) {
         }
         TPA.Commit();
       }
-      #if 0
-      if (TryConsumeToken(tok::greater) && Tok.is(tok::coloncolon) &&
-          isMissingTemplateKeywordBeforeScope(/*AnnotateInvalid=*/false))
-        return TPResult::True;
-      #endif
       return TPResult::Ambiguous;
     }
     ConsumeToken();
   }
 
+  #if 0
   // We can't do much to tell an expression apart from a template-argument,
   // but one good distinguishing factor is that a "decl-specifier" not
   // followed by '(' or '{' can't appear in an expression.
@@ -2380,6 +2373,7 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) {
     return TPResult::True;
   if (InvalidAsTemplateArgumentList)
     return TPResult::False;
+  #endif
 
   // FIXME: In many contexts, X<thing1, Type> can only be a
   // template-argument-list. But that's not true in general:
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 5ebe71e496a2e..22450fe6a6339 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -1826,7 +1826,7 @@ Parser::TryAnnotateName(CorrectionCandidateCallback *CCC,
   // we interpret this as a template-id if it can be, but if it can't be, then
   // this is an error recovery case.
   if (Classification.getKind() == Sema::NC_UndeclaredTemplate &&
-      isTemplateArgumentList(1) == TPResult::False) {
+      isTemplateArgumentList(1, TNK_Undeclared_template) == TPResult::False) {
     // It's not a template-id; re-classify without the '<' as a hint.
     Token FakeNext = Next;
     FakeNext.setKind(tok::unknown);
@@ -2195,7 +2195,7 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(
         // Only annotate an undeclared template name as a template-id if the
         // following tokens have the form of a template argument list.
         if (TNK != TNK_Undeclared_template ||
-            isTemplateArgumentList(1) != TPResult::False) {
+            isTemplateArgumentList(1, TNK) != TPResult::False) {
           // Consume the identifier.
           ConsumeToken();
           if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(),

>From 86973a89f835111ff263009c1d301c5b351fba6a Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Fri, 26 Jul 2024 16:02:09 -0400
Subject: [PATCH 12/15] [FOLD] correctly handle nested template argument lists

---
 clang/lib/Parse/ParseTentative.cpp | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index f6e7ffae033f3..7c71c09918ff3 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -13,6 +13,7 @@
 
 #include "clang/Parse/Parser.h"
 #include "clang/Parse/ParseDiagnostic.h"
+#include "clang/Parse/RAIIObjectsForParser.h"
 #include "clang/Sema/ParsedTemplate.h"
 using namespace clang;
 
@@ -2320,6 +2321,9 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip, TemplateN
   if (!TryConsumeToken(tok::less))
     return TPResult::False;
 
+  bool IsNestedTemplateArgumentList = !GreaterThanIsOperator;
+  GreaterThanIsOperatorScope G(GreaterThanIsOperator, false);
+
   while (true) {
     // An expression cannot be followed by a braced-init-list unless
     // its the right operand of an assignment operator.
@@ -2334,7 +2338,7 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip, TemplateN
     if (InvalidAsTemplateArgumentList)
       return TPResult::False;
 
-    if (!GreaterThanIsOperator || TNK != TNK_Non_template)
+    if (IsNestedTemplateArgumentList || TNK != TNK_Non_template)
       break;
 
     if (TryAnnotateOptionalCXXScopeToken())

>From a8729dde36cbce79418b9e8559671ae19ca931c5 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Fri, 26 Jul 2024 16:25:34 -0400
Subject: [PATCH 13/15] [FOLD] apply cwg1835 in all c++ language modes

---
 clang/lib/Sema/SemaTemplate.cpp               | 66 +++++++++----------
 .../class.derived/class.member.lookup/p8.cpp  |  4 +-
 clang/test/CXX/drs/cwg1xx.cpp                 | 18 +++--
 clang/test/SemaCXX/pseudo-destructors.cpp     | 10 ++-
 clang/test/SemaCXX/static-assert-cxx17.cpp    |  3 +-
 .../dependent-template-recover.cpp            |  8 ++-
 6 files changed, 51 insertions(+), 58 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index bad51237d5753..ea615aa18c2e2 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -409,36 +409,6 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
   LookupParsedName(Found, S, &SS, ObjectType,
                    /*AllowBuiltinCreation=*/false, EnteringContext);
 
-  bool IsDependent = Found.wasNotFoundInCurrentInstantiation();
-
-  bool ObjectTypeSearchedInScope = false;
-
-  // 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() &&
-      (!getLangOpts().CPlusPlus23 || !IsDependent)) {
-    if (S) {
-      LookupName(Found, S);
-    } else if (!SS.getUnqualifiedLookups().empty()) {
-      Found.addAllDecls(SS.getUnqualifiedLookups());
-      Found.resolveKind();
-    }
-    ObjectTypeSearchedInScope = true;
-  }
-
   // 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
@@ -474,6 +444,33 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
   if (Found.wasNotFoundInCurrentInstantiation())
     return false;
 
+  bool ObjectTypeSearchedInScope = false;
+
+  // C++ [basic.lookup.qual.general]p2:
+  //   A member-qualified name is the (unique) component name, if any, of
+  //   - an unqualified-id or
+  //   - a nested-name-specifier of the form type-name :: or namespace-name ::
+  //   in the id-expression of a class member access expression.
+  //
+  // C++ [basic.lookup.qual.general]p3:
+  //   [...] If nothing is found by qualified lookup for a member-qualified
+  //   name that is the terminal name of a nested-name-specifier and is not
+  //   dependent, it undergoes unqualified lookup.
+  //
+  // In 'x.A::B::y', 'A' will undergo unqualified lookup if qualified lookup
+  // in the type of 'x' finds nothing. If the lookup context is dependent,
+  // we perform the unqualified lookup in the template definition context
+  // and store the results so we can replicate the lookup during instantiation.
+  if (MayBeNNS && Found.empty() && !ObjectType.isNull()) {
+    if (S) {
+      LookupName(Found, S);
+    } else if (!SS.getUnqualifiedLookups().empty()) {
+      Found.addAllDecls(SS.getUnqualifiedLookups());
+      Found.resolveKind();
+    }
+    ObjectTypeSearchedInScope = true;
+  }
+
   if (Found.isAmbiguous())
     return false;
 
@@ -503,7 +500,7 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
     }
   }
 
-  if (Found.empty() && AllowTypoCorrection && !IsDependent) {
+  if (Found.empty() && AllowTypoCorrection) {
     // If we did not find any names, and this is not a disambiguation, attempt
     // to correct any typos.
     DeclarationName Name = Found.getLookupName();
@@ -544,12 +541,9 @@ bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS,
 
   NamedDecl *ExampleLookupResult =
       Found.empty() ? nullptr : Found.getRepresentativeDecl();
-  FilterAcceptableTemplateNames(Found, getLangOpts().CPlusPlus23 ||
-                                           !ObjectTypeSearchedInScope);
+  FilterAcceptableTemplateNames(Found);
   if (Found.empty()) {
-    if (IsDependent) {
-      Found.setNotFoundInCurrentInstantiation();
-    } else if (ExampleLookupResult && RequiredTemplate) {
+    if (ExampleLookupResult && RequiredTemplate) {
       // If a 'template' keyword was used, a lookup that finds only non-template
       // names is an error.
       Diag(Found.getNameLoc(), diag::err_template_kw_refers_to_non_template)
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 78e83c0ab4566..19fc0644be1b0 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<typename T>
 void DerivedT<T>::Inner() {
   Derived1T<T>::Foo();
   Derived2T<T>::Member = 42;
-  this->Derived1T<T>::Foo();
-  this->Derived2T<T>::Member = 42;
+  this->Derived1T<T>::Foo(); // expected-warning {{use 'template' keyword to treat 'Derived1T' as a dependent template name}}
+  this->Derived2T<T>::Member = 42; // expected-warning {{use 'template' keyword to treat 'Derived2T' as a dependent template name}}
   this->Foo(); // expected-error{{non-static member 'Foo' found in multiple base-class subobjects of type 'BaseT<int>'}}
 }
 
diff --git a/clang/test/CXX/drs/cwg1xx.cpp b/clang/test/CXX/drs/cwg1xx.cpp
index 86fe56a19b15f..6bca460818425 100644
--- a/clang/test/CXX/drs/cwg1xx.cpp
+++ b/clang/test/CXX/drs/cwg1xx.cpp
@@ -1,9 +1,9 @@
-// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify=expected,cxx98,cxx98-11,cxx98-14,cxx98-17,cxx98-20 -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,cxx98-11,cxx98-14,cxx98-17,cxx98-20,cxx11-14 -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,cxx98-14,cxx98-17,cxx98-20,cxx11-14 -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17,cxx98-17,cxx98-20 -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17,cxx98-20 -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17,since-cxx23 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify=expected,cxx98,cxx98-11,cxx98-14,cxx98-17 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,cxx98-11,cxx98-14,cxx98-17,cxx11-14 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,cxx98-14,cxx98-17,cxx11-14 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17,cxx98-17 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors
 
 #if __cplusplus == 199711L
 #define static_assert(...) __extension__ _Static_assert(__VA_ARGS__)
@@ -617,8 +617,6 @@ namespace cwg141 { // cwg141: 3.1
     //   expected-note@#cwg141-S {{'::cwg141::S<int>::n' declared here}}
     b.f<int>();
     // expected-error at -1 {{no member named 'f' in 'cwg141::B'}}
-    // cxx98-20-error at -2 {{expected '(' for function-style cast or type construction}}
-    // cxx98-20-error at -3 {{expected expression}}
     (void)b.S<int>::n;
   }
   template<typename T> struct C {
@@ -629,11 +627,11 @@ namespace cwg141 { // cwg141: 3.1
     }
     void h() {
       (void)t.S<int>::n;
-      // since-cxx23-error at -1 {{use 'template' keyword to treat 'S' as a dependent template name}}
+      // expected-error at -1 {{use 'template' keyword to treat 'S' as a dependent template name}}
     }
     void i() {
       (void)t.S<int>();
-      // since-cxx23-error at -1 {{use 'template' keyword to treat 'S' as a dependent template name}}
+      // expected-error at -1 {{use 'template' keyword to treat 'S' as a dependent template name}}
     }
   };
   void h() { C<B>().h(); } // ok
diff --git a/clang/test/SemaCXX/pseudo-destructors.cpp b/clang/test/SemaCXX/pseudo-destructors.cpp
index a630c54bd0bec..c474a2c4351b8 100644
--- a/clang/test/SemaCXX/pseudo-destructors.cpp
+++ b/clang/test/SemaCXX/pseudo-destructors.cpp
@@ -1,5 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm-only -std=c++11 -verify=expected,cxx98-20  %s
-// RUN: %clang_cc1 -emit-llvm-only -std=c++23 -verify=expected,since-cxx23 %s
+// RUN: %clang_cc1 -emit-llvm-only -verify -std=c++11 %s
 struct A {};
 
 enum Foo { F };
@@ -149,13 +148,12 @@ namespace TwoPhaseLookup {
   namespace Template {
     template<typename T> struct Y {};
     template<class U> using G = Y<U>;
-    template<typename T> void f(T *p) { p->~G<int>(); } // since-cxx23-error {{no member named 'G'}}
-                                                        // cxx98-20-error at -1 {{no member named '~Y' in 'TwoPhaseLookup::Template::N::G<int>'}}
+    template<typename T> void f(T *p) { p->~G<int>(); } // expected-error {{no member named 'G'}}
     void h1(Y<int> *p) { p->~G<int>(); }
-    void h2(Y<int> *p) { f(p); } // since-cxx23-note {{in instantiation of}}
+    void h2(Y<int> *p) { f(p); } // expected-note {{in instantiation of}}
     namespace N { template<typename T> struct G {}; }
     void h3(N::G<int> *p) { p->~G<int>(); }
-    void h4(N::G<int> *p) { f(p); } // cxx98-20-note {{in instantiation of}}
+    void h4(N::G<int> *p) { f(p); }
   }
 
   namespace TemplateUndeclared {
diff --git a/clang/test/SemaCXX/static-assert-cxx17.cpp b/clang/test/SemaCXX/static-assert-cxx17.cpp
index 41a7b025d0eb7..2ac52a767773f 100644
--- a/clang/test/SemaCXX/static-assert-cxx17.cpp
+++ b/clang/test/SemaCXX/static-assert-cxx17.cpp
@@ -98,7 +98,8 @@ void foo6() {
   // expected-error at -1{{static assertion failed due to requirement '(const X<int>[0]){} == nullptr'}}
   static_assert(sizeof(X<decltype(X<typename T::T>().X<typename T::T>::~X())>) == 0);
   // expected-error at -1{{static assertion failed due to requirement 'sizeof(X<void>) == 0'}} \
-  // expected-note at -1 {{evaluates to '8 == 0'}}
+  // expected-note at -1 {{evaluates to '8 == 0'}} \
+  // expected-warning at -1 {{use 'template' keyword to treat 'X' as a dependent template name}}
   static_assert(constexpr_return_false<typename T::T, typename T::U>());
   // expected-error at -1{{static assertion failed due to requirement 'constexpr_return_false<int, float>()'}}
 }
diff --git a/clang/test/SemaTemplate/dependent-template-recover.cpp b/clang/test/SemaTemplate/dependent-template-recover.cpp
index c763989e6dadb..1a41e5e9ac79f 100644
--- a/clang/test/SemaTemplate/dependent-template-recover.cpp
+++ b/clang/test/SemaTemplate/dependent-template-recover.cpp
@@ -7,7 +7,7 @@ struct X {
 
     t->operator+<U const, 1>(1); // expected-warning{{use 'template' keyword to treat 'operator +' as a dependent template name}}
     t->f1<int const, 2>(1); // expected-warning{{use 'template' keyword to treat 'f1' as a dependent template name}}
-    t->f1<3, int const>(1); // expected-error{{missing 'template' keyword prior to dependent template name 'f1'}}
+    t->f1<3, int const>(1); // expected-warning{{use 'template' keyword to treat 'f1' as a dependent template name}}
 
     T::getAs<U>(); // expected-warning{{use 'template' keyword to treat 'getAs' as a dependent template name}}
     t->T::getAs<U>(); // expected-warning{{use 'template' keyword to treat 'getAs' as a dependent template name}}
@@ -15,7 +15,7 @@ struct X {
     (*t).f2<N>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f2'}}
     (*t).f2<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f2'}}
     T::f2<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f2'}}
-    T::f2<0, int>(0); // expected-error{{missing 'template' keyword prior to dependent template name 'f2'}}
+    T::f2<0, int>(0); // expected-warning{{use 'template' keyword to treat 'f2' as a dependent template name}}
 
     T::foo<N < 2 || N >= 4>(); // expected-error{{missing 'template' keyword prior to dependent template name 'foo'}}
 
@@ -83,12 +83,14 @@ template<int N, typename T> void f(T t) {
   T::g<mb>(0);
 
   // ... but this one must be a template-id.
-  T::g<mb, int>(0); // expected-error {{missing 'template' keyword prior to dependent template name 'g'}}
+  T::g<mb, int>(0); // expected-warning {{use 'template' keyword to treat 'g' as a dependent template name}}
+                    // expected-error at -1 {{no matching function for call to 'g'}}
 }
 
 struct Y {
   template <int> void f(int);
   template <int = 0> static void g(int); // expected-warning 0-1{{extension}}
+                                         // expected-note at -1 {{candidate template ignored: invalid explicitly-specified argument for 1st template parameter}}
 };
 void q() { void (*p)(int) = Y::g; }
 template void f<0>(Y); // expected-note {{in instantiation of}}

>From 2f275f896b7db9864d0e331af07685eca55c7317 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Tue, 30 Jul 2024 09:35:21 -0400
Subject: [PATCH 14/15] [FOLD] further improvements to skipping template
 argument lists

---
 clang/include/clang/Parse/Parser.h |   1 +
 clang/lib/Parse/ParseTemplate.cpp  |   3 -
 clang/lib/Parse/ParseTentative.cpp | 159 ++++++++++++++++++++++++++---
 3 files changed, 148 insertions(+), 15 deletions(-)

diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 8b3953e092cc3..8f0c079e1133b 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -2766,6 +2766,7 @@ class Parser : public CodeCompletionHandler {
   TPResult TryParseTypeofSpecifier();
   TPResult TryParseProtocolQualifiers();
   TPResult TryParsePtrOperatorSeq();
+  TPResult TryParseNonConversionOperatorId();
   TPResult TryParseOperatorId();
   TPResult TryParseInitDeclaratorList(bool MayHaveTrailingReturnType = false);
   TPResult TryParseDeclarator(bool mayBeAbstract, bool mayHaveIdentifier = true,
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index 6e2f8faad42e6..d50e57582be71 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -1783,9 +1783,6 @@ bool Parser::isMissingTemplateKeywordBeforeScope(bool AnnotateInvalid) {
   assert(Tok.is(tok::coloncolon));
   Sema::DisableTypoCorrectionRAII DTC(Actions);
   ColonProtectionRAIIObject ColonProtection(*this);
-  std::optional<Sema::TentativeAnalysisScope> TAS;
-  if (!AnnotateInvalid)
-    TAS.emplace(Actions);
 
   SourceLocation StartLoc = Tok.getLocation();
   if (TryAnnotateTypeOrScopeToken())
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 7c71c09918ff3..dbf9e36d8340a 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -955,7 +955,7 @@ Parser::TPResult Parser::TryParsePtrOperatorSeq() {
 ///         literal-operator-id:
 ///           'operator' string-literal identifier
 ///           'operator' user-defined-string-literal
-Parser::TPResult Parser::TryParseOperatorId() {
+Parser::TPResult Parser::TryParseNonConversionOperatorId() {
   assert(Tok.is(tok::kw_operator));
   ConsumeToken();
 
@@ -1013,6 +1013,13 @@ Parser::TPResult Parser::TryParseOperatorId() {
     return TPResult::True;
   }
 
+  return TPResult::False;
+}
+Parser::TPResult Parser::TryParseOperatorId() {
+  if (TPResult TPR = TryParseNonConversionOperatorId();
+      TPR != TPResult::False)
+    return TPR;
+
   // Maybe this is a conversion-function-id.
   bool AnyDeclSpecifiers = false;
   while (true) {
@@ -2324,37 +2331,166 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip, TemplateN
   bool IsNestedTemplateArgumentList = !GreaterThanIsOperator;
   GreaterThanIsOperatorScope G(GreaterThanIsOperator, false);
 
+  auto TrySkipTemplateArgument = [&]() {
+    bool NextIsTemplateId = false;
+    unsigned TemplateDepth = 0;
+    while (true) {
+      switch (Tok.getKind()) {
+        case tok::eof:
+        case tok::annot_module_begin:
+        case tok::annot_module_end:
+        case tok::annot_module_include:
+        case tok::annot_repl_input_end:
+        case tok::semi:
+          return TPResult::False;
+
+        case tok::comma:
+        case tok::greater:
+        case tok::greatergreater:
+        case tok::greatergreatergreater:
+          return TPResult::True;
+
+        case tok::l_paren:
+          ConsumeParen();
+          if (!SkipUntil(tok::r_paren, StopAtSemi))
+            return TPResult::Error;
+          break;
+        case tok::l_brace:
+          ConsumeBrace();
+          if (!SkipUntil(tok::r_brace, StopAtSemi))
+            return TPResult::Error;
+          break;
+        case tok::l_square:
+          ConsumeBracket();
+          if (!SkipUntil(tok::r_square, StopAtSemi))
+            return TPResult::Error;
+          break;
+        case tok::question:
+        ConsumeToken();
+        if (!SkipUntil(tok::colon, StopAtSemi))
+            return TPResult::Error;
+        break;
+
+        #if 0
+        case tok::kw_template:
+          ConsumeToken();
+          NextIsTemplateId = true;
+          continue;
+        #endif
+        case tok::identifier:
+          ConsumeToken();
+          #if 0
+          if (Tok.is(tok::less)) {
+            if (!NextIsTemplateId)
+              return TPResult::Ambiguous;
+            ConsumeToken();
+            if (!SkipUntil({tok::greater, tok::greatergreater, tok::greatergreatergreater}, StopAtSemi))
+              return TPResult::Error;
+            break;
+          }
+          #else
+          if (Tok.is(tok::less))
+            return TPResult::Ambiguous;
+          break;
+          #endif
+
+        case tok::kw_operator:
+          if (TPResult TPR = TryParseNonConversionOperatorId();
+              TPR == TPResult::Error) {
+            return TPResult::Error;
+          } else if (TPR == TPResult::True) {
+            if (Tok.is(tok::less))
+              return TPResult::Ambiguous;
+          }
+          break;
+
+          #if 0
+          if (Tok.is(tok::less)) {
+            if (!NextIsTemplateId)
+              return TPResult::Ambiguous;
+            ConsumeToken();
+            if (!SkipUntil({tok::greater, tok::greatergreater, tok::greatergreatergreater}, StopAtSemi))
+              return TPResult::Error;
+          }
+          break;
+          #endif
+
+        case tok::kw_const_cast:
+        case tok::kw_dynamic_cast:
+        case tok::kw_reinterpret_cast:
+        case tok::kw_static_cast: {
+          ConsumeToken();
+          if (!TryConsumeToken(tok::less))
+            return TPResult::Error;
+          bool MayHaveTrailingReturnType = Tok.is(tok::kw_auto);
+
+          while (true) {
+            TPResult TPR = isCXXDeclarationSpecifier(ImplicitTypenameContext::Yes);
+            if (TPR == TPResult::False)
+              break;
+            if (TPR == TPResult::Error ||
+                TryConsumeDeclarationSpecifier() == TPResult::Error)
+              return TPResult::Error;
+          }
+
+          if (TryParseDeclarator(
+              /*mayBeAbstract=*/true,
+              /*mayHaveIdentifier=*/false,
+              /*mayHaveDirectInit=*/false,
+              /*mayHaveTrailingReturnType=*/MayHaveTrailingReturnType) == TPResult::Error)
+            return TPResult::Error;
+
+          if (!TryConsumeToken(tok::greater))
+            return TPResult::Error;
+          break;
+        }
+        default:
+          ConsumeAnyToken();
+          break;
+      }
+      NextIsTemplateId = false;
+    }
+  };
+
   while (true) {
     // An expression cannot be followed by a braced-init-list unless
     // its the right operand of an assignment operator.
     if (Tok.is(tok::l_brace))
       return TPResult::True;
 
+    if (TryAnnotateOptionalCXXScopeToken())
+      return TPResult::Error;
+
     bool InvalidAsTemplateArgumentList = false;
-    if (isCXXDeclarationSpecifier(ImplicitTypenameContext::No, TPResult::False,
-                                  &InvalidAsTemplateArgumentList) ==
-        TPResult::True)
-      return TPResult::True;
+    TPResult TPR = isCXXDeclarationSpecifier(ImplicitTypenameContext::No,
+                                             /*BracedCastResult=*/TPResult::Ambiguous,
+                                             &InvalidAsTemplateArgumentList);
     if (InvalidAsTemplateArgumentList)
       return TPResult::False;
 
+    if (TPR == TPResult::True)
+      return TPResult::True;
+
     if (IsNestedTemplateArgumentList || TNK != TNK_Non_template)
       break;
 
-    if (TryAnnotateOptionalCXXScopeToken())
-      return TPResult::Error;
+    if (TPR == TPResult::Ambiguous)
+      TryConsumeDeclarationSpecifier();
+
+    TPR = TrySkipTemplateArgument();
 
-    if (!SkipUntil({tok::comma, tok::less,
-        tok::greater, tok::greatergreater, tok::greatergreatergreater},
-        StopAtSemi | StopBeforeMatch))
+    if (TPR == TPResult::Error)
+      return TPResult::Error;
+    else if (TPR == TPResult::False)
       return TPResult::False;
 
-    if (Tok.isNot(tok::comma)) {
+    if (!TryConsumeToken(tok::comma)) {
       if (Tok.is(tok::less))
         break;
       if (TryConsumeToken(tok::greater) && Tok.is(tok::coloncolon) &&
           !NextToken().isOneOf(tok::kw_new, tok::kw_delete)) {
         TentativeParsingAction TPA(*this, /*Unannotated=*/true);
+        Sema::TentativeAnalysisScope TAS(Actions);
         if (isMissingTemplateKeywordBeforeScope(/*AnnotateInvalid=*/false)) {
           TPA.Revert();
           return TPResult::True;
@@ -2363,7 +2499,6 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip, TemplateN
       }
       return TPResult::Ambiguous;
     }
-    ConsumeToken();
   }
 
   #if 0

>From e3829cd5d915958101e094a6354869a755198083 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Tue, 30 Jul 2024 11:06:30 -0400
Subject: [PATCH 15/15] [FOLD] update test

---
 clang/test/CXX/dcl.decl/dcl.meaning/dcl.mptr/p2.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.mptr/p2.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.mptr/p2.cpp
index a06b107755596..33fb2b5fa82d8 100644
--- a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.mptr/p2.cpp
+++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.mptr/p2.cpp
@@ -36,7 +36,7 @@ template<typename T>
 int A0<T>::template C2<int>::D1::* f6();
 
 template<typename T>
-int A0<T>::template C0<int>::E0<int>::* f7(); // expected-error {{use 'template' keyword to treat 'E0' as a dependent template name}}
+int A0<T>::template C0<int>::E0<int>::* f7(); // expected-warning {{use 'template' keyword to treat 'E0' as a dependent template name}}
                                               // expected-error at -1 {{expected unqualified-id}}
 
 template<typename T>



More information about the libcxx-commits mailing list