https://github.com/cerisier updated 
https://github.com/llvm/llvm-project/pull/233862

>From 1d9f96d623c3bf532458b71d05079ce4d4504156 Mon Sep 17 00:00:00 2001
From: Corentin Kerisit <[email protected]>
Date: Tue, 20 Oct 2026 16:13:54 +0200
Subject: [PATCH 1/8] [Clang] Add failing tests for inline namespace
 redeclarations

Add intentionally failing regression tests for namespace extensions and 
qualified tag definitions found through inline namespaces (LLVM #95154).

Check semantic ownership, name lookup, mangling, and PCH serialization. All 
three tests fail on unmodified Clang; the following implementation commit fixes 
them.
---
 ...t-dump-inline-namespace-redeclarations.cpp | 39 +++++++++++++
 ...mangle-inline-namespace-redeclarations.cpp | 58 +++++++++++++++++++
 .../inline-namespace-redeclarations.cpp       | 32 ++++++++++
 3 files changed, 129 insertions(+)
 create mode 100644 clang/test/AST/ast-dump-inline-namespace-redeclarations.cpp
 create mode 100644 
clang/test/CodeGenCXX/mangle-inline-namespace-redeclarations.cpp
 create mode 100644 clang/test/SemaCXX/inline-namespace-redeclarations.cpp

diff --git a/clang/test/AST/ast-dump-inline-namespace-redeclarations.cpp 
b/clang/test/AST/ast-dump-inline-namespace-redeclarations.cpp
new file mode 100644
index 00000000000000..197a5c85f7a7fb
--- /dev/null
+++ b/clang/test/AST/ast-dump-inline-namespace-redeclarations.cpp
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -std=c++11 -ast-dump %s | FileCheck %s
+// RUN: %clang_cc1 -std=c++11 -emit-pch -o %t %s
+// RUN: %clang_cc1 -std=c++11 -include-pch %t -ast-dump-all -x c++ /dev/null | 
FileCheck %s
+
+namespace reopen {
+// CHECK: NamespaceDecl [[V1:0x[^ ]+]] {{.*}} v1 inline
+inline namespace v1 {
+// CHECK: NamespaceDecl [[NS:0x[^ ]+]] {{.*}} ns
+namespace ns {
+struct Y;
+}
+}
+// CHECK: NamespaceDecl {{.*}} parent [[V1]] prev [[NS]] {{.*}} ns
+namespace ns {
+struct Y {};
+}
+}
+
+namespace qualified {
+// CHECK: NamespaceDecl [[QV1:0x[^ ]+]] {{.*}} v1 inline
+inline namespace v1 {
+// CHECK: CXXRecordDecl [[X:0x[^ ]+]] {{.*}} struct X
+struct X;
+}
+}
+namespace alias = qualified;
+// CHECK: CXXRecordDecl {{.*}} parent [[QV1]] prev [[X]] {{.*}} struct X 
definition
+struct alias::X {};
+
+// Ordinary namespace redeclarations keep the current enclosing declaration
+// as both their lexical and semantic context.
+namespace ordinary {
+// CHECK: NamespaceDecl [[NESTED:0x[^ ]+]] {{.*}} nested
+namespace nested {}
+}
+namespace ordinary {
+// CHECK: NamespaceDecl {{0x[^ ]+}} prev [[NESTED]] {{.*}} nested
+namespace nested {}
+}
diff --git a/clang/test/CodeGenCXX/mangle-inline-namespace-redeclarations.cpp 
b/clang/test/CodeGenCXX/mangle-inline-namespace-redeclarations.cpp
new file mode 100644
index 00000000000000..fe1f5dbe72f91a
--- /dev/null
+++ b/clang/test/CodeGenCXX/mangle-inline-namespace-redeclarations.cpp
@@ -0,0 +1,58 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 -emit-llvm -o - 
%s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm -o - 
%s | FileCheck %s
+
+// GH95154: reopening a namespace through an inline namespace must not change
+// the namespace's semantic parent, including for newly declared members.
+namespace reopen {
+inline namespace v1 {
+namespace ns {
+struct Y;
+}
+}
+
+// CHECK-DAG: define {{.*}} @_ZN6reopen6beforeEPNS_2v12ns1YE(
+void before(v1::ns::Y *) {}
+
+namespace ns {
+struct X {};
+struct Y {};
+
+// CHECK-DAG: define {{.*}} @_ZN6reopen2v12ns1fEv(
+void f() {}
+// CHECK-DAG: @_ZN6reopen2v12ns5valueE =
+int value;
+}
+
+// CHECK-DAG: define {{.*}} @_ZN6reopen5afterEPNS_2v12ns1YE(
+void after(v1::ns::Y *) {}
+// CHECK-DAG: define {{.*}} @_ZN6reopen4takeEPNS_2v12ns1XE(
+void take(v1::ns::X *) {}
+}
+
+// A qualified class definition can find its declaration through an inline
+// namespace, as in libstdc++'s filesystem::_Dir definition.
+namespace qualified {
+inline namespace v1 {
+struct X;
+}
+}
+namespace alias = qualified;
+
+// CHECK-DAG: define {{.*}} @_Z6beforePN9qualified2v11XE(
+void before(alias::X *) {}
+
+struct alias::X {
+  void member() {}
+};
+
+// CHECK-DAG: define {{.*}} @_Z5afterPN9qualified2v11XE(
+void after(alias::X *p) { p->member(); }
+// CHECK-DAG: define {{.*}} @_ZN9qualified2v11X6memberEv(
+
+// An ordinary namespace must keep its existing mangling.
+namespace ordinary {
+struct X;
+}
+struct ordinary::X {};
+// CHECK-DAG: define {{.*}} @_Z4takePN8ordinary1XE(
+void take(ordinary::X *) {}
diff --git a/clang/test/SemaCXX/inline-namespace-redeclarations.cpp 
b/clang/test/SemaCXX/inline-namespace-redeclarations.cpp
new file mode 100644
index 00000000000000..6b6063f62da40d
--- /dev/null
+++ b/clang/test/SemaCXX/inline-namespace-redeclarations.cpp
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+// expected-no-diagnostics
+
+// Lookup must start in the semantic parent, rather than making the names in
+// the inline namespace ambiguous with those in its enclosing namespace.
+namespace reopen {
+constexpr int value = 0;
+inline namespace v1 {
+constexpr int value = 1;
+namespace ns {}
+}
+namespace ns {
+static_assert(value == 1, "lookup in the inline namespace");
+}
+}
+
+namespace qualified {
+constexpr int value = 0;
+inline namespace v1 {
+constexpr int value = 1;
+struct X;
+enum class E;
+}
+}
+namespace alias = qualified;
+struct alias::X {
+  static_assert(value == 1, "lookup in the inline namespace");
+};
+enum class alias::E { test = value };
+static_assert(static_cast<int>(alias::E::test) == 1,
+              "enum definitions retain their namespace too");

>From 7f74c2d74c64b4cdd8126d60510e88b42c010b70 Mon Sep 17 00:00:00 2001
From: Corentin Kerisit <[email protected]>
Date: Tue, 20 Oct 2026 16:14:03 +0200
Subject: [PATCH 2/8] [Clang] Preserve semantic parents across inline namespace
 redeclarations

Namespace extensions and qualified tag redeclarations found through inline 
namespaces must retain their original semantic parent. Preserve that ownership 
while keeping the lexical declaration context where the declaration was written.

This fixes name lookup and mangling that omitted the inline namespace and could 
merge distinct ABI symbols. The preceding regression tests fail before this 
change and pass afterward.

Fixes #95154.
---
 clang/lib/Sema/SemaDecl.cpp    |  6 ++++++
 clang/lib/Sema/SemaDeclCXX.cpp | 10 +++++++++-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 886b2a0b4c6d75..3aa5c3d073c241 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -18945,6 +18945,12 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind 
TUK, SourceLocation KWLoc,
             // tag. We're going to create a new Decl for it.
           }
 
