[clang] [clang] Avoid caching linkage before redeclaration merging (PR #221476)

via cfe-commits cfe-commits at lists.llvm.org
Sat Sep 5 11:19:21 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Vedran Miletić (vedranmiletic)

<details>
<summary>Changes</summary>

Linkage queries made while a declaration is still being formed can cache a result based only on a previous declaration. Compute linkage without caching in these paths, and defer applying #pragma redefine_extname until after redeclaration merging.

Fixes #<!-- -->112737
Fixes #<!-- -->173464

Assisted-by: Codex (OpenAI ChatGPT)

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


5 Files Affected:

- (modified) clang/include/clang/AST/Decl.h (+3) 
- (modified) clang/lib/AST/Decl.cpp (+6) 
- (modified) clang/lib/Sema/SemaDecl.cpp (+24-13) 
- (modified) clang/lib/Sema/SemaDeclAttr.cpp (+2-1) 
- (added) clang/test/SemaCXX/redecl-linkage-before-merge.cpp (+13) 


``````````diff
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index eafeeecac7794..ccae943ed4b6e 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -422,6 +422,9 @@ class NamedDecl : public Decl {
   /// those.
   Linkage getLinkageInternal() const;
 
+  /// Compute this entity's linkage without caching it.
+  Linkage computeLinkageInternal() const;
+
   /// Get the linkage from a semantic point of view. Entities in
   /// anonymous namespaces are external (in c++98).
   Linkage getFormalLinkage() const;
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index a620e9f211ca6..78225f877dca3 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -1188,6 +1188,12 @@ Linkage NamedDecl::getLinkageInternal() const {
       .getLinkage();
 }
 
+Linkage NamedDecl::computeLinkageInternal() const {
+  return LinkageComputer{}
+      .computeLVForDecl(this, LVComputationKind::forLinkageOnly())
+      .getLinkage();
+}
+
 static bool isExportedFromModuleInterfaceUnit(const NamedDecl *D) {
   // FIXME: Handle isModulePrivate.
   switch (D->getModuleOwnershipKind()) {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 2451baaf94703..b12c2c8858409 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -10670,19 +10670,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
     StringLiteral *SE = cast<StringLiteral>(E);
     NewFD->addAttr(
         AsmLabelAttr::Create(Context, SE->getString(), SE->getStrTokenLoc(0)));
-  } else if (!ExtnameUndeclaredIdentifiers.empty()) {
-    llvm::MapVector<IdentifierInfo *, AsmLabelAttr *>::iterator I =
-        ExtnameUndeclaredIdentifiers.find(NewFD->getIdentifier());
-    if (I != ExtnameUndeclaredIdentifiers.end()) {
-      if (isDeclExternC(NewFD)) {
-        NewFD->addAttr(I->second);
-        ExtnameUndeclaredIdentifiers.erase(I);
-      } else if (NewFD->getDeclContext()
-                     ->getRedeclContext()
-                     ->isTranslationUnit())
-        Diag(NewFD->getLocation(), diag::warn_redefine_extname_not_applied)
-            << /*Variable*/0 << NewFD;
-    }
   }
 
   // Copy the parameter declarations from the declarator D to the function
@@ -10850,6 +10837,26 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
     }
   }
 
+  // This linkage query must happen after merging previous declarations.
+  auto ApplyPragmaRedefineExtname = [&] {
+    if (D.getAsmLabel() || ExtnameUndeclaredIdentifiers.empty())
+      return;
+
+    auto I = ExtnameUndeclaredIdentifiers.find(NewFD->getIdentifier());
+    if (I == ExtnameUndeclaredIdentifiers.end())
+      return;
+
+    if (isDeclExternC(NewFD)) {
+      NewFD->addAttr(I->second);
+      ExtnameUndeclaredIdentifiers.erase(I);
+    } else if (NewFD->getDeclContext()
+                   ->getRedeclContext()
+                   ->isTranslationUnit()) {
+      Diag(NewFD->getLocation(), diag::warn_redefine_extname_not_applied)
+          << /*Variable*/ 0 << NewFD;
+    }
+  };
+
   if (!getLangOpts().CPlusPlus) {
     // Perform semantic checking on the function declaration.
     if (!NewFD->isInvalidDecl() && NewFD->isMain())
@@ -10869,6 +10876,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
             Previous.getResultKind() != LookupResultKind::FoundOverloaded) &&
            "previous declaration set still overloaded");
 
+    ApplyPragmaRedefineExtname();
+
     // Diagnose no-prototype function declarations with calling conventions that
     // don't support variadic calls. Only do this in C and do it after merging
     // possibly prototyped redeclarations.
@@ -11038,6 +11047,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
             Previous.getResultKind() != LookupResultKind::FoundOverloaded) &&
            "previous declaration set still overloaded");
 
+    ApplyPragmaRedefineExtname();
+
     NamedDecl *PrincipalDecl = (FunctionTemplate
                                 ? cast<NamedDecl>(FunctionTemplate)
                                 : NewFD);
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 106605962171b..c096c1a467cb4 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1809,7 +1809,8 @@ static void handleAliasAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
     }
   } else {
     const auto *VD = cast<VarDecl>(D);
-    if (VD->isThisDeclarationADefinition() && VD->isExternallyVisible()) {
+    if (VD->isThisDeclarationADefinition() &&
+        isExternallyVisible(VD->computeLinkageInternal())) {
       S.Diag(AL.getLoc(), diag::err_alias_is_definition) << VD << 0;
       return;
     }
diff --git a/clang/test/SemaCXX/redecl-linkage-before-merge.cpp b/clang/test/SemaCXX/redecl-linkage-before-merge.cpp
new file mode 100644
index 0000000000000..b9d8d05f124ca
--- /dev/null
+++ b/clang/test/SemaCXX/redecl-linkage-before-merge.cpp
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+
+// https://github.com/llvm/llvm-project/issues/173464
+// A linkage query before redeclaration merging must not cache a result that
+// depends on a previous declaration.
+typedef float v4f __attribute__((vector_size(16)));
+extern const v4f kAlias;
+const v4f kAlias __attribute__((alias("kOne")));
+
+// https://github.com/llvm/llvm-project/issues/112737
+#pragma redefine_extname foo_cpp bar_cpp
+static int foo_cpp(); // expected-warning {{#pragma redefine_extname is applicable to external C declarations only; not applied to function 'foo_cpp'}}
+extern int foo_cpp() { return 1; } // expected-warning {{#pragma redefine_extname is applicable to external C declarations only; not applied to function 'foo_cpp'}}

``````````

</details>


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


More information about the cfe-commits mailing list