Skip to main content

swiftui_native/
conformance.rs

1//! Synthesising Swift protocol conformances at run time.
2//!
3//! Some SwiftUI modifiers are generic over a protocol whose conformances a
4//! *client* is expected to declare — `PreferenceKey` above all. There is no
5//! Swift source here to declare one, and the framework exports only its own
6//! internal conformances, so the way to reach those modifiers is to build the
7//! conformance the compiler would have emitted.
8//!
9//! That is less daunting than it sounds, because of how Swift compiles
10//! generics: a generic function receives the witness table as an ordinary
11//! argument. Only a dynamic cast consults the global conformance table. So a
12//! witness table built here and passed straight to `View.preference(key:value:)`
13//! is used exactly as a compiler-emitted one would be — no registration
14//! required.
15//!
16//! # What a witness table is
17//!
18//! One word per protocol requirement, in the order the protocol descriptor
19//! lists them, behind one word pointing at the conformance descriptor:
20//!
21//! ```text
22//! [0] conformance descriptor
23//! [1] witness for requirement 0
24//! [2] witness for requirement 1
25//! ...
26//! ```
27//!
28//! A requirement is a function pointer, except for an associated type, whose
29//! slot holds the type's metadata directly. (The runtime treats a slot with
30//! bit 0 set as a mangled name to resolve instead; storing metadata avoids
31//! needing a generic environment at all.)
32//!
33//! Requirements that carry a *default implementation* — the protocol descriptor
34//! records one per requirement — need no witness from us: the default is
35//! copied in. For `PreferenceKey` that leaves three to supply: the associated
36//! `Value`, `defaultValue`, and `reduce(value:nextValue:)`.
37//!
38//! # Three things that are not optional
39//!
40//! * The descriptor must point at the witness table through its
41//!   **witness table pattern**. A conformance *lookup* does not hand back a
42//!   table it was given; it materialises one from the pattern, and a null
43//!   pattern makes `swift_conformsToProtocol` report no conformance at all.
44//! * The conformance must be **registered**. Passing the table as a generic
45//!   argument is not enough: building a generic type that constrains a
46//!   parameter to the protocol makes the runtime look the conformance up.
47//! * The conforming type must not be a **standard library** type. Registering
48//!   a conformance for `Swift.Int` leaves the lookup still reporting none;
49//!   a framework type registers and resolves normally.
50//!
51//! None of the three fails loudly. Each surfaces as a null metadata response
52//! several calls later.
53
54use core::ffi::c_void;
55
56/// A relative pointer: a 32-bit signed offset from its own address.
57///
58/// Swift uses these throughout its metadata so that descriptors can live in
59/// read-only memory with no relocations. Null is encoded as a zero offset,
60/// which is unambiguous because no descriptor points at itself.
61unsafe fn relative(at: *const i32) -> *const u8 {
62    unsafe {
63        if *at == 0 {
64            core::ptr::null()
65        } else {
66            (at as *const u8).offset(*at as isize)
67        }
68    }
69}
70
71/// A Swift protocol descriptor, read only as far as this needs.
72pub struct Protocol {
73    descriptor: *const u32,
74    requirements: *const u32,
75    count: usize,
76}
77
78impl Protocol {
79    /// Read the descriptor `symbol` names.
80    ///
81    /// # Safety
82    /// `symbol` must be a protocol descriptor (`...Mp`).
83    pub unsafe fn from_symbol(symbol: &'static core::ffi::CStr) -> Self {
84        unsafe {
85            let p = crate::resolve::sym(symbol) as *const u32;
86            assert!(!p.is_null(), "protocol descriptor not found: {symbol:?}");
87            // ContextDescriptor{flags, parent}, name, numSignatureRequirements,
88            // numRequirements, associatedTypeNames, then the signature
89            // requirements (twelve bytes each) and finally the requirements.
90            let signature = *p.add(3) as usize;
91            let count = *p.add(4) as usize;
92            let requirements = (p.add(6) as *const u8).add(12 * signature) as *const u32;
93            Self { descriptor: p, requirements, count }
94        }
95    }
96
97    /// How many requirements the protocol has, and so how wide a witness table
98    /// for it must be.
99    pub fn requirement_count(&self) -> usize {
100        self.count
101    }
102
103    /// Print each requirement's kind and whether it has a default.
104    ///
105    /// Only for working out what a protocol needs; nothing depends on it.
106    pub fn describe(&self) {
107        unsafe {
108            for i in 0..self.count {
109                let flags = *self.requirements.add(2 * i);
110                let kind = flags & 0x0f;
111                let name = match kind {
112                    0 => "base protocol", 1 => "method", 2 => "init", 3 => "getter",
113                    4 => "setter", 5 => "read coroutine", 6 => "modify coroutine",
114                    7 => "associated type", 8 => "associated conformance",
115                    _ => "?",
116                };
117                let dflt = self.default_witness(i);
118                println!(
119                    "   [{i}] {name}{} {}",
120                    if flags & 0x10 != 0 { " (instance)" } else { "" },
121                    if dflt.is_null() { "— must supply" } else { "— has a default" },
122                );
123            }
124        }
125    }
126
127    /// The default implementation of requirement `i`, if it has one.
128    /// Whether requirement `i` has a default, and so need not be supplied.
129    ///
130    /// The ratio of defaults to requirements is what decides whether a
131    /// conformance is worth synthesising: `View` has one requirement and
132    /// several defaults, `Layout` has many of each.
133    pub fn has_default(&self, i: usize) -> bool {
134        !unsafe { self.default_witness(i) }.is_null()
135    }
136
137    unsafe fn default_witness(&self, i: usize) -> *const u8 {
138        unsafe { relative(self.requirements.add(2 * i + 1) as *const i32) }
139    }
140}
141
142/// What to put in one slot of a witness table.
143#[derive(Clone, Copy)]
144pub enum Witness {
145    /// Use the default implementation the protocol descriptor records.
146    ///
147    /// # Panics
148    /// At build time, if the requirement has no default.
149    Default,
150    /// A function implementing the requirement.
151    Function(*const c_void),
152    /// Metadata for an associated type, stored directly.
153    ///
154    /// This is the form to use. SwiftUI's own tables instead hold a pointer to
155    /// a mangled name with bit 0 set — the runtime accepts either — but a name
156    /// written here fails to demangle, because the blob the runtime expects is
157    /// not a plain string: it carries symbolic references, which are relative
158    /// pointers into the emitting image. Metadata needs no such context.
159    Metadata(*const c_void),
160}
161
162/// A conformance descriptor, laid out the way the compiler emits one.
163///
164/// Both references are *indirect*: each 32-bit offset addresses a neighbouring
165/// pointer-sized slot rather than the target. A direct offset could not reach —
166/// this is heap memory and the protocol descriptor is in the shared cache,
167/// further apart than a signed 32-bit offset spans.
168///
169/// The two fields signal that indirection differently, which is easy to get
170/// wrong. The protocol reference is a *relative indirectable* pointer, where
171/// bit 0 of the offset means "indirect". The type reference is a plain offset,
172/// and its kind lives in `flags` instead — `TypeReferenceKind` occupies bits
173/// three to five, and kind 1 is `IndirectTypeDescriptor`. It also points at a
174/// *nominal type descriptor*, not at type metadata.
175#[repr(C)]
176struct ConformanceDescriptor {
177    protocol_ref: i32,
178    type_ref: i32,
179    witness_table_pattern: i32,
180    flags: u32,
181    // The slots the relative pointers above address.
182    protocol_slot: *const c_void,
183    type_slot: *const c_void,
184}
185
186/// What a conformance record says its conforming type *is*.
187///
188/// The kind is a two-bit field at bit 3 of the conformance flags, and the four
189/// values are the four things a type reference can be. This crate needs two of
190/// them: a Swift struct minted at run time is named by its nominal descriptor,
191/// and an Objective-C class minted at run time has no nominal descriptor at all
192/// — its identity is the class object.
193#[derive(Clone, Copy)]
194pub enum Conformer {
195    /// A nominal type descriptor (`…Mn`): a struct, an enum, or a Swift class.
196    TypeDescriptor(*const c_void),
197    /// An Objective-C class object, as `objc_allocateClassPair` returns.
198    ///
199    /// The route by which a class conforms to a Swift protocol without there
200    /// being a Swift class: `Observation.Observable` requires `AnyObject`, and
201    /// an `NSObject` subclass is one.
202    ObjCClass(*mut c_void),
203}
204
205impl Conformer {
206    /// The pointer that goes in the type slot.
207    fn slot_value(self) -> *const c_void {
208        match self {
209            Self::TypeDescriptor(d) => d,
210            Self::ObjCClass(c) => c as *const c_void,
211        }
212    }
213
214    /// The `TypeReferenceKind` bits for the conformance flags.
215    fn flags(self) -> u32 {
216        match self {
217            // `IndirectTypeDescriptor` — the slot holds the descriptor.
218            Self::TypeDescriptor(_) => 1 << 3,
219            // `IndirectObjCClass` — the slot holds the class object.
220            Self::ObjCClass(_) => 3 << 3,
221        }
222    }
223}
224
225/// A conformance *record*: one relative pointer to a descriptor.
226///
227/// This is what a compiled binary's `__swift5_proto` section is made of, and
228/// what [`register`] hands the runtime.
229#[repr(C)]
230struct ConformanceRecord {
231    descriptor: i32,
232}
233
234/// Record, descriptor and witness table in one allocation.
235///
236/// Everything here is wired together with *32-bit* relative offsets, which is
237/// the whole reason the three cannot be three separate `Box`es: the allocator
238/// is free to place them more than two gigabytes apart, and the offset then
239/// silently truncates. The failure is not at registration — it is later, and
240/// somewhere else entirely, because `swift_conformsToProtocol` walks *every*
241/// registered record on any lookup it has not cached. One bad record turns an
242/// unrelated protocol query into a wild read.
243///
244/// Laid out as words so the tail can carry a witness table of any length:
245///
246/// | word | contents                                    |
247/// |------|---------------------------------------------|
248/// | 0    | the record — a relative pointer to word 1    |
249/// | 1–2  | the descriptor's four 32-bit fields          |
250/// | 3    | the protocol slot the first field addresses  |
251/// | 4    | the type slot the second field addresses     |
252/// | 5…   | the witness table                            |
253const RECORD_WORD: usize = 0;
254const DESCRIPTOR_WORD: usize = 1;
255const PROTOCOL_SLOT_WORD: usize = 3;
256const TYPE_SLOT_WORD: usize = 4;
257const TABLE_WORD: usize = 5;
258
259/// A 32-bit relative offset from `at` to `to`, checked rather than truncated.
260unsafe fn offset(to: *const u8, at: *const u8) -> i32 {
261    let delta = to as isize - at as isize;
262    i32::try_from(delta).expect("relative pointer out of range")
263}
264
265/// A synthesized conformance, kept alive for the process's lifetime.
266pub struct Conformance {
267    table: *const c_void,
268    descriptor: *const c_void,
269}
270
271impl Conformance {
272    /// The conformance descriptor the runtime was handed.
273    ///
274    /// Only of interest when checking that a lookup found *this* conformance
275    /// rather than one the framework already had.
276    pub fn descriptor(&self) -> *const c_void {
277        self.descriptor
278    }
279
280    /// The witness table, ready to pass as a generic argument.
281    pub fn witness_table(&self) -> *const c_void {
282        self.table
283    }
284
285    /// Fill a witness in after the fact.
286    ///
287    /// For a conformance that cannot be complete when it is built. `Shape`
288    /// requires `View`, and a shape's `View.Body` is
289    /// `_ShapeView<Self, ForegroundStyle>` — whose metadata needs `Self: Shape`.
290    /// Neither table can be finished before the other exists, so the `Shape` one
291    /// is registered with its `View` slot empty, the metadata is instantiated
292    /// against it, and the slot is filled here. Swift resolves the same knot with
293    /// lazy witness tables; this is the same trick with the laziness written out.
294    ///
295    /// # Safety
296    /// `slot` must be within the protocol's requirement count, `witness` must be
297    /// the right kind of thing for it, and nothing may have *used* the slot yet —
298    /// a witness table the runtime has already cached a lookup for is still
299    /// readable, so writing to it is only safe before anything dispatches through
300    /// that entry.
301    pub unsafe fn patch(&self, slot: usize, witness: *const c_void) {
302        unsafe {
303            let words = self.table as *mut *const c_void;
304            *words.add(slot + 1) = witness;
305        }
306    }
307}
308
309/// Tell the runtime the conformance exists.
310///
311/// Passing the witness table as a generic argument is *not* enough. Building a
312/// generic type that constrains a parameter to the protocol — here
313/// `_PreferenceWritingModifier<K>` — makes the runtime look the conformance up
314/// rather than take the caller's word for it, and an unregistered one fails
315/// with a null metadata response and no diagnostic.
316unsafe fn register(record: *const ConformanceRecord) {
317    unsafe extern "C" {
318        fn swift_registerProtocolConformances(begin: *const c_void, end: *const c_void);
319    }
320    unsafe {
321        let begin = record as *const c_void;
322        let end = (record as *const u8).add(core::mem::size_of::<ConformanceRecord>());
323        swift_registerProtocolConformances(begin, end as *const c_void);
324    }
325}
326
327/// Build a witness table for `conforming_type: protocol`.
328///
329/// The conformance is leaked on purpose: a witness table handed to SwiftUI
330/// outlives any Rust scope, and there is no moment at which freeing it would
331/// be safe.
332///
333/// `type_descriptor` is the conforming type's *nominal type descriptor*
334/// (`...Mn`), not its metadata.
335///
336/// # Safety
337/// Every `Witness::Function` must have the signature the corresponding
338/// requirement expects, under Swift's witness-method convention — `Self`'s
339/// metadata arrives in x20 and an indirect result through x8. Getting one
340/// wrong corrupts memory inside SwiftUI rather than failing here.
341pub unsafe fn synthesize(
342    protocol: &Protocol,
343    type_descriptor: *const c_void,
344    witnesses: &[Witness],
345) -> Conformance {
346    unsafe { synthesize_for(protocol, Conformer::TypeDescriptor(type_descriptor), witnesses) }
347}
348
349/// [`synthesize`] for a conforming type that is not named by a nominal
350/// descriptor.
351///
352/// # Safety
353/// As [`synthesize`], and `conformer` must describe the type the witnesses were
354/// written for.
355pub unsafe fn synthesize_for(
356    protocol: &Protocol,
357    conformer: Conformer,
358    witnesses: &[Witness],
359) -> Conformance {
360    unsafe {
361        assert_eq!(
362            witnesses.len(),
363            protocol.count,
364            "a witness table needs one entry per requirement",
365        );
366
367        // One allocation, because every link below is a 32-bit relative offset
368        // and separate allocations can land out of range of one another.
369        let words = TABLE_WORD + 1 + protocol.count;
370        let block = Box::leak(vec![0u64; words].into_boxed_slice());
371        let base = block.as_mut_ptr();
372
373        // The witness table: its own conformance descriptor, then one word per
374        // requirement. A conformance *lookup* does not hand back a table it was
375        // given — it materialises one from this pattern, so a null pattern makes
376        // `swift_conformsToProtocol` report no conformance at all, which
377        // surfaces much later as a null metadata response.
378        let table = base.add(TABLE_WORD);
379        for (i, w) in witnesses.iter().enumerate() {
380            let word = match *w {
381                Witness::Default => {
382                    let d = protocol.default_witness(i);
383                    assert!(!d.is_null(), "requirement {i} has no default implementation");
384                    d as u64
385                }
386                Witness::Function(f) => f as u64,
387                Witness::Metadata(m) => m as u64,
388            };
389            *table.add(1 + i) = word;
390        }
391
392        let descriptor = base.add(DESCRIPTOR_WORD) as *mut ConformanceDescriptor;
393        *base.add(PROTOCOL_SLOT_WORD) = protocol.descriptor as u64;
394        *base.add(TYPE_SLOT_WORD) = conformer.slot_value() as u64;
395        *table = descriptor as u64;
396
397        let d = descriptor as *const u8;
398        let protocol_slot = base.add(PROTOCOL_SLOT_WORD) as *const u8;
399        let type_slot = base.add(TYPE_SLOT_WORD) as *const u8;
400        // Each offset is measured from the field that holds it. Bit 0 of the
401        // protocol reference means "indirect"; the type reference signals the
402        // same thing through `flags` instead.
403        (*descriptor).protocol_ref = offset(protocol_slot, d) | 1;
404        (*descriptor).type_ref = offset(type_slot, d.add(4));
405        (*descriptor).witness_table_pattern = offset(table as *const u8, d.add(8));
406        (*descriptor).flags = conformer.flags();
407
408        let record = base.add(RECORD_WORD) as *mut ConformanceRecord;
409        (*record).descriptor = offset(d, record as *const u8);
410
411        let conformance = Conformance {
412            table: table as *const c_void,
413            descriptor: descriptor as *const c_void,
414        };
415        register(record);
416        conformance
417    }
418}
419
420// ── A `PreferenceKey` whose `Value` is `Swift.Int` ──────────────────────────
421//
422// The three requirements without defaults, written out. `Self`'s metadata
423// arrives in x20 and is ignored: a preference key is never instantiated, it is
424// only ever named.
425
426// `static var defaultValue: Value { get }` returns through x8, which a Rust
427// signature cannot name, so the entry point moves it into x0 first.
428core::arch::global_asm!(
429    ".p2align 2",
430    ".globl _rswift_pref_default_value",
431    "_rswift_pref_default_value:",
432    "mov x0, x8",
433    "b _rswift_pref_default_value_impl",
434);
435
436#[unsafe(no_mangle)]
437unsafe extern "C" fn rswift_pref_default_value_impl(out: *mut i64) {
438    unsafe { *out = 0 };
439}
440
441// `static func reduce(value: inout Value, nextValue: () -> Value)`.
442//
443// `Value` is an associated type, so every mention of it is abstract from the
444// callee's side: `value` arrives by address, and `nextValue` returns *through
445// x8* rather than in x0. Reading x0 instead yields whatever the closure left
446// there, which for a single preference writer is often the right answer by
447// accident and stops being one as soon as the tree has two.
448#[unsafe(no_mangle)]
449unsafe extern "C" fn rswift_pref_reduce(
450    value: *mut i64,
451    next: *const c_void,
452    context: *mut c_void,
453) {
454    unsafe {
455        let mut produced: i64 = 0;
456        core::arch::asm!("blr {f}", f = in(reg) next,
457            in("x8") &mut produced as *mut i64, in("x20") context,
458            lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
459            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
460            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
461            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
462            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
463        );
464        *value = produced;
465    }
466}
467
468unsafe extern "C" {
469    #[link_name = "rswift_pref_default_value"]
470    fn pref_default_value();
471}
472
473/// A `PreferenceKey` conformance whose `Value` is `Swift.Int`.
474///
475/// A preference key is an identity, never a value: SwiftUI instantiates the
476/// `Value`, not the key. So the key can be any nominal type, and this borrows
477/// `SwiftUI.Spacer` rather than synthesizing a type descriptor of its own.
478///
479/// Not a stdlib type, though — `Swift.Int` is the obvious candidate and it does
480/// not work. Registering a conformance for it leaves
481/// `swift_conformsToProtocol` still reporting none, presumably because the
482/// runtime resolves stdlib types by a path that does not consult registered
483/// records. A framework type registers and resolves normally.
484pub fn preference_key() -> &'static Conformance {
485    use std::sync::OnceLock;
486    static CACHE: OnceLock<Conformance> = OnceLock::new();
487    CACHE.get_or_init(|| unsafe {
488        let protocol = Protocol::from_symbol(c"$s7SwiftUI13PreferenceKeyMp");
489        assert_eq!(protocol.requirement_count(), 5, "PreferenceKey changed shape");
490        synthesize(
491            &protocol,
492            crate::resolve::spacer_descriptor(),
493            &[
494                Witness::Metadata(crate::resolve::int_meta()),
495                Witness::Function(pref_default_value as *const c_void),
496                Witness::Function(rswift_pref_reduce as *const c_void),
497                Witness::Default,
498                Witness::Default,
499            ],
500        )
501    })
502}
503
504// `Conformance` is only ever read, and the memory it points at is leaked.
505unsafe impl Send for Conformance {}
506unsafe impl Sync for Conformance {}
507
508/// A `LayoutValueKey` conformance whose `Value` is `Swift.Int`.
509///
510/// A second protocol through the same machinery, which is the point: nothing
511/// above is specific to `PreferenceKey`. `LayoutValueKey` asks for two
512/// witnesses rather than three — an associated `Value` and a `defaultValue` —
513/// and the key type is borrowed the same way.
514pub fn layout_value_key() -> &'static Conformance {
515    use std::sync::OnceLock;
516    static CACHE: OnceLock<Conformance> = OnceLock::new();
517    CACHE.get_or_init(|| unsafe {
518        let protocol = Protocol::from_symbol(c"$s7SwiftUI14LayoutValueKeyMp");
519        assert_eq!(protocol.requirement_count(), 2, "LayoutValueKey changed shape");
520        synthesize(
521            &protocol,
522            crate::resolve::divider_descriptor(),
523            &[
524                Witness::Metadata(crate::resolve::int_meta()),
525                Witness::Function(pref_default_value as *const c_void),
526            ],
527        )
528    })
529}
530
531core::arch::global_asm!(
532    ".p2align 2",
533    ".globl _rswift_stub_out_witness",
534    "_rswift_stub_out_witness:",
535    "str xzr, [x8]",
536    "ret",
537);
538
539unsafe extern "C" {
540    #[link_name = "rswift_stub_out_witness"]
541    fn stub_out_witness();
542}
543
544/// A `PresentationSizing` conformance for `SwiftUI.EmptyView`.
545///
546/// The cheapest conformance there is: every one of the protocol's three
547/// requirements carries a default implementation, so nothing has to be written
548/// — the table is the defaults and a descriptor.
549pub fn presentation_sizing() -> &'static Conformance {
550    use std::sync::OnceLock;
551    static CACHE: OnceLock<Conformance> = OnceLock::new();
552    CACHE.get_or_init(|| unsafe {
553        let protocol = Protocol::from_symbol(c"$s7SwiftUI18PresentationSizingMp");
554        let witnesses = vec![Witness::Default; protocol.requirement_count()];
555        synthesize(&protocol, crate::resolve::empty_view_descriptor(), &witnesses)
556    })
557}
558
559/// A `PreviewContext` conformance for `SwiftUI.EmptyView`.
560///
561/// Its one requirement is a subscript getter that SwiftUI does not call while
562/// the modifier is being applied, so the witness writes a null and returns.
563pub fn preview_context() -> &'static Conformance {
564    use std::sync::OnceLock;
565    static CACHE: OnceLock<Conformance> = OnceLock::new();
566    CACHE.get_or_init(|| unsafe {
567        let protocol = Protocol::from_symbol(c"$s7SwiftUI14PreviewContextMp");
568        synthesize(
569            &protocol,
570            crate::resolve::empty_view_descriptor(),
571            &[Witness::Function(stub_out_witness as *const c_void)],
572        )
573    })
574}
575
576/// `draw(layout:in:)` — a renderer that draws nothing.
577///
578/// `Text.Layout` arrives by address in x0, the `GraphicsContext` `inout` in x1,
579/// and `Self` in x20 under the witness-method convention.
580///
581/// This used to be empty, on the grounds that `GraphicsContext.draw` is
582/// `@_alwaysEmitIntoClient` and so unreachable. That is not true of all of its
583/// overloads: `draw(_ text: Text, at: CGPoint, anchor: UnitPoint)` is exported.
584/// Drawing nothing did compile and did match the reference — both sides drew
585/// nothing — but it made the case worthless as a *visual* check, which is the
586/// only kind a renderer is subject to. The parity suite listed it under FLAT
587/// for exactly that reason.
588///
589/// The layout is ignored: what this draws is a `Text` of its own, which is
590/// enough to put pixels on the screen in a position the comparison can see.
591/// Following the layout would need `Text.Layout`'s `Collection` conformance,
592/// its lines and their runs.
593unsafe extern "C" fn text_renderer_draw(_layout: *const c_void, ctx: *mut c_void) {
594    unsafe {
595        let t = crate::views::text_value("Fx");
596        // `ctx` is the `inout` pointer, and `draw` does not want it. A
597        // `GraphicsContext` is a struct of one class reference, and the method
598        // takes that reference by value: the IR loads `%0`'s storage and passes
599        // the result. Handing over the address of the context instead faults.
600        let storage = *(ctx as *const *const c_void);
601        // Text in x0-x3, the point in d0/d1, the anchor in d2/d3, and the
602        // context in x4 — not x20. `.topLeading` is (0, 0).
603        core::arch::asm!("blr {f}", f = in(reg) crate::resolve::graphics_draw_text(),
604            in("x0") t[0], in("x1") t[1], in("x2") t[2], in("x3") t[3],
605            in("x4") storage,
606            in("d0") 0.0f64, in("d1") 0.0f64, in("d2") 0.0f64, in("d3") 0.0f64,
607            lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
608            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
609            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
610            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
611            clobber_abi("C"),
612        );
613    }
614}
615
616/// A `TextRenderer` conformance on `SwiftUI.EdgeInsets`.
617///
618/// `TextRenderer` refines `Animatable`, so the witness table's first slot is not
619/// a function but another *witness table* — the conforming type's `Animatable`
620/// one. Nothing here can synthesize that: `Animatable` wants an associated type,
621/// a getter, a setter and a `modify` coroutine. So the host type is chosen for
622/// already having the conformance. `EdgeInsets` is the smallest public SwiftUI
623/// type that exports all three of a metadata accessor, a nominal type descriptor
624/// and an `Animatable` witness table.
625pub fn text_renderer() -> &'static Conformance {
626    use std::sync::OnceLock;
627    static CACHE: OnceLock<Conformance> = OnceLock::new();
628    CACHE.get_or_init(|| unsafe {
629        let protocol = Protocol::from_symbol(c"$s7SwiftUI12TextRendererMp");
630        assert_eq!(protocol.requirement_count(), 4, "TextRenderer changed shape");
631        synthesize(
632            &protocol,
633            crate::resolve::edge_insets_descriptor(),
634            &[
635                Witness::Metadata(crate::resolve::edge_insets_animatable_wt()),
636                Witness::Function(text_renderer_draw as *const c_void),
637                Witness::Default,
638                Witness::Default,
639            ],
640        )
641    })
642}
643
644// A key path's `id` witness returns the associated `ID` through x8 under the
645// witness-method convention, and `Self` arrives in x20 — neither of which a
646// plain `extern "C"` signature can name. The body is small enough to write
647// outright: store the constant and return.
648core::arch::global_asm!(
649    ".p2align 2",
650    ".globl _rswift_identifiable_id",
651    "_rswift_identifiable_id:",
652    "mov x0, #0",
653    "str x0, [x8]",
654    "ret",
655);
656
657unsafe extern "C" {
658    #[link_name = "rswift_identifiable_id"]
659    fn identifiable_id();
660}
661
662/// An `Identifiable` conformance on `SwiftUI.EdgeInsets`, with `ID == Int`.
663///
664/// Three requirements: the associated `ID`, its `Hashable` conformance, and the
665/// `id` getter. Unlike `TextRenderer` none of them needs a base protocol, so the
666/// host type only has to export a nominal type descriptor — but `EdgeInsets` is
667/// reused anyway, because the parity reference has to name the same type and a
668/// public one keeps that possible.
669///
670/// Every instance reports the same id. That is what `[EdgeInsets]` can mean here
671/// — the values are indistinguishable, so distinct ids would be a fiction.
672pub fn identifiable() -> &'static Conformance {
673    use std::sync::OnceLock;
674    static CACHE: OnceLock<Conformance> = OnceLock::new();
675    CACHE.get_or_init(|| unsafe {
676        let protocol = Protocol::from_symbol(c"$ss12IdentifiableMp");
677        assert_eq!(protocol.requirement_count(), 3, "Identifiable changed shape");
678        synthesize(
679            &protocol,
680            crate::resolve::edge_insets_descriptor(),
681            &[
682                Witness::Metadata(crate::resolve::int_hashable_wt()),
683                Witness::Metadata(crate::resolve::int_meta()),
684                Witness::Function(identifiable_id as *const c_void),
685            ],
686        )
687    })
688}
689
690/// An `Identifiable` conformance on `Swift.String`, with `ID == Int`.
691///
692/// `dragContainer` wants an item that is *both* `Transferable` and
693/// `Identifiable`. `String` already has the first and no public SwiftUI type has
694/// either, so the second is synthesized onto it — the standard library exports a
695/// nominal type descriptor for `String` like any other type, and a conformance
696/// record registered from here is as real as one a Swift module declares.
697pub fn string_identifiable() -> &'static Conformance {
698    use std::sync::OnceLock;
699    static CACHE: OnceLock<Conformance> = OnceLock::new();
700    CACHE.get_or_init(|| unsafe {
701        let protocol = Protocol::from_symbol(c"$ss12IdentifiableMp");
702        synthesize(
703            &protocol,
704            crate::resolve::string_descriptor(),
705            &[
706                Witness::Metadata(crate::resolve::int_hashable_wt()),
707                Witness::Metadata(crate::resolve::int_meta()),
708                Witness::Function(identifiable_id as *const c_void),
709            ],
710        )
711    })
712}
713
714/// An `Observable` conformance on `Foundation.JSONDecoder`.
715///
716/// `Observation.Observable` has **no requirements at all** — the macro that
717/// usually applies it supplies everything through extensions — so the witness
718/// table is nothing but its own descriptor. What is scarce is not the
719/// conformance but the *class*: `focusedValue` wants `AnyObject & Observable`,
720/// and a parity reference has to be able to name the same type. `JSONDecoder`
721/// is the only public Swift class reachable here that exports a nominal type
722/// descriptor, which is what registering a conformance needs.
723pub fn observable() -> &'static Conformance {
724    use std::sync::OnceLock;
725    static CACHE: OnceLock<Conformance> = OnceLock::new();
726    CACHE.get_or_init(|| unsafe {
727        let protocol = Protocol::from_symbol(c"$s11Observation10ObservableMp");
728        assert_eq!(protocol.requirement_count(), 0, "Observable changed shape");
729        synthesize(&protocol, crate::resolve::json_decoder_descriptor(), &[])
730    })
731}
732
733crate::thunk::runtime_thunk! {
734    /// `ObservableObject.objectWillChange`, as the compiler writes it.
735    ///
736    /// The IR for a retroactive conformance is four instructions long:
737    ///
738    /// ```llvm
739    /// define ... void @"...objectWillChange...TW"(ptr noalias sret(ptr) %0,
740    ///         ptr swiftself %1, ptr %Self, ptr %SelfWitnessTable) {
741    ///   %2 = call ... @"$s10Foundation11JSONDecoderCMa"(i64 0)
742    ///   %3 = extractvalue %swift.metadata_response %2, 0
743    ///   %4 = call ... @"...Combine default..."(ptr %3, ptr %SelfWitnessTable,
744    ///                                          ptr swiftself %1)
745    ///   store ptr %4, ptr %0
746    /// }
747    /// ```
748    ///
749    /// Every argument the default wants is already in the register it wants it
750    /// in — `Self` in x0, the witness table in x1, the instance in x20 — so
751    /// the only work is keeping x8 alive across the call and storing the
752    /// result through it. (The compiler re-derives `Self` from the metadata
753    /// accessor instead of forwarding x0; for a concrete conformance the two
754    /// are the same pointer.)
755    ///
756    /// The publisher comes back at +1 and is stored without a retain, which is
757    /// what the compiler emits: the caller owns what the getter returns.
758    fn object_will_change = "rswift_object_will_change" via RSWIFT_OBJECT_WILL_CHANGE {
759        prologue: [
760            "stp x29, x30, [sp, #-32]!",
761            "mov x29, sp",
762            "str x8, [sp, #16]",
763        ],
764        branch: "blr",
765        epilogue: [
766            "ldr x8, [sp, #16]",
767            "str x0, [x8]",
768            "ldp x29, x30, [sp], #32",
769            "ret",
770        ],
771    }
772}
773
774/// An `ObservableObject` conformance on `Foundation.JSONDecoder`.
775///
776/// Three requirements, and the protocol has defaults for two of them: the
777/// associated `ObjectWillChangePublisher` and its `Publisher` conformance both
778/// come from the constrained extension where that type is
779/// `ObservableObjectPublisher`. Only the getter has to be supplied, and it
780/// forwards to Combine's own default — so what this builds is the same table
781/// `extension JSONDecoder: ObservableObject {}` compiles to, which is what lets
782/// the parity reference declare exactly that and dump the same value.
783///
784/// The object this hangs on used to be `ImageRenderer<AnyView>`, the one class
785/// in SwiftUI that already conforms. That worked, but nothing could check it:
786/// reflecting into a live `ImageRenderer` faults, so the three modifiers taking
787/// an `ObservableObject` had no parity case at all. A `JSONDecoder` dumps in
788/// two lines.
789pub fn observable_object() -> &'static Conformance {
790    use std::sync::OnceLock;
791    static CACHE: OnceLock<Conformance> = OnceLock::new();
792    CACHE.get_or_init(|| unsafe {
793        let protocol = Protocol::from_symbol(c"$s7Combine16ObservableObjectMp");
794        assert_eq!(protocol.requirement_count(), 3, "ObservableObject changed shape");
795        RSWIFT_OBJECT_WILL_CHANGE = crate::resolve::observable_object_will_change();
796        synthesize(
797            &protocol,
798            crate::resolve::json_decoder_descriptor(),
799            &[
800                // ObjectWillChangePublisher: Publisher
801                Witness::Default,
802                // ObjectWillChangePublisher == ObservableObjectPublisher
803                Witness::Default,
804                Witness::Function(object_will_change as *const c_void),
805            ],
806        )
807    })
808}
809
810/// `(Double) -> NSString` — how an axis describes a value aloud.
811unsafe extern "C" fn describe_axis_value(_block: *mut c_void, value: f64) -> *mut c_void {
812    unsafe { crate::objc::nsstring(&format!("{value}")) }
813}
814
815/// `makeChartDescriptor()` — a chart with one numeric axis and no series.
816///
817/// Built through the Objective-C runtime, because `AXChartDescriptor` is an
818/// Objective-C class: an axis, then the chart. The axis initialiser takes a
819/// *block* for turning a value into speech, which is the only place this crate
820/// hands Objective-C a callback rather than the other way round — see
821/// [`crate::objc::GlobalBlock`].
822///
823/// The result is returned `+1`, so a fresh one is built each call rather than
824/// a cached one handed out repeatedly.
825unsafe extern "C" fn make_chart_descriptor() -> *mut c_void {
826    use crate::objc;
827    unsafe {
828        static DESCRIPTOR: objc::BlockDescriptor = objc::BlockDescriptor::empty();
829        let block = objc::GlobalBlock::new(describe_axis_value as *const c_void, &DESCRIPTOR);
830        let block = &block as *const objc::GlobalBlock as *mut c_void;
831
832        type AxisInit = unsafe extern "C" fn(
833            *mut c_void, *const c_void, *mut c_void, f64, f64, *mut c_void, *mut c_void,
834        ) -> *mut c_void;
835        let axis_init: AxisInit = core::mem::transmute(objc::msg_send_addr());
836        let axis = axis_init(
837            objc::send(objc::class(c"OBJC_CLASS_$_AXNumericDataAxisDescriptor"), objc::sel(c"alloc")),
838            objc::sel(c"initWithTitle:lowerBound:upperBound:gridlinePositions:valueDescriptionProvider:"),
839            objc::nsstring("Value"),
840            0.0,
841            1.0,
842            core::ptr::null_mut(),
843            block,
844        );
845
846        type ChartInit = unsafe extern "C" fn(
847            *mut c_void, *const c_void, *mut c_void, *mut c_void, *mut c_void, *mut c_void,
848            *mut c_void,
849        ) -> *mut c_void;
850        let chart_init: ChartInit = core::mem::transmute(objc::msg_send_addr());
851        let empty = objc::send(objc::class(c"OBJC_CLASS_$_NSArray"), objc::sel(c"array"));
852        chart_init(
853            objc::send(objc::class(c"OBJC_CLASS_$_AXChartDescriptor"), objc::sel(c"alloc")),
854            objc::sel(c"initWithTitle:summary:xAxisDescriptor:yAxisDescriptor:series:"),
855            objc::nsstring("Chart"),
856            core::ptr::null_mut(),
857            axis,
858            core::ptr::null_mut(),
859            empty,
860        )
861    }
862}
863
864/// An `AXChartDescriptorRepresentable` conformance on `SwiftUI.EdgeInsets`.
865///
866/// Two requirements, one of which has a default; the one that does not is
867/// `makeChartDescriptor()`, and VoiceOver is the only thing that ever calls it.
868/// The modifier stores the *representable*, so what a parity run compares is
869/// the `EdgeInsets` — the descriptor is built on demand and never appears in
870/// the value.
871pub fn chart_descriptor_representable() -> &'static Conformance {
872    use std::sync::OnceLock;
873    static CACHE: OnceLock<Conformance> = OnceLock::new();
874    CACHE.get_or_init(|| unsafe {
875        let protocol = Protocol::from_symbol(c"$s7SwiftUI30AXChartDescriptorRepresentableMp");
876        assert_eq!(protocol.requirement_count(), 2, "AXChartDescriptorRepresentable changed shape");
877        synthesize(
878            &protocol,
879            crate::resolve::edge_insets_descriptor(),
880            &[Witness::Function(make_chart_descriptor as *const c_void), Witness::Default],
881        )
882    })
883}