+          // Qualified lookup can find a tag in an inline namespace. Its
+          // redeclaration belongs to that namespace, which need not be the
+          // context named by the qualifier.
+          if (SS.isNotEmpty())
+            SearchDC = PrevTagDecl->getDeclContext();
+
           // Okay, we're going to make a redeclaration.  If this is some kind
           // of reference, make sure we build the redeclaration in the same DC
           // as the original, and ignore the current access specifier.
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index cd9771526bee37..d7a2011b0c78cd 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -12097,8 +12097,16 @@ Decl *Sema::ActOnStartNamespaceDef(Scope *NamespcScope,
                                       &IsInline, PrevNS);
   }
 
+  // Namespace lookup can find a namespace in an inline namespace. An extension
+  // of that namespace retains its semantic parent, even when it is written in
+  // an enclosing namespace.
+  DeclContext *SemanticContext = CurContext;
+  if (PrevNS && !CurContext->getRedeclContext()->Equals(
+                    PrevNS->getDeclContext()->getRedeclContext()))
+    SemanticContext = PrevNS->getDeclContext();
   NamespaceDecl *Namespc = NamespaceDecl::Create(
-      Context, CurContext, IsInline, StartLoc, Loc, II, PrevNS, IsNested);
+      Context, SemanticContext, IsInline, StartLoc, Loc, II, PrevNS, IsNested);
+  Namespc->setLexicalDeclContext(CurContext);
   if (IsInvalid)
     Namespc->setInvalidDecl();
 

