[clang] 7bfb98c - [Clang] Implement resolution for CWG1835 (#92957)

via cfe-commits cfe-commits at lists.llvm.org
Tue Jul 9 16:00:25 PDT 2024


Author: Krystian Stasiowski
Date: 2024-07-09T19:00:19-04:00
New Revision: 7bfb98c34687d9784f36937c3ff3e735698b498a

URL: https://github.com/llvm/llvm-project/commit/7bfb98c34687d9784f36937c3ff3e735698b498a
DIFF: https://github.com/llvm/llvm-project/commit/7bfb98c34687d9784f36937c3ff3e735698b498a.diff

LOG: [Clang] Implement resolution for CWG1835 (#92957)

CWG1835 was one of the many core issues resolved by P1787R6: "Declarations and where to
find them" (http://wg21.link/p1787r6). Its resolution changes how
member-qualified names (as defined by [basic.lookup.qual.general] p2) are looked
up. This patch implementation that resolution.

Previously, an _identifier_ following `.` or `->` would be first looked
up in the type of the object expression (i.e. qualified lookup), and
then in the context of the _postfix-expression_ (i.e. unqualified
lookup) if nothing was found; the result of the second lookup was
required to name a class template. Notably, this second lookup would
occur even when the object expression was dependent, and its result
would be used to determine whether a `<` token is the start of a
_template-argument_list_.

The new wording in [basic.lookup.qual.general] p2 states:
> 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. A
***qualified name*** is
> - a member-qualified name or
> - the terminal name of
>     - a _qualified-id_,
>     - a _using-declarator_,
>     - a _typename-specifier_,
>     - a _qualified-namespace-specifier_, or
> - a _nested-name-specifier_, _elaborated-type-specifier_, or
_class-or-decltype_ that has a _nested-name-specifier_.
>
> The _lookup context_ of a member-qualified name is the type of its
associated object expression (considered dependent if the object
expression is type-dependent). The lookup context of any other qualified
name is the type, template, or namespace nominated by the preceding
_nested-name-specifier_.

And [basic.lookup.qual.general] p3 now states:
> _Qualified name lookup_ in a class, namespace, or enumeration performs
a search of the scope associated with it except as specified below.
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 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 non-standardese terms, these two paragraphs essentially state the
following:
- A name that immediately follows `.` or `->` in a class member access
expression is a member-qualified name
- A member-qualified name will be first looked up in the type of the
object expression `T` unless `T` is a dependent type that is _not_ the
current instantiation, e.g.
```
template<typename T>
struct A
{
    void f(T* t)
    {
        this->x; // type of the object expression is 'A<T>'. although 'A<T>' is dependent, it is the
                 // current instantiation so we look up 'x' in the template definition context.
        
        t->y; // type of the object expression is 'T' ('->' is transformed to '.' per [expr.ref]). 
              // 'T' is dependent and is *not* the current instantiation, so we lookup 'y' in the 
              // template instantiation context.
    }
};
```
- If the first lookup finds nothing and:
- the member-qualified name is the first component of a
_nested-name-specifier_ (which could be an _identifier_ or a
_simple-template-id_), and either:
- the type of the object expression is the current instantiation and it
has no dependent base classes, or
        - the type of the object expression is not dependent

  then we lookup the name again, this time via unqualified lookup.

Although the second (unqualified) lookup is stated not to occur when the
member-qualified name is dependent, a dependent name will _not_ be
dependent once the template is instantiated, so the second lookup must
"occur" during instantiation if qualified lookup does not find anything.
This means that we must perform the second (unqualified) lookup during
parsing even when the type of the object expression is dependent, but
those results are _not_ used to determine whether a `<` token is the
start of a _template-argument_list_; they are stored so we can replicate
the second lookup during instantiation.

In even simpler terms (paraphrasing the meeting minutes from the review of P1787; see https://wiki.edg.com/bin/view/Wg21summer2020/P1787%28Lookup%29Review2020-06-15Through2020-06-18):
- Unqualified lookup always happens for the first name in a
_nested-name-specifier_ that follows `.` or `->`
- The result of that lookup is only used to determine whether `<` is the
start of a _template-argument-list_ if the first (qualified) lookup
found nothing and the lookup context:
    - is not dependent, or 
    - is the current instantiation and has no dependent base classes.

An example:
```
struct A 
{
     void f();
};

template<typename T>
using B = A;

template<typename T>
struct C : A
{
    template<typename U>
    void g();

    void h(T* t)
    {
        this->g<int>(); // ok, '<' is the start of a template-argument-list ('g' was found via qualified lookup in the current instantiation)
        this->B<void>::f(); // ok, '<' is the start of a template-argument-list (current instantiation has no dependent bases, 'B' was found via unqualified lookup)
        t->g<int>(); // error: '<' means less than (unqualified lookup does not occur for a member-qualified name that isn't the first component of a nested-name-specifier)
        t->B<void>::f(); // error: '<' means less than (unqualified lookup does not occur if the name is dependent)
        t->template B<void>::f(); // ok: '<' is the start of a template-argument-list ('template' keyword used)
    }
};
```

Some additional notes:
- Per [basic.lookup.qual.general] p1, lookup for a
member-qualified name only considers namespaces, types, and templates
whose specializations are types if it's an _identifier_ followed by
`::`; lookup for the component name of a _simple-template-id_ followed
by `::` is _not_ subject to this rule.
- The wording which specifies when the second unqualified lookup occurs
appears to be paradoxical. We are supposed to do it only for the first
component name of a _nested-name-specifier_ that follows `.` or `->`
when qualified lookup finds nothing. However, when that name is followed
by `<` (potentially starting a _simple-template-id_) we don't _know_
whether it will be the start of a _nested-name-specifier_ until we do
the lookup -- but we aren't supposed to do the lookup until we know it's
part of a _nested-name-specifier_! ***However***, since we only do the
second lookup when the first lookup finds nothing (and the name isn't
dependent), ***and*** since neither lookup is type-only, the only valid
option is for the name to be the _template-name_ in a
_simple-template-id_ that is followed by `::` (it can't be an
_unqualified-id_ naming a member because we already determined that the
lookup context doesn't have a member with that name). Thus, we can lock
into the _nested-name-specifier_ interpretation and do the second lookup
without having to know whether the _simple-template-id_ will be followed
by `::` yet.

Added: 
    clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p3-example3.cpp
    clang/test/CXX/basic/basic.lookup/basic.lookup.qual/basic.lookup.qual.general/p3.cpp
    clang/test/CXX/temp/temp.names/p3-23.cpp

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/AST/ExprCXX.h
    clang/include/clang/AST/Stmt.h
    clang/include/clang/AST/UnresolvedSet.h
    clang/include/clang/Basic/DiagnosticParseKinds.td
    clang/include/clang/Parse/Parser.h
    clang/include/clang/Sema/DeclSpec.h
    clang/include/clang/Sema/Lookup.h
    clang/include/clang/Sema/Sema.h
    clang/lib/AST/ASTImporter.cpp
    clang/lib/AST/ExprCXX.cpp
    clang/lib/AST/ItaniumMangle.cpp
    clang/lib/Parse/ParseExpr.cpp
    clang/lib/Parse/ParseExprCXX.cpp
    clang/lib/Sema/SemaCXXScopeSpec.cpp
    clang/lib/Sema/SemaCoroutine.cpp
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/lib/Sema/SemaExpr.cpp
    clang/lib/Sema/SemaExprMember.cpp
    clang/lib/Sema/SemaOverload.cpp
    clang/lib/Sema/SemaStmtAsm.cpp
    clang/lib/Sema/SemaTemplate.cpp
    clang/lib/Sema/SemaTemplateInstantiate.cpp
    clang/lib/Sema/TreeTransform.h
    clang/lib/Serialization/ASTReaderStmt.cpp
    clang/lib/Serialization/ASTWriterStmt.cpp
    clang/test/CXX/basic/basic.lookup/basic.lookup.classref/p1-cxx11.cpp
    clang/test/CXX/basic/basic.lookup/basic.lookup.classref/p1.cpp
    clang/test/CXX/class.derived/class.member.lookup/p8.cpp
    clang/test/CXX/drs/cwg1xx.cpp
    clang/test/CXX/temp/temp.res/p3.cpp
    clang/test/FixIt/fixit.cpp
    clang/test/Misc/warning-flags.c
    clang/test/Parser/cxx2a-concepts-requires-expr.cpp
    clang/test/SemaCXX/cxx0x-noexcept-expression.cpp
    clang/test/SemaCXX/pseudo-destructors.cpp
    clang/test/SemaCXX/static-assert-cxx17.cpp
    clang/test/SemaTemplate/dependent-base-classes.cpp
    clang/test/SemaTemplate/dependent-template-recover.cpp
    clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
    clang/test/SemaTemplate/template-id-expr.cpp
    clang/test/SemaTemplate/typename-specifier-3.cpp
    libcxx/include/regex

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5f5f3a34c0862..fac4c8e69dc15 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -306,6 +306,9 @@ Resolutions to C++ Defect Reports
 - Clang now considers ``noexcept(typeid(expr))`` more carefully, instead of always assuming that ``std::bad_typeid`` can be thrown.
   (`CWG2191: Incorrect result for noexcept(typeid(v)) <https://cplusplus.github.io/CWG/issues/2191.html>`_).
 
+- 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 c2feac525c1ea..edaea6fe27cc3 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 9cd7a364cd3f1..257a61c97c9c6 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 12aab09f28556..0bd2e35bf2e31 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 6880fa4bb0b03..b4f5270a35956 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3368,15 +3368,11 @@ class Parser : public CodeCompletionHandler {
   BaseResult ParseBaseSpecifier(Decl *ClassDecl);
   AccessSpecifier getAccessSpecifierIfPresent() const;
 
-  bool ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
-                                    ParsedType ObjectType,
-                                    bool ObjectHadErrors,
-                                    SourceLocation TemplateKWLoc,
-                                    IdentifierInfo *Name,
-                                    SourceLocation NameLoc,
-                                    bool EnteringContext,
-                                    UnqualifiedId &Id,
-                                    bool AssumeTemplateId);
+  bool ParseUnqualifiedIdTemplateId(
+      CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors,
+      SourceLocation TemplateKWLoc, SourceLocation TildeLoc,
+      IdentifierInfo *Name, SourceLocation NameLoc, bool EnteringContext,
+      UnqualifiedId &Id, bool AssumeTemplateId);
   bool ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext,
                                   ParsedType ObjectType,
                                   UnqualifiedId &Result);

diff  --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 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 57994f4033922..6be6f6725e5b7 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2802,7 +2802,8 @@ class Sema final : public SemaBase {
   /// (e.g., Base::), perform name lookup for that identifier as a
   /// nested-name-specifier within the given scope, and return the result of
   /// that name lookup.
-  NamedDecl *FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS);
+  bool LookupFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS,
+                                   UnresolvedSetImpl &R);
 
   /// Keeps information about an identifier in a nested-name-spec.
   ///
@@ -2842,9 +2843,6 @@ class Sema final : public SemaBase {
   /// \param EnteringContext If true, enter the context specified by the
   ///        nested-name-specifier.
   /// \param SS Optional nested name specifier preceding the identifier.
-  /// \param ScopeLookupResult Provides the result of name lookup within the
-  ///        scope of the nested-name-specifier that was computed at template
-  ///        definition time.
   /// \param ErrorRecoveryLookup Specifies if the method is called to improve
   ///        error recovery and what kind of recovery is performed.
   /// \param IsCorrectedToColon If not null, suggestion of replace '::' -> ':'
@@ -2853,11 +2851,6 @@ class Sema final : public SemaBase {
   ///        not '::'.
   /// \param OnlyNamespace If true, only considers namespaces in lookup.
   ///
-  /// This routine 
diff ers only slightly from ActOnCXXNestedNameSpecifier, in
-  /// that it contains an extra parameter \p ScopeLookupResult, which provides
-  /// the result of name lookup within the scope of the nested-name-specifier
-  /// that was computed at template definition time.
-  ///
   /// If ErrorRecoveryLookup is true, then this call is used to improve error
   /// recovery.  This means that it should not emit diagnostics, it should
   /// just return true on failure.  It also means it should only return a valid
@@ -2866,7 +2859,6 @@ class Sema final : public SemaBase {
   /// specifier.
   bool BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
                                    bool EnteringContext, CXXScopeSpec &SS,
-                                   NamedDecl *ScopeLookupResult,
                                    bool ErrorRecoveryLookup,
                                    bool *IsCorrectedToColon = nullptr,
                                    bool OnlyNamespace = false);
@@ -8566,11 +8558,12 @@ class Sema final : public SemaBase {
                           const TemplateArgumentListInfo *TemplateArgs,
                           bool IsDefiniteInstance, const Scope *S);
 
-  ExprResult ActOnDependentMemberExpr(
-      Expr *Base, QualType BaseType, bool IsArrow, SourceLocation OpLoc,
-      const CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
-      NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo,
-      const TemplateArgumentListInfo *TemplateArgs);
+  ExprResult
+  ActOnDependentMemberExpr(Expr *Base, QualType BaseType, bool IsArrow,
+                           SourceLocation OpLoc, const CXXScopeSpec &SS,
+                           SourceLocation TemplateKWLoc,
+                           const DeclarationNameInfo &NameInfo,
+                           const TemplateArgumentListInfo *TemplateArgs);
 
   /// The main callback when the parser finds something like
   ///   expression . [nested-name-specifier] identifier
@@ -8626,15 +8619,14 @@ class Sema final : public SemaBase {
   ExprResult BuildMemberReferenceExpr(
       Expr *Base, QualType BaseType, SourceLocation OpLoc, bool IsArrow,
       CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
-      NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo,
+      const DeclarationNameInfo &NameInfo,
       const TemplateArgumentListInfo *TemplateArgs, const Scope *S,
       ActOnMemberAccessExtraArgs *ExtraArgs = nullptr);
 
   ExprResult
   BuildMemberReferenceExpr(Expr *Base, QualType BaseType, SourceLocation OpLoc,
                            bool IsArrow, const CXXScopeSpec &SS,
-                           SourceLocation TemplateKWLoc,
-                           NamedDecl *FirstQualifierInScope, LookupResult &R,
+                           SourceLocation TemplateKWLoc, LookupResult &R,
                            const TemplateArgumentListInfo *TemplateArgs,
                            const Scope *S, bool SuppressQualifierCheck = false,
                            ActOnMemberAccessExtraArgs *ExtraArgs = nullptr);
@@ -11122,15 +11114,14 @@ class Sema final : public SemaBase {
                      QualType ObjectType, bool EnteringContext,
                      RequiredTemplateKind RequiredTemplate = SourceLocation(),
                      AssumedTemplateKind *ATK = nullptr,
-                     bool AllowTypoCorrection = true);
+                     bool AllowTypoCorrection = true, bool MayBeNNS = false);
 
-  TemplateNameKind isTemplateName(Scope *S, CXXScopeSpec &SS,
-                                  bool hasTemplateKeyword,
-                                  const UnqualifiedId &Name,
-                                  ParsedType ObjectType, bool EnteringContext,
-                                  TemplateTy &Template,
-                                  bool &MemberOfUnknownSpecialization,
-                                  bool Disambiguation = false);
+  TemplateNameKind
+  isTemplateName(Scope *S, CXXScopeSpec &SS, bool hasTemplateKeyword,
+                 const UnqualifiedId &Name, ParsedType ObjectType,
+                 bool EnteringContext, TemplateTy &Template,
+                 bool &MemberOfUnknownSpecialization,
+                 bool Disambiguation = false, bool MayBeNNS = false);
 
   /// Try to resolve an undeclared template name as a type template.
   ///
@@ -11459,12 +11450,11 @@ class Sema final : public SemaBase {
   /// For example, given "x.MetaFun::template apply", the scope specifier
   /// \p SS will be "MetaFun::", \p TemplateKWLoc contains the location
   /// of the "template" keyword, and "apply" is the \p Name.
-  TemplateNameKind ActOnTemplateName(Scope *S, CXXScopeSpec &SS,
-                                     SourceLocation TemplateKWLoc,
-                                     const UnqualifiedId &Name,
-                                     ParsedType ObjectType,
-                                     bool EnteringContext, TemplateTy &Template,
-                                     bool AllowInjectedClassName = false);
+  TemplateNameKind
+  ActOnTemplateName(Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
+                    const UnqualifiedId &Name, ParsedType ObjectType,
+                    bool EnteringContext, TemplateTy &Template,
+                    bool AllowInjectedClassName = false, bool MayBeNNS = false);
 
   DeclResult ActOnClassTemplateSpecialization(
       Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,

diff  --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 4e1b3a5a94de7..9bb035c07b8ae 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -8439,8 +8439,14 @@ ExpectedStmt ASTNodeImporter::VisitCXXDependentScopeMemberExpr(
   auto ToOperatorLoc = importChecked(Err, E->getOperatorLoc());
   auto ToQualifierLoc = importChecked(Err, E->getQualifierLoc());
   auto ToTemplateKeywordLoc = importChecked(Err, E->getTemplateKeywordLoc());
-  auto ToFirstQualifierFoundInScope =
-      importChecked(Err, E->getFirstQualifierFoundInScope());
+
+  UnresolvedSet<8> ToUnqualifiedLookups;
+  for (auto D : E->unqualified_lookups())
+    if (auto ToDOrErr = import(D.getDecl()))
+      ToUnqualifiedLookups.addDecl(*ToDOrErr);
+    else
+      return ToDOrErr.takeError();
+
   if (Err)
     return std::move(Err);
 
@@ -8474,7 +8480,7 @@ ExpectedStmt ASTNodeImporter::VisitCXXDependentScopeMemberExpr(
 
   return CXXDependentScopeMemberExpr::Create(
       Importer.getToContext(), ToBase, ToType, E->isArrow(), ToOperatorLoc,
-      ToQualifierLoc, ToTemplateKeywordLoc, ToFirstQualifierFoundInScope,
+      ToQualifierLoc, ToTemplateKeywordLoc, ToUnqualifiedLookups.pairs(),
       ToMemberNameInfo, ResInfo);
 }
 

diff  --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 8d2a1b5611ccc..9d2883a8debb7 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 5444dcf027fe2..f14c4762bee6f 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 2dd2e1b83548b..b98eb70a848e1 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -2343,10 +2343,9 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
       }
 
       if (!LHS.isInvalid())
-        LHS = Actions.ActOnMemberAccessExpr(getCurScope(), LHS.get(), OpLoc,
-                                            OpKind, SS, TemplateKWLoc, Name,
-                                 CurParsedObjCImpl ? CurParsedObjCImpl->Dcl
-                                                   : nullptr);
+        LHS = Actions.ActOnMemberAccessExpr(
+            getCurScope(), LHS.get(), OpLoc, OpKind, SS, TemplateKWLoc, Name,
+            CurParsedObjCImpl ? CurParsedObjCImpl->Dcl : nullptr);
       if (!LHS.isInvalid()) {
         if (Tok.is(tok::less))
           checkPotentialAngleBracket(LHS);

diff  --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 1d364f77a8146..17be090dea3fd 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -100,7 +100,8 @@ void Parser::CheckForTemplateAndDigraph(Token &Next, ParsedType ObjectType,
   bool MemberOfUnknownSpecialization;
   if (!Actions.isTemplateName(getCurScope(), SS, /*hasTemplateKeyword=*/false,
                               TemplateName, ObjectType, EnteringContext,
-                              Template, MemberOfUnknownSpecialization))
+                              Template, MemberOfUnknownSpecialization,
+                              /*Disambiguation=*/false, /*MayBeNNS=*/true))
     return;
 
   FixDigraph(*this, PP, Next, SecondToken, tok::unknown,
@@ -353,7 +354,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
       TemplateTy Template;
       TemplateNameKind TNK = Actions.ActOnTemplateName(
           getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType,
-          EnteringContext, Template, /*AllowInjectedClassName*/ true);
+          EnteringContext, Template, /*AllowInjectedClassName*/ true,
+          /*MayBeNNS=*/true);
       if (AnnotateTemplateIdToken(Template, TNK, SS, TemplateKWLoc,
                                   TemplateName, false))
         return true;
@@ -405,7 +407,6 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
                                       : TemplateId->TemplateNameLoc;
         SS.SetInvalid(SourceRange(StartLoc, CCLoc));
       }
-
       continue;
     }
 
@@ -528,18 +529,19 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
       UnqualifiedId TemplateName;
       TemplateName.setIdentifier(&II, Tok.getLocation());
       bool MemberOfUnknownSpecialization;
-      if (TemplateNameKind TNK = Actions.isTemplateName(getCurScope(), SS,
-                                              /*hasTemplateKeyword=*/false,
-                                                        TemplateName,
-                                                        ObjectType,
-                                                        EnteringContext,
-                                                        Template,
-                                              MemberOfUnknownSpecialization)) {
+      if (TemplateNameKind TNK = Actions.isTemplateName(
+              getCurScope(), SS,
+              /*hasTemplateKeyword=*/false, TemplateName, ObjectType,
+              EnteringContext, Template, MemberOfUnknownSpecialization,
+              /*Disambiguation=*/false,
+              /*MayBeNNS=*/true)) {
         // If lookup didn't find anything, we treat the name as a template-name
         // anyway. C++20 requires this, and in prior language modes it improves
         // error recovery. But before we commit to this, check that we actually
         // have something that looks like a template-argument-list next.
-        if (!IsTypename && TNK == TNK_Undeclared_template &&
+        if (!IsTypename &&
+            (TNK == TNK_Undeclared_template ||
+             (!HasScopeSpecifier && ObjectType)) &&
             isTemplateArgumentList(1) == TPResult::False)
           break;
 
@@ -566,11 +568,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
           // member of an unknown specialization. However, this will only
           // parse correctly as a template, so suggest the keyword 'template'
           // before 'getAs' and treat this as a dependent template name.
-          unsigned DiagID = diag::err_missing_dependent_template_keyword;
-          if (getLangOpts().MicrosoftExt)
-            DiagID = diag::warn_missing_dependent_template_keyword;
-
-          Diag(Tok.getLocation(), DiagID)
+          Diag(Tok.getLocation(), diag::ext_missing_dependent_template_keyword)
               << II.getName()
               << FixItHint::CreateInsertion(Tok.getLocation(), "template ");
         }
@@ -1920,12 +1918,12 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc,
   // argument list. This affects examples such as
   //   void f(auto *p) { p->~X<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,
@@ -2532,8 +2530,9 @@ bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS, DeclaratorContext Context) {
 /// \returns true if a parse error occurred, false otherwise.
 bool Parser::ParseUnqualifiedIdTemplateId(
     CXXScopeSpec &SS, ParsedType ObjectType, bool ObjectHadErrors,
-    SourceLocation TemplateKWLoc, IdentifierInfo *Name, SourceLocation NameLoc,
-    bool EnteringContext, UnqualifiedId &Id, bool AssumeTemplateId) {
+    SourceLocation TemplateKWLoc, SourceLocation TildeLoc, IdentifierInfo *Name,
+    SourceLocation NameLoc, bool EnteringContext, UnqualifiedId &Id,
+    bool AssumeTemplateId) {
   assert(Tok.is(tok::less) && "Expected '<' to finish parsing a template-id");
 
   TemplateTy Template;
@@ -2547,13 +2546,14 @@ bool Parser::ParseUnqualifiedIdTemplateId(
       // this template-id is used to form a nested-name-specifier or not.
       TNK = Actions.ActOnTemplateName(getCurScope(), SS, TemplateKWLoc, Id,
                                       ObjectType, EnteringContext, Template,
-                                      /*AllowInjectedClassName*/ true);
+                                      /*AllowInjectedClassName=*/true,
+                                      TildeLoc.isValid());
     } else {
       bool MemberOfUnknownSpecialization;
-      TNK = Actions.isTemplateName(getCurScope(), SS,
-                                   TemplateKWLoc.isValid(), Id,
-                                   ObjectType, EnteringContext, Template,
-                                   MemberOfUnknownSpecialization);
+      TNK = Actions.isTemplateName(
+          getCurScope(), SS, TemplateKWLoc.isValid(), Id, ObjectType,
+          EnteringContext, Template, MemberOfUnknownSpecialization,
+          /*Disambiguation=*/false, TildeLoc.isValid());
       // If lookup found nothing but we're assuming that this is a template
       // name, double-check that makes sense syntactically before committing
       // to it.
@@ -2580,13 +2580,13 @@ bool Parser::ParseUnqualifiedIdTemplateId(
             else
               Name += Id.Identifier->getName();
           }
-          Diag(Id.StartLocation, diag::err_missing_dependent_template_keyword)
+          Diag(Id.StartLocation, diag::ext_missing_dependent_template_keyword)
               << Name
               << FixItHint::CreateInsertion(Id.StartLocation, "template ");
         }
         TNK = Actions.ActOnTemplateName(
             getCurScope(), SS, TemplateKWLoc, Id, ObjectType, EnteringContext,
-            Template, /*AllowInjectedClassName*/ true);
+            Template, /*AllowInjectedClassName=*/true, TildeLoc.isValid());
       } else if (TNK == TNK_Non_template) {
         return false;
       }
@@ -2611,14 +2611,16 @@ bool Parser::ParseUnqualifiedIdTemplateId(
     bool MemberOfUnknownSpecialization;
     TemplateName.setIdentifier(Name, NameLoc);
     if (ObjectType) {
-      TNK = Actions.ActOnTemplateName(
-          getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType,
-          EnteringContext, Template, /*AllowInjectedClassName*/ true);
+      TNK = Actions.ActOnTemplateName(getCurScope(), SS, TemplateKWLoc,
+                                      TemplateName, ObjectType, EnteringContext,
+                                      Template, /*AllowInjectedClassName=*/true,
+                                      /*MayBeNNS=*/true);
     } else {
       TNK = Actions.isTemplateName(getCurScope(), SS, TemplateKWLoc.isValid(),
-                                   TemplateName, ObjectType,
-                                   EnteringContext, Template,
-                                   MemberOfUnknownSpecialization);
+                                   TemplateName, ObjectType, EnteringContext,
+                                   Template, MemberOfUnknownSpecialization,
+                                   /*Disambiguation=*/false,
+                                   /*MayBeNNS=*/true);
 
       if (TNK == TNK_Non_template && !Id.DestructorName.get()) {
         Diag(NameLoc, diag::err_destructor_template_id)
@@ -2680,7 +2682,7 @@ bool Parser::ParseUnqualifiedIdTemplateId(
   if (Id.getKind() == UnqualifiedIdKind::IK_ConstructorName)
     Id.setConstructorName(Type.get(), NameLoc, RAngleLoc);
   else
-    Id.setDestructorName(Id.StartLocation, Type.get(), RAngleLoc);
+    Id.setDestructorName(TildeLoc, Type.get(), RAngleLoc);
 
   return false;
 }
@@ -3028,8 +3030,9 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
     if (Tok.is(tok::less))
       return ParseUnqualifiedIdTemplateId(
           SS, ObjectType, ObjectHadErrors,
-          TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), Id, IdLoc,
-          EnteringContext, Result, TemplateSpecified);
+          TemplateKWLoc ? *TemplateKWLoc : SourceLocation(),
+          /*TildeLoc=*/SourceLocation(), Id, IdLoc, EnteringContext, Result,
+          TemplateSpecified);
 
     if (TemplateSpecified) {
       TemplateNameKind TNK =
@@ -3124,13 +3127,15 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
         Tok.is(tok::less))
       return ParseUnqualifiedIdTemplateId(
           SS, ObjectType, ObjectHadErrors,
-          TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), nullptr,
-          SourceLocation(), EnteringContext, Result, TemplateSpecified);
+          TemplateKWLoc ? *TemplateKWLoc : SourceLocation(),
+          /*TildeLoc=*/SourceLocation(), /*Name=*/nullptr,
+          /*NameLoc=*/SourceLocation(), EnteringContext, Result,
+          TemplateSpecified);
     else if (TemplateSpecified &&
              Actions.ActOnTemplateName(
                  getCurScope(), SS, *TemplateKWLoc, Result, ObjectType,
                  EnteringContext, Template,
-                 /*AllowInjectedClassName*/ true) == TNK_Non_template)
+                 /*AllowInjectedClassName=*/true) == TNK_Non_template)
       return true;
 
     return false;
