Skip to content

[IRGen] Emit __objc_classrefs for ObjC classes in mangled type metadata - #90130

Open
AdamCmiel wants to merge 2 commits into
swiftlang:mainfrom
AdamCmiel:fix-objc-generic-classref-static-link
Open

[IRGen] Emit __objc_classrefs for ObjC classes in mangled type metadata#90130
AdamCmiel wants to merge 2 commits into
swiftlang:mainfrom
AdamCmiel:fix-objc-generic-classref-static-link

Conversation

@AdamCmiel

Copy link
Copy Markdown

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 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 i64, 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 already produces, ensuring the linker pulls the class from static archives via normal symbol resolution -- no -ObjC flag required.

Implementation note: uses getAnyNominal() + dyn_cast<ClassDecl> rather than getClassOrBoundGenericClass(), because after ObjC generic type parameter erasure (getRuntimeReifiedType), the class appears as a type node that getClassOrBoundGenericClass() does not recognize.

Reproduction

4 modules (3 ObjC, 1 Swift property wrapper) compiled into static archives, linked without -ObjC:

Compiler ObjC class symbols in binary Result
Unfixed (Swift 6.4) 0 SIGSEGV (exit 139)
Fixed 4 (Model, BaseType, WRP, ObjCGenericType) Success

Fixes #85441

@AdamCmiel
AdamCmiel requested a review from rjmccall as a code owner June 23, 2026 16:31
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
AdamCmiel force-pushed the fix-objc-generic-classref-static-link branch from c4e4304 to c2f5f74 Compare June 23, 2026 17:14
@AdamCmiel

Copy link
Copy Markdown
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.
@AdamCmiel

Copy link
Copy Markdown
Author

@swift-ci Please test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Runtime crash (in the runtime libswiftCore) in getTypeContextDescriptor for property wrapper

1 participant