[IRGen] Emit __objc_classrefs for ObjC classes in mangled type metadata - #90130
Open
AdamCmiel wants to merge 2 commits into
Open
[IRGen] Emit __objc_classrefs for ObjC classes in mangled type metadata#90130AdamCmiel wants to merge 2 commits into
AdamCmiel wants to merge 2 commits into
Conversation
When Swift emits a type metadata reference via a mangled name string (e.g., for Optional<SomeObjCGenericClass>), the ObjC class name is encoded textually and resolved at runtime via objc_getClass(). Unlike direct ObjC class usage (alloc/init, message sends), which emits a classref entry in __DATA,__objc_classrefs creating a linker-visible undefined reference to OBJC_CLASS_$_<name>, the mangled-name path created no such reference. This means when ObjC classes are provided by static archives and the only Swift reference to them is through generic type metadata (e.g., Optional<SomeObjCClass>, Array<(SomeObjCClass, Int)>), the linker has no undefined symbol to trigger archive member extraction. The class definition is never linked. At runtime, objc_getClass() returns nil, the demangling cache stores 0 (a non-negative value, treated as 'filled'), and the next access dereferences null-8 for the value witness table: EXC_BAD_ACCESS at 0xFFFFFFFFFFFFFFF8. Fix: in getTypeRefImpl, when emitting type refs for the Metadata role, walk the type to find ObjC class declarations and emit an __objc_classrefs entry for each. This creates the same undefined OBJC_CLASS_$_<name> reference that direct ObjC usage produces, ensuring the linker pulls the class from static archives via normal symbol resolution -- no -ObjC flag required. Implementation note: must use getAnyNominal() + dyn_cast<ClassDecl> rather than getClassOrBoundGenericClass(), because after ObjC generic type parameter erasure (getRuntimeReifiedType), the class appears as an UnboundGenericType-like node that getClassOrBoundGenericClass() does not recognize. Fixes swiftlang#85441 rdar://143519725
AdamCmiel
force-pushed
the
fix-objc-generic-classref-static-link
branch
from
June 23, 2026 17:14
c4e4304 to
c2f5f74
Compare
Author
|
@swift-ci Please test |
…classes The previous implementation emitted classref entries for all ObjC classes found in mangled type metadata strings, which created hard undefined symbol references that broke linking when the class wasn't available at link time (e.g. compile-only steps or libraries without the ObjC implementation linked). Narrow the check to isTypeErasedGenericClass() -- only ObjC lightweight-generic classes need classrefs here, since their type parameters are erased at runtime and the class name is resolved solely via objc_getClass() from the mangled metadata string.
Author
|
@swift-ci Please test |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When Swift emits a type metadata reference via a mangled name string (e.g., for
Optional<SomeObjCGenericClass>), the ObjC class name is encoded textually and resolved at runtime viaobjc_getClass(). Unlike direct ObjC class usage (alloc/init, message sends), which emits a classref entry in__DATA,__objc_classrefscreating a linker-visible undefined reference to_OBJC_CLASS_$_<name>, the mangled-name path created no such reference.This means when ObjC classes are provided by static archives and the only Swift reference to them is through generic type metadata (e.g.,
Optional<SomeObjCClass>,Array<(SomeObjCClass, Int)>), the linker has no undefined symbol to trigger archive member extraction. The class definition is never linked.At runtime,
objc_getClass()returns nil, the demangling cache stores 0 (a non-negative i64, treated as "filled"), and the next access dereferences null-8 for the value witness table:EXC_BAD_ACCESSat0xFFFFFFFFFFFFFFF8.Fix
In
getTypeRefImpl, when emitting type refs for theMetadatarole, walk the type to find ObjC class declarations and emit an__objc_classrefsentry for each. This creates the same undefined_OBJC_CLASS_$_<name>reference that direct ObjC usage already produces, ensuring the linker pulls the class from static archives via normal symbol resolution -- no-ObjCflag required.Implementation note: uses
getAnyNominal()+dyn_cast<ClassDecl>rather thangetClassOrBoundGenericClass(), because after ObjC generic type parameter erasure (getRuntimeReifiedType), the class appears as a type node thatgetClassOrBoundGenericClass()does not recognize.Reproduction
4 modules (3 ObjC, 1 Swift property wrapper) compiled into static archives, linked without
-ObjC:Fixes #85441