[clang] [Clang] Fix isWeakImported() to traverse redeclaration chain for avai… (PR #181482)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Feb 14 07:09:45 PST 2026
https://github.com/kevinlzh1108 updated https://github.com/llvm/llvm-project/pull/181482
>From 7f9f455552f801a11033371e8d0acbca07e52273 Mon Sep 17 00:00:00 2001
From: kevinlzh1108 <kevinlzh1108 at gmail.com>
Date: Sat, 14 Feb 2026 22:41:32 +0800
Subject: [PATCH] [Clang] Fix isWeakImported() to traverse redeclaration chain
for availability attributes
Decl::isWeakImported() only checked attributes on getMostRecentDecl(),
which missed availability attributes on earlier declarations in the
redeclaration chain. This caused incorrect strong linking when a forward
declaration (@class) without availability attributes became the most
recent declaration, shadowing the original @interface declaration that
carried the availability(ios,introduced=14.0) attribute.
This is particularly problematic with Clang Modules and Precompiled
Headers (PCH), where module deserialization order can cause a forward
declaration from one module (e.g., UIKit's @class UTType) to become
the most recent declaration, even though the original @interface
declaration (in UniformTypeIdentifiers) has the availability attribute.
The fix changes isWeakImported() to iterate over all redeclarations
using redecls(), consistent with how Decl::isReferenced() already
traverses the redeclaration chain in the same file.
Fixes a bug where apps targeting iOS < 14.0 would strongly link
UniformTypeIdentifiers symbols instead of weak-linking them, causing
crashes on older iOS versions.
---
clang/lib/AST/DeclBase.cpp | 18 ++++---
.../attr-availability-redecl-weak.m | 47 +++++++++++++++++++
2 files changed, 58 insertions(+), 7 deletions(-)
create mode 100644 clang/test/CodeGenObjC/attr-availability-redecl-weak.m
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 0a1e442656c35..0c122f808dea4 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -872,14 +872,18 @@ bool Decl::isWeakImported() const {
if (!canBeWeakImported(IsDefinition))
return false;
- for (const auto *A : getMostRecentDecl()->attrs()) {
- if (isa<WeakImportAttr>(A))
- return true;
-
- if (const auto *Availability = dyn_cast<AvailabilityAttr>(A)) {
- if (CheckAvailability(getASTContext(), Availability, nullptr,
- VersionTuple()) == AR_NotYetIntroduced)
+ // Traverse the entire redeclaration chain, since availability attributes
+ // may not be present on the most recent declaration (e.g., a @class forward
+ // declaration may lack the availability attribute from the @interface).
+ for (const auto *D : redecls()) {
+ for (const auto *A : D->attrs()) {
+ if (isa<WeakImportAttr>(A))
return true;
+
+ if (const auto *Availability = dyn_cast<AvailabilityAttr>(A))
+ if (CheckAvailability(getASTContext(), Availability, nullptr,
+ VersionTuple()) == AR_NotYetIntroduced)
+ return true;
}
}
diff --git a/clang/test/CodeGenObjC/attr-availability-redecl-weak.m b/clang/test/CodeGenObjC/attr-availability-redecl-weak.m
new file mode 100644
index 0000000000000..e1000e66a6042
--- /dev/null
+++ b/clang/test/CodeGenObjC/attr-availability-redecl-weak.m
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios12.0 -emit-llvm -o - %s | FileCheck %s
+
+// Test that isWeakImported() correctly traverses the redeclaration chain
+// to find availability attributes, even when a forward declaration (@class)
+// without availability attributes becomes the most recent declaration.
+
+// Case 1: @interface (with availability) first, then @class (without availability).
+// The @class becomes getMostRecentDecl(). Without the fix, isWeakImported()
+// would only check the @class's attributes and miss the availability attribute,
+// resulting in strong linkage instead of extern_weak.
+
+__attribute__((availability(ios,introduced=14.0)))
+ at interface WeakRedecl1
+ at end
+
+ at class WeakRedecl1;
+
+ at implementation WeakRedecl1 (TestCategory1)
+ at end
+
+// CHECK: @"OBJC_CLASS_$_WeakRedecl1" = extern_weak global
+
+// Case 2: @class first, then @interface (with availability).
+// This order already worked before the fix because @interface becomes
+// getMostRecentDecl() and carries the availability attribute.
+// We test it here to ensure the fix doesn't regress this case.
+
+ at class WeakRedecl2;
+
+__attribute__((availability(ios,introduced=14.0)))
+ at interface WeakRedecl2
+ at end
+
+ at implementation WeakRedecl2 (TestCategory2)
+ at end
+
+// CHECK: @"OBJC_CLASS_$_WeakRedecl2" = extern_weak global
+
+// Case 3: Single declaration with availability (baseline, no redeclaration).
+__attribute__((availability(ios,introduced=14.0)))
+ at interface WeakSingle
+ at end
+
+ at implementation WeakSingle (TestCategory3)
+ at end
+
+// CHECK: @"OBJC_CLASS_$_WeakSingle" = extern_weak global
More information about the cfe-commits
mailing list