[clang] [Clang][Sema] Detect inline namespace ambiguity on namespace redefinition (PR #234689)

via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 22 09:48:55 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: cs24btech11059

<details>
<summary>Changes</summary>

Fixes #<!-- -->98199: an issue where Clang accepted ambiguous reopening of namespaces (involving inline namespaces), instead of raising an error.

Clang currently accepts this:

```c++
inline namespace A { namespace N {} }
inline namespace B { namespace N {} }
namespace N {}  // here
```
According to [[namespace.def]/p2](https://eel.is/c++draft/namespace.def#:~:text=identifier%20is%20the%20name%20of%20the%20namespace.%20The%20identifier%20is%20looked%20up%20by%20searching%20for%20it%20in%20the%20scopes%20of%20the%20namespace%20A%20in%20which%20D%20appears%20and%20of%20every%20element%20of%20the%20inline%20namespace%20set%20of%20A.%20If%20the%20lookup%20finds%20a%20namespace%2Ddefinition%20for%20a%20namespace%20N%2C%20D%20extends%20N%2C), we should be emitting an error. But current clang instead parses the ambiguous extension of the namespace as `::N` instead of considering whether to extend `A::N` or `B::N`.  gcc reports the error, but msvc and clang don't: https://godbolt.org/z/osh7snqKe.

### Code changes
- Clang currently only handles R.isSingleResult() when deciding whether a namespace definition extends an existing one, so in an ambiguous case (like above), R.isSingleResult() returns false and is treated as a brand new namespace.
- Internally, in those ambiguous cases, R stores all ambiguous matches and classifies it as LookupResultKind::Ambiguous.
- We catch this ambiguity by adding a check for R.isAmbiguous() before supposing it's a new namespace, and emit an error with found candidates.

### Tests
- Added a new test file: `clang/test/SemaCXX/inline-namespace-ambiguity.cpp`, covering cases related to this issue where current clang doesn't emit errors where it should.

---
Full diff: https://github.com/llvm/llvm-project/pull/234689.diff


2 Files Affected:

- (modified) clang/lib/Sema/SemaDeclCXX.cpp (+15-2) 
- (added) clang/test/SemaCXX/inline-namespace-ambiguity.cpp (+178) 


``````````diff
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 0fe0daa71c1bc..66acd630f35c9 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -12077,8 +12077,20 @@ Decl *Sema::ActOnStartNamespaceDef(Scope *NamespcScope,
       PrevNS = getStdNamespace();
       IsStd = true;
       AddToKnown = !IsInline;
+    } else if (R.isAmbiguous()) {
+      // Handle an ambiguous namespace reopening (often after inlining).
+      // Eg:
+      //    inline namespace A{ namespace N{}; };
+      //    inline namespace B{ namespace N{}; };
+      //    namespace N{};  // ambiguity: are we reopening A::N or B::N?
+      Diag(Loc, diag::err_ambiguous_reference) << II;
+      for (auto *D : R) {
+        Diag(D->getLocation(), diag::note_ambiguous_candidate) << D;
+      }
+      IsInvalid = true;
+      // Continue on to push Namespc as current DeclContext and return it.
     } else {
-      // We've seen this namespace for the first time.
+      // We're seeing this namespace for the first time.
       AddToKnown = !IsInline;
     }
   } else {
@@ -12161,7 +12173,8 @@ Decl *Sema::ActOnStartNamespaceDef(Scope *NamespcScope,
   ActOnDocumentableDecl(Namespc);
 
   // Although we could have an invalid decl (i.e. the namespace name is a
-  // redefinition), push it as current DeclContext and try to continue parsing.
+  // redefinition, or an ambiguous namespace reopening), push it as current
+  // DeclContext and try to continue parsing.
   // FIXME: We should be able to push Namespc here, so that the each DeclContext
   // for the namespace has the declarations that showed up in that particular
   // namespace definition.
diff --git a/clang/test/SemaCXX/inline-namespace-ambiguity.cpp b/clang/test/SemaCXX/inline-namespace-ambiguity.cpp
new file mode 100644
index 0000000000000..ffec07fe260d3
--- /dev/null
+++ b/clang/test/SemaCXX/inline-namespace-ambiguity.cpp
@@ -0,0 +1,178 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+
+namespace gh98199_0{
+inline namespace A {
+  namespace N {}
+}
+inline namespace B {
+  namespace N {}
+}
+namespace N {   // expected-error {{reference to 'N' is ambiguous}}
+    // expected-note at -6 {{candidate found by name lookup is 'gh98199_0::A::N'}}
+    // expected-note at -4 {{candidate found by name lookup is 'gh98199_0::B::N'}}
+  void f();
+}
+}
+
+namespace gh98199_1{
+inline namespace A{
+    inline namespace A_ { namespace N {} }
+    inline namespace A__ { namespace N {} }
+}
+
+inline namespace B{
+    inline namespace B_ { namespace N {} }
+    inline namespace B__ { namespace N {} }
+}
+
+namespace N{    // expected-error {{reference to 'N' is ambiguous}}
+    // expected-note at -10 {{candidate found by name lookup is 'gh98199_1::A_::N'}}
+    // expected-note at -10 {{candidate found by name lookup is 'gh98199_1::A__::N'}}
+    // expected-note at -7 {{candidate found by name lookup is 'gh98199_1::B_::N'}}
+    // expected-note at -7 {{candidate found by name lookup is 'gh98199_1::B__::N'}}
+}
+}
+
+namespace gh98199_2{
+namespace A{ namespace N{} }
+using namespace A;
+namespace N{}   // no error
+
+inline namespace B{ namespace M{} }
+namespace M{}   // no error
+}
+
+namespace gh98199_3{
+namespace A{ namespace N{} }
+namespace B{ namespace N{} }
+using namespace A;
+using namespace B;
+namespace N{}   // no error
+namespace A::N{};   // no error
+}
+
+namespace gh98199_4{
+namespace A{
+    inline namespace A_{
+        inline namespace A__{
+            namespace N{}
+        }
+    }
+}
+inline namespace B{
+    inline namespace B_1{
+        inline namespace B__{
+            namespace N{}
+        }
+    }
+    inline namespace B_2{
+        namespace N{}
+    }
+    namespace N{    // expected-error {{reference to 'N' is ambiguous}}
+        // expected-note at -7 {{candidate found by name lookup is 'gh98199_4::B_1::N'}}
+        // expected-note at -4 {{candidate found by name lookup is 'gh98199_4::B_2::N'}}
+    }
+}
+
+using namespace A;
+
+namespace N{    // expected-error {{reference to 'N' is ambiguous}}
+    // expected-note at -15 {{candidate found by name lookup is 'gh98199_4::B_1::N'}}
+    // expected-note at -12 {{candidate found by name lookup is 'gh98199_4::B_2::N'}}
+}
+}
+
+namespace gh98199_5{
+inline namespace A { namespace N {} }
+inline namespace B { namespace N {} }
+inline namespace C { namespace N {} }
+namespace N {}  // expected-error {{reference to 'N' is ambiguous}}
+    // expected-note at -4 {{candidate found by name lookup is 'gh98199_5::A::N'}}
+    // expected-note at -4 {{candidate found by name lookup is 'gh98199_5::B::N'}}
+    // expected-note at -4 {{candidate found by name lookup is 'gh98199_5::C::N'}}
+}
+
+namespace gh98199_6{
+namespace C{
+  inline namespace A { namespace N {} }
+  inline namespace B { namespace N {} }
+}
+namespace C{
+  namespace N {}  // expected-error {{reference to 'N' is ambiguous}}
+    // expected-note at -5 {{candidate found by name lookup is 'gh98199_6::C::A::N'}}
+    // expected-note at -5 {{candidate found by name lookup is 'gh98199_6::C::B::N'}}
+}
+
+namespace C::N{    // expected-error {{reference to 'N' is ambiguous}}
+    // expected-note at -10 {{candidate found by name lookup is 'gh98199_6::C::A::N'}}
+    // expected-note at -10 {{candidate found by name lookup is 'gh98199_6::C::B::N'}}
+}
+}
+
+namespace gh98199_7{
+inline namespace A { namespace N {} }
+namespace B { namespace N {} }
+namespace N {}   // no error
+}
+
+namespace gh98199_8{
+inline namespace A { inline namespace A_ { namespace N {} } }
+inline namespace B { namespace N {} }
+namespace N{  // expected-error {{reference to 'N' is ambiguous}}
+    // expected-note at -3 {{candidate found by name lookup is 'gh98199_8::A::N'}}
+    // expected-note at -3 {{candidate found by name lookup is 'gh98199_8::B::N'}}
+}
+}
+
+namespace gh98199_9{
+namespace { namespace N {} }
+namespace N {}   // no error
+}
+
+namespace gh98199_10{
+namespace {namespace N{} }
+inline namespace A { namespace N{} }
+namespace N{}   // no error
+}
+
+namespace gh98199_11{
+namespace { namespace N{} }
+namespace { inline namespace A{ namespace N{} } }
+namespace N{}
+namespace { namespace N{ // expected-error {{reference to 'N' is ambiguous}}
+    // expected-note at -4 {{candidate found by name lookup is 'gh98199_11::(anonymous namespace)::N'}}
+    // expected-note at -4 {{candidate found by name lookup is 'gh98199_11::(anonymous namespace)::A::N'}}
+}   // anon namespace
+}   // namespace N
+}
+
+namespace gh98199_12{
+inline namespace A { namespace X {} namespace Y {} }
+inline namespace B { namespace X {} namespace Y {} }
+namespace X {}  // expected-error {{reference to 'X' is ambiguous}}
+    // expected-note at -3 {{candidate found by name lookup is 'gh98199_12::A::X'}}
+    // expected-note at -3 {{candidate found by name lookup is 'gh98199_12::B::X'}}
+namespace Y {}  // expected-error {{reference to 'Y' is ambiguous}}
+    // expected-note at -6 {{candidate found by name lookup is 'gh98199_12::A::Y'}}
+    // expected-note at -6 {{candidate found by name lookup is 'gh98199_12::B::Y'}}
+}
+
+namespace gh98199_13{
+inline namespace A { namespace N { int x; } }
+namespace N{
+    int x;  // expected-error {{redefinition of 'x'}}
+    // expected-note at -3 {{previous definition is here}}
+}
+}
+
+namespace gh98199_14{
+namespace C {
+  inline namespace A { namespace N {} }
+  inline namespace B { namespace N {} }
+}
+namespace Other {
+  using namespace C;
+  namespace N {}   // no error
+}
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/234689


More information about the cfe-commits mailing list