Skip to content

c-interop: teach @c to handle ObjC type metatype Class. rdar://179401427#90608

Merged
nkcsgexi merged 1 commit into
swiftlang:mainfrom
nkcsgexi:179401427
Jul 12, 2026
Merged

c-interop: teach @c to handle ObjC type metatype Class. rdar://179401427#90608
nkcsgexi merged 1 commit into
swiftlang:mainfrom
nkcsgexi:179401427

Conversation

@nkcsgexi

Copy link
Copy Markdown
Contributor

ClangImporter imports C 'Class' as AnyClass, but declaring a C-interop function that takes/returns it via @c or @_extern(c) was rejected. Support it across three layers:

Sema: make AnyClass (an ExistentialMetatypeType of AnyObject) representable in ForeignLanguage::C, gated on ObjC interop since 'Class' is an ObjC runtime type. Reuse getObjCObjectRepresentable so C and ObjC agree on the kind.

SILGen: a @c/@_extern(c) function is emitted directly as its C entry point (no separate native thunk), so bridge the metatype at that boundary in place: objc_to_thick_metatype on a 'Class' parameter in the prolog, and thick_to_objc_metatype on an AnyClass result in the epilog. Both are no-ops unless the native and foreign SIL types differ.

PrintAsClang: keep the @c declaration in the plain-C section and emit '#include <objc/objc.h>' there when a @c decl uses 'Class', so the generated header stays usable from plain C.

ClangImporter imports C 'Class' as AnyClass, but declaring a C-interop
function that takes/returns it via @c or @_extern(c) was rejected. Support it
across three layers:

Sema: make AnyClass (an ExistentialMetatypeType of AnyObject) representable in
ForeignLanguage::C, gated on ObjC interop since 'Class' is an ObjC runtime
type. Reuse getObjCObjectRepresentable so C and ObjC agree on the kind.

SILGen: a @c/@_extern(c) function is emitted directly as its C entry point
(no separate native thunk), so bridge the metatype at that boundary in place:
objc_to_thick_metatype on a 'Class' parameter in the prolog, and
thick_to_objc_metatype on an AnyClass result in the epilog. Both are no-ops
unless the native and foreign SIL types differ.

PrintAsClang: keep the @c declaration in the plain-C section and emit
'#include <objc/objc.h>' there when a @c decl uses 'Class', so the generated
header stays usable from plain C.
@nkcsgexi

Copy link
Copy Markdown
Contributor Author

@swift-ci please smoke test

@nkcsgexi

Copy link
Copy Markdown
Contributor Author

@swift-ci please smoke test Linux

@nkcsgexi nkcsgexi merged commit 53a91ef into swiftlang:main Jul 12, 2026
3 checks passed
Comment thread lib/AST/Type.cpp
// ClangImporter imports C 'Class' as AnyClass. Restrict to the AnyObject
// class metatype and gate on ObjC interop, since 'Class' is an ObjC runtime
// type. Reuse the ObjC path's result so bridging stays consistent.
if (language == ForeignLanguage::C &&

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate on the motivation here? Why do we want to support this with @c as opposed to @objc?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! It's basically because the original issue that the originator reported was about using @_extern(c) on a global function taking Class as a parameter.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I was just wondering if this is something we wanted to support as opposed to asking the originator to use @objc.

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.

2 participants