[clang] [Clang] diagnose deleted/default redeclaration of defined friend functions (PR #136717)

Oleksandr T. via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 22 16:27:26 PDT 2025


https://github.com/a-tarasyuk updated https://github.com/llvm/llvm-project/pull/136717

>From cca0eacad86036539b41971794cc2ee0d1e2d577 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Tue, 22 Apr 2025 18:35:46 +0300
Subject: [PATCH 1/3] [Clang] diagnose deleted/defaulted redeclaration of
 defined friend functions

---
 clang/docs/ReleaseNotes.rst                   |  2 ++
 clang/lib/Parse/ParseCXXInlineMethods.cpp     |  7 ++++++
 .../SemaCXX/cxx2c-delete-with-message.cpp     | 22 ++++++++-----------
 3 files changed, 18 insertions(+), 13 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5ccd346a93b4f..4362c23ce4058 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -462,6 +462,8 @@ Bug Fixes in This Version
 - Fixed a crash when ``#embed`` appears as a part of a failed constant
   evaluation. The crashes were happening during diagnostics emission due to
   unimplemented statement printer. (#GH132641)
+- Clang now correctly diagnoses the deleted/defaulted redeclaration of defined
+  friend functions. (#GH135680)
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index b1064eb02b907..ab19c4d6dd706 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -142,6 +142,13 @@ NamedDecl *Parser::ParseCXXInlineMethodDef(
       SkipUntil(tok::semi);
     }
 
+    Decl *PrevDecl = FnD->getPreviousDecl();
+    if (PrevDecl && isa<FunctionDecl>(PrevDecl) &&
+        PrevDecl->getLexicalDeclContext() == FnD->getLexicalDeclContext()) {
+      Actions.CheckForFunctionRedefinition(FnD->getAsFunction(),
+                                           cast<FunctionDecl>(PrevDecl));
+    }
+
     return FnD;
   }
 
diff --git a/clang/test/SemaCXX/cxx2c-delete-with-message.cpp b/clang/test/SemaCXX/cxx2c-delete-with-message.cpp
index 5609da18c05aa..e4a68bdcf2a4d 100644
--- a/clang/test/SemaCXX/cxx2c-delete-with-message.cpp
+++ b/clang/test/SemaCXX/cxx2c-delete-with-message.cpp
@@ -274,26 +274,22 @@ void operators() {
 
 namespace gh135506 {
 struct a {
-  // FIXME: We currently don't diagnose these invalid redeclarations if the
-  // second declaration is defaulted or deleted. This probably needs to be
-  // handled in ParseCXXInlineMethodDef() after parsing the defaulted/deleted
-  // body.
-  friend consteval int f() { return 3; }
-  friend consteval int f() = delete("foo");
+  friend consteval int f() { return 3; }    // expected-note {{previous definition is here}}
+  friend consteval int f() = delete("foo"); // expected-error {{redefinition of 'f'}}
 
-  friend consteval int g() { return 3; }
-  friend consteval int g() = delete;
+  friend consteval int g() { return 3; } // expected-note {{previous definition is here}}
+  friend consteval int g() = delete;     // expected-error {{redefinition of 'g'}}
 
-  friend int h() { return 3; }
-  friend int h() = delete;
+  friend int h() { return 3; } // expected-note {{previous definition is here}}
+  friend int h() = delete;     // expected-error {{redefinition of 'h'}}
 
-  friend consteval int i() = delete; // expected-note {{previous definition is here}}
+  friend consteval int i() = delete;     // expected-note {{previous definition is here}}
   friend consteval int i() { return 3; } // expected-error {{redefinition of 'i'}}
 };
 
 struct b {
-  friend consteval bool operator==(b, b) { return true; } // expected-note {{previous declaration is here}}
-  friend consteval bool operator==(b, b) = default; // expected-error {{defaulting this equality comparison operator is not allowed because it was already declared outside the class}}
+  friend consteval bool operator==(b, b) { return true; } // expected-note {{previous definition is here}}
+  friend consteval bool operator==(b, b) = default;       // expected-error {{redefinition of 'operator=='}}
 };
 
 struct c {

>From 3efff6ca1726eb4b20ad801a8f55130b8bc38780 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Tue, 22 Apr 2025 19:02:01 +0300
Subject: [PATCH 2/3] use isa_and_present helper

---
 clang/lib/Parse/ParseCXXInlineMethods.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index ab19c4d6dd706..5f8ef891dd06f 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -143,7 +143,7 @@ NamedDecl *Parser::ParseCXXInlineMethodDef(
     }
 
     Decl *PrevDecl = FnD->getPreviousDecl();
-    if (PrevDecl && isa<FunctionDecl>(PrevDecl) &&
+    if (isa_and_present<FunctionDecl>(PrevDecl) &&
         PrevDecl->getLexicalDeclContext() == FnD->getLexicalDeclContext()) {
       Actions.CheckForFunctionRedefinition(FnD->getAsFunction(),
                                            cast<FunctionDecl>(PrevDecl));

>From ab9dff49a6d0ef75e51b61495a27ae4d5cea3b6a Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 23 Apr 2025 02:27:13 +0300
Subject: [PATCH 3/3] add additional test cases

---
 clang/lib/Parse/ParseCXXInlineMethods.cpp     | 11 +++---
 .../SemaCXX/cxx2c-delete-with-message.cpp     | 37 +++++++++++--------
 2 files changed, 28 insertions(+), 20 deletions(-)

diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index 5f8ef891dd06f..d28353486c493 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -142,11 +142,12 @@ NamedDecl *Parser::ParseCXXInlineMethodDef(
       SkipUntil(tok::semi);
     }
 
-    Decl *PrevDecl = FnD->getPreviousDecl();
-    if (isa_and_present<FunctionDecl>(PrevDecl) &&
-        PrevDecl->getLexicalDeclContext() == FnD->getLexicalDeclContext()) {
-      Actions.CheckForFunctionRedefinition(FnD->getAsFunction(),
-                                           cast<FunctionDecl>(PrevDecl));
+    if (FunctionDecl *FD =
+            dyn_cast_if_present<FunctionDecl>(FnD->getPreviousDecl())) {
+      if (isa<CXXRecordDecl>(FD->getLexicalDeclContext()) ||
+          Actions.getDefaultedFunctionKind(FD).asComparison() ==
+              Sema::DefaultedComparisonKind::None)
+        Actions.CheckForFunctionRedefinition(FnD->getAsFunction(), FD);
     }
 
     return FnD;
diff --git a/clang/test/SemaCXX/cxx2c-delete-with-message.cpp b/clang/test/SemaCXX/cxx2c-delete-with-message.cpp
index e4a68bdcf2a4d..78524c50e615c 100644
--- a/clang/test/SemaCXX/cxx2c-delete-with-message.cpp
+++ b/clang/test/SemaCXX/cxx2c-delete-with-message.cpp
@@ -273,27 +273,34 @@ void operators() {
 };
 
 namespace gh135506 {
-struct a {
-  friend consteval int f() { return 3; }    // expected-note {{previous definition is here}}
-  friend consteval int f() = delete("foo"); // expected-error {{redefinition of 'f'}}
+struct s1 {
+  friend consteval int a() { return 3; }    // expected-note {{previous definition is here}}
+  friend consteval int a() = delete("foo"); // expected-error {{redefinition of 'a'}}
 
-  friend consteval int g() { return 3; } // expected-note {{previous definition is here}}
-  friend consteval int g() = delete;     // expected-error {{redefinition of 'g'}}
+  friend consteval int b() { return 3; } // expected-note {{previous definition is here}}
+  friend consteval int b() = delete;     // expected-error {{redefinition of 'b'}}
 
-  friend int h() { return 3; } // expected-note {{previous definition is here}}
-  friend int h() = delete;     // expected-error {{redefinition of 'h'}}
+  friend int c() { return 3; } // expected-note {{previous definition is here}}
+  friend int c() = delete;     // expected-error {{redefinition of 'c'}}
 
-  friend consteval int i() = delete;     // expected-note {{previous definition is here}}
-  friend consteval int i() { return 3; } // expected-error {{redefinition of 'i'}}
+  friend consteval int d() = delete;     // expected-note {{previous definition is here}}
+  friend consteval int d() { return 3; } // expected-error {{redefinition of 'd'}}
 };
 
-struct b {
-  friend consteval bool operator==(b, b) { return true; } // expected-note {{previous definition is here}}
-  friend consteval bool operator==(b, b) = default;       // expected-error {{redefinition of 'operator=='}}
+struct s2 {
+  friend consteval bool operator==(s2, s2) { return true; } // expected-note {{previous definition is here}}
+  friend consteval bool operator==(s2, s2) = default;       // expected-error {{redefinition of 'operator=='}}
 };
 
-struct c {
-  friend consteval bool operator==(c, c) = default; // expected-note {{previous definition is here}}
-  friend consteval bool operator==(c, c) { return true; } // expected-error {{redefinition of 'operator=='}}
+struct s3 {
+  friend consteval bool operator==(s3, s3) = default;       // expected-note {{previous definition is here}}
+  friend consteval bool operator==(s3, s3) { return true; } // expected-error {{redefinition of 'operator=='}}
 };
+
+void e() {}                              // expected-note {{previous definition is here}}
+struct s4 { friend void e() = delete; }; // expected-error {{redefinition of 'e'}}
+
+struct s5 { friend void f() {} };         // expected-note {{previous definition is here}}
+struct s6 { friend void f() = delete; };  // expected-error {{redefinition of 'f'}}
+
 }



More information about the cfe-commits mailing list