Add opt-in Objective-C binding generation#780
Merged
tannergooding merged 5 commits intoJul 15, 2026
Merged
Conversation
Introduce `--generate-objective-c-bindings`, mapping an `@protocol` onto a layout-faithful struct (first field `isa`, mirroring `struct objc_object`) with a raw `Selectors` cache and friendly `objc_msgSend` members, plus a small inline `libobjc` support surface (`SEL`, `objc_msgSend`, `sel_registerName`, `objc_getClass`). Modeled on the COM feature: the struct is the object and is used via `Greeter*`, with the raw selector/msgSend substrate and friendly members exposed simultaneously. Scoped to instance methods with scalar/pointer/void signatures; variadics and by-value aggregates are diagnosed rather than emitted with a wrong ABI. Off by default, so existing behavior is unchanged. Also fix a NullReferenceException in `VisitRef` when a stray top-level Objective-C ref hangs off the translation unit and there is no active output builder. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Map instance @Property declarations onto friendly members: readonly maps to an expression-bodied getter, read-write to a get/set block dispatching the synthesized setter selector. Selectors are deduplicated across methods and accessors, and a shared BuildObjCMsgSend helper emits the per-call-site cast. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Generalize the emitter to any ObjCContainerDecl and map @interface onto the same opaque { void* isa } struct as @protocol (modern Objective-C uses non-fragile ivars, so there is no stable field layout to mirror), plus a cached Class object. Class (+) methods and properties become static members dispatched on Class; instance members dispatch on this. Record the superclass and adopted protocols in NativeTypeName (and NativeInheritance when enabled) rather than flattening, since dispatch is dynamic and every struct shares one layout. Flag @optional protocol members so callers know to guard with respondsToSelector:, and map ObjCObjectPointerType (id, NSObject *) to the generated struct pointer or void* instead of falling back to the native spelling. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Emit aggregate return/parameter types through the plain objc_msgSend entry point, which is ABI-correct on arm64 where there is no objc_msgSend_stret. Flag each affected member with a note that x86-64 stret is unsupported. Only variadic methods remain outside the message-send subset. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Map `@interface Foo (Bar)` onto a reopened `partial struct` for the class, emitting only the category's new message members. Make the nested `Selectors` cache `partial` so the interface and each category can contribute selectors; class members continue to dispatch on the interface's `Class` object. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
tannergooding
marked this pull request as ready for review
July 15, 2026 02:21
3 tasks
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.
Opt-in Objective-C binding generation, behind
--generate-objective-c-bindings. This is the first phased slice of #254 and mirrors how we already emit COM: expose the raw C ABI and a friendly wrapper on the same type, keeping the layout faithful.The parse/interop half of #254 has been in place for years (ClangSharp self-hosts the full Clang AST, and
clangsharp_Cursor_getSelectoralready exists), so this PR is emitter-only.Model
An
@protocol/@interface/category maps to apublic unsafe partial structwhose only field isvoid* isa— a byte-for-bytestruct objc_object. The object pointer isthis;isais the identity/class hook. Unlike COM'slpVtblthere is no fixed dispatch table, because Objective-C dispatch is dynamic throughobjc_msgSend. Modern non-fragile ivars are exactly why the struct is opaque rather than a fixed-layout record.Each in-subset method/property emits: a cached
SELin a nestedpublic static partial class Selectors, and a friendly member that casts the single rawobjc_msgSendentry point to the call site's exactdelegate* unmanaged<...>signature and invokes it with the implicit(self, _cmd)leading args. Instance members recoverselfviaUnsafe.AsPointer(ref this); class (+) members arestaticand dispatch onobjc_getClass(...). A small inlineObjectiveC/SELruntime surface is emitted once per output.ABI
Targets arm64 only, and says so. On Apple Silicon there is no
objc_msgSend_stret/_fpretsplit — struct and FP returns/args all go through the singleobjc_msgSendunder AAPCS64, which is what the JIT follows for the generated signature. By-value aggregates are therefore emitted through plainobjc_msgSendwith the real struct type, each flagged with a one-line note that x86-64_stretis intentionally unsupported (Apple dropped x86-64 macOS). This sidesteps per-triple variant selection entirely.What's included
ObjCProtocol,ObjCInterface,ObjCCategory(goto defaultwhen off).@protocol→ opaque struct +isa+Selectors+ friendly instance methods/properties.@interface→ same, plus the backingClassobject and class (+) members.@required/@optionalsurfaced, with arespondsToSelector:-guard note on optional members.[NativeTypeName]; members are not flattened (dynamic dispatch + identical layout means a pointer cast to the superclass just works).ObjCObjectPointerTypemapping (id→void*,NSObject *→NSObject*).partial structandpartialSelectors, adding only new members.@protocol(no backing class).Non-goals (intentional)
...can't be a fixed function-pointer signature.@implementation— definition-side; adds no callable surface over the@interface._stret/_fpretvariants are not supported.Before this is ready to merge
ObjectiveC/SELruntime surface should stay generated, move to a small hand-written support library, or be left to the consumer.Contributes to #254.