[clang] [Clang] Reland "Diagnose UB and emit error when identifier has both internal and external linkage" (PR #193567)

Vlad Serebrennikov via cfe-commits cfe-commits at lists.llvm.org
Thu Apr 23 04:17:14 PDT 2026


================
@@ -4802,6 +4802,27 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) {
       return New->setInvalidDecl();
     }
   }
+
+  // C2y 6.7.1p7: an identifier shall not appear with both internal and
+  // external linkage within a translation unit. Before C2y this was UB
+  // (C11 6.2.2p7).
+  //
+  // In C, a local shadow prevents a block-scope extern from inheriting the
+  // file-scope static's internal linkage (C2y 6.2.2p4), so it defaults to
+  // external linkage, creating the conflict.
+  //
+  // In C++, block-scope extern declarations target the enclosing namespace
+  // scope ([dcl.meaning.general]/3.5), bypassing local shadows entirely, so
+  // the extern always inherits internal linkage. No conflict arises.
+  if (!getLangOpts().CPlusPlus && New->isLocalVarDecl() &&
+      New->hasExternalStorage() && Old->isFileVarDecl() && Old->hasLinkage() &&
+      Previous.isShadowed() && Old->getFormalLinkage() == Linkage::Internal) {
----------------
Endilll wrote:

I have several questions:
1) Shouldn't we add similar code to `MergeFunctionDecl` and add tests with functions?
2) `Old->isFileVarDecl()` might be too optimistic. Consider:
```cpp
static int i = 0; // #1
void f() {
  extern int i; // #2, receives internal linkage from #1
  {
    int i;
    {
      extern int i; // do we fail to diagnose, because #2 is not at file scope?
    }
  }
}
```

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


More information about the cfe-commits mailing list