@@ -3220,8 +3225,8 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, ParsedType ObjectType,
       Result.setDestructorName(TildeLoc, nullptr, ClassNameLoc);
       return ParseUnqualifiedIdTemplateId(
           SS, ObjectType, ObjectHadErrors,
-          TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), ClassName,
-          ClassNameLoc, EnteringContext, Result, TemplateSpecified);
+          TemplateKWLoc ? *TemplateKWLoc : SourceLocation(), TildeLoc,
+          ClassName, ClassNameLoc, EnteringContext, Result, TemplateSpecified);
     }
 
     // Note that this is a destructor name.

diff  --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp
index 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
     // 
diff erent 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 59487bf57baa9..490cd6e864c43 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;
 
@@ -4901,16 +4903,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;
 
@@ -14336,8 +14334,10 @@ class MemberBuilder: public ExprBuilder {
 public:
   Expr *build(Sema &S, SourceLocation Loc) const override {
     return assertNotNull(S.BuildMemberReferenceExpr(
-        Builder.build(S, Loc), Type, Loc, IsArrow, SS, SourceLocation(),
-        nullptr, MemberLookup, nullptr, nullptr).get());
+                              Builder.build(S, Loc), Type, Loc, IsArrow, SS,
+                              /*TemplateKwLoc=*/SourceLocation(), MemberLookup,
+                              /*TemplateArgs=*/nullptr, /*S=*/nullptr)
+                             .get());
   }
 
   MemberBuilder(const ExprBuilder &Builder, QualType Type, bool IsArrow,
@@ -14543,13 +14543,11 @@ buildSingleCopyAssignRecursively(Sema &S, SourceLocation Loc, QualType T,
                    Loc);
 
     // Create the reference to operator=.
-    ExprResult OpEqualRef
-      = S.BuildMemberReferenceExpr(To.build(S, Loc), T, Loc, /*IsArrow=*/false,
-                                   SS, /*TemplateKWLoc=*/SourceLocation(),
-                                   /*FirstQualifierInScope=*/nullptr,
-                                   OpLookup,
-                                   /*TemplateArgs=*/nullptr, /*S*/nullptr,
-                                   /*SuppressQualifierCheck=*/true);
+    ExprResult OpEqualRef = S.BuildMemberReferenceExpr(
+        To.build(S, Loc), T, Loc, /*IsArrow=*/false, SS,
+        /*TemplateKWLoc=*/SourceLocation(), OpLookup,
+        /*TemplateArgs=*/nullptr, /*S*/ nullptr,
+        /*SuppressQualifierCheck=*/true);
     if (OpEqualRef.isInvalid())
       return StmtError();
 
@@ -17155,8 +17153,9 @@ bool Sema::EvaluateStaticAssertMessageAsString(Expr *Message,
 
   auto BuildExpr = [&](LookupResult &LR) {
     ExprResult Res = BuildMemberReferenceExpr(
-        Message, Message->getType(), Message->getBeginLoc(), false,
-        CXXScopeSpec(), SourceLocation(), nullptr, LR, nullptr, nullptr);
+        Message, Message->getType(), Message->getBeginLoc(), /*IsArrow=*/false,
+        /*SS=*/CXXScopeSpec(), /*TemplateKWLoc=*/SourceLocation(), LR,
+        /*TemplateArgs=*/nullptr, /*S=*/nullptr);
     if (Res.isInvalid())
       return ExprError();
     Res = BuildCallExpr(nullptr, Res.get(), Loc, std::nullopt, Loc, nullptr,

diff  --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 45991e66b3e43..376eed6b72983 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -2624,7 +2624,7 @@ recoverFromMSUnqualifiedLookup(Sema &S, ASTContext &Context,
     return CXXDependentScopeMemberExpr::Create(
         Context, /*This=*/nullptr, ThisType, /*IsArrow=*/true,
         /*Op=*/SourceLocation(), NestedNameSpecifierLoc(), TemplateKWLoc,
-        /*FirstQualifierFoundInScope=*/nullptr, NameInfo, TemplateArgs);
+        /*UnqualifiedLookups=*/std::nullopt, NameInfo, TemplateArgs);
   }
 
   // Synthesize a fake NNS that points to the derived class.  This will

diff  --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index 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 5ea6b06121c7c..f49635aa445a6 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -16043,13 +16043,11 @@ Sema::BuildForRangeBeginEndCall(SourceLocation Loc,
 
   CandidateSet->clear(OverloadCandidateSet::CSK_Normal);
   if (!MemberLookup.empty()) {
-    ExprResult MemberRef =
-        BuildMemberReferenceExpr(Range, Range->getType(), Loc,
-                                 /*IsPtr=*/false, CXXScopeSpec(),
-                                 /*TemplateKWLoc=*/SourceLocation(),
-                                 /*FirstQualifierInScope=*/nullptr,
-                                 MemberLookup,
-                                 /*TemplateArgs=*/nullptr, S);
+    ExprResult MemberRef = BuildMemberReferenceExpr(
+        Range, Range->getType(), Loc,
+        /*IsPtr=*/false, /*SS=*/CXXScopeSpec(),
+        /*TemplateKWLoc=*/SourceLocation(), MemberLookup,
+        /*TemplateArgs=*/nullptr, S);
     if (MemberRef.isInvalid()) {
       *CallExpr = ExprError();
       return FRS_DiagnosticIssued;

diff  --git a/clang/lib/Sema/SemaStmtAsm.cpp b/clang/lib/Sema/SemaStmtAsm.cpp
index 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 07b3f793b3a29..f84e7e0d87188 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -173,15 +173,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;
@@ -212,8 +209,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) {
@@ -379,7 +377,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;
 
@@ -388,92 +386,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())
@@ -493,7 +488,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.
@@ -505,11 +500,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;
@@ -542,13 +541,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) {
@@ -740,7 +734,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);
 }
@@ -5693,14 +5687,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 ?
@@ -5735,9 +5725,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);
@@ -5776,7 +5767,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 a7bc6749c5852..64c5a16b2e4c3 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 655f248d11383..f6021440cda69 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 
diff erent 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 
diff erent 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 
diff erent 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;
 
@@ -12358,7 +12318,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);
@@ -12366,16 +12327,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>
@@ -13502,9 +13458,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;
@@ -13530,7 +13485,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();
   }
