Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 9, 2024

Conversation

sdkrystian
Copy link
Member

Fixes this crash caused by #90152.

Will add tests shortly.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels May 8, 2024
@llvmbot
Copy link
Member

llvmbot commented May 8, 2024

@llvm/pr-subscribers-clang

Author: Krystian Stasiowski (sdkrystian)

Changes

Fixes this crash caused by #90152.

Will add tests shortly.


Full diff: https://github.com/llvm/llvm-project/pull/91498.diff

2 Files Affected:

  • (modified) clang/lib/Sema/SemaLookup.cpp (+13-15)
  • (modified) clang/lib/Sema/SemaTemplate.cpp (+2-5)
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index e63da5875d2c9..6bd5932212b92 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 7e57fa0696725..480bc74c2001a 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;

@sdkrystian sdkrystian requested a review from erichkeane May 8, 2024 19:13
Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

few little things? Mostly looks ok.

DeclContext *LookupContext) {
auto *LookupRecord = dyn_cast_if_present<CXXRecordDecl>(LookupContext);
return Name.getCXXOverloadedOperator() == OO_Equal && LookupRecord &&
!LookupRecord->isBeingDefined() && LookupRecord->isDependentContext();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the !isBeingDefined? That seems weird given the name of this function.

Also, instead of making it static, can you just toss it in the anonymous NS above?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason operator= is dependent when the current class is a templated entity is because each specialization declares its own set of special member functions, and according to [special] p1, they are "declared at the closing } of the class-specifier". When instantiating a templated class, they are declared after all other member declarations are instantiated.

If the lookup context is the current instantiation and the current instantiation is incomplete (e.g. because operator= is named outside a complete-class context), we will never find the implicitly declared copy/move assignment operators because they are always declared last (neither in the template definition context, nor in the template instantiation context). So, we just treat it like any other unqualified name during lookup.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I'll add a test which calls operator= with a non-dependent argument -- in that case it will be diagnosed before the template is instantiated)

@sdkrystian sdkrystian requested a review from erichkeane May 8, 2024 20:36
@sdkrystian sdkrystian force-pushed the fix-operator-equal-crash branch from fa3eb50 to 02c05a2 Compare May 8, 2024 22:05
@sdkrystian sdkrystian merged commit 62b5b61 into llvm:main May 9, 2024
3 of 4 checks passed
@glandium
Copy link
Contributor

glandium commented May 9, 2024

This caused some breakage in something completely unrelated to operator= O_o

This is from webrtc code in Firefox:

/tmp/gecko/third_party/libwebrtc/rtc_base/containers/flat_map.h:331:49: error: out-of-line definition of 'try_emplace' does not match any declaration in 'flat_map<Key, Mapped, Compare, Container>'
  331 | auto flat_map<Key, Mapped, Compare, Container>::try_emplace(K&& key,
      |                                                 ^~~~~~~~~~~
/tmp/gecko/third_party/libwebrtc/rtc_base/containers/flat_map.h:343:49: error: out-of-line definition of 'try_emplace' does not match any declaration in 'flat_map<Key, Mapped, Compare, Container>'
  343 | auto flat_map<Key, Mapped, Compare, Container>::try_emplace(const_iterator hint,
      |                                                 ^~~~~~~~~~~
2 errors generated.

The lines with error are: https://searchfox.org/mozilla-central/rev/c34cf367c29601ed56ae4ea51e20b28cd8331f9c/third_party/libwebrtc/rtc_base/containers/flat_map.h#331,343

The corresponding declarations are:
https://searchfox.org/mozilla-central/rev/c34cf367c29601ed56ae4ea51e20b28cd8331f9c/third_party/libwebrtc/rtc_base/containers/flat_map.h#243,248

I don't see how they differ.

@glandium
Copy link
Contributor

glandium commented May 9, 2024

Reverting just the SemaTemplate.cpp change fixes it.

@sdkrystian
Copy link
Member Author

@glandium I've reduced it to the following:

template<typename T>
struct A
{
    static constexpr bool B = true;
};

template<bool V>
struct C { };

template<typename T>
struct D
{
    C<A<T>::B> f();
};

template<typename T>
auto D<T>::f() -> C<A<T>::B> { }

The problem is that we build a DependentScopeDeclRefExpr for A<T>::B in the first declaration of f, and a CXXDependentScopeMemberExpr for the second declaration of f.

Since we have no idea whether A<T>::B will be an implicit member access, I think that the correct behavior is that ActOnDependentIdExpression should never build a CXXDependentScopeMemberExpr.

@aeubanks
Copy link
Contributor

aeubanks commented May 9, 2024

Chromium is also seeing similar breakages. @sdkrystian is this breaking valid code? I can't tell from your latest comment. (if it is breaking valid code we should revert)

@sdkrystian
Copy link
Member Author

@aeubanks I think I'm going to revert this & maybe partially revert the changes in #90152 which cause operator= to be treated as a dependent name when the current class is templated. There are lots of edge cases that need to be accounted for. Thoughts @erichkeane ?

sdkrystian added a commit to sdkrystian/llvm-project that referenced this pull request May 9, 2024
@erichkeane
Copy link
Collaborator

@aeubanks I think I'm going to revert this & maybe partially revert the changes in #90152 which cause operator= to be treated as a dependent name when the current class is templated. There are lots of edge cases that need to be accounted for. Thoughts @erichkeane ?

Its sad to do so, but I think it makes the most sense. We've got a good amount of feedback of the breakages, and I think figuring them out and reapplying these changes in a followup patch is a perfectly reasonable way forward.

sdkrystian added a commit that referenced this pull request May 9, 2024
… from #91498, #90999, and #90152 (#91620)

This reverts changes in #91498, #90999, and #90152 which make
`operator=` dependent whenever the current class is templated.
searlmc1 pushed a commit to ROCm/llvm-project that referenced this pull request Oct 22, 2024
cherry-picked:
8009bbe [email protected]              Tue Apr 30 14:25:09 2024 -0400 Reapply "[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 (llvm#84050)" (llvm#90152)
3191e0b [email protected]              Fri May  3 17:07:52 2024 -0400 [Clang][Sema] Fix template name lookup for operator= (llvm#90999)
62b5b61 [email protected]              Wed May  8 20:49:59 2024 -0400 [Clang][Sema] Fix lookup of dependent operator= outside of complete-class contexts (llvm#91498)
75ebcbf [email protected]              Thu May  9 16:34:40 2024 -0400 [Clang][Sema] Revert changes to operator= lookup in templated classes from llvm#91498, llvm#90999, and llvm#90152 (llvm#91620)
596a9c1 [email protected]              Mon May 13 12:24:46 2024 -0400 [Clang][Sema] Fix bug where operator-> typo corrects in the current instantiation (llvm#91972)
fd87d76 [email protected]              Mon May 20 13:55:01 2024 -0400 [Clang][Sema] Don't build CXXDependentScopeMemberExprs for potentially implicit class member access expressions (llvm#92318)
e75b58c [email protected]              Mon May 20 14:50:58 2024 -0400 [Clang][Sema] Do not add implicit 'const' when matching constexpr function template explicit specializations after C++14 (llvm#92449)
bae2c54 [email protected] Mon Jul  1 20:55:57 2024 +0300 [clang][NFC] Move documentation of `Sema` functions into `Sema.h`
e6d305e [email protected]                 Mon Sep  4 16:54:42 2023 +0200 Add support of Windows Trace Logging macros

Change-Id: I521b2ebabd7eb9a0df78c577992bfd8508ba44fd
searlmc1 pushed a commit to ROCm/llvm-project that referenced this pull request Mar 27, 2025
… lookup (llvm#91620)

This patch cherry-pick missing part of 75ebcbf.

Fixes: SWDEV-520199

The original commit message is:

[Clang][Sema] Revert changes to operator= lookup in templated classes from llvm#91498, llvm#90999, and llvm#90152 (llvm#91620)

This reverts changes in llvm#91498, llvm#90999, and llvm#90152 which make
`operator=` dependent whenever the current class is templated.

Change-Id: I352db5cc20ecf4a0ccdbb921c63da0867651ffc4
rocm-ci pushed a commit to ROCm/llvm-project that referenced this pull request Apr 11, 2025
… lookup (llvm#91620)

This patch cherry-pick missing part of 75ebcbf.

Fixes: SWDEV-520199

The original commit message is:

[Clang][Sema] Revert changes to operator= lookup in templated classes from llvm#91498, llvm#90999, and llvm#90152 (llvm#91620)

This reverts changes in llvm#91498, llvm#90999, and llvm#90152 which make
`operator=` dependent whenever the current class is templated.

Change-Id: I352db5cc20ecf4a0ccdbb921c63da0867651ffc4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants