[clang] [Clang][Sema] Diagnose class member access expressions naming non-existent members of the current instantiation prior to instantiation in the absence of dependent base classes (PR #84050)

Krystian Stasiowski via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 7 09:43:37 PST 2024


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

>From f5ed116971fd90608b84578fdb61761924d65c98 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Fri, 1 Mar 2024 08:08:52 -0500
Subject: [PATCH 1/8] [Clang][Sema] Earlier resolution of class member access
 expressions naming member of the current instantiation

---
 clang/lib/AST/Expr.cpp                      |   2 +-
 clang/lib/Sema/SemaExpr.cpp                 |   4 +-
 clang/lib/Sema/SemaExprMember.cpp           | 121 ++++++++++++++------
 clang/lib/Sema/SemaOverload.cpp             |   3 +
 clang/test/SemaTemplate/dependent-names.cpp |   4 +-
 5 files changed, 93 insertions(+), 41 deletions(-)

diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index b4de2155adcebd..643c938d63c654 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -103,7 +103,7 @@ const Expr *Expr::skipRValueSubobjectAdjustments(
       }
     } else if (const auto *ME = dyn_cast<MemberExpr>(E)) {
       if (!ME->isArrow()) {
-        assert(ME->getBase()->getType()->isRecordType());
+        assert(ME->getBase()->getType()->getAsRecordDecl());
         if (const auto *Field = dyn_cast<FieldDecl>(ME->getMemberDecl())) {
           if (!Field->isBitField() && !Field->getType()->isReferenceType()) {
             E = ME->getBase();
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 47bb263f56aade..e071c3e580b9d8 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -666,7 +666,9 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) {
   // expressions of certain types in C++.
   if (getLangOpts().CPlusPlus &&
       (E->getType() == Context.OverloadTy ||
-       T->isDependentType() ||
+      // FIXME: This is a hack! We want the lvalue-to-rvalue conversion applied
+      // to pointer types even if the pointee type is dependent.
+      (T->isDependentType() && !T->isPointerType()) ||
        T->isRecordType()))
     return E;
 
diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index 32998ae60eafe2..fd55e6f46a1066 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -624,8 +624,8 @@ namespace {
 // classes, one of its base classes.
 class RecordMemberExprValidatorCCC final : public CorrectionCandidateCallback {
 public:
-  explicit RecordMemberExprValidatorCCC(const RecordType *RTy)
-      : Record(RTy->getDecl()) {
+  explicit RecordMemberExprValidatorCCC(QualType RTy)
+      : Record(RTy->getAsRecordDecl()) {
     // Don't add bare keywords to the consumer since they will always fail
     // validation by virtue of not being associated with any decls.
     WantTypeSpecifiers = false;
@@ -671,33 +671,55 @@ class RecordMemberExprValidatorCCC final : public CorrectionCandidateCallback {
 
 static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R,
                                      Expr *BaseExpr,
-                                     const RecordType *RTy,
+                                     QualType RTy,
                                      SourceLocation OpLoc, bool IsArrow,
                                      CXXScopeSpec &SS, bool HasTemplateArgs,
                                      SourceLocation TemplateKWLoc,
                                      TypoExpr *&TE) {
+  RecordDecl *RDecl = RTy->getAsRecordDecl();
+  DeclContext *DC = SemaRef.computeDeclContext(RTy);
+  // If the object expression is dependent and isn't the current instantiation,
+  // lookup will not find anything and we must defer until instantiation.
+  if (!DC) {
+    R.setNotFoundInCurrentInstantiation();
+    return false;
+  }
+
+  // FIXME: Should this use Name.isDependentName()?
+  if (DeclarationName Name = R.getLookupName();
+      Name.getNameKind() == DeclarationName::CXXConversionFunctionName &&
+      Name.getCXXNameType()->isDependentType()) {
+    R.setNotFoundInCurrentInstantiation();
+    return false;
+  }
+
   SourceRange BaseRange = BaseExpr ? BaseExpr->getSourceRange() : SourceRange();
-  RecordDecl *RDecl = RTy->getDecl();
-  if (!SemaRef.isThisOutsideMemberFunctionBody(QualType(RTy, 0)) &&
-      SemaRef.RequireCompleteType(OpLoc, QualType(RTy, 0),
+  if (!RTy->isDependentType() &&
+      !SemaRef.isThisOutsideMemberFunctionBody(RTy) &&
+      SemaRef.RequireCompleteType(OpLoc, RTy,
                                   diag::err_typecheck_incomplete_tag,
                                   BaseRange))
     return true;
 
   if (HasTemplateArgs || TemplateKWLoc.isValid()) {
     // LookupTemplateName doesn't expect these both to exist simultaneously.
-    QualType ObjectType = SS.isSet() ? QualType() : QualType(RTy, 0);
+    QualType ObjectType = SS.isSet() ? QualType() : RTy;
 
     bool MOUS;
     return SemaRef.LookupTemplateName(R, nullptr, SS, ObjectType, false, MOUS,
                                       TemplateKWLoc);
   }
 
-  DeclContext *DC = RDecl;
   if (SS.isSet()) {
     // If the member name was a qualified-id, look into the
     // nested-name-specifier.
     DC = SemaRef.computeDeclContext(SS, false);
+    // We tried to look into a dependent context that is not the current
+    // instantiation. Defer lookup until instantiation.
+    if (!DC) {
+      R.setNotFoundInCurrentInstantiation();
+      return false;
+    }
 
     if (SemaRef.RequireCompleteDeclContext(SS, DC)) {
       SemaRef.Diag(SS.getRange().getEnd(), diag::err_typecheck_incomplete_tag)
@@ -717,7 +739,7 @@ static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R,
   // The record definition is complete, now look up the member.
   SemaRef.LookupQualifiedName(R, DC, SS);
 
-  if (!R.empty())
+  if (!R.empty() || R.wasNotFoundInCurrentInstantiation())
     return false;
 
   DeclarationName Typo = R.getLookupName();
@@ -781,6 +803,7 @@ Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType,
                                const TemplateArgumentListInfo *TemplateArgs,
                                const Scope *S,
                                ActOnMemberAccessExtraArgs *ExtraArgs) {
+  #if 0
   if (BaseType->isDependentType() ||
       (SS.isSet() && isDependentScopeSpecifier(SS)) ||
       NameInfo.getName().isDependentName())
@@ -788,6 +811,7 @@ Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType,
                                     IsArrow, OpLoc,
                                     SS, TemplateKWLoc, FirstQualifierInScope,
                                     NameInfo, TemplateArgs);
+  #endif
 
   LookupResult R(*this, NameInfo, LookupMemberName);
 
@@ -797,7 +821,7 @@ Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType,
     QualType RecordTy = BaseType;
     if (IsArrow) RecordTy = RecordTy->castAs<PointerType>()->getPointeeType();
     if (LookupMemberExprInRecord(
-            *this, R, nullptr, RecordTy->castAs<RecordType>(), OpLoc, IsArrow,
+            *this, R, nullptr, RecordTy, OpLoc, IsArrow,
             SS, TemplateArgs != nullptr, TemplateKWLoc, TE))
       return ExprError();
     if (TE)
@@ -990,6 +1014,14 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
                                const Scope *S,
                                bool SuppressQualifierCheck,
                                ActOnMemberAccessExtraArgs *ExtraArgs) {
+
+  if (R.wasNotFoundInCurrentInstantiation() || (SS.isValid() && !computeDeclContext(SS, false))) {
+    return ActOnDependentMemberExpr(BaseExpr, BaseExprType,
+                                    IsArrow, OpLoc,
+                                    SS, TemplateKWLoc, FirstQualifierInScope,
+                                    R.getLookupNameInfo(), TemplateArgs);
+  }
+
   QualType BaseType = BaseExprType;
   if (IsArrow) {
     assert(BaseType->isPointerType());
@@ -1028,11 +1060,11 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
     // Rederive where we looked up.
     DeclContext *DC = (SS.isSet()
                        ? computeDeclContext(SS, false)
-                       : BaseType->castAs<RecordType>()->getDecl());
+                       : BaseType->getAsRecordDecl());
 
     if (ExtraArgs) {
       ExprResult RetryExpr;
-      if (!IsArrow && BaseExpr) {
+      if (!IsArrow && BaseExpr && !BaseExpr->isTypeDependent()) {
         SFINAETrap Trap(*this, true);
         ParsedType ObjectType;
         bool MayBePseudoDestructor = false;
@@ -1055,9 +1087,16 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
       }
     }
 
-    Diag(R.getNameLoc(), diag::err_no_member)
-      << MemberName << DC
-      << (BaseExpr ? BaseExpr->getSourceRange() : SourceRange());
+    if(DC) {
+      Diag(R.getNameLoc(), diag::err_no_member)
+          << MemberName << DC
+          << (BaseExpr ? BaseExpr->getSourceRange() : SourceRange());
+    } else {
+      // FIXME: Is this needed?
+      Diag(R.getNameLoc(), diag::err_no_member)
+          << MemberName << BaseExprType
+          << (BaseExpr ? BaseExpr->getSourceRange() : SourceRange());
+    }
     return ExprError();
   }
 
@@ -1287,7 +1326,10 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R,
     return ExprError();
 
   QualType BaseType = BaseExpr.get()->getType();
+
+  #if 0
   assert(!BaseType->isDependentType());
+  #endif
 
   DeclarationName MemberName = R.getLookupName();
   SourceLocation MemberLoc = R.getNameLoc();
@@ -1299,29 +1341,32 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R,
   if (IsArrow) {
     if (const PointerType *Ptr = BaseType->getAs<PointerType>())
       BaseType = Ptr->getPointeeType();
-    else if (const ObjCObjectPointerType *Ptr
+    else if (!BaseType->isDependentType()) {
+      if (const ObjCObjectPointerType *Ptr
                = BaseType->getAs<ObjCObjectPointerType>())
       BaseType = Ptr->getPointeeType();
-    else if (BaseType->isRecordType()) {
-      // Recover from arrow accesses to records, e.g.:
-      //   struct MyRecord foo;
-      //   foo->bar
-      // This is actually well-formed in C++ if MyRecord has an
-      // overloaded operator->, but that should have been dealt with
-      // by now--or a diagnostic message already issued if a problem
-      // was encountered while looking for the overloaded operator->.
-      if (!S.getLangOpts().CPlusPlus) {
-        S.Diag(OpLoc, diag::err_typecheck_member_reference_suggestion)
-          << BaseType << int(IsArrow) << BaseExpr.get()->getSourceRange()
-          << FixItHint::CreateReplacement(OpLoc, ".");
+      else if (BaseType->isRecordType()) {
+        // Recover from arrow accesses to records, e.g.:
+         //   struct MyRecord foo;
+        //   foo->bar
+        // This is actually well-formed in C++ if MyRecord has an
+        // overloaded operator->, but that should have been dealt with
+        // by now--or a diagnostic message already issued if a problem
+        // was encountered while looking for the overloaded operator->.
+        if (!S.getLangOpts().CPlusPlus) {
+          S.Diag(OpLoc, diag::err_typecheck_member_reference_suggestion)
+              << BaseType << int(IsArrow) << BaseExpr.get()->getSourceRange()
+              << FixItHint::CreateReplacement(OpLoc, ".");
+        }
+        IsArrow = false;
+      } else if (BaseType->isFunctionType()) {
+        goto fail;
+      } else {
+        S.Diag(MemberLoc, diag::err_typecheck_member_reference_arrow)
+            << BaseType << BaseExpr.get()->getSourceRange();
+        return ExprError();
       }
-      IsArrow = false;
-    } else if (BaseType->isFunctionType()) {
-      goto fail;
-    } else {
-      S.Diag(MemberLoc, diag::err_typecheck_member_reference_arrow)
-        << BaseType << BaseExpr.get()->getSourceRange();
-      return ExprError();
+
     }
   }
 
@@ -1341,9 +1386,9 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R,
   }
 
   // Handle field access to simple records.
-  if (const RecordType *RTy = BaseType->getAs<RecordType>()) {
+  if (BaseType->getAsRecordDecl() || BaseType->isDependentType()) {
     TypoExpr *TE = nullptr;
-    if (LookupMemberExprInRecord(S, R, BaseExpr.get(), RTy, OpLoc, IsArrow, SS,
+    if (LookupMemberExprInRecord(S, R, BaseExpr.get(), BaseType, OpLoc, IsArrow, SS,
                                  HasTemplateArgs, TemplateKWLoc, TE))
       return ExprError();
 
@@ -1781,12 +1826,14 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base,
   if (Result.isInvalid()) return ExprError();
   Base = Result.get();
 
+  #if 0
   if (Base->getType()->isDependentType() || Name.isDependentName() ||
       isDependentScopeSpecifier(SS)) {
     return ActOnDependentMemberExpr(Base, Base->getType(), IsArrow, OpLoc, SS,
                                     TemplateKWLoc, FirstQualifierInScope,
                                     NameInfo, TemplateArgs);
   }
+  #endif
 
   ActOnMemberAccessExtraArgs ExtraArgs = {S, Id, ObjCImpDecl};
   ExprResult Res = BuildMemberReferenceExpr(
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index b0c693f078efe2..efe5c1219712c6 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -5789,7 +5789,10 @@ static ImplicitConversionSequence TryObjectArgumentInitialization(
     return ICS;
   }
 
+  // FIXME: Should this check getAsRecordDecl instead?
+  #if 0
   assert(FromType->isRecordType());
+  #endif
 
   QualType ClassType = S.Context.getTypeDeclType(ActingContext);
   // C++98 [class.dtor]p2:
diff --git a/clang/test/SemaTemplate/dependent-names.cpp b/clang/test/SemaTemplate/dependent-names.cpp
index 641ec950054f57..a661f14af80d34 100644
--- a/clang/test/SemaTemplate/dependent-names.cpp
+++ b/clang/test/SemaTemplate/dependent-names.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s 
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
 
 typedef double A;
 template<typename T> class B {
@@ -334,7 +334,7 @@ int arr[sizeof(Sub)];
 namespace PR11421 {
 template < unsigned > struct X {
   static const unsigned dimension = 3;
-  template<unsigned dim=dimension> 
+  template<unsigned dim=dimension>
   struct Y: Y<dim> { }; // expected-error{{circular inheritance between 'Y<dim>' and 'Y<dim>'}}
 };
 typedef X<3> X3;

>From e0b3ccb21e4373ef192586d1442bb8d03b75a29e Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Fri, 1 Mar 2024 11:54:23 -0500
Subject: [PATCH 2/8] [FOLD] update tests

---
 .../temp.res/temp.dep/temp.dep.type/p4.cpp    | 110 ++++++++++++++++++
 .../SemaTemplate/instantiate-function-1.cpp   |  14 +--
 2 files changed, 117 insertions(+), 7 deletions(-)
 create mode 100644 clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp

diff --git a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp
new file mode 100644
index 00000000000000..7b36e3ad3b0131
--- /dev/null
+++ b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp
@@ -0,0 +1,110 @@
+// RUN: %clang_cc1 -Wno-unused-value -verify %s
+
+namespace N0 {
+  template<typename T>
+  struct A {
+    int x;
+    void f();
+    using X = int;
+
+    void not_instantiated(A *a, A &b) {
+      x;
+      f();
+      new X;
+
+      this->x;
+      this->f();
+      this->A::x;
+      this->A::f();
+
+      a->x;
+      a->f();
+      a->A::x;
+      a->A::f();
+
+      (*this).x;
+      (*this).f();
+      (*this).A::x;
+      (*this).A::f();
+
+      b.x;
+      b.f();
+      b.A::x;
+      b.A::f();
+
+      A::x;
+      A::f();
+      new A::X;
+
+      y; // expected-error{{use of undeclared identifier 'y'}}
+      g(); // expected-error{{use of undeclared identifier 'g'}}
+      new Y; // expected-error{{unknown type name 'Y'}}
+
+      this->y; // expected-error{{no member named 'y' in 'A<T>'}}
+      this->g(); // expected-error{{no member named 'g' in 'A<T>'}}
+      this->A::y; // expected-error{{no member named 'y' in 'A<T>'}}
+      this->A::g(); // expected-error{{no member named 'g' in 'A<T>'}}
+
+      a->y; // expected-error{{no member named 'y' in 'A<T>'}}
+      a->g(); // expected-error{{no member named 'g' in 'A<T>'}}
+      a->A::y; // expected-error{{no member named 'y' in 'A<T>'}}
+      a->A::g(); // expected-error{{no member named 'g' in 'A<T>'}}
+
+      // FIXME: An overloaded unary 'operator*' is built for these
+      // even though the operand is a pointer (to a dependent type).
+      // Type::isOverloadableType should return false for such cases.
+      (*this).y;
+      (*this).g();
+      (*this).A::y;
+      (*this).A::g();
+
+      b.y; // expected-error{{no member named 'y' in 'A<T>'}}
+      b.g(); // expected-error{{no member named 'g' in 'A<T>'}}
+      b.A::y; // expected-error{{no member named 'y' in 'A<T>'}}
+      b.A::g(); // expected-error{{no member named 'g' in 'A<T>'}}
+
+      A::y; // expected-error{{no member named 'y' in 'A<T>'}}
+      A::g(); // expected-error{{no member named 'g' in 'A<T>'}}
+      new A::Y; // expected-error{{no type named 'Y' in 'A<T>'}}
+    }
+  };
+} // namespace N0
+
+namespace N1 {
+  struct A {
+    template<int I>
+    void f();
+  };
+
+  template<typename T>
+  struct B {
+    template<int I>
+    void f();
+
+    A x;
+    A g();
+
+    void not_instantiated(B *a, B &b) {
+      f<0>();
+      this->f<0>();
+      a->f<0>();
+      // FIXME: This should not require 'template'!
+      (*this).f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}}
+      b.f<0>();
+
+      x.f<0>();
+      this->x.f<0>();
+      a->x.f<0>();
+      // FIXME: This should not require 'template'!
+      (*this).x.f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}}
+      b.x.f<0>();
+
+      // FIXME: None of these should require 'template'!
+      g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}}
+      this->g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}}
+      a->g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}}
+      (*this).g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}}
+      b.g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}}
+    }
+  };
+} // namespace N1
diff --git a/clang/test/SemaTemplate/instantiate-function-1.cpp b/clang/test/SemaTemplate/instantiate-function-1.cpp
index ceef2743774805..a4967264c654b7 100644
--- a/clang/test/SemaTemplate/instantiate-function-1.cpp
+++ b/clang/test/SemaTemplate/instantiate-function-1.cpp
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fsyntax-only -verify %s
 template<typename T, typename U>
 struct X0 {
-  void f(T x, U y) { 
+  void f(T x, U y) {
     (void)(x + y); // expected-error{{invalid operands}}
   }
 };
@@ -41,7 +41,7 @@ template <typename T> struct X4 {
   T f() const {
     return; // expected-error{{non-void function 'f' should return a value}}
   }
-  
+
   T g() const {
     return 1; // expected-error{{void function 'g' should not return a value}}
   }
@@ -64,7 +64,7 @@ template<typename T, typename U, typename V> struct X6 {
     // IfStmt
     if (t > 0)
       return u;
-    else { 
+    else {
       if (t < 0)
         return v; // expected-error{{cannot initialize return object of type}}
     }
@@ -131,12 +131,12 @@ template<typename T> struct Member0 {
     t;
     t.f;
     t->f;
-    
+
     T* tp;
     tp.f; // expected-error{{member reference base type 'T *' is not a structure or union}}
     tp->f;
 
-    this->f;
+    this->f; // expected-error{{reference to non-static member function must be called}}
     this.f; // expected-error{{member reference base type 'Member0<T> *' is not a structure or union}}
   }
 };
@@ -239,11 +239,11 @@ namespace PR9880 {
     static yes_tag check(char[sizeof(&U::luaIndex)]);
     enum { value = sizeof(check<T>(0)) == sizeof(yes_tag) };
   };
-  
+
   class SomeClass {
   public:
     int luaIndex(lua_State* L);
   };
-  
+
   int i = HasIndexMetamethod<SomeClass>::value;
 }

>From 58f4833f674d5f7c9908ef15ecb1d107fc384f8d Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Mon, 4 Mar 2024 06:59:58 -0500
Subject: [PATCH 3/8] [FOLD] build DependentMemberExpr for
 LookupResult::FoundUnresolved

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

diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index fd55e6f46a1066..95ad16571de8d6 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -676,7 +676,6 @@ static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R,
                                      CXXScopeSpec &SS, bool HasTemplateArgs,
                                      SourceLocation TemplateKWLoc,
                                      TypoExpr *&TE) {
-  RecordDecl *RDecl = RTy->getAsRecordDecl();
   DeclContext *DC = SemaRef.computeDeclContext(RTy);
   // If the object expression is dependent and isn't the current instantiation,
   // lookup will not find anything and we must defer until instantiation.
@@ -1015,7 +1014,9 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
                                bool SuppressQualifierCheck,
                                ActOnMemberAccessExtraArgs *ExtraArgs) {
 
-  if (R.wasNotFoundInCurrentInstantiation() || (SS.isValid() && !computeDeclContext(SS, false))) {
+  if (R.wasNotFoundInCurrentInstantiation() ||
+      (SS.isValid() && !computeDeclContext(SS, false)) ||
+      (R.isUnresolvableResult() && R.isClassLookup() && R.getNamingClass()->isDependentContext())) {
     return ActOnDependentMemberExpr(BaseExpr, BaseExprType,
                                     IsArrow, OpLoc,
                                     SS, TemplateKWLoc, FirstQualifierInScope,

>From 29e99ac9f9d1360d2aff7af0d6431eba9fd52975 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Tue, 5 Mar 2024 10:03:05 -0500
Subject: [PATCH 4/8] [FOLD] TransformOverloadExprDecls filters template names

---
 clang/lib/Sema/SemaExprMember.cpp |  3 +--
 clang/lib/Sema/TreeTransform.h    | 20 ++++++++++++++++++++
 2 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index 95ad16571de8d6..fd3f8860d46f98 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -1015,8 +1015,7 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
                                ActOnMemberAccessExtraArgs *ExtraArgs) {
 
   if (R.wasNotFoundInCurrentInstantiation() ||
-      (SS.isValid() && !computeDeclContext(SS, false)) ||
-      (R.isUnresolvableResult() && R.isClassLookup() && R.getNamingClass()->isDependentContext())) {
+      (SS.isValid() && !computeDeclContext(SS, false))) {
     return ActOnDependentMemberExpr(BaseExpr, BaseExprType,
                                     IsArrow, OpLoc,
                                     SS, TemplateKWLoc, FirstQualifierInScope,
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 409aee73d960eb..ceedbda979182b 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -12930,6 +12930,26 @@ bool TreeTransform<Derived>::TransformOverloadExprDecls(OverloadExpr *Old,
   // Resolve a kind, but don't do any further analysis.  If it's
   // ambiguous, the callee needs to deal with it.
   R.resolveKind();
+
+  if (Old->hasTemplateKeyword() && !R.empty()) {
+    NamedDecl *FoundDecl = R.getRepresentativeDecl()->getUnderlyingDecl();
+    getSema().FilterAcceptableTemplateNames(R,
+                                            /*AllowFunctionTemplates=*/true,
+                                            /*AllowDependent=*/true);
+    if (R.empty()) {
+      // If a 'template' keyword was used, a lookup that finds only non-template
+      // names is an error.
+      getSema().Diag(R.getNameLoc(), diag::err_template_kw_refers_to_non_template)
+          << R.getLookupName()
+          << Old->getQualifierLoc().getSourceRange()
+          << Old->hasTemplateKeyword()
+          << Old->getTemplateKeywordLoc();
+      getSema().Diag(FoundDecl->getLocation(), diag::note_template_kw_refers_to_non_template)
+          << R.getLookupName();
+        return true;
+    }
+  }
+
   return false;
 }
 

>From 1d1e247ed1081c8533b4743319ba31d6ae28a0fc Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Tue, 5 Mar 2024 10:50:53 -0500
Subject: [PATCH 5/8] [FOLD] update tests

---
 clang/test/AST/HLSL/this-reference-template.hlsl         | 2 +-
 clang/test/CodeGenCXX/mangle.cpp                         | 8 --------
 clang/test/Index/annotate-nested-name-specifier.cpp      | 4 ++--
 clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp | 4 ++--
 4 files changed, 5 insertions(+), 13 deletions(-)

diff --git a/clang/test/AST/HLSL/this-reference-template.hlsl b/clang/test/AST/HLSL/this-reference-template.hlsl
index 60e057986ebf80..d427e73044b788 100644
--- a/clang/test/AST/HLSL/this-reference-template.hlsl
+++ b/clang/test/AST/HLSL/this-reference-template.hlsl
@@ -24,7 +24,7 @@ void main() {
 // CHECK:     -CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <line:8:3, line:10:3> line:8:5 getFirst 'K ()' implicit-inline
 // CHECK-NEXT:-CompoundStmt 0x{{[0-9A-Fa-f]+}} <col:16, line:10:3>
 // CHECK-NEXT:-ReturnStmt 0x{{[0-9A-Fa-f]+}} <line:9:4, col:16>
-// CHECK-NEXT:-CXXDependentScopeMemberExpr 0x{{[0-9A-Fa-f]+}} <col:11, col:16> '<dependent type>' lvalue .First
+// CHECK-NEXT:-MemberExpr 0x{{[0-9A-Fa-f]+}} <col:11, col:16> 'K' lvalue .First 0x{{[0-9A-Fa-f]+}}
 // CHECK-NEXT:-CXXThisExpr 0x{{[0-9A-Fa-f]+}} <col:11> 'Pair<K, V>' lvalue this
 // CHECK-NEXT:-CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <line:12:3, line:14:3> line:12:5 getSecond 'V ()' implicit-inline
 // CHECK-NEXT:-CompoundStmt 0x{{[0-9A-Fa-f]+}} <col:17, line:14:3>
diff --git a/clang/test/CodeGenCXX/mangle.cpp b/clang/test/CodeGenCXX/mangle.cpp
index 31467d943840e0..d0800af55c87e8 100644
--- a/clang/test/CodeGenCXX/mangle.cpp
+++ b/clang/test/CodeGenCXX/mangle.cpp
@@ -1032,10 +1032,6 @@ namespace test51 {
   template <typename T>
   decltype(S1<T>().~S1<T>(), S1<T>().~S1<T>()) fun4() {};
   template <typename T>
-  decltype(S1<int>().~S1<T>()) fun5(){};
-  template <template <typename T> class U>
-  decltype(S1<int>().~U<int>()) fun6(){};
-  template <typename T>
   decltype(E().E::~T()) fun7() {}
   template <template <typename> class U>
   decltype(X<int>::Y().U<int>::Y::~Y()) fun8() {}
@@ -1047,10 +1043,6 @@ namespace test51 {
   // CHECK-LABEL: @_ZN6test514fun3I2S1IiEiEEDTcldtcvS1_IT0_E_EdnT_EEv
   template void fun4<int>();
   // CHECK-LABEL: @_ZN6test514fun4IiEEDTcmcldtcv2S1IT_E_Edn2S1IS2_EEcldtcvS3__Edn2S1IS2_EEEv
-  template void fun5<int>();
-  // CHECK-LABEL: @_ZN6test514fun5IiEEDTcldtcv2S1IiE_Edn2S1IT_EEEv
-  template void fun6<S1>();
-  // CHECK-LABEL: @_ZN6test514fun6I2S1EEDTcldtcvS1_IiE_EdnT_IiEEEv
   template void fun7<E>();
   // CHECK-LABEL: @_ZN6test514fun7INS_1EEEEDTcldtcvS1__Esr1EEdnT_EEv
   template void fun8<X>();
diff --git a/clang/test/Index/annotate-nested-name-specifier.cpp b/clang/test/Index/annotate-nested-name-specifier.cpp
index a7338db6b05b77..3181497258407f 100644
--- a/clang/test/Index/annotate-nested-name-specifier.cpp
+++ b/clang/test/Index/annotate-nested-name-specifier.cpp
@@ -132,7 +132,7 @@ struct X8 {
 
 struct X9 : X8 {
   typedef X8 inherited;
-  void f() { 
+  void f() {
     inherited::f();
   }
 };
@@ -299,7 +299,7 @@ struct X9 : X8 {
 // CHECK: Identifier: "type" [77:16 - 77:20] TypeRef=X4::type:70:13
 // CHECK: Punctuation: ">" [77:20 - 77:21] MemberRefExpr=
 // CHECK: Punctuation: "::" [77:21 - 77:23] MemberRefExpr=
-// CHECK: Identifier: "g" [77:23 - 77:24] MemberRefExpr=
+// CHECK: Identifier: "g" [77:23 - 77:24] OverloadedDeclRef=
 // CHECK: Punctuation: "(" [77:24 - 77:25] CallExpr=
 // CHECK: Identifier: "t" [77:25 - 77:26] DeclRefExpr=t:74:12
 // CHECK: Punctuation: ")" [77:26 - 77:27] CallExpr=
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
index b75da7bc1ed069..3af67671aa5b5c 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1569,8 +1569,8 @@ TEST_P(ASTMatchersTest, IsArrow_MatchesMemberVariablesViaArrow) {
       matches("class Y { void x() { y; } int y; };", memberExpr(isArrow())));
   EXPECT_TRUE(notMatches("class Y { void x() { (*this).y; } int y; };",
                          memberExpr(isArrow())));
-  EXPECT_TRUE(matches("template <class T> class Y { void x() { this->m; } };",
-                      cxxDependentScopeMemberExpr(isArrow())));
+  EXPECT_TRUE(matches("template <class T> class Y { void x() { this->m; } int m; };",
+                         memberExpr(isArrow())));
   EXPECT_TRUE(
       notMatches("template <class T> class Y { void x() { (*this).m; } };",
                  cxxDependentScopeMemberExpr(isArrow())));

>From 12977860857f7f5a534417e85a6e88bb7ccc3501 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Tue, 5 Mar 2024 12:33:40 -0500
Subject: [PATCH 6/8] [FOLD] cleanup

---
 clang/lib/Sema/SemaExprMember.cpp           | 19 -------------------
 clang/test/SemaTemplate/dependent-names.cpp |  4 ++--
 2 files changed, 2 insertions(+), 21 deletions(-)

diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index fd3f8860d46f98..eb87e0edda0128 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -802,16 +802,6 @@ Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType,
                                const TemplateArgumentListInfo *TemplateArgs,
                                const Scope *S,
                                ActOnMemberAccessExtraArgs *ExtraArgs) {
-  #if 0
-  if (BaseType->isDependentType() ||
-      (SS.isSet() && isDependentScopeSpecifier(SS)) ||
-      NameInfo.getName().isDependentName())
-    return ActOnDependentMemberExpr(Base, BaseType,
-                                    IsArrow, OpLoc,
-                                    SS, TemplateKWLoc, FirstQualifierInScope,
-                                    NameInfo, TemplateArgs);
-  #endif
-
   LookupResult R(*this, NameInfo, LookupMemberName);
 
   // Implicit member accesses.
@@ -1826,15 +1816,6 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base,
   if (Result.isInvalid()) return ExprError();
   Base = Result.get();
 
-  #if 0
-  if (Base->getType()->isDependentType() || Name.isDependentName() ||
-      isDependentScopeSpecifier(SS)) {
-    return ActOnDependentMemberExpr(Base, Base->getType(), IsArrow, OpLoc, SS,
-                                    TemplateKWLoc, FirstQualifierInScope,
-                                    NameInfo, TemplateArgs);
-  }
-  #endif
-
   ActOnMemberAccessExtraArgs ExtraArgs = {S, Id, ObjCImpDecl};
   ExprResult Res = BuildMemberReferenceExpr(
       Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc,
diff --git a/clang/test/SemaTemplate/dependent-names.cpp b/clang/test/SemaTemplate/dependent-names.cpp
index a661f14af80d34..641ec950054f57 100644
--- a/clang/test/SemaTemplate/dependent-names.cpp
+++ b/clang/test/SemaTemplate/dependent-names.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s 
 
 typedef double A;
 template<typename T> class B {
@@ -334,7 +334,7 @@ int arr[sizeof(Sub)];
 namespace PR11421 {
 template < unsigned > struct X {
   static const unsigned dimension = 3;
-  template<unsigned dim=dimension>
+  template<unsigned dim=dimension> 
   struct Y: Y<dim> { }; // expected-error{{circular inheritance between 'Y<dim>' and 'Y<dim>'}}
 };
 typedef X<3> X3;

>From 3374b3bad0b1ca8f541de5bd8bbebba42c21ff5a Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Tue, 5 Mar 2024 13:03:00 -0500
Subject: [PATCH 7/8] [FOLD] format

---
 clang/lib/Sema/SemaExpr.cpp                   |  7 +--
 clang/lib/Sema/SemaExprMember.cpp             | 59 ++++++++-----------
 clang/lib/Sema/SemaOverload.cpp               |  6 +-
 clang/lib/Sema/TreeTransform.h                | 14 ++---
 .../ASTMatchers/ASTMatchersNarrowingTest.cpp  |  5 +-
 5 files changed, 41 insertions(+), 50 deletions(-)

diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index e071c3e580b9d8..22216dc9e0fb5f 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -666,10 +666,9 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) {
   // expressions of certain types in C++.
   if (getLangOpts().CPlusPlus &&
       (E->getType() == Context.OverloadTy ||
-      // FIXME: This is a hack! We want the lvalue-to-rvalue conversion applied
-      // to pointer types even if the pointee type is dependent.
-      (T->isDependentType() && !T->isPointerType()) ||
-       T->isRecordType()))
+       // FIXME: This is a hack! We want the lvalue-to-rvalue conversion applied
+       // to pointer types even if the pointee type is dependent.
+       (T->isDependentType() && !T->isPointerType()) || T->isRecordType()))
     return E;
 
   // The C standard is actually really unclear on this point, and
diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index eb87e0edda0128..da32ea251aeee1 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -670,8 +670,7 @@ class RecordMemberExprValidatorCCC final : public CorrectionCandidateCallback {
 }
 
 static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R,
-                                     Expr *BaseExpr,
-                                     QualType RTy,
+                                     Expr *BaseExpr, QualType RTy,
                                      SourceLocation OpLoc, bool IsArrow,
                                      CXXScopeSpec &SS, bool HasTemplateArgs,
                                      SourceLocation TemplateKWLoc,
@@ -695,9 +694,8 @@ static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R,
   SourceRange BaseRange = BaseExpr ? BaseExpr->getSourceRange() : SourceRange();
   if (!RTy->isDependentType() &&
       !SemaRef.isThisOutsideMemberFunctionBody(RTy) &&
-      SemaRef.RequireCompleteType(OpLoc, RTy,
-                                  diag::err_typecheck_incomplete_tag,
-                                  BaseRange))
+      SemaRef.RequireCompleteType(
+          OpLoc, RTy, diag::err_typecheck_incomplete_tag, BaseRange))
     return true;
 
   if (HasTemplateArgs || TemplateKWLoc.isValid()) {
@@ -792,16 +790,12 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R,
                                    Decl *ObjCImpDecl, bool HasTemplateArgs,
                                    SourceLocation TemplateKWLoc);
 
-ExprResult
-Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType,
-                               SourceLocation OpLoc, bool IsArrow,
-                               CXXScopeSpec &SS,
-                               SourceLocation TemplateKWLoc,
-                               NamedDecl *FirstQualifierInScope,
-                               const DeclarationNameInfo &NameInfo,
-                               const TemplateArgumentListInfo *TemplateArgs,
-                               const Scope *S,
-                               ActOnMemberAccessExtraArgs *ExtraArgs) {
+ExprResult Sema::BuildMemberReferenceExpr(
+    Expr *Base, QualType BaseType, SourceLocation OpLoc, bool IsArrow,
+    CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
+    NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo,
+    const TemplateArgumentListInfo *TemplateArgs, const Scope *S,
+    ActOnMemberAccessExtraArgs *ExtraArgs) {
   LookupResult R(*this, NameInfo, LookupMemberName);
 
   // Implicit member accesses.
@@ -809,9 +803,9 @@ Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType,
     TypoExpr *TE = nullptr;
     QualType RecordTy = BaseType;
     if (IsArrow) RecordTy = RecordTy->castAs<PointerType>()->getPointeeType();
-    if (LookupMemberExprInRecord(
-            *this, R, nullptr, RecordTy, OpLoc, IsArrow,
-            SS, TemplateArgs != nullptr, TemplateKWLoc, TE))
+    if (LookupMemberExprInRecord(*this, R, nullptr, RecordTy, OpLoc, IsArrow,
+                                 SS, TemplateArgs != nullptr, TemplateKWLoc,
+                                 TE))
       return ExprError();
     if (TE)
       return TE;
@@ -1006,9 +1000,8 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
 
   if (R.wasNotFoundInCurrentInstantiation() ||
       (SS.isValid() && !computeDeclContext(SS, false))) {
-    return ActOnDependentMemberExpr(BaseExpr, BaseExprType,
-                                    IsArrow, OpLoc,
-                                    SS, TemplateKWLoc, FirstQualifierInScope,
+    return ActOnDependentMemberExpr(BaseExpr, BaseExprType, IsArrow, OpLoc, SS,
+                                    TemplateKWLoc, FirstQualifierInScope,
                                     R.getLookupNameInfo(), TemplateArgs);
   }
 
@@ -1048,9 +1041,8 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
 
   if (R.empty()) {
     // Rederive where we looked up.
-    DeclContext *DC = (SS.isSet()
-                       ? computeDeclContext(SS, false)
-                       : BaseType->getAsRecordDecl());
+    DeclContext *DC = (SS.isSet() ? computeDeclContext(SS, false)
+                                  : BaseType->getAsRecordDecl());
 
     if (ExtraArgs) {
       ExprResult RetryExpr;
@@ -1077,7 +1069,7 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
       }
     }
 
-    if(DC) {
+    if (DC) {
       Diag(R.getNameLoc(), diag::err_no_member)
           << MemberName << DC
           << (BaseExpr ? BaseExpr->getSourceRange() : SourceRange());
@@ -1317,9 +1309,9 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R,
 
   QualType BaseType = BaseExpr.get()->getType();
 
-  #if 0
+#if 0
   assert(!BaseType->isDependentType());
-  #endif
+#endif
 
   DeclarationName MemberName = R.getLookupName();
   SourceLocation MemberLoc = R.getNameLoc();
@@ -1332,12 +1324,12 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R,
     if (const PointerType *Ptr = BaseType->getAs<PointerType>())
       BaseType = Ptr->getPointeeType();
     else if (!BaseType->isDependentType()) {
-      if (const ObjCObjectPointerType *Ptr
-               = BaseType->getAs<ObjCObjectPointerType>())
-      BaseType = Ptr->getPointeeType();
+      if (const ObjCObjectPointerType *Ptr =
+              BaseType->getAs<ObjCObjectPointerType>())
+        BaseType = Ptr->getPointeeType();
       else if (BaseType->isRecordType()) {
         // Recover from arrow accesses to records, e.g.:
-         //   struct MyRecord foo;
+        //   struct MyRecord foo;
         //   foo->bar
         // This is actually well-formed in C++ if MyRecord has an
         // overloaded operator->, but that should have been dealt with
@@ -1356,7 +1348,6 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R,
             << BaseType << BaseExpr.get()->getSourceRange();
         return ExprError();
       }
-
     }
   }
 
@@ -1378,8 +1369,8 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R,
   // Handle field access to simple records.
   if (BaseType->getAsRecordDecl() || BaseType->isDependentType()) {
     TypoExpr *TE = nullptr;
-    if (LookupMemberExprInRecord(S, R, BaseExpr.get(), BaseType, OpLoc, IsArrow, SS,
-                                 HasTemplateArgs, TemplateKWLoc, TE))
+    if (LookupMemberExprInRecord(S, R, BaseExpr.get(), BaseType, OpLoc, IsArrow,
+                                 SS, HasTemplateArgs, TemplateKWLoc, TE))
       return ExprError();
 
     // Returning valid-but-null is how we indicate to the caller that
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index efe5c1219712c6..184714eb6126e7 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -5789,10 +5789,10 @@ static ImplicitConversionSequence TryObjectArgumentInitialization(
     return ICS;
   }
 
-  // FIXME: Should this check getAsRecordDecl instead?
-  #if 0
+// FIXME: Should this check getAsRecordDecl instead?
+#if 0
   assert(FromType->isRecordType());
-  #endif
+#endif
 
   QualType ClassType = S.Context.getTypeDeclType(ActingContext);
   // C++98 [class.dtor]p2:
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index ceedbda979182b..4a71162e00758c 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -12939,14 +12939,14 @@ bool TreeTransform<Derived>::TransformOverloadExprDecls(OverloadExpr *Old,
     if (R.empty()) {
       // If a 'template' keyword was used, a lookup that finds only non-template
       // names is an error.
-      getSema().Diag(R.getNameLoc(), diag::err_template_kw_refers_to_non_template)
-          << R.getLookupName()
-          << Old->getQualifierLoc().getSourceRange()
-          << Old->hasTemplateKeyword()
-          << Old->getTemplateKeywordLoc();
-      getSema().Diag(FoundDecl->getLocation(), diag::note_template_kw_refers_to_non_template)
+      getSema().Diag(R.getNameLoc(),
+                     diag::err_template_kw_refers_to_non_template)
+          << R.getLookupName() << Old->getQualifierLoc().getSourceRange()
+          << Old->hasTemplateKeyword() << Old->getTemplateKeywordLoc();
+      getSema().Diag(FoundDecl->getLocation(),
+                     diag::note_template_kw_refers_to_non_template)
           << R.getLookupName();
-        return true;
+      return true;
     }
   }
 
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
index 3af67671aa5b5c..16b18b80f29019 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1569,8 +1569,9 @@ TEST_P(ASTMatchersTest, IsArrow_MatchesMemberVariablesViaArrow) {
       matches("class Y { void x() { y; } int y; };", memberExpr(isArrow())));
   EXPECT_TRUE(notMatches("class Y { void x() { (*this).y; } int y; };",
                          memberExpr(isArrow())));
-  EXPECT_TRUE(matches("template <class T> class Y { void x() { this->m; } int m; };",
-                         memberExpr(isArrow())));
+  EXPECT_TRUE(
+      matches("template <class T> class Y { void x() { this->m; } int m; };",
+              memberExpr(isArrow())));
   EXPECT_TRUE(
       notMatches("template <class T> class Y { void x() { (*this).m; } };",
                  cxxDependentScopeMemberExpr(isArrow())));

>From 607932dd0dc71ad106b49bcd5f80d8a2c062e2b8 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Thu, 7 Mar 2024 12:43:15 -0500
Subject: [PATCH 8/8] [FOLD] more tests

---
 .../temp.res/temp.dep/temp.dep.type/p4.cpp    | 383 +++++++++++++++---
 1 file changed, 323 insertions(+), 60 deletions(-)

diff --git a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp
index 7b36e3ad3b0131..2649addf1175ac 100644
--- a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp
+++ b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp
@@ -1,71 +1,334 @@
 // RUN: %clang_cc1 -Wno-unused-value -verify %s
 
 namespace N0 {
-  template<typename T>
   struct A {
-    int x;
-    void f();
-    using X = int;
-
-    void not_instantiated(A *a, A &b) {
-      x;
-      f();
-      new X;
-
-      this->x;
-      this->f();
-      this->A::x;
-      this->A::f();
-
-      a->x;
-      a->f();
-      a->A::x;
-      a->A::f();
-
-      (*this).x;
-      (*this).f();
-      (*this).A::x;
-      (*this).A::f();
-
-      b.x;
-      b.f();
-      b.A::x;
-      b.A::f();
-
-      A::x;
-      A::f();
-      new A::X;
-
-      y; // expected-error{{use of undeclared identifier 'y'}}
-      g(); // expected-error{{use of undeclared identifier 'g'}}
-      new Y; // expected-error{{unknown type name 'Y'}}
-
-      this->y; // expected-error{{no member named 'y' in 'A<T>'}}
-      this->g(); // expected-error{{no member named 'g' in 'A<T>'}}
-      this->A::y; // expected-error{{no member named 'y' in 'A<T>'}}
-      this->A::g(); // expected-error{{no member named 'g' in 'A<T>'}}
-
-      a->y; // expected-error{{no member named 'y' in 'A<T>'}}
-      a->g(); // expected-error{{no member named 'g' in 'A<T>'}}
-      a->A::y; // expected-error{{no member named 'y' in 'A<T>'}}
-      a->A::g(); // expected-error{{no member named 'g' in 'A<T>'}}
+    int x0;
+    static int x1;
+    int x2;
+    static int x3;
+
+    void f0();
+    static void f1();
+    void f2();
+    static void f3();
+
+    using M0 = int;
+    using M1 = int;
+
+    struct C0 { };
+    struct C1 { };
+  };
+
+  template<typename T>
+  struct B : A {
+    int x4;
+    static int x5;
+
+    using A::x2;
+    using A::x3;
+
+    void f4();
+    static void f5();
+
+    using A::f2;
+    using A::f3;
+
+    using M2 = int;
+
+    using A::M1;
+
+    struct C2 { };
+
+    using A::C1;
+
+    void not_instantiated(B *a, B &b) {
+      // All of the following should be found in the current instantiation.
+
+      new M0;
+      new B::M0;
+      new A::M0;
+      new B::A::M0;
+      new C0;
+      new B::C0;
+      new A::C0;
+      new B::A::C0;
+      new M1;
+      new B::M1;
+      new A::M1;
+      new B::A::M1;
+      new C1;
+      new B::C1;
+      new A::C1;
+      new B::A::C1;
+      new M2;
+      new B::M2;
+      new C2;
+      new B::C2;
+
+      x0;
+      B::x0;
+      A::x0;
+      B::A::x0;
+      x1;
+      B::x1;
+      A::x1;
+      B::A::x1;
+      x2;
+      B::x2;
+      A::x2;
+      B::A::x2;
+      x3;
+      B::x3;
+      A::x3;
+      B::A::x3;
+      x4;
+      B::x4;
+      x5;
+      B::x5;
+
+      f0();
+      B::f0();
+      A::f0();
+      B::A::f0();
+      f1();
+      B::f1();
+      A::f1();
+      B::A::f1();
+      f2();
+      B::f2();
+      A::f2();
+      B::A::f2();
+      f3();
+      B::f3();
+      A::f3();
+      B::A::f3();
+      f4();
+      B::f4();
+      f5();
+      B::f5();
+
+      this->x0;
+      this->B::x0;
+      this->A::x0;
+      this->B::A::x0;
+      this->x1;
+      this->B::x1;
+      this->A::x1;
+      this->B::A::x1;
+      this->x2;
+      this->B::x2;
+      this->A::x2;
+      this->B::A::x2;
+      this->x3;
+      this->B::x3;
+      this->A::x3;
+      this->B::A::x3;
+      this->x4;
+      this->B::x4;
+      this->x5;
+      this->B::x5;
+
+      this->f0();
+      this->B::f0();
+      this->A::f0();
+      this->B::A::f0();
+      this->f1();
+      this->B::f1();
+      this->A::f1();
+      this->B::A::f1();
+      this->f2();
+      this->B::f2();
+      this->A::f2();
+      this->B::A::f2();
+      this->f3();
+      this->B::f3();
+      this->A::f3();
+      this->B::A::f3();
+      this->f4();
+      this->B::f4();
+      this->f5();
+      this->B::f5();
+
+      a->x0;
+      a->B::x0;
+      a->A::x0;
+      a->B::A::x0;
+      a->x1;
+      a->B::x1;
+      a->A::x1;
+      a->B::A::x1;
+      a->x2;
+      a->B::x2;
+      a->A::x2;
+      a->B::A::x2;
+      a->x3;
+      a->B::x3;
+      a->A::x3;
+      a->B::A::x3;
+      a->x4;
+      a->B::x4;
+      a->x5;
+      a->B::x5;
+
+      a->f0();
+      a->B::f0();
+      a->A::f0();
+      a->B::A::f0();
+      a->f1();
+      a->B::f1();
+      a->A::f1();
+      a->B::A::f1();
+      a->f2();
+      a->B::f2();
+      a->A::f2();
+      a->B::A::f2();
+      a->f3();
+      a->B::f3();
+      a->A::f3();
+      a->B::A::f3();
+      a->f4();
+      a->B::f4();
+      a->f5();
+      a->B::f5();
+
+      (*this).x0;
+      (*this).B::x0;
+      (*this).A::x0;
+      (*this).B::A::x0;
+      (*this).x1;
+      (*this).B::x1;
+      (*this).A::x1;
+      (*this).B::A::x1;
+      (*this).x2;
+      (*this).B::x2;
+      (*this).A::x2;
+      (*this).B::A::x2;
+      (*this).x3;
+      (*this).B::x3;
+      (*this).A::x3;
+      (*this).B::A::x3;
+      (*this).x4;
+      (*this).B::x4;
+      (*this).x5;
+      (*this).B::x5;
+
+      (*this).f0();
+      (*this).B::f0();
+      (*this).A::f0();
+      (*this).B::A::f0();
+      (*this).f1();
+      (*this).B::f1();
+      (*this).A::f1();
+      (*this).B::A::f1();
+      (*this).f2();
+      (*this).B::f2();
+      (*this).A::f2();
+      (*this).B::A::f2();
+      (*this).f3();
+      (*this).B::f3();
+      (*this).A::f3();
+      (*this).B::A::f3();
+      (*this).f4();
+      (*this).B::f4();
+      (*this).f5();
+      (*this).B::f5();
+
+      b.x0;
+      b.B::x0;
+      b.A::x0;
+      b.B::A::x0;
+      b.x1;
+      b.B::x1;
+      b.A::x1;
+      b.B::A::x1;
+      b.x2;
+      b.B::x2;
+      b.A::x2;
+      b.B::A::x2;
+      b.x3;
+      b.B::x3;
+      b.A::x3;
+      b.B::A::x3;
+      b.x4;
+      b.B::x4;
+      b.x5;
+      b.B::x5;
+
+      b.f0();
+      b.B::f0();
+      b.A::f0();
+      b.B::A::f0();
+      b.f1();
+      b.B::f1();
+      b.A::f1();
+      b.B::A::f1();
+      b.f2();
+      b.B::f2();
+      b.A::f2();
+      b.B::A::f2();
+      b.f3();
+      b.B::f3();
+      b.A::f3();
+      b.B::A::f3();
+      b.f4();
+      b.B::f4();
+      b.f5();
+      b.B::f5();
+
+      // None of the following should be found in the current instantiation.
+
+      new M3; // expected-error{{unknown type name 'M3'}}
+      new B::M3; // expected-error{{no type named 'M3' in 'B<T>'}}
+      new A::M3; // expected-error{{no type named 'M3' in 'N0::A'}}
+      new B::A::M3; // expected-error{{no type named 'M3' in 'N0::A'}}
+
+      x6; // expected-error{{use of undeclared identifier 'x6'}}
+      B::x6; // expected-error{{no member named 'x6' in 'B<T>'}}
+      A::x6; // expected-error{{no member named 'x6' in 'N0::A'}}
+      B::A::x6; // expected-error{{no member named 'x6' in 'N0::A'}}
+      f6(); // expected-error{{use of undeclared identifier 'f6'}}
+      B::f6(); // expected-error{{no member named 'f6' in 'B<T>'}}
+      A::f6(); // expected-error{{no member named 'f6' in 'N0::A'}}
+      B::A::f6(); // expected-error{{no member named 'f6' in 'N0::A'}}
+
+      this->x6; // expected-error{{no member named 'x6' in 'B<T>'}}
+      this->B::x6; // expected-error{{no member named 'x6' in 'B<T>'}}
+      this->A::x6; // expected-error{{no member named 'x6' in 'N0::A'}}
+      this->B::A::x6; // expected-error{{no member named 'x6' in 'N0::A'}}
+      this->f6(); // expected-error{{no member named 'f6' in 'B<T>'}}
+      this->B::f6(); // expected-error{{no member named 'f6' in 'B<T>'}}
+      this->A::f6(); // expected-error{{no member named 'f6' in 'N0::A'}}
+      this->B::A::f6(); // expected-error{{no member named 'f6' in 'N0::A'}}
+
+      a->x6; // expected-error{{no member named 'x6' in 'B<T>'}}
+      a->B::x6; // expected-error{{no member named 'x6' in 'B<T>'}}
+      a->A::x6; // expected-error{{no member named 'x6' in 'N0::A'}}
+      a->B::A::x6; // expected-error{{no member named 'x6' in 'N0::A'}}
+      a->f6(); // expected-error{{no member named 'f6' in 'B<T>'}}
+      a->B::f6(); // expected-error{{no member named 'f6' in 'B<T>'}}
+      a->A::f6(); // expected-error{{no member named 'f6' in 'N0::A'}}
+      a->B::A::f6(); // expected-error{{no member named 'f6' in 'N0::A'}}
 
       // FIXME: An overloaded unary 'operator*' is built for these
       // even though the operand is a pointer (to a dependent type).
       // Type::isOverloadableType should return false for such cases.
-      (*this).y;
-      (*this).g();
-      (*this).A::y;
-      (*this).A::g();
-
-      b.y; // expected-error{{no member named 'y' in 'A<T>'}}
-      b.g(); // expected-error{{no member named 'g' in 'A<T>'}}
-      b.A::y; // expected-error{{no member named 'y' in 'A<T>'}}
-      b.A::g(); // expected-error{{no member named 'g' in 'A<T>'}}
-
-      A::y; // expected-error{{no member named 'y' in 'A<T>'}}
-      A::g(); // expected-error{{no member named 'g' in 'A<T>'}}
-      new A::Y; // expected-error{{no type named 'Y' in 'A<T>'}}
+      (*this).x6;
+      (*this).B::x6;
+      (*this).A::x6;
+      (*this).B::A::x6;
+      (*this).f6();
+      (*this).B::f6();
+      (*this).A::f6();
+      (*this).B::A::f6();
+
+      b.x6; // expected-error{{no member named 'x6' in 'B<T>'}}
+      b.B::x6; // expected-error{{no member named 'x6' in 'B<T>'}}
+      b.A::x6; // expected-error{{no member named 'x6' in 'N0::A'}}
+      b.B::A::x6; // expected-error{{no member named 'x6' in 'N0::A'}}
+      b.f6(); // expected-error{{no member named 'f6' in 'B<T>'}}
+      b.B::f6(); // expected-error{{no member named 'f6' in 'B<T>'}}
+      b.A::f6(); // expected-error{{no member named 'f6' in 'N0::A'}}
+      b.B::A::f6(); // expected-error{{no member named 'f6' in 'N0::A'}}
     }
   };
 } // namespace N0



More information about the cfe-commits mailing list