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