>From a91304a38cac0f6773e932fbb66bc29a7dc9628c Mon Sep 17 00:00:00 2001
From: Corentin Kerisit <[email protected]>
Date: Thu, 22 Oct 2026 09:52:36 +0200
Subject: [PATCH 3/8] [Clang] Clarify namespace boundaries in inline namespace
 tests

---
 .../ast-dump-inline-namespace-redeclarations.cpp | 16 ++++++++--------
 .../mangle-inline-namespace-redeclarations.cpp   | 14 +++++++-------
 .../SemaCXX/inline-namespace-redeclarations.cpp  | 10 +++++-----
 3 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/clang/test/AST/ast-dump-inline-namespace-redeclarations.cpp 
b/clang/test/AST/ast-dump-inline-namespace-redeclarations.cpp
index 197a5c85f7a7fb..3e60028418fa97 100644
--- a/clang/test/AST/ast-dump-inline-namespace-redeclarations.cpp
+++ b/clang/test/AST/ast-dump-inline-namespace-redeclarations.cpp
@@ -8,21 +8,21 @@ inline namespace v1 {
 // CHECK: NamespaceDecl [[NS:0x[^ ]+]] {{.*}} ns
 namespace ns {
 struct Y;
-}
-}
+} // namespace ns
+} // namespace v1
 // CHECK: NamespaceDecl {{.*}} parent [[V1]] prev [[NS]] {{.*}} ns
 namespace ns {
 struct Y {};
-}
-}
+} // namespace ns
+} // namespace reopen
 
 namespace qualified {
 // CHECK: NamespaceDecl [[QV1:0x[^ ]+]] {{.*}} v1 inline
 inline namespace v1 {
 // CHECK: CXXRecordDecl [[X:0x[^ ]+]] {{.*}} struct X
 struct X;
-}
-}
+} // namespace v1
+} // namespace qualified
 namespace alias = qualified;
 // CHECK: CXXRecordDecl {{.*}} parent [[QV1]] prev [[X]] {{.*}} struct X 
definition
 struct alias::X {};
@@ -32,8 +32,8 @@ struct alias::X {};
 namespace ordinary {
 // CHECK: NamespaceDecl [[NESTED:0x[^ ]+]] {{.*}} nested
 namespace nested {}
-}
+} // namespace ordinary
 namespace ordinary {
 // CHECK: NamespaceDecl {{0x[^ ]+}} prev [[NESTED]] {{.*}} nested
 namespace nested {}
-}
+} // namespace ordinary
diff --git a/clang/test/CodeGenCXX/mangle-inline-namespace-redeclarations.cpp 
b/clang/test/CodeGenCXX/mangle-inline-namespace-redeclarations.cpp
index fe1f5dbe72f91a..9c7d9ae6a89dd5 100644
--- a/clang/test/CodeGenCXX/mangle-inline-namespace-redeclarations.cpp
+++ b/clang/test/CodeGenCXX/mangle-inline-namespace-redeclarations.cpp
@@ -7,8 +7,8 @@ namespace reopen {
 inline namespace v1 {
 namespace ns {
 struct Y;
-}
-}
+} // namespace ns
+} // namespace v1
 
 // CHECK-DAG: define {{.*}} @_ZN6reopen6beforeEPNS_2v12ns1YE(
 void before(v1::ns::Y *) {}
