[clang] [Clang][Sema] Fix lookup of dependent operator= outside of complete-class contexts (PR #91498)

Krystian Stasiowski via cfe-commits cfe-commits at lists.llvm.org
Wed May 8 15:06:05 PDT 2024


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

>From 60d2030216403c7cfa8272396497d31aed314288 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Wed, 8 May 2024 12:18:49 -0400
Subject: [PATCH 1/5] [Clang][Sema] Fix lookup of dependent operator= outside
 of complete-class contexts

---
 clang/lib/Sema/SemaLookup.cpp   | 28 +++++++++++++---------------
 clang/lib/Sema/SemaTemplate.cpp |  7 ++-----
 2 files changed, 15 insertions(+), 20 deletions(-)

diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index e63da5875d2c..6bd5932212b9 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -1269,19 +1269,19 @@ struct FindLocalExternScope {
 };
 } // end anonymous namespace
 
+static bool isDependentAssignmentOperator(DeclarationName Name,
+                                          DeclContext *LookupContext) {
+  auto *LookupRecord = dyn_cast_if_present<CXXRecordDecl>(LookupContext);
+  return Name.getCXXOverloadedOperator() == OO_Equal && LookupRecord &&
+         !LookupRecord->isBeingDefined() && LookupRecord->isDependentContext();
+}
+
 bool Sema::CppLookupName(LookupResult &R, Scope *S) {
   assert(getLangOpts().CPlusPlus && "Can perform only C++ lookup");
 
   DeclarationName Name = R.getLookupName();
   Sema::LookupNameKind NameKind = R.getLookupKind();
 
-  // If this is the name of an implicitly-declared special member function,
-  // go through the scope stack to implicitly declare
-  if (isImplicitlyDeclaredMemberFunctionName(Name)) {
-    for (Scope *PreS = S; PreS; PreS = PreS->getParent())
-      if (DeclContext *DC = PreS->getEntity())
-        DeclareImplicitMemberFunctionsWithName(*this, Name, R.getNameLoc(), DC);
-  }
   // C++23 [temp.dep.general]p2:
   //   The component name of an unqualified-id is dependent if
   //   - it is a conversion-function-id whose conversion-type-id
@@ -1299,9 +1299,8 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
   if (isImplicitlyDeclaredMemberFunctionName(Name)) {
     for (Scope *PreS = S; PreS; PreS = PreS->getParent())
       if (DeclContext *DC = PreS->getEntity()) {
-        if (DC->isDependentContext() && isa<CXXRecordDecl>(DC) &&
-            Name.getCXXOverloadedOperator() == OO_Equal &&
-            !R.isTemplateNameLookup()) {
+        if (!R.isTemplateNameLookup() &&
+            isDependentAssignmentOperator(Name, DC)) {
           R.setNotFoundInCurrentInstantiation();
           return false;
         }
@@ -2472,8 +2471,6 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,
     }
   } QL(LookupCtx);
 
-  bool TemplateNameLookup = R.isTemplateNameLookup();
-  CXXRecordDecl *LookupRec = dyn_cast<CXXRecordDecl>(LookupCtx);
   if (!InUnqualifiedLookup && !R.isForRedeclaration()) {
     // C++23 [temp.dep.type]p5:
     //   A qualified name is dependent if
@@ -2486,13 +2483,14 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,
     if (DeclarationName Name = R.getLookupName();
         (Name.getNameKind() == DeclarationName::CXXConversionFunctionName &&
          Name.getCXXNameType()->isDependentType()) ||
-        (Name.getCXXOverloadedOperator() == OO_Equal && LookupRec &&
-         LookupRec->isDependentContext() && !TemplateNameLookup)) {
+        (!R.isTemplateNameLookup() &&
+         isDependentAssignmentOperator(Name, LookupCtx))) {
       R.setNotFoundInCurrentInstantiation();
       return false;
     }
   }
 
+  CXXRecordDecl *LookupRec = dyn_cast<CXXRecordDecl>(LookupCtx);
   if (LookupDirect(*this, R, LookupCtx)) {
     R.resolveKind();
     if (LookupRec)
@@ -2604,7 +2602,7 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx,
         //   template, and if the name is used as a template-name, the
         //   reference refers to the class template itself and not a
         //   specialization thereof, and is not ambiguous.
-        if (TemplateNameLookup)
+        if (R.isTemplateNameLookup())
           if (auto *TD = getAsTemplateNameDecl(ND))
             ND = TD;
 
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 7e57fa069672..480bc74c2001 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -726,7 +726,7 @@ Sema::ActOnDependentIdExpression(const CXXScopeSpec &SS,
                                  const DeclarationNameInfo &NameInfo,
                                  bool isAddressOfOperand,
                            const TemplateArgumentListInfo *TemplateArgs) {
-  DeclContext *DC = getFunctionLevelDeclContext();
+  QualType ThisType = getCurrentThisType();
 
   // C++11 [expr.prim.general]p12:
   //   An id-expression that denotes a non-static data member or non-static
@@ -748,10 +748,7 @@ Sema::ActOnDependentIdExpression(const CXXScopeSpec &SS,
     IsEnum = isa_and_nonnull<EnumType>(NNS->getAsType());
 
   if (!MightBeCxx11UnevalField && !isAddressOfOperand && !IsEnum &&
-      isa<CXXMethodDecl>(DC) &&
-      cast<CXXMethodDecl>(DC)->isImplicitObjectMemberFunction()) {
-    QualType ThisType = cast<CXXMethodDecl>(DC)->getThisType().getNonReferenceType();
-
+      !ThisType.isNull()) {
     // Since the 'this' expression is synthesized, we don't need to
     // perform the double-lookup check.
     NamedDecl *FirstQualifierInScope = nullptr;

>From 75c5e42ebc01299924c26b9fc03eaa2621696c8e Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Wed, 8 May 2024 15:11:02 -0400
Subject: [PATCH 2/5] [FOLD] add test

---
 .../test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp  | 8 ++++++++
 1 file changed, 8 insertions(+)

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 46dd52f8c4c1..00507261406f 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
@@ -471,6 +471,14 @@ namespace N3 {
       this->C::operator=(*this);
     }
   };
+
+  template<typename T>
+  struct D {
+    auto instantiated(D& d) -> decltype(operator=(d)); // expected-error {{use of undeclared 'operator='}}
+  };
+
+  template struct D<int>; // expected-note {{in instantiation of template class 'D<int>' requested here}}
+
 } // namespace N3
 
 namespace N4 {

>From 1fa4a767e4abdd514d6d515e2c8c09be4e9ea03c Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Wed, 8 May 2024 16:02:08 -0400
Subject: [PATCH 3/5] [FOLD] add test where template is not instantiated

---
 .../test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp | 9 +++++++--
 1 file changed, 7 insertions(+), 2 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 00507261406f..1a62fe688658 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
@@ -474,10 +474,15 @@ namespace N3 {
 
   template<typename T>
   struct D {
-    auto instantiated(D& d) -> decltype(operator=(d)); // expected-error {{use of undeclared 'operator='}}
+    auto not_instantiated() -> decltype(operator=(0)); // expected-error {{use of undeclared 'operator='}}
   };
 
-  template struct D<int>; // expected-note {{in instantiation of template class 'D<int>' requested here}}
+  template<typename T>
+  struct E {
+    auto instantiated(E& e) -> decltype(operator=(e)); // expected-error {{use of undeclared 'operator='}}
+  };
+
+  template struct E<int>; // expected-note {{in instantiation of template class 'E<int>' requested here}}
 
 } // namespace N3
 

>From 3731d0a3ff7dfb27b06e969fdc1610bfa1b2d94e Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Wed, 8 May 2024 16:03:55 -0400
Subject: [PATCH 4/5] [FOLD] address review comments

---
 clang/lib/Sema/SemaLookup.cpp | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 6bd5932212b9..100249450d5c 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -1248,6 +1248,13 @@ static DeclContext *findOuterContext(Scope *S) {
   return nullptr;
 }
 
+static bool isDependentAssignmentOperator(DeclarationName Name,
+                                          DeclContext *LookupContext) {
+  const auto *LookupRecord = dyn_cast_if_present<CXXRecordDecl>(LookupContext);
+  return Name.getCXXOverloadedOperator() == OO_Equal && LookupRecord &&
+         !LookupRecord->isBeingDefined() && LookupRecord->isDependentContext();
+}
+
 namespace {
 /// An RAII object to specify that we want to find block scope extern
 /// declarations.
@@ -1269,13 +1276,6 @@ struct FindLocalExternScope {
 };
 } // end anonymous namespace
 
-static bool isDependentAssignmentOperator(DeclarationName Name,
-                                          DeclContext *LookupContext) {
-  auto *LookupRecord = dyn_cast_if_present<CXXRecordDecl>(LookupContext);
-  return Name.getCXXOverloadedOperator() == OO_Equal && LookupRecord &&
-         !LookupRecord->isBeingDefined() && LookupRecord->isDependentContext();
-}
-
 bool Sema::CppLookupName(LookupResult &R, Scope *S) {
   assert(getLangOpts().CPlusPlus && "Can perform only C++ lookup");
 

>From 02c05a2381b5212f8266c2411f3c22d3cda5273b Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Wed, 8 May 2024 16:20:32 -0400
Subject: [PATCH 5/5] [FOLD] address review comments (again)

---
 clang/lib/Sema/SemaLookup.cpp                 | 21 ++++++++++++-------
 .../temp.res/temp.dep/temp.dep.type/p4.cpp    |  2 +-
 2 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 100249450d5c..e20de338ebb1 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -1248,13 +1248,6 @@ static DeclContext *findOuterContext(Scope *S) {
   return nullptr;
 }
 
-static bool isDependentAssignmentOperator(DeclarationName Name,
-                                          DeclContext *LookupContext) {
-  const auto *LookupRecord = dyn_cast_if_present<CXXRecordDecl>(LookupContext);
-  return Name.getCXXOverloadedOperator() == OO_Equal && LookupRecord &&
-         !LookupRecord->isBeingDefined() && LookupRecord->isDependentContext();
-}
-
 namespace {
 /// An RAII object to specify that we want to find block scope extern
 /// declarations.
@@ -1274,6 +1267,20 @@ struct FindLocalExternScope {
   LookupResult &R;
   bool OldFindLocalExtern;
 };
+
+/// Returns true if 'operator=' should be treated as a dependent name.
+bool isDependentAssignmentOperator(DeclarationName Name,
+                                   DeclContext *LookupContext) {
+  const auto *LookupRecord = dyn_cast_if_present<CXXRecordDecl>(LookupContext);
+  // If the lookup context is the current instantiation but we are outside a
+  // complete-class context, we will never find the implicitly declared
+  // copy/move assignment operators because they are declared at the closing '}'
+  // of the class specifier. In such cases, we treat 'operator=' like any other
+  // unqualified name because the results of name lookup in the template
+  // definition/instantiation context will always be the same.
+  return Name.getCXXOverloadedOperator() == OO_Equal && LookupRecord &&
+         !LookupRecord->isBeingDefined() && LookupRecord->isDependentContext();
+}
 } // end anonymous namespace
 
 bool Sema::CppLookupName(LookupResult &R, Scope *S) {
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 1a62fe688658..43053c18c507 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
@@ -482,7 +482,7 @@ namespace N3 {
     auto instantiated(E& e) -> decltype(operator=(e)); // expected-error {{use of undeclared 'operator='}}
   };
 
-  template struct E<int>; // expected-note {{in instantiation of template class 'E<int>' requested here}}
+  template struct E<int>; // expected-note {{in instantiation of template class 'N3::E<int>' requested here}}
 
 } // namespace N3
 



More information about the cfe-commits mailing list