@@ -14791,19 +14746,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();
   }
@@ -14822,23 +14775,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());
@@ -14847,15 +14793,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>
@@ -14916,11 +14856,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));
 }
 
@@ -16277,22 +16217,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();
 }
 
@@ -16440,13 +16376,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 e23ceffb10bfe..5a5d7be69a2c3 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1993,42 +1993,43 @@ void ASTStmtReader::VisitCXXDependentScopeMemberExpr(
     CXXDependentScopeMemberExpr *E) {
   VisitExpr(E);
 
-  unsigned NumTemplateArgs = Record.readInt();
   CurrentUnpackingBits.emplace(Record.readInt());
-  bool HasTemplateKWAndArgsInfo = CurrentUnpackingBits->getNextBit();
-  bool HasFirstQualifierFoundInScope = CurrentUnpackingBits->getNextBit();
-
-  assert((HasTemplateKWAndArgsInfo == E->hasTemplateKWAndArgsInfo()) &&
-         "Wrong HasTemplateKWAndArgsInfo!");
-  assert(
-      (HasFirstQualifierFoundInScope == E->hasFirstQualifierFoundInScope()) &&
-      "Wrong HasFirstQualifierFoundInScope!");
-
-  if (HasTemplateKWAndArgsInfo)
-    ReadTemplateKWAndArgsInfo(
-        *E->getTrailingObjects<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
@@ -4075,16 +4076,16 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
       break;
 
     case EXPR_CXX_DEPENDENT_SCOPE_MEMBER: {
-      unsigned NumTemplateArgs = Record[ASTStmtReader::NumExprFields];
       BitsUnpacker DependentScopeMemberBits(
-          Record[ASTStmtReader::NumExprFields + 1]);
-      bool HasTemplateKWAndArgsInfo = DependentScopeMemberBits.getNextBit();
+          Record[ASTStmtReader::NumExprFields]);
+      bool HasQualifier = DependentScopeMemberBits.getNextBit();
+      bool HasTemplateInfo = DependentScopeMemberBits.getNextBit();
+      unsigned NumUnqualifiedLookups = Record[ASTStmtReader::NumExprFields + 1];
+      unsigned NumTemplateArgs = Record[ASTStmtReader::NumExprFields + 2];
 
-      bool HasFirstQualifierFoundInScope =
-          DependentScopeMemberBits.getNextBit();
       S = CXXDependentScopeMemberExpr::CreateEmpty(
-          Context, HasTemplateKWAndArgsInfo, NumTemplateArgs,
-          HasFirstQualifierFoundInScope);
+          Context, HasQualifier, NumUnqualifiedLookups, HasTemplateInfo,
+          NumTemplateArgs);
       break;
     }
 

diff  --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index ea499019c9d16..30c5b1bc56f47 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1988,34 +1988,41 @@ void ASTStmtWriter::VisitCXXDependentScopeMemberExpr(
     CXXDependentScopeMemberExpr *E) {
   VisitExpr(E);
 
-  // Don't emit anything here (or if you do you will have to update
-  // the corresponding deserialization function).
-  Record.push_back(E->getNumTemplateArgs());
-  CurrentPackingBits.updateBits();
-  CurrentPackingBits.addBit(E->hasTemplateKWAndArgsInfo());
-  CurrentPackingBits.addBit(E->hasFirstQualifierFoundInScope());
-
-  if (E->hasTemplateKWAndArgsInfo()) {
-    const ASTTemplateKWAndArgsInfo &ArgInfo =
-        *E->getTrailingObjects<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 37ab93559e369..a4d735e05e9b8 100644
--- a/clang/test/CXX/temp/temp.res/p3.cpp
+++ b/clang/test/CXX/temp/temp.res/p3.cpp
@@ -30,6 +30,6 @@ template<typename T> int A<T>::template C<int>::*f5() {} // expected-error {{has
 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 c616a77f36619..b9aeffbf034b2 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);
   }
 };


        


More information about the cfe-commits mailing list