@@ -21,21 +21,21 @@ struct Y {};
 void f() {}
 // CHECK-DAG: @_ZN6reopen2v12ns5valueE =
 int value;
-}
+} // namespace ns
 
 // CHECK-DAG: define {{.*}} @_ZN6reopen5afterEPNS_2v12ns1YE(
 void after(v1::ns::Y *) {}
 // CHECK-DAG: define {{.*}} @_ZN6reopen4takeEPNS_2v12ns1XE(
 void take(v1::ns::X *) {}
-}
+} // namespace reopen
 
 // A qualified class definition can find its declaration through an inline
 // namespace, as in libstdc++'s filesystem::_Dir definition.
 namespace qualified {
 inline namespace v1 {
 struct X;
-}
-}
+} // namespace v1
+} // namespace qualified
 namespace alias = qualified;
 
 // CHECK-DAG: define {{.*}} @_Z6beforePN9qualified2v11XE(
@@ -52,7 +52,7 @@ void after(alias::X *p) { p->member(); }
 // An ordinary namespace must keep its existing mangling.
 namespace ordinary {
 struct X;
-}
+} // namespace ordinary
 struct ordinary::X {};
 // CHECK-DAG: define {{.*}} @_Z4takePN8ordinary1XE(
 void take(ordinary::X *) {}
