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
