[clang] [Sema] Fix friend inline redeclarations treated as overloads (PR #199239)
via cfe-commits
cfe-commits at lists.llvm.org
Fri May 22 10:19:50 PDT 2026
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 <ajaywakodikarsocial at gmail.com>
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}}
+};
More information about the cfe-commits
mailing list