diff --git a/clang/test/SemaCXX/inline-namespace-redeclarations.cpp 
b/clang/test/SemaCXX/inline-namespace-redeclarations.cpp
index 6b6063f62da40d..bf9fae056e5304 100644
--- a/clang/test/SemaCXX/inline-namespace-redeclarations.cpp
+++ b/clang/test/SemaCXX/inline-namespace-redeclarations.cpp
@@ -9,11 +9,11 @@ constexpr int value = 0;
 inline namespace v1 {
 constexpr int value = 1;
 namespace ns {}
-}
+} // namespace v1
 namespace ns {
 static_assert(value == 1, "lookup in the inline namespace");
-}
-}
+} // namespace ns
+} // namespace reopen
 
 namespace qualified {
 constexpr int value = 0;
@@ -21,8 +21,8 @@ inline namespace v1 {
 constexpr int value = 1;
 struct X;
 enum class E;
-}
-}
+} // namespace v1
+} // namespace qualified
 namespace alias = qualified;
 struct alias::X {
   static_assert(value == 1, "lookup in the inline namespace");

>From a32ec6a23a338bafe3d06381116490e6babbbbfc Mon Sep 17 00:00:00 2001
From: Corentin Kerisit <[email protected]>
Date: Thu, 22 Oct 2026 09:53:27 +0200
Subject: [PATCH 4/8] [Clang] Extend CWG2061 coverage for inline namespace
 lookup

Check that reopening a namespace through its enclosing namespace retains lookup 
through the original inline namespace. The added assertion fails with ambiguous 
lookup on unmodified Clang and passes with the semantic-parent fix.
---
 clang/test/CXX/drs/cwg20xx.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/clang/test/CXX/drs/cwg20xx.cpp b/clang/test/CXX/drs/cwg20xx.cpp
index 5c23ef9c5240bf..236f9c70c47030 100644
--- a/clang/test/CXX/drs/cwg20xx.cpp
+++ b/clang/test/CXX/drs/cwg20xx.cpp
@@ -125,7 +125,9 @@ static_assert(__is_same(decltype(a), decltype(b)));
 namespace cwg2061 { // cwg2061: 2.7
 #if __cplusplus >= 201103L
   namespace A {
+    constexpr int value = 0;
     inline namespace b {
+      constexpr int value = 1;
       namespace C {
         // 'f' is the example from the DR.  'S' is an example where if we 
didn't
         // properly handle the two being the same, we would get an incomplete
@@ -138,6 +140,9 @@ namespace cwg2061 { // cwg2061: 2.7
 
   namespace A {
     namespace C {
+      // GH95154: reopening C must retain b as its semantic parent, so lookup
+      // finds b::value before considering A::value.
+      static_assert(value == 1, "lookup in the original inline namespace");
       template<> void f<int>() { }
       template<> struct S<int> { };
     }

>From 0fcabd0b5783cdb53c3000488e4186c1221ab801 Mon Sep 17 00:00:00 2001
From: Corentin Kerisit <[email protected]>
Date: Wed, 28 Oct 2026 18:38:53 +0100
Subject: [PATCH 5/8] [Clang] Remove unrelated CWG2061 test extension

Keep the inline namespace lookup regression in its standalone Sema test; it 
does not test the wording change that resolved CWG2061.
---
 clang/test/CXX/drs/cwg20xx.cpp | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/clang/test/CXX/drs/cwg20xx.cpp b/clang/test/CXX/drs/cwg20xx.cpp
index 236f9c70c47030..5c23ef9c5240bf 100644
--- a/clang/test/CXX/drs/cwg20xx.cpp
+++ b/clang/test/CXX/drs/cwg20xx.cpp
@@ -125,9 +125,7 @@ static_assert(__is_same(decltype(a), decltype(b)));
 namespace cwg2061 { // cwg2061: 2.7
 #if __cplusplus >= 201103L
   namespace A {
-    constexpr int value = 0;
     inline namespace b {
-      constexpr int value = 1;
       namespace C {
         // 'f' is the example from the DR.  'S' is an example where if we 
didn't
         // properly handle the two being the same, we would get an incomplete
@@ -140,9 +138,6 @@ namespace cwg2061 { // cwg2061: 2.7
 
   namespace A {
     namespace C {
-      // GH95154: reopening C must retain b as its semantic parent, so lookup
-      // finds b::value before considering A::value.
-      static_assert(value == 1, "lookup in the original inline namespace");
       template<> void f<int>() { }
       template<> struct S<int> { };
     }

>From d654bce18b0c8ff9ffa55d9c3a34066186bd046a Mon Sep 17 00:00:00 2001
From: Corentin Kerisit <[email protected]>
Date: Wed, 28 Oct 2026 18:38:54 +0100
Subject: [PATCH 6/8] [Clang] Clarify expected lookup scopes in namespace tests

---
 clang/test/SemaCXX/inline-namespace-redeclarations.cpp | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/clang/test/SemaCXX/inline-namespace-redeclarations.cpp 
b/clang/test/SemaCXX/inline-namespace-redeclarations.cpp
index bf9fae056e5304..6248990747d828 100644
--- a/clang/test/SemaCXX/inline-namespace-redeclarations.cpp
+++ b/clang/test/SemaCXX/inline-namespace-redeclarations.cpp
@@ -11,7 +11,8 @@ constexpr int value = 1;
 namespace ns {}
 } // namespace v1
 namespace ns {
-static_assert(value == 1, "lookup in the inline namespace");
+static_assert(value == 1,
+              "'value' should be found in the parent scope of 'ns', which is 
'v1'");
 } // namespace ns
 } // namespace reopen
 
@@ -25,8 +26,9 @@ enum class E;
 } // namespace qualified
 namespace alias = qualified;
 struct alias::X {
-  static_assert(value == 1, "lookup in the inline namespace");
+  static_assert(value == 1,
+                "'value' should be found in the parent scope of 'X', which is 
'v1'");
 };
 enum class alias::E { test = value };
 static_assert(static_cast<int>(alias::E::test) == 1,
-              "enum definitions retain their namespace too");
+              "'value' was supposed to be found in the parent scope of 'E', 
which is 'v1'");

>From 6448905468e8f2b460904ce29175e34be544d6ef Mon Sep 17 00:00:00 2001
From: Corentin Kerisit <[email protected]>
Date: Wed, 28 Oct 2026 19:19:12 +0100
Subject: [PATCH 7/8] [Clang] Verify inline namespace redeclarations through
 diagnostics

Replace AST-dump checks with redefinition diagnostics and namespace lookup 
assertions, both with textual inclusion and after PCH deserialization. Keep the 
ordinary namespace control and make the expected lookup scopes explicit.
---
 ...t-dump-inline-namespace-redeclarations.cpp | 39 -----------
 .../PCH/inline-namespace-redeclarations.cpp   | 65 +++++++++++++++++++
 2 files changed, 65 insertions(+), 39 deletions(-)
 delete mode 100644 clang/test/AST/ast-dump-inline-namespace-redeclarations.cpp
 create mode 100644 clang/test/PCH/inline-namespace-redeclarations.cpp

diff --git a/clang/test/AST/ast-dump-inline-namespace-redeclarations.cpp 
b/clang/test/AST/ast-dump-inline-namespace-redeclarations.cpp
deleted file mode 100644
index 3e60028418fa97..00000000000000
--- a/clang/test/AST/ast-dump-inline-namespace-redeclarations.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-// RUN: %clang_cc1 -std=c++11 -ast-dump %s | FileCheck %s
-// RUN: %clang_cc1 -std=c++11 -emit-pch -o %t %s
-// RUN: %clang_cc1 -std=c++11 -include-pch %t -ast-dump-all -x c++ /dev/null | 
FileCheck %s
-
-namespace reopen {
-// CHECK: NamespaceDecl [[V1:0x[^ ]+]] {{.*}} v1 inline
-inline namespace v1 {
-// CHECK: NamespaceDecl [[NS:0x[^ ]+]] {{.*}} ns
-namespace ns {
-struct Y;
-} // namespace ns
-} // namespace v1
-// CHECK: NamespaceDecl {{.*}} parent [[V1]] prev [[NS]] {{.*}} ns
-namespace ns {
-struct Y {};
-} // namespace ns
-} // namespace reopen
-
-namespace qualified {
-// CHECK: NamespaceDecl [[QV1:0x[^ ]+]] {{.*}} v1 inline
-inline namespace v1 {
-// CHECK: CXXRecordDecl [[X:0x[^ ]+]] {{.*}} struct X
-struct X;
-} // namespace v1
-} // namespace qualified
-namespace alias = qualified;
-// CHECK: CXXRecordDecl {{.*}} parent [[QV1]] prev [[X]] {{.*}} struct X 
definition
-struct alias::X {};
-
-// Ordinary namespace redeclarations keep the current enclosing declaration
-// as both their lexical and semantic context.
-namespace ordinary {
-// CHECK: NamespaceDecl [[NESTED:0x[^ ]+]] {{.*}} nested
-namespace nested {}
-} // namespace ordinary
-namespace ordinary {
-// CHECK: NamespaceDecl {{0x[^ ]+}} prev [[NESTED]] {{.*}} nested
-namespace nested {}
-} // namespace ordinary
diff --git a/clang/test/PCH/inline-namespace-redeclarations.cpp 
b/clang/test/PCH/inline-namespace-redeclarations.cpp
new file mode 100644
index 00000000000000..5d0fae09a96fdd
--- /dev/null
+++ b/clang/test/PCH/inline-namespace-redeclarations.cpp
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 -std=c++11 -include %s -verify %s
+// RUN: %clang_cc1 -std=c++11 -emit-pch -o %t %s
+// RUN: %clang_cc1 -std=c++11 -include-pch %t -verify %s
+
+#ifndef HEADER
+#define HEADER
+
+namespace reopen {
+constexpr int value = 0;
+inline namespace v1 {
+constexpr int value = 1;
+namespace ns {
+struct Y;
+} // namespace ns
+} // namespace v1
+namespace ns {
+struct Y {};
+} // namespace ns
+} // namespace reopen
+
+namespace qualified {
+constexpr int value = 0;
+inline namespace v1 {
+constexpr int value = 1;
+struct X;
+} // namespace v1
+} // namespace qualified
+namespace alias = qualified;
+struct alias::X {
+  static constexpr int get();
+};
+
+namespace ordinary {
+namespace nested {
+struct Z {};
+} // namespace nested
+} // namespace ordinary
+
+#else
+
+// The previous definitions may have been read from the PCH.
+// expected-note@* 3 {{previous definition is here}}
+
+namespace reopen {
+namespace ns {
+static_assert(
+    value == 1,
+    "'value' should be found in the parent scope of 'ns', which is 'v1'");
+struct Y {}; // expected-error {{redefinition of 'Y'}}
+} // namespace ns
+} // namespace reopen
+
+constexpr int alias::X::get() { return value; }
+static_assert(
+    alias::X::get() == 1,
+    "'value' should be found in the parent scope of 'X', which is 'v1'");
+struct alias::X {}; // expected-error {{redefinition of 'X'}}
+
+namespace ordinary {
+namespace nested {
+struct Z {}; // expected-error {{redefinition of 'Z'}}
+} // namespace nested
+} // namespace ordinary
+
+#endif

>From 9ab300b07791c065b25d34564d7a1c5b0b07ca38 Mon Sep 17 00:00:00 2001
From: Corentin Kerisit <[email protected]>
Date: Wed, 28 Oct 2026 19:59:47 +0100
Subject: [PATCH 8/8] [Clang] Minimize inline namespace mangling regressions

Keep one ordered symbol check for each reported failure: extending a namespace 
through an inline namespace and defining a class through a namespace alias. 
Remove overlapping lookup, PCH, redefinition, and ordinary namespace coverage.

Both symbols omit the inline namespace on unmodified Clang and include it with 
the fix.
---
 ...mangle-inline-namespace-redeclarations.cpp | 50 +++-----------
 .../PCH/inline-namespace-redeclarations.cpp   | 65 -------------------
 .../inline-namespace-redeclarations.cpp       | 34 ----------
 3 files changed, 8 insertions(+), 141 deletions(-)
 delete mode 100644 clang/test/PCH/inline-namespace-redeclarations.cpp
 delete mode 100644 clang/test/SemaCXX/inline-namespace-redeclarations.cpp

diff --git a/clang/test/CodeGenCXX/mangle-inline-namespace-redeclarations.cpp 
b/clang/test/CodeGenCXX/mangle-inline-namespace-redeclarations.cpp
index 9c7d9ae6a89dd5..d1504bbc0e2404 100644
--- a/clang/test/CodeGenCXX/mangle-inline-namespace-redeclarations.cpp
+++ b/clang/test/CodeGenCXX/mangle-inline-namespace-redeclarations.cpp
@@ -1,58 +1,24 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 -emit-llvm -o - 
%s | FileCheck %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm -o - 
%s | FileCheck %s
 
-// GH95154: reopening a namespace through an inline namespace must not change
-// the namespace's semantic parent, including for newly declared members.
-namespace reopen {
+// GH95154: reopening ns must preserve v1 in its members' mangled names.
 inline namespace v1 {
-namespace ns {
-struct Y;
-} // namespace ns
+namespace ns {}
 } // namespace v1
 
-// CHECK-DAG: define {{.*}} @_ZN6reopen6beforeEPNS_2v12ns1YE(
-void before(v1::ns::Y *) {}
-
 namespace ns {
-struct X {};
-struct Y {};
-
-// CHECK-DAG: define {{.*}} @_ZN6reopen2v12ns1fEv(
+// CHECK-LABEL: define {{.*}} @_ZN2v12ns1fEv(
 void f() {}
-// CHECK-DAG: @_ZN6reopen2v12ns5valueE =
-int value;
 } // namespace ns
 
-// CHECK-DAG: define {{.*}} @_ZN6reopen5afterEPNS_2v12ns1YE(
-void after(v1::ns::Y *) {}
-// CHECK-DAG: define {{.*}} @_ZN6reopen4takeEPNS_2v12ns1XE(
-void take(v1::ns::X *) {}
-} // namespace reopen
-
-// A qualified class definition can find its declaration through an inline
-// namespace, as in libstdc++'s filesystem::_Dir definition.
+// A qualified definition must preserve v1 in X's mangled name, as required
+// by libstdc++'s filesystem::_Dir definition.
 namespace qualified {
 inline namespace v1 {
 struct X;
 } // namespace v1
 } // namespace qualified
 namespace alias = qualified;
+struct alias::X {};
 
-// CHECK-DAG: define {{.*}} @_Z6beforePN9qualified2v11XE(
-void before(alias::X *) {}
-
-struct alias::X {
-  void member() {}
-};
-
-// CHECK-DAG: define {{.*}} @_Z5afterPN9qualified2v11XE(
-void after(alias::X *p) { p->member(); }
-// CHECK-DAG: define {{.*}} @_ZN9qualified2v11X6memberEv(
-
-// An ordinary namespace must keep its existing mangling.
-namespace ordinary {
-struct X;
-} // namespace ordinary
-struct ordinary::X {};
-// CHECK-DAG: define {{.*}} @_Z4takePN8ordinary1XE(
-void take(ordinary::X *) {}
+// CHECK-LABEL: define {{.*}} @_Z1fPN9qualified2v11XE(
+void f(alias::X *) {}
diff --git a/clang/test/PCH/inline-namespace-redeclarations.cpp 
b/clang/test/PCH/inline-namespace-redeclarations.cpp
deleted file mode 100644
index 5d0fae09a96fdd..00000000000000
--- a/clang/test/PCH/inline-namespace-redeclarations.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-// RUN: %clang_cc1 -std=c++11 -include %s -verify %s
-// RUN: %clang_cc1 -std=c++11 -emit-pch -o %t %s
-// RUN: %clang_cc1 -std=c++11 -include-pch %t -verify %s
-
-#ifndef HEADER
-#define HEADER
-
-namespace reopen {
-constexpr int value = 0;
-inline namespace v1 {
-constexpr int value = 1;
-namespace ns {
-struct Y;
-} // namespace ns
-} // namespace v1
-namespace ns {
-struct Y {};
-} // namespace ns
-} // namespace reopen
-
-namespace qualified {
-constexpr int value = 0;
-inline namespace v1 {
-constexpr int value = 1;
-struct X;
-} // namespace v1
-} // namespace qualified
-namespace alias = qualified;
-struct alias::X {
-  static constexpr int get();
-};
-
-namespace ordinary {
-namespace nested {
-struct Z {};
-} // namespace nested
-} // namespace ordinary
-
-#else
-
-// The previous definitions may have been read from the PCH.
-// expected-note@* 3 {{previous definition is here}}
-
-namespace reopen {
-namespace ns {
-static_assert(
-    value == 1,
-    "'value' should be found in the parent scope of 'ns', which is 'v1'");
-struct Y {}; // expected-error {{redefinition of 'Y'}}
-} // namespace ns
-} // namespace reopen
-
-constexpr int alias::X::get() { return value; }
-static_assert(
-    alias::X::get() == 1,
-    "'value' should be found in the parent scope of 'X', which is 'v1'");
-struct alias::X {}; // expected-error {{redefinition of 'X'}}
-
-namespace ordinary {
-namespace nested {
-struct Z {}; // expected-error {{redefinition of 'Z'}}
-} // namespace nested
-} // namespace ordinary
-
-#endif
diff --git a/clang/test/SemaCXX/inline-namespace-redeclarations.cpp 
b/clang/test/SemaCXX/inline-namespace-redeclarations.cpp
deleted file mode 100644
index 6248990747d828..00000000000000
--- a/clang/test/SemaCXX/inline-namespace-redeclarations.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
-// expected-no-diagnostics
-
-// Lookup must start in the semantic parent, rather than making the names in
-// the inline namespace ambiguous with those in its enclosing namespace.
-namespace reopen {
-constexpr int value = 0;
-inline namespace v1 {
-constexpr int value = 1;
-namespace ns {}
-} // namespace v1
-namespace ns {
-static_assert(value == 1,
-              "'value' should be found in the parent scope of 'ns', which is 
'v1'");
-} // namespace ns
-} // namespace reopen
-
-namespace qualified {
-constexpr int value = 0;
-inline namespace v1 {
-constexpr int value = 1;
-struct X;
-enum class E;
-} // namespace v1
-} // namespace qualified
-namespace alias = qualified;
-struct alias::X {
-  static_assert(value == 1,
-                "'value' should be found in the parent scope of 'X', which is 
'v1'");
-};
-enum class alias::E { test = value };
-static_assert(static_cast<int>(alias::E::test) == 1,
-              "'value' was supposed to be found in the parent scope of 'E', 
which is 'v1'");

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

Reply via email to