https://github.com/arrowten created 
https://github.com/llvm/llvm-project/pull/199239

Matching friend inline declarations were incorrectly treated as distinct 
overloads instead of redeclarations, leading to spurious
ambiguity diagnostics. This patch fixes the redeclaration handling and adds 
test coverage.

Fixes #180851.

>From 945a85efdc3a01a4ccd9b9b36306c029e8b5859e Mon Sep 17 00:00:00 2001
From: Ajay Wakodikar <[email protected]>
Date: Fri, 22 May 2026 13:11:15 -0400
Subject: [PATCH] [Sema] Fix friend inline redeclarations treated as overloads

---
 clang/docs/ReleaseNotes.rst                   |  1 +
 clang/lib/Sema/SemaDeclCXX.cpp                | 19 ++++++++--
 .../SemaCXX/friend-inline-redeclaration.cpp   | 36 +++++++++++++++++++
 3 files changed, 53 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/SemaCXX/friend-inline-redeclaration.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index cf16e40d026c3..4780feef7a8f5 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -664,6 +664,7 @@ Bug Fixes to C++ Support
 - Fixed a use-after-free bug when parsing default arguments containing lambdas 
in declarations with template-id declarators. (#GH196725)
 - Fixed a crash in constant evaluation using placement new on an array which 
was later initialized. (#GH196450)
 - Fixed an issue where Clang incorrectly accepted invalid unqualified uses of 
local nested class names outside their declaring scope. (#GH184622)
+- Fixed incorrect ambiguity diagnostics involving friend inline function 
redeclarations. (#GH180851)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index de837ff0608d0..af7cf3870608c 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -691,9 +691,22 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, 
FunctionDecl *Old,
     // C++11 [dcl.fcn.spec]p4:
     //   If the definition of a function appears in a translation unit before 
its
     //   first declaration as inline, the program is ill-formed.
-    Diag(New->getLocation(), diag::err_inline_decl_follows_def) << New;
-    Diag(Def->getLocation(), diag::note_previous_definition);
-    Invalid = true;
+    //
+    // Exception: a friend inline redeclaration of a function that takes the
+    // enclosing class as a parameter is valid. The class was incomplete at the
+    // point of the original definition, so it could not have been declared
+    // inline there.
+    bool HasEnclosingClassParam = false;
+    if (auto *RD = dyn_cast<CXXRecordDecl>(New->getLexicalDeclContext()))
+      for (const ParmVarDecl *P : New->parameters())
+        if (P->getType().getNonReferenceType()->getAsCXXRecordDecl() == RD)
+          HasEnclosingClassParam = true;
+
+    if (!HasEnclosingClassParam) {
+      Diag(New->getLocation(), diag::err_inline_decl_follows_def) << New;
+      Diag(Def->getLocation(), diag::note_previous_definition);
+      Invalid = true;
+    }
   }
 
   // C++17 [temp.deduct.guide]p3:
diff --git a/clang/test/SemaCXX/friend-inline-redeclaration.cpp 
b/clang/test/SemaCXX/friend-inline-redeclaration.cpp
new file mode 100644
index 0000000000000..959fd4dbbc99d
--- /dev/null
+++ b/clang/test/SemaCXX/friend-inline-redeclaration.cpp
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++23 %s
+
+// Friend inline re-declaration, where the parameter type is a SAME class
+struct A;
+
+int foo(A&) { return 1; }
+double compute(A&&) { return 3.14; }
+char process(A&, char) { return 'x'; }
+long action(long, A&) { return 42; }
+
+struct A {
+  friend inline int foo(A&);
+  friend inline double compute(A&&);
+  friend inline char process(A&, char);
+  friend inline long action(long, A&);
+};
+
+// Friend inline re-declaration, but the parameter type is a DIFFERENT class,
+// not the enclosing one. The exception should NOT apply.
+struct Other {};
+struct Owner;
+
+int bar(Other&) { return 0; } // expected-note {{previous definition is here}}
+
+struct Owner {
+  friend inline int bar(Other&); // expected-error {{inline declaration of 
'bar' follows non-inline definition}}
+};
+
+// Friend inline re-declaration with no parameters at all.
+// The exception requires at least one parameter of the enclosing class type.
+int init() { return 0; } // expected-note {{previous definition is here}}
+
+struct Engine {
+  friend inline int init(); // expected-error {{inline declaration of 'init' 
follows non-inline definition}}
+};

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to