Skip to main content

swiftui_native/views/
collections.rs

1//! ForEach and the container family.
2
3use super::*;
4use crate::abi;
5use crate::resolve;
6use core::ffi::c_void;
7use crate::mangle::{self, swift_type};
8
9// ── ForEach and the container family ──
10
11mangle::result_types! {
12    /// `ForEach<Range<Int>, Int, AnyView>`
13    /// `ForEach<Range<Int>, Int, AnyView>`
14    pub(crate) FOREACH_T = ty("ForEach").of(vec![Type::Verbatim("Sn".into()).of(vec![Type::Verbatim("Si".into())]), Type::Verbatim("Si".into()), ty("AnyView")]);
15    /// `List<Never, AnyView>`
16    /// `List<Never, AnyView>`
17    pub(crate) LIST_T = ty("List").of(vec![Type::Verbatim("s5NeverO".into()), ty("AnyView")]);
18    /// `List<Never, ForEach<[Int], Int, HStack<AnyView>>>` — what
19    /// `List(_:id:rowContent:)` builds. The rows are wrapped in an `HStack` by
20    /// the initialiser itself, not by anything here.
21    pub(crate) LIST_ID_T = ty("List").of(vec![
22        Type::Verbatim("s5NeverO".into()),
23        ty("ForEach").of(vec![
24            Type::Verbatim("Sa".into()).of(vec![Type::Verbatim("Si".into())]),
25            Type::Verbatim("Si".into()),
26            ty("HStack").of(vec![ty("AnyView")]),
27        ]),
28    ]);
29    /// The same over `String` identifiers.
30    pub(crate) LIST_ID_STR_T = ty("List").of(vec![
31        Type::Verbatim("s5NeverO".into()),
32        ty("ForEach").of(vec![
33            Type::Verbatim("Sa".into()).of(vec![Type::Verbatim("SS".into())]),
34            Type::Verbatim("SS".into()),
35            ty("HStack").of(vec![ty("AnyView")]),
36        ]),
37    ]);
38    /// `TabView<Int, AnyView>`
39    /// `TabView<Int, AnyView>`
40    pub(crate) TABVIEW_T = ty("TabView").of(vec![Type::Verbatim("Si".into()), ty("AnyView")]);
41    /// `NavigationStack<NavigationPath, AnyView>`
42    /// `NavigationStack<NavigationPath, AnyView>`
43    pub(crate) NAVSTACK_T = ty("NavigationStack").of(vec![ty("NavigationPath"), ty("AnyView")]);
44    /// `GroupBox<EmptyView, AnyView>`
45    /// `GroupBox<EmptyView, AnyView>`
46    pub(crate) GROUPBOX_T = ty("GroupBox").of(vec![ty("EmptyView"), ty("AnyView")]);
47    /// `GridRow<AnyView>`
48    /// `GridRow<AnyView>`
49    pub(crate) GRIDROW_T = ty("GridRow").of(vec![ty("AnyView")]);
50    /// `Form<AnyView>`
51    /// `Form<AnyView>`
52    pub(crate) FORM_T = ty("Form").of(vec![ty("AnyView")]);
53    /// `DisclosureGroup<Text, AnyView>`
54    /// `DisclosureGroup<Text, AnyView>`
55    pub(crate) DISCLOSURE_T = ty("DisclosureGroup").of(vec![ty("Text"), ty("AnyView")]);
56    /// `SwiftUI.Stepper<SwiftUI.Text>`
57    /// `Stepper<Text>`
58    pub(crate) STEPPER_T = ty("Stepper").of(vec![ty("Text")]);
59    /// `Grid<AnyView>` — the content closure hands back an `AnyView`, so that, not
60    /// the row type, is `Grid`'s `Content`.
61    /// `Grid<AnyView>`
62    pub(crate) GRID_T = ty("Grid").of(vec![ty("AnyView")]);
63    /// `Menu<Text, AnyView>`
64    /// `Menu<Text, AnyView>`
65    pub(crate) MENU_T = ty("Menu").of(vec![ty("Text"), ty("AnyView")]);
66    /// `Gauge<AnyView, EmptyView, EmptyView, EmptyView>`
67    /// `Gauge<AnyView, EmptyView, EmptyView, EmptyView>`
68    pub(crate) GAUGE_T = ty("Gauge").of(vec![ty("AnyView"), ty("EmptyView"), ty("EmptyView"), ty("EmptyView")]);
69}
70
71
72
73
74
75
76/// The Rust side of a `ForEach` content closure.
77pub(crate) type IndexBuilder = Box<dyn Fn(i64) -> ViewHandle>;
78
79/// `ForEach`'s `(Int) -> Content` body: build the view for this index and hand
80/// SwiftUI a `+1` reference to it.
81pub(crate) unsafe extern "C" fn emit_index_view(out: *mut u8, index: i64, payload: *mut c_void) {
82    let typed = unsafe { &*(payload as *const TypedIndexBuilder) };
83    let view = (typed.builder)(index);
84    unsafe {
85        if typed.meta.is_null() {
86            *(out as *mut u64) = view.into_anyview_word();
87            return;
88        }
89        // SwiftUI sized each row for the type the probe produced; writing a
90        // different one would scribble past it. Swift enforces the same rule at
91        // compile time by making the closure's result `some View`, i.e. one type.
92        assert_eq!(
93            view.meta, typed.meta,
94            "a ForEach builder returned {} after returning {} — every row has to \
95             be one type. Use `view_if!` for a branch, which builds a \
96             `_ConditionalContent` and so stays one.",
97            crate::diag::type_name(view.meta),
98            crate::diag::type_name(typed.meta),
99        );
100        let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(view.meta);
101        if vwt.size > 0 {
102            (vwt.initialize_with_copy)(out as *mut c_void, view.value_ptr() as *mut c_void, view.meta);
103        }
104    }
105}
106
107/// An index builder together with the row type it was found to produce.
108pub(crate) struct TypedIndexBuilder {
109    pub(crate) builder: IndexBuilder,
110    pub(crate) meta: *const c_void,
111}
112
113/// `ForEach(0..<count) { index in … }`.
114///
115/// SwiftUI calls the closure whenever it rebuilds the body, not just once at
116/// construction, so the builder is leaked deliberately — it has to outlive the
117/// `ForEach` value.
118/// A range `ForEach` can run over.
119///
120/// Swift has two: `ForEach(0..<n)`, which is special-cased and identifies rows
121/// by position, and `ForEach(a...b, id: \.self)`, which identifies them by
122/// value. Rust spells those `0..n` and `a..=b`, and they reach different
123/// SwiftUI initialisers and build different types — so which one a caller
124/// wrote is worth keeping rather than normalising away.
125pub trait ForEachRange {
126    /// Run `builder` over this range.
127    fn for_each_rows(self, builder: impl Fn(i64) -> ViewHandle + 'static) -> ViewHandle;
128}
129
130impl ForEachRange for std::ops::Range<i64> {
131    fn for_each_rows(self, builder: impl Fn(i64) -> ViewHandle + 'static) -> ViewHandle {
132        for_each_half_open(self, builder)
133    }
134}
135
136impl ForEachRange for std::ops::RangeInclusive<i64> {
137    fn for_each_rows(self, builder: impl Fn(i64) -> ViewHandle + 'static) -> ViewHandle {
138        for_each_closed(*self.start(), *self.end(), builder)
139    }
140}
141
142/// `ForEach` over a range of indices.
143///
144/// `0..n` is `ForEach(0..<n)` — positional identity, which is what Swift's
145/// special-cased overload gives. `a..=b` is `ForEach(a...b, id: \.self)` —
146/// identity by value, so a row that moves animates as a move and its state
147/// follows it.
148pub fn for_each<R: ForEachRange>(
149    range: R,
150    builder: impl Fn(i64) -> ViewHandle + 'static,
151) -> ViewHandle {
152    range.for_each_rows(builder)
153}
154
155fn for_each_half_open(range: std::ops::Range<i64>, builder: impl Fn(i64) -> ViewHandle + 'static) -> ViewHandle {
156    unsafe {
157        let boxed: IndexBuilder = Box::new(builder);
158        // Ask the builder what a row is. The first index is one it will be asked
159        // for anyway — but it is an extra call, and a builder with side effects
160        // will notice.
161        let probe = boxed(range.start);
162        let (known_meta, known_wt, known_size) = result_type(&FOREACH_T);
163        // `ForEach<Data, ID, Content>`: three parameters, then their
164        // conformances, so the content is at 2 and its `View` witness at 5.
165        let typed = super::substitute_content(known_meta, 6, 2, 5, probe.meta, probe.wt);
166        let promised = if typed.is_some() { probe.meta } else { core::ptr::null() };
167        let (content_meta, content_wt) = match typed {
168            Some(_) => (probe.meta, probe.wt),
169            None => (resolve::anyview_meta(), resolve::anyview_wt()),
170        };
171        let (meta, wt) = typed.unwrap_or((known_meta, known_wt));
172        let size = if typed.is_some() { abi::type_bytes(meta) } else { known_size };
173        drop(probe);
174
175        let payload = Box::into_raw(Box::new(TypedIndexBuilder { builder: boxed, meta: promised }))
176            as *mut c_void;
177        let ctx = Box::leak(Box::new(abi::IndexClosureCtx::new(emit_index_view, payload)))
178            as *mut abi::IndexClosureCtx;
179
180        let mut result = vec![0u8; size];
181        // ForEach.init(_: Range<Int>, content: (Int) -> C)
182        //   x0/x1 = range bounds, x2 = closure fn, x3 = closure ctx,
183        //   x4 = C.Type, x5 = C: View WT, x8 = @out
184        core::arch::asm!("blr {f}", f = in(reg) resolve::foreach_range_init(),
185            in("x0") range.start,
186            in("x1") range.end,
187            in("x2") abi::index_closure_thunk as *const c_void,
188            in("x3") ctx,
189            in("x4") content_meta,
190            in("x5") content_wt,
191            in("x8") result.as_mut_ptr(),
192            lateout("x6") _, lateout("x7") _,
193            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
194            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
195            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
196            clobber_abi("C"),
197        );
198        wrap_owned(result, meta, wt)
199    }
200}
201
202/// `ForEach` over any Rust collection.
203///
204/// Identity note: this is built on `ForEach(0..<count)`, so SwiftUI identifies
205/// rows *positionally*. Inserting in the middle of `items` makes SwiftUI treat
206/// every later row as changed rather than moved. That is the same semantics as
207/// writing `ForEach(0..<n)` in Swift.
208///
209/// For rows SwiftUI identifies *by value* — so a move animates as a move and
210/// per-row state follows the row — use [`list_identified`] or
211/// [`list_identified_str`], which pass a real `\Int.self` / `\String.self` key
212/// path. They are list constructors rather than a general
213/// `for_each_identified` because the plain `ForEach(_:id:content:)` over `View`
214/// content is inlinable and exports no symbol; `List(_:id:rowContent:)` does
215/// export one and builds the identified `ForEach` itself.
216pub fn for_each_items<T: Clone + 'static>(
217    items: &[T],
218    builder: impl Fn(&T) -> ViewHandle + 'static,
219) -> ViewHandle {
220    let owned: Vec<T> = items.to_vec();
221    for_each(0..owned.len() as i64, move |i| builder(&owned[i as usize]))
222}
223
224/// A container whose only argument is a `() -> Content` closure.
225///
226///   x0 = closure fn, x1 = closure ctx,
227///   x2 = Content.Type, x3 = Content: View WT, x8 = @out
228pub(crate) unsafe fn content_container(
229    child: &ViewHandle,
230    init: *const c_void,
231    mangled: &[u8],
232) -> ViewHandle {
233    unsafe {
234        let kid_handles = Child::new(child, Content::Erased);
235        let handles = kid_handles.element();
236        let payload = TuplePayload { children: handles.as_ptr(), count: 1 };
237        let mut ctx =
238            abi::ClosureCtx::new(emit_tuple, &payload as *const TuplePayload as *mut c_void);
239
240        let (meta, wt, size) = result_type(mangled);
241        let mut result = vec![0u8; size];
242        core::arch::asm!("blr {f}", f = in(reg) init,
243            in("x0") abi::swift_closure_thunk as *const c_void,
244            in("x1") &mut ctx as *mut abi::ClosureCtx,
245            in("x2") resolve::anyview_meta(),
246            in("x3") resolve::anyview_wt(),
247            in("x8") result.as_mut_ptr(),
248            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
249            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
250            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
251            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
252            clobber_abi("C"),
253        );
254        wrap_owned(result, meta, wt)
255    }
256}
257
258/// `List { … }` with static content.
259pub fn list(child: &ViewHandle) -> ViewHandle {
260    // `List<SelectionValue, Content>` — one leading parameter, the selection,
261    // which stays whatever `LIST_T` was declared with (`Never`).
262    unsafe { content_container_typed(child, resolve::list_init(), &LIST_T,
263        ContentShape { args: 3, meta_at: 1, wt_at: 2 }) }
264}
265
266/// `TabView { … }`.
267pub fn tab_view(child: &ViewHandle) -> ViewHandle {
268    unsafe { content_container_typed(child, resolve::tabview_init(), &TABVIEW_T,
269        ContentShape { args: 3, meta_at: 1, wt_at: 2 }) }
270}
271
272/// `NavigationStack { … }`.
273pub fn navigation_stack(child: &ViewHandle) -> ViewHandle {
274    unsafe { content_container_typed(child, resolve::navstack_init(), &NAVSTACK_T,
275        ContentShape { args: 3, meta_at: 1, wt_at: 2 }) }
276}
277
278/// `GroupBox { … }`.
279pub fn group_box(child: &ViewHandle) -> ViewHandle {
280    unsafe { content_container_typed(child, resolve::groupbox_init(), &GROUPBOX_T,
281        ContentShape { args: 3, meta_at: 1, wt_at: 2 }) }
282}
283
284/// `GridRow { … }` over N children.
285pub fn grid_row(children: &[ViewHandle]) -> ViewHandle {
286    unsafe { grid_row_impl(children, None) }
287}
288
289/// `GridRow(alignment:) { … }` — how this row's cells sit against each other
290/// vertically, overriding the grid's own `alignment` for this row only.
291pub fn grid_row_aligned(
292    children: &[ViewHandle],
293    alignment: super::VerticalAlignment,
294) -> ViewHandle {
295    unsafe { grid_row_impl(children, Some(alignment)) }
296}
297
298unsafe fn grid_row_impl(
299    children: &[ViewHandle],
300    alignment: Option<super::VerticalAlignment>,
301) -> ViewHandle {
302    unsafe {
303        // The cells are the row's `@ViewBuilder` body, at their own types.
304        //
305        // This used to wrap them in an `HStack` and box that, which was not a
306        // spelling difference: a `GridRow`'s cells are what the *grid* aligns
307        // into columns, and an `HStack` between them takes that job away. Two
308        // rows whose first cells differed in width had their second columns
309        // start at different x — 1.9% of the pixels, against a Swift reference
310        // that had been written with the same `HStack` in it and so agreed.
311        // `grid_columns` in `examples/parity` is that case.
312        let (content_meta, content_wt, offsets) = super::tuple_content(children);
313        let kids = super::tuple_children(children, &offsets);
314        let payload = TuplePayload { children: kids.as_ptr(), count: kids.len() };
315        let mut ctx =
316            abi::ClosureCtx::new(emit_tuple, &payload as *const TuplePayload as *mut c_void);
317
318        let (known_meta, _, _) = result_type(&GRIDROW_T);
319        let meta = abi::generic_metadata_from(known_meta, &[content_meta, content_wt]);
320        assert!(!meta.is_null(), "GridRow metadata");
321        let wt = abi::get_view_wt(meta);
322        assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
323        let size = abi::type_bytes(meta);
324        let mut result = vec![0u8; size];
325        // GridRow.init(alignment: VerticalAlignment?, content: () -> C)
326        //   x0 = alignment payload, x1 = alignment tag (1 = nil),
327        //   x2 = closure fn, x3 = closure ctx,
328        //   x4 = C.Type, x5 = C: View WT, x8 = @out
329        // `Optional<VerticalAlignment>`: the word in x0, the tag in x1.
330        // `VerticalAlignment` has no spare bits the optional could use, so the
331        // tag is a separate argument rather than folded into the payload.
332        let (align, tag) = match alignment {
333            Some(a) => (super::static_getter(a.resolved(), resolve::valign_meta()).0, 0u64),
334            None => (0u64, 1u64),
335        };
336        core::arch::asm!("blr {f}", f = in(reg) resolve::gridrow_init(),
337            in("x0") align,
338            in("x1") tag,
339            in("x2") abi::swift_closure_thunk as *const c_void,
340            in("x3") &mut ctx as *mut abi::ClosureCtx,
341            in("x4") content_meta,
342            in("x5") content_wt,
343            in("x8") result.as_mut_ptr(),
344            lateout("x6") _, lateout("x7") _,
345            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
346            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
347            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
348            clobber_abi("C"),
349        );
350        wrap_owned(result, meta, wt)
351    }
352}
353
354
355
356
357/// `Form { … }`.
358///
359/// `Form.init(content:)` is `@inlinable`, so there is no symbol to call. The
360/// struct is exactly the size of its content, though — it stores nothing else —
361/// so the value is built directly.
362pub fn form(child: &ViewHandle) -> ViewHandle {
363    unsafe {
364        // `Form<Content>` stores nothing but its content, so there is no
365        // initialiser to call — the value *is* the child. Building
366        // `Form<Text>` rather than `Form<AnyView>` is then a matter of naming
367        // the type and copying the child in through its own value witness,
368        // which is what keeps a refcounted field retained; a raw byte copy of
369        // an `AnyView` word happened to work only because the word was the
370        // reference.
371        let (known_meta, known_wt, known_size) = result_type(&FORM_T);
372        let meta = abi::generic_metadata_from(known_meta, &[child.meta, child.wt]);
373        let wt = if meta.is_null() { core::ptr::null() } else { abi::get_view_wt(meta) };
374        if !wt.is_null() {
375            let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(child.meta);
376            let mut out = vec![0u8; abi::type_bytes(meta)];
377            if vwt.size > 0 {
378                (vwt.initialize_with_copy)(
379                    out.as_mut_ptr() as *mut c_void,
380                    child.value_ptr() as *mut c_void,
381                    child.meta,
382                );
383            }
384            return wrap_owned(out, meta, wt);
385        }
386        assert_eq!(known_size, 8, "Form<AnyView> is expected to hold only its content");
387        let owned = child.clone();
388        let buf = owned.to_anyview_word().to_le_bytes();
389        wrap_buf(&buf, known_meta, known_wt)
390    }
391}
392
393/// `DisclosureGroup(_:content:)`.
394pub fn disclosure_group(label: &str, child: &ViewHandle) -> ViewHandle {
395    unsafe {
396        let ss = abi::swift_string(label);
397        let s0 = u64::from_le_bytes(ss[..8].try_into().unwrap());
398        let s1 = u64::from_le_bytes(ss[8..].try_into().unwrap());
399        let (k0, k1, k2, k3) = localized_string_key(s0, s1);
400
401        let (known_meta, known_wt, known_size) = result_type(&DISCLOSURE_T);
402        // Two parameters, then their `View` conformances.
403        let typed = super::substitute_content(known_meta, 4, 1, 3, child.meta, child.wt);
404        let (meta, wt) = typed.unwrap_or((known_meta, known_wt));
405        let size = if typed.is_some() { abi::type_bytes(meta) } else { known_size };
406        let (content_meta, content_wt) = match typed {
407            Some(_) => (child.meta, child.wt),
408            None => (resolve::anyview_meta(), resolve::anyview_wt()),
409        };
410        let kind = if typed.is_some() { Content::Concrete } else { Content::Erased };
411
412        let kid_handles = Child::new(child, kind);
413        let handles = kid_handles.element();
414        let payload = TuplePayload { children: handles.as_ptr(), count: 1 };
415        let mut ctx =
416            abi::ClosureCtx::new(emit_tuple, &payload as *const TuplePayload as *mut c_void);
417
418        let mut result = vec![0u8; size];
419        // init(_: LocalizedStringKey, content: () -> C)
420        //   x0-x3 = key, x4 = closure fn, x5 = closure ctx,
421        //   x6 = C.Type, x7 = C: View WT, x8 = @out
422        core::arch::asm!("blr {f}", f = in(reg) resolve::disclosure_init(),
423            in("x0") k0, in("x1") k1, in("x2") k2, in("x3") k3,
424            in("x4") abi::swift_closure_thunk as *const c_void,
425            in("x5") &mut ctx as *mut abi::ClosureCtx,
426            in("x6") content_meta,
427            in("x7") content_wt,
428            in("x8") result.as_mut_ptr(),
429            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
430            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
431            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
432            clobber_abi("C"),
433        );
434        wrap_owned(result, meta, wt)
435    }
436}
437
438
439
440/// Create a `Stepper` over a closed range.
441pub fn stepper(label: &str, value: f64, lower: f64, upper: f64, step: f64) -> ViewHandle {
442    unsafe {
443        let mut binding = constant_binding(
444            &value.to_le_bytes(),
445            resolve::double_meta(),
446            mangle::bound(&swift_type!(SwiftUI.Binding), &[mangle::DOUBLE]).as_bytes(),
447        );
448        stepper_with_binding(label, &mut binding, lower, upper, step)
449    }
450}
451
452/// Shared tail of [`stepper`] and `stepper_bound`.
453///
454/// # Safety
455/// `binding` must be a `Binding<Double>`.
456pub(crate) unsafe fn stepper_with_binding(
457    label: &str,
458    binding: &mut [u8],
459    lower: f64,
460    upper: f64,
461    step: f64,
462) -> ViewHandle {
463    unsafe {
464        let ss = abi::swift_string(label);
465        let s0 = u64::from_le_bytes(ss[..8].try_into().unwrap());
466        let s1 = u64::from_le_bytes(ss[8..].try_into().unwrap());
467        let (k0, k1, k2, k3) = localized_string_key(s0, s1);
468
469        let double_meta = resolve::double_meta();
470        let stride_wt = abi::get_view_wt_for_proto(double_meta, resolve::strideable_proto());
471        assert!(!stride_wt.is_null(), "Double: Strideable");
472
473        let mut range = [0u8; 16];
474        range[..8].copy_from_slice(&lower.to_le_bytes());
475        range[8..].copy_from_slice(&upper.to_le_bytes());
476        let mut step_buf = step.to_le_bytes();
477
478        let (meta, wt, size) = result_type(&STEPPER_T);
479        let mut result = vec![0u8; size];
480        let noop = noop_closure as *const c_void;
481        // Stepper.init(_:value:in:step:onEditingChanged:)
482        //   x0-x3 = key, x4 = @in Binding<V>, x5 = @in ClosedRange<V>,
483        //   x6 = @in V.Stride, x7 = closure fn,
484        //   [sp] = closure ctx, [sp+8] = V.Type, [sp+16] = V: Strideable WT,
485        //   [sp+24] = V.Stride.Type
486        core::arch::asm!(
487            "sub sp, sp, #32",
488            "stp xzr, {dm}, [sp]",
489            "stp {sw}, {dm}, [sp, #16]",
490            "blr {f}",
491            "add sp, sp, #32",
492            f = in(reg) resolve::stepper_init(),
493            dm = in(reg) double_meta,
494            sw = in(reg) stride_wt,
495            in("x0") k0, in("x1") k1, in("x2") k2, in("x3") k3,
496            in("x4") binding.as_mut_ptr(),
497            in("x5") range.as_mut_ptr(),
498            in("x6") step_buf.as_mut_ptr(),
499            in("x7") noop,
500            in("x8") result.as_mut_ptr(),
501            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
502            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
503            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
504            clobber_abi("C"),
505        );
506        wrap_owned(result, meta, wt)
507    }
508}
509
510
511
512
513
514/// `Grid { … }` over `GridRow` children.
515///
516/// The rows are the grid's `@ViewBuilder` body at their own types, so this is a
517/// `Grid<GridRow<…>>` for one row and a `Grid<TupleView<(GridRow<…>, …)>>` for
518/// several — the same types Swift builds from the same source.
519pub fn grid(rows: &[ViewHandle]) -> ViewHandle {
520    unsafe {
521        // Several rows are a `TupleView`, not a `VStack`. Wrapping them in a
522        // stack built `Grid<AnyView>` where Swift builds
523        // `Grid<TupleView<(GridRow<…>, GridRow<…>)>>` — it rendered the same, and
524        // it was a different type, with a `VStack.init` in the trace that the
525        // compiler's IR has no call to.
526        let (content_meta, content_wt, offsets) = super::tuple_content(rows);
527        let handles = super::tuple_children(rows, &offsets);
528        let payload = TuplePayload { children: handles.as_ptr(), count: handles.len() };
529        let mut ctx =
530            abi::ClosureCtx::new(emit_tuple, &payload as *const TuplePayload as *mut c_void);
531
532        let (ah, av) = static_getter(resolve::alignment_center(), resolve::alignment_meta());
533        let (known_meta, _, _) = result_type(&GRID_T);
534        let meta = abi::generic_metadata_from(known_meta, &[content_meta, content_wt]);
535        assert!(!meta.is_null(), "Grid metadata");
536        let wt = abi::get_view_wt(meta);
537        assert!(!wt.is_null(), "Grid !: View");
538        let size = abi::type_bytes(meta);
539        let mut result = vec![0u8; size];
540        // Grid.init(alignment:horizontalSpacing:verticalSpacing:content:)
541        //   x0/x1 = Alignment, x2/x3 = hSpacing (payload, tag 1 = nil),
542        //   x4/x5 = vSpacing, x6 = closure fn, x7 = closure ctx,
543        //   [sp] = Content.Type, [sp+8] = Content: View WT, x8 = @out
544        //
545        // Eight registers are already spoken for, so the generic arguments spill.
546        core::arch::asm!(
547            "sub sp, sp, #16",
548            "stp {cm}, {cw}, [sp]",
549            "blr {f}",
550            "add sp, sp, #16",
551            f = in(reg) resolve::grid_init(),
552            cm = in(reg) content_meta,
553            cw = in(reg) content_wt,
554            in("x0") ah, in("x1") av,
555            in("x2") 0u64, in("x3") 1u64,
556            in("x4") 0u64, in("x5") 1u64,
557            in("x6") abi::swift_closure_thunk as *const c_void,
558            in("x7") &mut ctx as *mut abi::ClosureCtx,
559            in("x8") result.as_mut_ptr(),
560            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
561            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
562            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
563            clobber_abi("C"),
564        );
565        wrap_owned(result, meta, wt)
566    }
567}
568
569/// `Menu(_:content:)`.
570pub fn menu(label: &str, child: &ViewHandle) -> ViewHandle {
571    unsafe {
572        let ss = abi::swift_string(label);
573        let s0 = u64::from_le_bytes(ss[..8].try_into().unwrap());
574        let s1 = u64::from_le_bytes(ss[8..].try_into().unwrap());
575        let (k0, k1, k2, k3) = localized_string_key(s0, s1);
576
577        let (known_meta, known_wt, known_size) = result_type(&MENU_T);
578        // Two parameters, then their `View` conformances.
579        let typed = super::substitute_content(known_meta, 4, 1, 3, child.meta, child.wt);
580        let (meta, wt) = typed.unwrap_or((known_meta, known_wt));
581        let size = if typed.is_some() { abi::type_bytes(meta) } else { known_size };
582        let (content_meta, content_wt) = match typed {
583            Some(_) => (child.meta, child.wt),
584            None => (resolve::anyview_meta(), resolve::anyview_wt()),
585        };
586        let kind = if typed.is_some() { Content::Concrete } else { Content::Erased };
587
588        let kid_handles = Child::new(child, kind);
589        let handles = kid_handles.element();
590        let payload = TuplePayload { children: handles.as_ptr(), count: 1 };
591        let mut ctx =
592            abi::ClosureCtx::new(emit_tuple, &payload as *const TuplePayload as *mut c_void);
593
594        let mut result = vec![0u8; size];
595        // Menu.init(_: LocalizedStringKey, content: () -> C)
596        //   x0-x3 = key, x4 = closure fn, x5 = closure ctx,
597        //   x6 = C.Type, x7 = C: View WT, x8 = @out
598        core::arch::asm!("blr {f}", f = in(reg) resolve::menu_init(),
599            in("x0") k0, in("x1") k1, in("x2") k2, in("x3") k3,
600            in("x4") abi::swift_closure_thunk as *const c_void,
601            in("x5") &mut ctx as *mut abi::ClosureCtx,
602            in("x6") content_meta,
603            in("x7") content_wt,
604            in("x8") result.as_mut_ptr(),
605            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
606            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
607            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
608            clobber_abi("C"),
609        );
610        wrap_owned(result, meta, wt)
611    }
612}
613
614/// `Gauge(value:in:label:)`.
615pub fn gauge(value: f64, lower: f64, upper: f64, label: &ViewHandle) -> ViewHandle {
616    unsafe {
617        let double_meta = resolve::double_meta();
618        let bfp_wt =
619            abi::get_view_wt_for_proto(double_meta, resolve::binary_floating_point_proto());
620        assert!(!bfp_wt.is_null(), "Double: BinaryFloatingPoint");
621
622        let mut value_buf = value.to_le_bytes();
623        let mut range = [0u8; 16];
624        range[..8].copy_from_slice(&lower.to_le_bytes());
625        range[8..].copy_from_slice(&upper.to_le_bytes());
626
627        // `Gauge<Label, CurrentValueLabel, MinimumValueLabel, MaximumValueLabel>`
628        // — four parameters, so `Label`'s witness is at 4. `V` belongs to the
629        // initialiser, not the type, which is why it is not among them.
630        let (known_meta, known_wt, known_size) = result_type(&GAUGE_T);
631        let typed = super::substitute_args(known_meta, 8, &[(0, label.meta)], &[(4, label.wt)]);
632        let (meta, wt) = typed.unwrap_or((known_meta, known_wt));
633        let size = if typed.is_some() { abi::type_bytes(meta) } else { known_size };
634        let (label_meta, label_wt) = match typed {
635            Some(_) => (label.meta, label.wt),
636            None => (resolve::anyview_meta(), resolve::anyview_wt()),
637        };
638        let kind = if typed.is_some() { Content::Concrete } else { Content::Erased };
639
640        let kid_handles = Child::new(label, kind);
641        let handles = kid_handles.element();
642        let payload = TuplePayload { children: handles.as_ptr(), count: 1 };
643        let mut ctx =
644            abi::ClosureCtx::new(emit_tuple, &payload as *const TuplePayload as *mut c_void);
645
646        let mut result = vec![0u8; size];
647        // Gauge.init(value:in:label:)
648        //   x0 = @in V, x1 = @in ClosedRange<V>, x2 = closure fn, x3 = closure ctx,
649        //   x4 = Label.Type, x5 = V.Type, x6 = Label: View WT,
650        //   x7 = V: BinaryFloatingPoint WT, x8 = @out
651        core::arch::asm!("blr {f}", f = in(reg) resolve::gauge_init(),
652            in("x0") value_buf.as_mut_ptr(),
653            in("x1") range.as_mut_ptr(),
654            in("x2") abi::swift_closure_thunk as *const c_void,
655            in("x3") &mut ctx as *mut abi::ClosureCtx,
656            in("x4") label_meta,
657            in("x5") double_meta,
658            in("x6") label_wt,
659            in("x7") bfp_wt,
660            in("x8") result.as_mut_ptr(),
661            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
662            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
663            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
664            clobber_abi("C"),
665        );
666        wrap_owned(result, meta, wt)
667    }
668}
669
670/// `Group { … }` — a container that groups without laying out.
671///
672/// Its initialiser has the same shape as `GroupBox`'s: a content closure and
673/// the two generic words, nothing else.
674pub fn group(child: &ViewHandle) -> ViewHandle {
675    unsafe { content_container_typed(child, resolve::group_init(), &GROUP_T,
676        ContentShape { args: 2, meta_at: 0, wt_at: 1 }) }
677}
678
679/// `ControlGroup { … }`
680pub fn control_group(child: &ViewHandle) -> ViewHandle {
681    unsafe { content_container_typed(child, resolve::control_group_init(), &CONTROL_GROUP_T,
682        ContentShape { args: 2, meta_at: 0, wt_at: 1 }) }
683}
684
685/// `ViewThatFits(in:) { … }` — the first child that fits.
686///
687/// One argument ahead of the closure: an `Axis.Set`, which is a one-byte option
688/// set, so the closure and the generic words all shift one register right.
689pub fn view_that_fits(child: &ViewHandle, axes: &[super::AxisSet]) -> ViewHandle {
690    unsafe {
691        // `ViewThatFits<Content>` — one parameter, so its witness is at 1.
692        let (known_meta, known_wt, known_size) = result_type(&VIEW_THAT_FITS_T);
693        let typed = super::substitute_args(known_meta, 2, &[(0, child.meta)], &[(1, child.wt)]);
694        let (meta, wt) = typed.unwrap_or((known_meta, known_wt));
695        let size = if typed.is_some() { abi::type_bytes(meta) } else { known_size };
696        let (content_meta, content_wt) = match typed {
697            Some(_) => (child.meta, child.wt),
698            None => (resolve::anyview_meta(), resolve::anyview_wt()),
699        };
700        let kind = if typed.is_some() { Content::Concrete } else { Content::Erased };
701
702        let kid_handles = Child::new(child, kind);
703        let handles = kid_handles.element();
704        let payload = TuplePayload { children: handles.as_ptr(), count: 1 };
705        let mut ctx =
706            abi::ClosureCtx::new(emit_tuple, &payload as *const TuplePayload as *mut c_void);
707        let g: Vec<&core::ffi::CStr> = axes.iter().map(|a| a.getter()).collect();
708        let set = super::option_set_word(resolve::axis_set_ma(), &g);
709
710        let mut result = vec![0u8; size];
711        core::arch::asm!("blr {f}", f = in(reg) resolve::view_that_fits_init(),
712            in("x0") set,
713            in("x1") abi::swift_closure_thunk as *const c_void,
714            in("x2") &mut ctx as *mut abi::ClosureCtx,
715            in("x3") content_meta,
716            in("x4") content_wt,
717            in("x8") result.as_mut_ptr(),
718            lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
719            lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
720            lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
721            lateout("lr") _, lateout("x20") _, lateout("x21") _, clobber_abi("C"),
722        );
723        wrap_owned(result, meta, wt)
724    }
725}
726
727// ── Section, NavigationSplitView, Canvas, EditButton ──
728
729mangle::result_types! {
730    /// `Section<AnyView, AnyView, EmptyView>`
731    /// `Section<AnyView, AnyView, EmptyView>`
732    pub(crate) SECTION_T = ty("Section").of(vec![ty("AnyView"), ty("AnyView"), ty("EmptyView")]);
733    /// `Section<EmptyView, AnyView, EmptyView>`
734    /// `Section<EmptyView, AnyView, EmptyView>`
735    pub(crate) SECTION_CONTENT_T = ty("Section").of(vec![ty("EmptyView"), ty("AnyView"), ty("EmptyView")]);
736    /// `Section<AnyView, AnyView, AnyView>` — header *and* footer.
737    pub(crate) SECTION_HF_T = ty("Section").of(vec![ty("AnyView"), ty("AnyView"), ty("AnyView")]);
738    /// `NavigationSplitView<AnyView, AnyView, AnyView>` — all three columns.
739    pub(crate) NAV_SPLIT3_T = ty("NavigationSplitView")
740        .of(vec![ty("AnyView"), ty("AnyView"), ty("AnyView")]);
741    /// `Section<EmptyView, AnyView, AnyView>` — footer, no header.
742    pub(crate) SECTION_FOOTER_T = ty("Section").of(vec![ty("EmptyView"), ty("AnyView"), ty("AnyView")]);
743    /// `NavigationSplitView<AnyView, EmptyView, AnyView>`
744    /// `NavigationSplitView<AnyView, EmptyView, AnyView>`
745    pub(crate) NAV_SPLIT_T = ty("NavigationSplitView").of(vec![ty("AnyView"), ty("EmptyView"), ty("AnyView")]);
746    /// `Canvas<EmptyView>`
747    /// `Canvas<EmptyView>`
748    pub(crate) CANVAS_T = ty("Canvas").of(vec![ty("EmptyView")]);
749    /// `EditButton` — gated like `edit_button`, whose macOS implementation is
750    /// a trap stub rather than a missing symbol.
751    #[cfg(not(target_os = "macos"))]
752    pub(crate) EDIT_BUTTON_T = ty("EditButton");
753}
754
755/// `Section { … }` — no header, no footer.
756pub fn section_content(content: &ViewHandle) -> ViewHandle {
757    // `Section<Parent, Content, Footer>` — the content is the *middle*
758    // parameter, so this is the case the `shape` generalisation exists for.
759    // Three parameters and three `View` conformances, so the witness vector
760    // starts at 3 and the content's is at 4.
761    unsafe {
762        content_container_typed(
763            content,
764            resolve::section_content_init(),
765            &SECTION_CONTENT_T,
766            ContentShape { args: 6, meta_at: 1, wt_at: 4 },
767        )
768    }
769}
770
771/// A `+1` copy of a view's value, for an `@in` parameter the callee consumes.
772///
773/// For an erased parameter the copy is the `AnyView` word; for a typed one it is
774/// the value itself, at its own stride. Either way the callee *destroys* what it
775/// is handed, so this must be a copy and never the handle's own bytes — the
776/// handle is still holding them.
777unsafe fn in_value(view: &ViewHandle, typed: bool) -> Vec<u8> {
778    unsafe {
779        if typed {
780            let mut buf = vec![0u8; abi::type_bytes(view.meta)];
781            abi::copy_value(buf.as_mut_ptr(), view.value_ptr() as *const u8, view.meta);
782            buf
783        } else {
784            view.to_anyview_word().to_le_bytes().to_vec()
785        }
786    }
787}
788
789/// `Section(header:) { … }`.
790///
791/// The header is a *value* parameter of generic type, so it travels indirectly —
792/// x0 holds the address of the `AnyView` word, not the word. It is `@in`, i.e.
793/// consumed, so the handle is retained and forgotten rather than dropped here.
794///
795/// Generic order is `Section<Parent, Content, Footer>`; this overload pins
796/// `Footer == EmptyView`, leaving two metadata words and two witness tables.
797pub fn section(header: &ViewHandle, content: &ViewHandle) -> ViewHandle {
798    unsafe {
799        // `Section<Parent, Content, Footer>` — three parameters, so the witnesses
800        // are at 3, 4 and 5. This overload pins `Footer`, which is left as the
801        // known instantiation's `EmptyView`.
802        let (known_meta, known_wt, known_size) = result_type(&SECTION_T);
803        let typed = super::substitute_args(
804            known_meta,
805            6,
806            &[(0, header.meta), (1, content.meta)],
807            &[(3, header.wt), (4, content.wt)],
808        );
809        let (meta, wt) = typed.unwrap_or((known_meta, known_wt));
810        let size = if typed.is_some() { abi::type_bytes(meta) } else { known_size };
811        let (parent_meta, parent_wt, content_meta, content_wt) = match typed {
812            Some(_) => (header.meta, header.wt, content.meta, content.wt),
813            None => (
814                resolve::anyview_meta(),
815                resolve::anyview_wt(),
816                resolve::anyview_meta(),
817                resolve::anyview_wt(),
818            ),
819        };
820        let kind = if typed.is_some() { Content::Concrete } else { Content::Erased };
821        let mut header_in = in_value(header, typed.is_some());
822
823        let kid_handles = Child::new(content, kind);
824        let handles = kid_handles.element();
825        let payload = TuplePayload { children: handles.as_ptr(), count: 1 };
826        let mut ctx =
827            abi::ClosureCtx::new(emit_tuple, &payload as *const TuplePayload as *mut c_void);
828
829        let mut result = vec![0u8; size];
830        // Section.init(header: Parent, content: () -> Content)
831        //   x0 = &header, x1 = closure fn, x2 = closure ctx,
832        //   x3 = Parent.Type, x4 = Content.Type,
833        //   x5 = Parent: View WT, x6 = Content: View WT, x8 = @out
834        core::arch::asm!("blr {f}", f = in(reg) resolve::section_init(),
835            in("x0") header_in.as_mut_ptr(),
836            in("x1") abi::swift_closure_thunk as *const c_void,
837            in("x2") &mut ctx as *mut abi::ClosureCtx,
838            in("x3") parent_meta,
839            in("x4") content_meta,
840            in("x5") parent_wt,
841            in("x6") content_wt,
842            in("x8") result.as_mut_ptr(),
843            lateout("x7") _,
844            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
845            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
846            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
847            clobber_abi("C"),
848        );
849        wrap_owned(result, meta, wt)
850    }
851}
852
853/// `Section(header:footer:) { … }`.
854///
855/// A footer is where a settings group's explanatory line goes, and there was no
856/// way to put one there: only the no-header and header-only initialisers were
857/// bound. Both of these are exported.
858///
859/// Both the header and the footer are `@in` value parameters of generic type,
860/// so each travels as the *address* of its `AnyView` word and is consumed —
861/// retained and forgotten here rather than dropped.
862pub fn section_header_footer(
863    header: &ViewHandle,
864    footer: &ViewHandle,
865    content: &ViewHandle,
866) -> ViewHandle {
867    unsafe {
868        let (known_meta, known_wt, known_size) = result_type(&SECTION_HF_T);
869        let typed = super::substitute_args(
870            known_meta,
871            6,
872            &[(0, header.meta), (1, content.meta), (2, footer.meta)],
873            &[(3, header.wt), (4, content.wt), (5, footer.wt)],
874        );
875        let (meta, wt) = typed.unwrap_or((known_meta, known_wt));
876        let size = if typed.is_some() { abi::type_bytes(meta) } else { known_size };
877        let any = resolve::anyview_meta();
878        let any_wt = resolve::anyview_wt();
879        let [parent_meta, content_meta, footer_meta] = match typed {
880            Some(_) => [header.meta, content.meta, footer.meta],
881            None => [any; 3],
882        };
883        let [parent_wt, content_wt, footer_wt] = match typed {
884            Some(_) => [header.wt, content.wt, footer.wt],
885            None => [any_wt; 3],
886        };
887        let kind = if typed.is_some() { Content::Concrete } else { Content::Erased };
888        let mut header_in = in_value(header, typed.is_some());
889        let mut footer_in = in_value(footer, typed.is_some());
890
891        let kid_handles = Child::new(content, kind);
892        let handles = kid_handles.element();
893        let payload = TuplePayload { children: handles.as_ptr(), count: 1 };
894        let mut ctx =
895            abi::ClosureCtx::new(emit_tuple, &payload as *const TuplePayload as *mut c_void);
896
897        let mut result = vec![0u8; size];
898        // Section.init(header: Parent, footer: Footer, content: () -> Content)
899        //   x0 = &header, x1 = &footer, x2 = closure fn, x3 = closure ctx,
900        //   x4 = Parent.Type, x5 = Content.Type, x6 = Footer.Type,
901        //   x7 = Parent: View WT, [sp] = Content: View WT,
902        //   [sp+8] = Footer: View WT, x8 = @out
903        //
904        // The metadata follows the *generic signature's* order — Parent,
905        // Content, Footer — not the argument order, which puts the footer
906        // second.
907        core::arch::asm!(
908            "sub sp, sp, #16",
909            "stp {cw}, {fw}, [sp]",
910            "blr {f}",
911            "add sp, sp, #16",
912            f = in(reg) resolve::section_hf_init(),
913            cw = in(reg) content_wt,
914            fw = in(reg) footer_wt,
915            in("x0") header_in.as_mut_ptr(),
916            in("x1") footer_in.as_mut_ptr(),
917            in("x2") abi::swift_closure_thunk as *const c_void,
918            in("x3") &mut ctx as *mut abi::ClosureCtx,
919            in("x4") parent_meta,
920            in("x5") content_meta,
921            in("x6") footer_meta,
922            in("x7") parent_wt,
923            in("x8") result.as_mut_ptr(),
924            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
925            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
926            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
927            clobber_abi("C"),
928        );
929        wrap_owned(result, meta, wt)
930    }
931}
932
933/// `Section(footer:) { … }` — a footer with no header.
934///
935/// `Parent` is pinned to `EmptyView` by the extension, so only `Content` and
936/// `Footer` need metadata.
937pub fn section_footer(footer: &ViewHandle, content: &ViewHandle) -> ViewHandle {
938    unsafe {
939        // `Parent` is pinned to `EmptyView` here, so only `Content` (1) and
940        // `Footer` (2) move, with their witnesses at 4 and 5.
941        let (known_meta, known_wt, known_size) = result_type(&SECTION_FOOTER_T);
942        let typed = super::substitute_args(
943            known_meta,
944            6,
945            &[(1, content.meta), (2, footer.meta)],
946            &[(4, content.wt), (5, footer.wt)],
947        );
948        let (meta, wt) = typed.unwrap_or((known_meta, known_wt));
949        let size = if typed.is_some() { abi::type_bytes(meta) } else { known_size };
950        let any = resolve::anyview_meta();
951        let any_wt = resolve::anyview_wt();
952        let (content_meta, footer_meta, content_wt, footer_wt) = match typed {
953            Some(_) => (content.meta, footer.meta, content.wt, footer.wt),
954            None => (any, any, any_wt, any_wt),
955        };
956        let kind = if typed.is_some() { Content::Concrete } else { Content::Erased };
957        let mut footer_in = in_value(footer, typed.is_some());
958
959        let kid_handles = Child::new(content, kind);
960        let handles = kid_handles.element();
961        let payload = TuplePayload { children: handles.as_ptr(), count: 1 };
962        let mut ctx =
963            abi::ClosureCtx::new(emit_tuple, &payload as *const TuplePayload as *mut c_void);
964
965        let mut result = vec![0u8; size];
966        // Section.init(footer: Footer, content: () -> Content)
967        //   x0 = &footer, x1 = closure fn, x2 = closure ctx,
968        //   x3 = Content.Type, x4 = Footer.Type,
969        //   x5 = Content: View WT, x6 = Footer: View WT, x8 = @out
970        core::arch::asm!("blr {f}", f = in(reg) resolve::section_footer_init(),
971            in("x0") footer_in.as_mut_ptr(),
972            in("x1") abi::swift_closure_thunk as *const c_void,
973            in("x2") &mut ctx as *mut abi::ClosureCtx,
974            in("x3") content_meta,
975            in("x4") footer_meta,
976            in("x5") content_wt,
977            in("x6") footer_wt,
978            in("x8") result.as_mut_ptr(),
979            lateout("x7") _,
980            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
981            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
982            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
983            clobber_abi("C"),
984        );
985        wrap_owned(result, meta, wt)
986    }
987}
988
989/// `Section(isExpanded:) { … } header: { … }` — a section that collapses.
990///
991/// The disclosure state is a `Binding<Bool>` over Rust state, so the section
992/// opens and closes by itself *and* whatever is bound sees it — which is the
993/// point of taking a binding rather than an initial value.
994///
995/// Unlike [`section`](fn@section), the header here is a **closure**, not a
996/// value: this
997/// initialiser takes `content:` and `header:` as two `() -> some View`, so both
998/// travel as a function-and-context pair rather than as an `@in` address.
999pub fn section_expanded(
1000    is_expanded: &crate::views::BoundState<bool>,
1001    header: &ViewHandle,
1002    content: &ViewHandle,
1003) -> ViewHandle {
1004    unsafe {
1005        let binding = crate::views::bool_binding(is_expanded);
1006        let b = binding_words(&mut binding.clone());
1007
1008        let (known_meta, known_wt, known_size) = result_type(&SECTION_T);
1009        let typed = super::substitute_args(
1010            known_meta,
1011            6,
1012            &[(0, header.meta), (1, content.meta)],
1013            &[(3, header.wt), (4, content.wt)],
1014        );
1015        let (meta, wt) = typed.unwrap_or((known_meta, known_wt));
1016        let size = if typed.is_some() { abi::type_bytes(meta) } else { known_size };
1017        let any = resolve::anyview_meta();
1018        let any_wt = resolve::anyview_wt();
1019        let (parent_meta, content_meta, parent_wt, content_wt) = match typed {
1020            Some(_) => (header.meta, content.meta, header.wt, content.wt),
1021            None => (any, any, any_wt, any_wt),
1022        };
1023        let kind = if typed.is_some() { Content::Concrete } else { Content::Erased };
1024
1025        let head_kids = Child::new(header, kind);
1026        let head_handles = head_kids.element();
1027        let head_payload = TuplePayload { children: head_handles.as_ptr(), count: 1 };
1028        let mut head_ctx =
1029            abi::ClosureCtx::new(emit_tuple, &head_payload as *const TuplePayload as *mut c_void);
1030
1031        let body_kids = Child::new(content, kind);
1032        let body_handles = body_kids.element();
1033        let body_payload = TuplePayload { children: body_handles.as_ptr(), count: 1 };
1034        let mut body_ctx =
1035            abi::ClosureCtx::new(emit_tuple, &body_payload as *const TuplePayload as *mut c_void);
1036
1037        let mut result = vec![0u8; size];
1038        // Section.init(isExpanded: Binding<Bool>, content: () -> B, header: () -> A)
1039        //   x0..x2 = the binding, x3/x4 = content closure,
1040        //   x5/x6 = header closure, x7 = Parent.Type,
1041        //   [sp] = Content.Type, [sp+8] = Parent: View WT,
1042        //   [sp+16] = Content: View WT, x8 = @out
1043        core::arch::asm!(
1044            "sub sp, sp, #32",
1045            "stp {cm}, {pw}, [sp]",
1046            "str {cw}, [sp, #16]",
1047            "blr {f}",
1048            "add sp, sp, #32",
1049            f = in(reg) resolve::section_expanded_init(),
1050            cm = in(reg) content_meta,
1051            pw = in(reg) parent_wt,
1052            cw = in(reg) content_wt,
1053            in("x0") b[0],
1054            in("x1") b[1],
1055            in("x2") b[2],
1056            in("x3") abi::swift_closure_thunk as *const c_void,
1057            in("x4") &mut body_ctx as *mut abi::ClosureCtx,
1058            in("x5") abi::swift_closure_thunk as *const c_void,
1059            in("x6") &mut head_ctx as *mut abi::ClosureCtx,
1060            in("x7") parent_meta,
1061            in("x8") result.as_mut_ptr(),
1062            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1063            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1064            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
1065            clobber_abi("C"),
1066        );
1067        wrap_owned(result, meta, wt)
1068    }
1069}
1070
1071/// `NavigationSplitView { sidebar } detail: { detail }`.
1072///
1073/// Two content closures, so two (fn, ctx) pairs fill x0–x3 and the four generic
1074/// words land in x4–x7 with nothing spilled. `Content == EmptyView` is pinned by
1075/// this overload, so only `Sidebar` and `Detail` are passed.
1076pub fn navigation_split_view(sidebar: &ViewHandle, detail: &ViewHandle) -> ViewHandle {
1077    unsafe {
1078        // `NavigationSplitView<Sidebar, Content, Detail>` — `Content` is pinned
1079        // to `EmptyView` by this overload, so the sidebar is 0 and the detail 2,
1080        // with their witnesses at 3 and 5.
1081        let (known_meta, known_wt, known_size) = result_type(&NAV_SPLIT_T);
1082        let typed = super::substitute_args(
1083            known_meta,
1084            6,
1085            &[(0, sidebar.meta), (2, detail.meta)],
1086            &[(3, sidebar.wt), (5, detail.wt)],
1087        );
1088        let (meta, wt) = typed.unwrap_or((known_meta, known_wt));
1089        let size = if typed.is_some() { abi::type_bytes(meta) } else { known_size };
1090        let (side_meta, detail_meta, side_wt, detail_wt) = match typed {
1091            Some(_) => (sidebar.meta, detail.meta, sidebar.wt, detail.wt),
1092            None => (
1093                resolve::anyview_meta(),
1094                resolve::anyview_meta(),
1095                resolve::anyview_wt(),
1096                resolve::anyview_wt(),
1097            ),
1098        };
1099        let kind = if typed.is_some() { Content::Concrete } else { Content::Erased };
1100
1101        let kid_side_handles = Child::new(sidebar, kind);
1102        let side_handles = kid_side_handles.element();
1103        let side_payload = TuplePayload { children: side_handles.as_ptr(), count: 1 };
1104        let mut side_ctx =
1105            abi::ClosureCtx::new(emit_tuple, &side_payload as *const TuplePayload as *mut c_void);
1106        let kid_detail_handles = Child::new(detail, kind);
1107        let detail_handles = kid_detail_handles.element();
1108        let detail_payload = TuplePayload { children: detail_handles.as_ptr(), count: 1 };
1109        let mut detail_ctx =
1110            abi::ClosureCtx::new(emit_tuple, &detail_payload as *const TuplePayload as *mut c_void);
1111
1112        let mut result = vec![0u8; size];
1113        core::arch::asm!("blr {f}", f = in(reg) resolve::nav_split_init(),
1114            in("x0") abi::swift_closure_thunk as *const c_void,
1115            in("x1") &mut side_ctx as *mut abi::ClosureCtx,
1116            in("x2") abi::swift_closure_thunk as *const c_void,
1117            in("x3") &mut detail_ctx as *mut abi::ClosureCtx,
1118            in("x4") side_meta,
1119            in("x5") detail_meta,
1120            in("x6") side_wt,
1121            in("x7") detail_wt,
1122            in("x8") result.as_mut_ptr(),
1123            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1124            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1125            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
1126            clobber_abi("C"),
1127        );
1128        wrap_owned(result, meta, wt)
1129    }
1130}
1131
1132/// The renderer Canvas stores: `(inout GraphicsContext, CGSize) -> Void`.
1133///
1134/// The empty body ignores every argument, which is safe at any signature — the
1135/// context is `inout` so nothing is consumed, and `CGSize` arrives in d0/d1.
1136unsafe extern "C" fn empty_renderer() {}
1137
1138/// `Canvas { context, size in … }` with an empty renderer.
1139///
1140/// `colorMode` is a `ColorRenderingMode`, a *resilient* enum, so it is passed by
1141/// address rather than in a register — case 0 (`.nonLinear`) is the default the
1142/// trailing-closure spelling produces.
1143pub fn canvas() -> ViewHandle {
1144    unsafe {
1145        let mode_meta = abi::resolve_type_by_mangled_name(b"7SwiftUI18ColorRenderingModeO");
1146        assert!(!mode_meta.is_null(), "ColorRenderingMode metadata");
1147        let mut mode = vec![0u8; abi::type_bytes(mode_meta)];
1148        abi::inject_enum_tag(&mut mode, mode_meta, 0);
1149
1150        let (meta, wt, size) = result_type(&CANVAS_T);
1151        let mut result = vec![0u8; size];
1152        // Canvas.init(opaque:colorMode:rendersAsynchronously:renderer:)
1153        //   x0 = opaque, x1 = &colorMode, x2 = rendersAsynchronously,
1154        //   x3 = renderer fn, x4 = renderer ctx, x8 = @out
1155        core::arch::asm!("blr {f}", f = in(reg) resolve::canvas_init(),
1156            in("x0") 0u64,
1157            in("x1") mode.as_ptr(),
1158            in("x2") 0u64,
1159            in("x3") empty_renderer as *const c_void,
1160            in("x4") 0u64,
1161            in("x8") result.as_mut_ptr(),
1162            lateout("x5") _, lateout("x6") _, lateout("x7") _,
1163            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1164            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1165            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
1166            clobber_abi("C"),
1167        );
1168        wrap_owned(result, meta, wt)
1169    }
1170}
1171
1172/// `EditButton()`.
1173///
1174/// `EditButton` is `@available(macOS, unavailable)`. Availability is a *source*
1175/// restriction and not a linkage one, so `$s7SwiftUI10EditButtonVACycfC` is in
1176/// the macOS export table and binds cleanly — but here the symbol is not the
1177/// whole story either. Calling it on macOS reaches `Fatal error: Unavailable
1178/// code reached` and traps: what Apple shipped at that address is a stub, not an
1179/// initialiser. So this is gated rather than merely bound, and the trap is the
1180/// evidence for the gate.
1181///
1182/// That is the other half of the `defaultHoverEffect` lesson. An exported symbol
1183/// proves the name resolves; only calling it proves there is an implementation.
1184#[cfg(not(target_os = "macos"))]
1185pub fn edit_button() -> ViewHandle {
1186    unsafe {
1187        let (meta, wt, size) = result_type(&EDIT_BUTTON_T);
1188        let mut result = vec![0u8; size.max(1)];
1189        core::arch::asm!("blr {f}", f = in(reg) resolve::edit_button_init(),
1190            in("x8") result.as_mut_ptr(),
1191            lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
1192            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
1193            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1194            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1195            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
1196            clobber_abi("C"),
1197        );
1198        wrap_owned(result, meta, wt)
1199    }
1200}
1201
1202// ── ColorPicker and DatePicker ──
1203
1204mangle::result_types! {
1205    /// `ColorPicker<Text>`
1206    /// `ColorPicker<Text>`
1207    pub(crate) COLOR_PICKER_T = ty("ColorPicker").of(vec![ty("Text")]);
1208    /// `DatePicker<Text>`
1209    /// `DatePicker<Text>`
1210    pub(crate) DATE_PICKER_T = ty("DatePicker").of(vec![ty("Text")]);
1211}
1212
1213/// `ColorPicker(_:selection:supportsOpacity:)`.
1214///
1215/// `Color` is one word, so `Binding<Color>` is 24 bytes — three registers, loaded
1216/// rather than passed by address. That puts the `LocalizedStringKey` in x0–x3,
1217/// the binding in x4–x6 and `supportsOpacity` in x7, with nothing spilled.
1218/// Shared tail of `color_picker` and `color_picker_bound`.
1219///
1220/// # Safety
1221/// `b` must be a `Binding<Color>`.
1222pub(crate) unsafe fn color_picker_with_binding(
1223    title: &str,
1224    supports_opacity: bool,
1225    b: &[u8],
1226) -> ViewHandle {
1227    unsafe {
1228        let ss = abi::swift_string(title);
1229        let s0 = u64::from_le_bytes(ss[..8].try_into().expect("lo"));
1230        let s1 = u64::from_le_bytes(ss[8..].try_into().expect("hi"));
1231        let (k0, k1, k2, k3) = super::localized_string_key(s0, s1);
1232        let w = |i: usize| u64::from_le_bytes(b[i * 8..i * 8 + 8].try_into().expect("word"));
1233
1234        let (meta, wt, size) = result_type(&COLOR_PICKER_T);
1235        let mut result = vec![0u8; size];
1236        core::arch::asm!("blr {f}", f = in(reg) resolve::color_picker_init(),
1237            in("x0") k0, in("x1") k1, in("x2") k2, in("x3") k3,
1238            in("x4") w(0), in("x5") w(1), in("x6") w(2),
1239            in("x7") u64::from(supports_opacity),
1240            in("x8") result.as_mut_ptr(),
1241            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1242            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1243            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
1244            clobber_abi("C"),
1245        );
1246        wrap_owned(result, meta, wt)
1247    }
1248}
1249
1250/// `Date(timeIntervalSinceReferenceDate:)`, in a buffer ready to pass by address.
1251///
1252/// Foundation's `Date` is resilient, so its size comes from the metadata rather
1253/// than being assumed to be the one `Double` it holds.
1254pub(crate) unsafe fn date_bytes(interval: f64) -> Vec<u8> {
1255    unsafe {
1256        let meta = abi::metadata0(resolve::date_ma());
1257        let mut buf = vec![0u8; abi::type_bytes(meta)];
1258        core::arch::asm!("blr {f}", f = in(reg) resolve::date_tisrd_init(),
1259            in("d0") interval,
1260            in("x8") buf.as_mut_ptr(),
1261            lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
1262            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
1263            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1264            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1265            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
1266            clobber_abi("C"),
1267        );
1268        buf
1269    }
1270}
1271
1272/// `DatePicker(_:selection:displayedComponents:)` showing the date components.
1273///
1274/// Both the binding and the components travel by address: `Date` is resilient,
1275/// so `Binding<Date>` is address-only, and `DatePickerComponents` returns its
1276/// statics through x8 rather than in a register — unlike `Axis.Set` and the
1277/// other nested `.Set` types this crate binds.
1278///
1279/// Swift spells the argument `[.date]`, which goes through
1280/// `SetAlgebra.init(arrayLiteral:)`. For a single member that is a union of one,
1281/// so the member's own bytes are passed directly; the parity case's value
1282/// comparison is what confirms the two agree.
1283/// Shared tail of `date_picker` and `date_picker_bound`.
1284///
1285/// # Safety
1286/// `binding` must be a `Binding<Date>`.
1287pub(crate) unsafe fn date_picker_with_binding_components(
1288    title: &str,
1289    binding: &[u8],
1290    components: &[DatePickerComponent],
1291) -> ViewHandle {
1292    unsafe {
1293        let ss = abi::swift_string(title);
1294        let s0 = u64::from_le_bytes(ss[..8].try_into().expect("lo"));
1295        let s1 = u64::from_le_bytes(ss[8..].try_into().expect("hi"));
1296        let (k0, k1, k2, k3) = super::localized_string_key(s0, s1);
1297        let components = date_picker_components(components);
1298
1299        let (meta, wt, size) = result_type(&DATE_PICKER_T);
1300        let mut result = vec![0u8; size];
1301        core::arch::asm!("blr {f}", f = in(reg) resolve::date_picker_init(),
1302            in("x0") k0, in("x1") k1, in("x2") k2, in("x3") k3,
1303            in("x4") binding.as_ptr(),
1304            in("x5") components.as_ptr(),
1305            in("x8") result.as_mut_ptr(),
1306            lateout("x6") _, lateout("x7") _,
1307            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1308            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1309            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
1310            clobber_abi("C"),
1311        );
1312        wrap_owned(result, meta, wt)
1313    }
1314}
1315
1316/// `DatePicker(_:selection:in:displayedComponents:)` — a date picker with
1317/// bounds.
1318///
1319/// "Pick a date after today" is a constraint a date picker exists to express,
1320/// and only the unbounded initialiser was bound — so every picker offered every
1321/// date and the check had to happen afterwards.
1322///
1323/// `lower` and `upper` are seconds from Apple's reference date, the same scale
1324/// [`date_picker_bound`] takes, so a range is two of whatever that function is
1325/// already being given.
1326pub fn date_picker_range(
1327    title: &str,
1328    state: &super::BoundState<f64>,
1329    lower: f64,
1330    upper: f64,
1331) -> ViewHandle {
1332    date_picker_range_components(title, state, lower, upper, &[DatePickerComponent::Date])
1333}
1334
1335/// As [`date_picker_range`], showing the named components.
1336///
1337/// `&[DatePickerComponent::Date, DatePickerComponent::HourAndMinute]` is
1338/// Swift's `[.date, .hourAndMinute]` — a date *and* time picker, which is what
1339/// most uses of this control actually want.
1340pub fn date_picker_range_components(
1341    title: &str,
1342    state: &super::BoundState<f64>,
1343    lower: f64,
1344    upper: f64,
1345    components: &[DatePickerComponent],
1346) -> ViewHandle {
1347    unsafe {
1348        // `Date` is one `Double`, so the same eight bytes a slider would write.
1349        let binding = super::scalar_binding::<f64, 8>(
1350            state,
1351            abi::metadata0(resolve::date_ma()),
1352            mangle::bound(&swift_type!(SwiftUI.Binding), &[mangle::DATE]).as_bytes(),
1353            |v| v.to_le_bytes(),
1354            f64::from_le_bytes,
1355        );
1356        let ss = abi::swift_string(title);
1357        let s0 = u64::from_le_bytes(ss[..8].try_into().expect("title lo"));
1358        let s1 = u64::from_le_bytes(ss[8..].try_into().expect("title hi"));
1359        let (k0, k1, k2, k3) = super::localized_string_key(s0, s1);
1360        let components = date_picker_components(components);
1361
1362        // `ClosedRange<Date>` travels by *address*, not in registers. `Date`
1363        // is resilient — which is why the binding beside it goes by address
1364        // too — so a range of them is address-only however small it measures.
1365        let lo = date_bytes(lower);
1366        let hi = date_bytes(upper);
1367        let n = lo.len();
1368        let mut range = vec![0u8; n * 2];
1369        range[..n].copy_from_slice(&lo);
1370        range[n..].copy_from_slice(&hi);
1371
1372        let (meta, wt, size) = result_type(&DATE_PICKER_T);
1373        let mut result = vec![0u8; size];
1374        // DatePicker.init(_:selection:in:displayedComponents:)
1375        //   x0-x3 = key, x4 = @in Binding<Date>, x5/x6 = the range,
1376        //   x7 = @in DatePickerComponents, x8 = @out
1377        core::arch::asm!("blr {f}", f = in(reg) resolve::date_picker_range_init(),
1378            in("x0") k0, in("x1") k1, in("x2") k2, in("x3") k3,
1379            in("x4") binding.as_ptr(),
1380            in("x5") range.as_ptr(),
1381            in("x6") components.as_ptr(),
1382            lateout("x7") _,
1383            in("x8") result.as_mut_ptr(),
1384            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1385            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1386            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
1387            clobber_abi("C"),
1388        );
1389        wrap_owned(result, meta, wt)
1390    }
1391}
1392
1393mangle::result_types! {
1394    /// `Picker<Text, Int, AnyView>`
1395    /// `Picker<Text, Int, AnyView>`
1396    pub(crate) PICKER_T = ty("Picker").of(vec![ty("Text"), Type::Verbatim("Si".into()), ty("AnyView")]);
1397}
1398
1399/// `Picker(_:selection:) { … }` over an `Int` selection.
1400///
1401/// Four generic words for two parameters — `Picker<Label, SelectionValue, Content>`
1402/// needs `SelectionValue: Hashable` as well as `Content: View`, and the
1403/// declaration order is *selection before content*, which is not the order the
1404/// type name reads in. Only x7 is left by the time they start, so three spill.
1405///
1406/// The spilled words are stored *from registers*. Building them in a local array
1407/// and copying would read through `sp`, and `sub sp, sp, #32` has already moved
1408/// it out from under anything the compiler materialised sp-relative.
1409///
1410/// `Binding<SelectionValue>` travels by address here even though `Binding<Int>`
1411/// is three loadable words: the initialiser is generic over `SelectionValue`, so
1412/// from the callee's side the binding is address-only.
1413/// Shared tail of `picker` and `picker_bound`.
1414///
1415/// # Safety
1416/// `binding` must be a binding of the type this control edits.
1417pub(crate) unsafe fn picker_with_binding(title: &str, content: &ViewHandle, binding: &[u8]) -> ViewHandle {
1418    unsafe {
1419        let ss = abi::swift_string(title);
1420        let s0 = u64::from_le_bytes(ss[..8].try_into().expect("lo"));
1421        let s1 = u64::from_le_bytes(ss[8..].try_into().expect("hi"));
1422        let (k0, k1, k2, k3) = super::localized_string_key(s0, s1);
1423
1424
1425        // `Picker<Label, SelectionValue, Content>` — three parameters, so
1426        // `Content`'s witness is at 5. `SelectionValue`'s conformance is
1427        // `Hashable` rather than `View`, and is left as the known one.
1428        let (known_meta, known_wt, known_size) = result_type(&PICKER_T);
1429        let typed = super::substitute_args(known_meta, 6, &[(2, content.meta)], &[(5, content.wt)]);
1430        let (meta, wt) = typed.unwrap_or((known_meta, known_wt));
1431        let size = if typed.is_some() { abi::type_bytes(meta) } else { known_size };
1432        let (content_meta, content_wt) = match typed {
1433            Some(_) => (content.meta, content.wt),
1434            None => (resolve::anyview_meta(), resolve::anyview_wt()),
1435        };
1436        let kind = if typed.is_some() { Content::Concrete } else { Content::Erased };
1437
1438        let kid_handles = Child::new(content, kind);
1439        let handles = kid_handles.element();
1440        let payload = TuplePayload { children: handles.as_ptr(), count: 1 };
1441        let mut ctx =
1442            abi::ClosureCtx::new(emit_tuple, &payload as *const TuplePayload as *mut c_void);
1443
1444        let mut result = vec![0u8; size];
1445        // Picker.init(_:selection:content:)
1446        //   x0-x3 = key, x4 = &Binding<SelectionValue>,
1447        //   x5 = content fn, x6 = content ctx, x7 = SelectionValue.Type,
1448        //   [sp] = Content.Type, [sp+8] = SelectionValue: Hashable WT,
1449        //   [sp+16] = Content: View WT, x8 = @out
1450        core::arch::asm!(
1451            "sub sp, sp, #32",
1452            "stp {cm}, {hw}, [sp]",
1453            "str {vw}, [sp, #16]",
1454            "blr {f}",
1455            "add sp, sp, #32",
1456            f = in(reg) resolve::picker_init(),
1457            cm = in(reg) content_meta,
1458            hw = in(reg) resolve::int_hashable_wt(),
1459            vw = in(reg) content_wt,
1460            in("x0") k0, in("x1") k1, in("x2") k2, in("x3") k3,
1461            in("x4") binding.as_ptr(),
1462            in("x5") abi::swift_closure_thunk as *const c_void,
1463            in("x6") &mut ctx as *mut abi::ClosureCtx,
1464            in("x7") resolve::int_meta(),
1465            in("x8") result.as_mut_ptr(),
1466            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1467            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1468            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
1469            clobber_abi("C"),
1470        );
1471        wrap_owned(result, meta, wt)
1472    }
1473}
1474
1475// ── Table and TableColumn ──
1476
1477// These three names are the compiler's own, copied from `-emit-ir`, rather than
1478// rebuilt by `result_types!`. `TableColumn<…, Text, Text>` repeats a type inside
1479// one argument list, and the crate's substitution numbering resolved the second
1480// `Text` to `TableColumn` — the runtime then refused the name. A name Swift
1481// emitted is by construction the one the runtime accepts.
1482
1483/// `SwiftUI.TableColumn<SwiftUI.InterfaceOrientation, Swift.Never, SwiftUI.Text, SwiftUI.Text>`
1484pub(crate) const TABLE_COLUMN_T: &[u8] =
1485    b"7SwiftUI11TableColumnVyAA20InterfaceOrientationVs5NeverOAA4TextVAIG";
1486/// `[SwiftUI.InterfaceOrientation]`
1487pub(crate) const IO_ARRAY_T: &[u8] = b"Say7SwiftUI20InterfaceOrientationVG";
1488/// `SwiftUI.Table<SwiftUI.InterfaceOrientation, SwiftUI.TableForEachContent<[SwiftUI.InterfaceOrientation]>, SwiftUI.TableColumn<SwiftUI.InterfaceOrientation, Swift.Never, SwiftUI.Text, SwiftUI.Text>>`
1489pub(crate) const TABLE_T: &[u8] =
1490    b"7SwiftUI5TableVyAA20InterfaceOrientationVAA0C14ForEachContentVySayAEGGAA0C6ColumnVyAEs5NeverOAA4TextVAOGG";
1491
1492/// A protocol witness table, looked up at run time rather than linked.
1493///
1494/// SwiftUI exports conformance *descriptors* but no `…WP` symbols for these —
1495/// the compiler emits a lazy accessor per client instead. Since the conformance
1496/// is registered in the process's conformance table, asking the runtime for it
1497/// gives the same table the accessor would have cached.
1498pub(crate) unsafe fn conformance_wt(meta: *const c_void, proto: *const c_void) -> *const c_void {
1499    unsafe {
1500        assert!(!meta.is_null() && !proto.is_null(), "conformance lookup needs both operands");
1501        type Conforms = unsafe extern "C" fn(*const c_void, *const c_void) -> *const c_void;
1502        let f: Conforms = core::mem::transmute(resolve::conforms_to_protocol());
1503        let wt = f(meta, proto);
1504        assert!(!wt.is_null(), "no registered conformance");
1505        wt
1506    }
1507}
1508
1509/// What a table column's cell closure needs to build one `Text`.
1510struct ColumnCell {
1511    text: String,
1512}
1513
1514/// `TableColumn`'s `(RowValue) -> Text` body.
1515///
1516/// The row arrives by address in x0 because `RowValue` is generic, and the
1517/// `Text` goes back through x8 — which is what [`abi::index_closure_thunk`]
1518/// already shuffles into place. The row is ignored here; each call builds a
1519/// fresh `+1` `Text`, so ownership transfers correctly however often SwiftUI
1520/// re-renders.
1521unsafe extern "C" fn emit_cell_text(out: *mut u8, _row: i64, payload: *mut c_void) {
1522    unsafe {
1523        let cell = &*(payload as *const ColumnCell);
1524        let words = super::text_verbatim_words(&cell.text);
1525        core::ptr::copy_nonoverlapping(words.as_ptr() as *const u8, out, 32);
1526    }
1527}
1528
1529/// What the `columns:` closure needs to rebuild its column.
1530struct ColumnsPayload {
1531    title: String,
1532    cell: *mut c_void,
1533}
1534
1535/// `Table`'s `columns:` body — one `TableColumn`, rebuilt per call.
1536unsafe extern "C" fn emit_columns(out: *mut u8, payload: *mut c_void) {
1537    unsafe {
1538        let p = &*(payload as *const ColumnsPayload);
1539        let io_meta = abi::metadata0(resolve::interface_orientation_ma());
1540        let ss = abi::swift_string(&p.title);
1541        let s0 = u64::from_le_bytes(ss[..8].try_into().expect("lo"));
1542        let s1 = u64::from_le_bytes(ss[8..].try_into().expect("hi"));
1543        let (k0, k1, k2, k3) = super::localized_string_key(s0, s1);
1544        // TableColumn.init(_:content:)
1545        //   x0-x3 = key, x4 = content fn, x5 = content ctx,
1546        //   x6 = RowValue.Type, x7 = Content.Type,
1547        //   [sp] = RowValue: Identifiable WT, [sp+8] = Content: View WT, x8 = @out
1548        core::arch::asm!(
1549            "sub sp, sp, #16",
1550            "stp {iw}, {vw}, [sp]",
1551            "blr {f}",
1552            "add sp, sp, #16",
1553            f = in(reg) resolve::table_column_init(),
1554            iw = in(reg) conformance_wt(io_meta, resolve::identifiable_proto()),
1555            vw = in(reg) resolve::text_wt(),
1556            in("x0") k0, in("x1") k1, in("x2") k2, in("x3") k3,
1557            in("x4") abi::index_closure_thunk as *const c_void,
1558            in("x5") p.cell,
1559            in("x6") io_meta,
1560            in("x7") resolve::text_meta(),
1561            in("x8") out,
1562            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1563            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1564            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
1565            clobber_abi("C"),
1566        );
1567    }
1568}
1569
1570/// `Table(rows) { TableColumn(title) { row in Text(cell) } }`.
1571///
1572/// Rows are `InterfaceOrientation` values because `Table` needs
1573/// `Value: Identifiable`, and a Rust type cannot be made to conform without
1574/// synthesising a nominal Swift type — a different problem from binding an ABI.
1575/// `InterfaceOrientation` is one of the few SwiftUI types that already conforms
1576/// and can be built from its statics, so the whole call is reachable, and the
1577/// same expression compiles in Swift for the reference.
1578///
1579/// `Value: Identifiable` is not among the witness tables passed: the runtime
1580/// derives it from `Columns.TableRowValue`, whose associated conformance the
1581/// `TableColumnContent` table already carries. So five generic words, not six,
1582/// and nothing spills.
1583pub fn table(rows: &[Orientation], title: &str, cell: &str) -> ViewHandle {
1584    unsafe {
1585        let io_meta = abi::metadata0(resolve::interface_orientation_ma());
1586        let stride = abi::type_stride(io_meta);
1587        let mut bytes = vec![0u8; stride * rows.len()];
1588        for (i, o) in rows.iter().enumerate() {
1589            let v = super::resilient_static(io_meta, o.getter());
1590            bytes[i * stride..i * stride + v.len()].copy_from_slice(&v);
1591        }
1592        let array = abi::swift_array(io_meta, rows.len(), &bytes);
1593
1594        let cell_payload = Box::into_raw(Box::new(ColumnCell { text: cell.to_string() })) as *mut c_void;
1595        let cell_ctx = Box::leak(Box::new(abi::IndexClosureCtx::new(emit_cell_text, cell_payload)))
1596            as *mut abi::IndexClosureCtx as *mut c_void;
1597        let cols_payload = Box::into_raw(Box::new(ColumnsPayload {
1598            title: title.to_string(),
1599            cell: cell_ctx,
1600        })) as *mut c_void;
1601        let mut cols_ctx = abi::ClosureCtx::new(emit_columns, cols_payload);
1602
1603        let column_meta = abi::resolve_type_by_mangled_name(TABLE_COLUMN_T);
1604        assert!(!column_meta.is_null(), "TableColumn metadata");
1605        let array_meta = abi::resolve_type_by_mangled_name(IO_ARRAY_T);
1606        assert!(!array_meta.is_null(), "Array<InterfaceOrientation> metadata");
1607
1608        let (meta, wt, size) = result_type(TABLE_T);
1609        let mut result = vec![0u8; size];
1610        // Table.init(_:columns:)
1611        //   x0 = &data, x1 = columns fn, x2 = columns ctx,
1612        //   x3 = Value.Type, x4 = Columns.Type, x5 = Data.Type,
1613        //   x6 = Columns: TableColumnContent WT,
1614        //   x7 = Data: RandomAccessCollection WT, x8 = @out
1615        core::arch::asm!("blr {f}", f = in(reg) resolve::table_init(),
1616            in("x0") &array as *const u64,
1617            in("x1") abi::swift_closure_thunk as *const c_void,
1618            in("x2") &mut cols_ctx as *mut abi::ClosureCtx,
1619            in("x3") io_meta,
1620            in("x4") column_meta,
1621            in("x5") array_meta,
1622            in("x6") conformance_wt(column_meta, resolve::table_column_content_proto()),
1623            in("x7") conformance_wt(array_meta, resolve::random_access_proto()),
1624            in("x8") result.as_mut_ptr(),
1625            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1626            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1627            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
1628            clobber_abi("C"),
1629        );
1630        wrap_owned(result, meta, wt)
1631    }
1632}
1633
1634swift_statics! {
1635    /// A member of `SwiftUI.InterfaceOrientation` — [`table`]'s row type.
1636    ///
1637    /// Not a natural thing to tabulate, but `Table` needs `Value: Identifiable`,
1638    /// and this is one of the few SwiftUI types that both conforms and can be
1639    /// built from its own statics. See [`table`].
1640    pub enum Orientation {
1641        Portrait = c"$s7SwiftUI20InterfaceOrientationV8portraitACvgZ",
1642        PortraitUpsideDown = c"$s7SwiftUI20InterfaceOrientationV18portraitUpsideDownACvgZ",
1643        LandscapeLeft = c"$s7SwiftUI20InterfaceOrientationV13landscapeLeftACvgZ",
1644        LandscapeRight = c"$s7SwiftUI20InterfaceOrientationV14landscapeRightACvgZ",
1645    }
1646}
1647
1648// ── OutlineGroup ──
1649
1650/// `SwiftUI.OutlineGroup<[Swift.Int], Swift.Int, SwiftUI.AnyView, SwiftUI.AnyView, SwiftUI.DisclosureGroup<SwiftUI.AnyView, SwiftUI.OutlineSubgroupChildren>>`
1651pub(crate) const OUTLINE_GROUP_T: &[u8] =
1652    b"7SwiftUI12OutlineGroupVySaySiGSiAA7AnyViewVAfA010DisclosureD0VyAfA0C16SubgroupChildrenVGG";
1653
1654/// The two key path patterns `OutlineGroup` needs, one slot each.
1655///
1656/// `static mut` rather than boxed for the reason [`crate::keypath`] documents:
1657/// every reference inside a pattern is a 32-bit offset, and the thunks it points
1658/// at are in this image's text segment.
1659static mut OUTLINE_ID_SLOT: crate::keypath::IdentitySlot = crate::keypath::IdentitySlot::new();
1660static mut OUTLINE_CHILDREN_SLOT: crate::keypath::ComputedSlot = crate::keypath::ComputedSlot::new();
1661
1662/// `OutlineGroup`'s `(Element) -> Leaf` body.
1663///
1664/// The element arrives by address because it is generic; this leaf ignores it,
1665/// so what the pointer addresses never matters. Each call hands back a `+1`
1666/// `AnyView`.
1667unsafe extern "C" fn emit_leaf_view(out: *mut u8, _element: i64, payload: *mut c_void) {
1668    unsafe {
1669        let leaf = &*(payload as *const ViewHandle);
1670        let copy = leaf.clone();
1671        *(out as *mut u64) = copy.into_anyview_word();
1672    }
1673}
1674
1675/// `OutlineGroup(root, id:children:content:)` over `Int` nodes with no children.
1676///
1677/// The row type is `Int` because the two key paths are *synthesised*: no Swift
1678/// type has to exist for a get-only computed key path, only a getter, so the
1679/// root can be any type whose metadata is nameable. `Int` is the plainest.
1680///
1681/// Only four generic words, and only one of them metadata — `Leaf`. `Data`, `ID`
1682/// and `Element` are not passed at all; the runtime takes them from the
1683/// `KeyPath<Root, Value>` class metadata of the two key paths, which is why
1684/// their mangled names have to be right rather than merely plausible.
1685pub fn outline_group(root: i64, leaf: &ViewHandle) -> ViewHandle {
1686    unsafe {
1687        // Built once, not per call: rebuilding one per render handed SwiftUI a
1688        // fresh object every time and never got it back — 45 KB a frame.
1689        //
1690        // `OutlineGroup.init` *consumes* its key paths; the compiled reference
1691        // never releases what `swift_getKeyPath` returned. So the cached object
1692        // is retained at each use, which is what the per-call build was
1693        // accidentally paying for. Reusing it without the retain over-releases
1694        // and traps on the third render.
1695        static ID_KP: std::sync::OnceLock<u64> = std::sync::OnceLock::new();
1696        static CHILDREN_KP: std::sync::OnceLock<u64> = std::sync::OnceLock::new();
1697        // `\Int.self`, not a computed key path that happens to return the root.
1698        // Both are correct, but only the identity one reflects the same as the
1699        // reference's — a computed component is described by whatever symbol
1700        // `dladdr` finds at its getter, and a getter written in Rust has no
1701        // Swift property name to find.
1702        let id_kp = *ID_KP.get_or_init(|| {
1703            crate::keypath::build_identity(crate::static_mut(&raw mut OUTLINE_ID_SLOT), b"Si", b"Si")
1704        });
1705        let children_kp = *CHILDREN_KP.get_or_init(|| {
1706            crate::keypath::build_computed(
1707                crate::static_mut(&raw mut OUTLINE_CHILDREN_SLOT),
1708                b"Si",
1709                b"SaySiGSg",
1710                crate::keypath::kp_nil_children_get as *const c_void,
1711            )
1712        });
1713        swift_runtime_sys::RuntimeRaw::swift_retain(id_kp as *mut c_void);
1714        swift_runtime_sys::RuntimeRaw::swift_retain(children_kp as *mut c_void);
1715
1716        let leaf_payload = Box::into_raw(Box::new(leaf.clone())) as *mut c_void;
1717        let ctx = Box::leak(Box::new(abi::IndexClosureCtx::new(emit_leaf_view, leaf_payload)))
1718            as *mut abi::IndexClosureCtx;
1719
1720        // Cached, like every other case's `result_type` call. Resolving a type
1721        // from a mangled *string* is a demangle plus a metadata-cache probe, and
1722        // asking for the conformance is a second scan — per render that is the
1723        // difference between bounded and growing.
1724        static ARRAY_SK_WT: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
1725        let array_sk_wt = *ARRAY_SK_WT.get_or_init(|| {
1726            let array_meta = abi::resolve_type_by_mangled_name(b"SaySiG");
1727            assert!(!array_meta.is_null(), "Array<Int> metadata");
1728            conformance_wt(array_meta, resolve::random_access_proto()) as usize
1729        }) as *const c_void;
1730
1731        let (meta, wt, size) = result_type(OUTLINE_GROUP_T);
1732        let mut result = vec![0u8; size];
1733        let root_value = root;
1734        // OutlineGroup.init(_:id:children:content:)
1735        //   x0 = &root, x1 = id KeyPath, x2 = children KeyPath,
1736        //   x3 = content fn, x4 = content ctx, x5 = Leaf.Type,
1737        //   x6 = Data: RandomAccessCollection WT, x7 = ID: Hashable WT,
1738        //   [sp] = Leaf: View WT, x8 = @out
1739        core::arch::asm!(
1740            "sub sp, sp, #16",
1741            "str {lw}, [sp]",
1742            "blr {f}",
1743            "add sp, sp, #16",
1744            f = in(reg) resolve::outline_group_init(),
1745            lw = in(reg) resolve::anyview_wt(),
1746            in("x0") &root_value as *const i64,
1747            in("x1") id_kp,
1748            in("x2") children_kp,
1749            in("x3") abi::index_closure_thunk as *const c_void,
1750            in("x4") ctx,
1751            in("x5") resolve::anyview_meta(),
1752            in("x6") array_sk_wt,
1753            in("x7") resolve::int_hashable_wt(),
1754            in("x8") result.as_mut_ptr(),
1755            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1756            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1757            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
1758            clobber_abi("C"),
1759        );
1760        wrap_owned(result, meta, wt)
1761    }
1762}
1763
1764// ── The nine modifiers the coverage parser used to hide ──
1765
1766/// A `View` modifier taking a style value built by a zero-argument initialiser.
1767///
1768/// Exactly the shape `progressViewStyle` has: the style goes in x0 by address,
1769/// then `Self.Type`, `S.Type`, `Self: View` and `S: SomeStyle` fill x1–x4, with
1770/// the receiver in x20 and the opaque result through x8.
1771pub(crate) unsafe fn zero_arg_style_modifier(
1772    view: &ViewHandle,
1773    style_ma: *const c_void,
1774    style_init: *const c_void,
1775    style_wt: *const c_void,
1776    func: *const c_void,
1777    oret: *const c_void,
1778) -> ViewHandle {
1779    unsafe {
1780        let style_meta = abi::metadata0(style_ma);
1781        assert!(!style_meta.is_null(), "style metadata");
1782        let mut style = vec![0u8; abi::type_bytes(style_meta)];
1783        core::arch::asm!("blr {f}", f = in(reg) style_init,
1784            in("x8") style.as_mut_ptr(),
1785            in("x20") style_meta,
1786            lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
1787            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
1788            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1789            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1790            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
1791        );
1792
1793        let subst = [view.meta, style_meta, view.wt, style_wt];
1794        let (meta, wt, size) = super::opaque_result_type(oret, &subst);
1795        let mut result = vec![0u8; size];
1796        let this = view.value_ptr();
1797        core::arch::asm!("blr {f}", f = in(reg) func,
1798            in("x0") style.as_mut_ptr(),
1799            in("x1") subst[0], in("x2") subst[1], in("x3") subst[2], in("x4") subst[3],
1800            in("x8") result.as_mut_ptr(),
1801            in("x20") this,
1802            lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
1803            lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
1804            lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
1805            lateout("lr") _, lateout("x21") _, clobber_abi("C"),
1806        );
1807        wrap_owned(result, meta, wt)
1808    }
1809}
1810
1811/// `.navigationSplitViewStyle(.automatic)`
1812/// `NavigationSplitView { sidebar } content: { content } detail: { detail }`.
1813///
1814/// The three-column shape — a sidebar, a list, and the selected item — which is
1815/// what a Mail or Notes window is. Only the two-column initialiser was bound,
1816/// pinning the middle column to `EmptyView`, so the middle one could not be
1817/// filled at all.
1818///
1819/// All three are `() -> some View` closures, so each is a function-and-context
1820/// pair; the metadata and witnesses follow in generic order behind them.
1821pub fn navigation_split_view3(
1822    sidebar: &ViewHandle,
1823    content: &ViewHandle,
1824    detail: &ViewHandle,
1825) -> ViewHandle {
1826    unsafe {
1827        // All three columns move here, so every parameter and every witness is
1828        // substituted.
1829        let (known_meta, known_wt, known_size) = result_type(&NAV_SPLIT3_T);
1830        let typed = super::substitute_args(
1831            known_meta,
1832            6,
1833            &[(0, sidebar.meta), (1, content.meta), (2, detail.meta)],
1834            &[(3, sidebar.wt), (4, content.wt), (5, detail.wt)],
1835        );
1836        let (meta, wt) = typed.unwrap_or((known_meta, known_wt));
1837        let size = if typed.is_some() { abi::type_bytes(meta) } else { known_size };
1838        let kind = if typed.is_some() { Content::Concrete } else { Content::Erased };
1839
1840        // One leaked child list and closure context per column.
1841        let side_kids = Child::new(sidebar, kind);
1842        let side_handles = side_kids.element();
1843        let side_payload = TuplePayload { children: side_handles.as_ptr(), count: 1 };
1844        let mut side_ctx =
1845            abi::ClosureCtx::new(emit_tuple, &side_payload as *const TuplePayload as *mut c_void);
1846
1847        let mid_kids = Child::new(content, kind);
1848        let mid_handles = mid_kids.element();
1849        let mid_payload = TuplePayload { children: mid_handles.as_ptr(), count: 1 };
1850        let mut mid_ctx =
1851            abi::ClosureCtx::new(emit_tuple, &mid_payload as *const TuplePayload as *mut c_void);
1852
1853        let detail_kids = Child::new(detail, kind);
1854        let detail_handles = detail_kids.element();
1855        let detail_payload = TuplePayload { children: detail_handles.as_ptr(), count: 1 };
1856        let mut detail_ctx =
1857            abi::ClosureCtx::new(emit_tuple, &detail_payload as *const TuplePayload as *mut c_void);
1858
1859        let mut result = vec![0u8; size];
1860        let any = resolve::anyview_meta();
1861        let any_wt = resolve::anyview_wt();
1862        let [side_meta, mid_meta, detail_meta] = match typed {
1863            Some(_) => [sidebar.meta, content.meta, detail.meta],
1864            None => [any; 3],
1865        };
1866        let [side_wt, mid_wt, detail_wt] = match typed {
1867            Some(_) => [sidebar.wt, content.wt, detail.wt],
1868            None => [any_wt; 3],
1869        };
1870        // NavigationSplitView.init(sidebar:content:detail:)
1871        //   x0/x1, x2/x3, x4/x5 = the three closures in that order,
1872        //   x6 = Sidebar.Type, x7 = Content.Type,
1873        //   [sp] = Detail.Type, [sp+8..] = the three View witnesses,
1874        //   x8 = @out
1875        core::arch::asm!(
1876            "sub sp, sp, #32",
1877            "stp {dm}, {sw}, [sp]",
1878            "stp {cw}, {dw}, [sp, #16]",
1879            "blr {f}",
1880            "add sp, sp, #32",
1881            f = in(reg) resolve::nav_split3_init(),
1882            dm = in(reg) detail_meta,
1883            sw = in(reg) side_wt,
1884            cw = in(reg) mid_wt,
1885            dw = in(reg) detail_wt,
1886            in("x0") abi::swift_closure_thunk as *const c_void,
1887            in("x1") &mut side_ctx as *mut abi::ClosureCtx,
1888            in("x2") abi::swift_closure_thunk as *const c_void,
1889            in("x3") &mut mid_ctx as *mut abi::ClosureCtx,
1890            in("x4") abi::swift_closure_thunk as *const c_void,
1891            in("x5") &mut detail_ctx as *mut abi::ClosureCtx,
1892            in("x6") side_meta,
1893            in("x7") mid_meta,
1894            in("x8") result.as_mut_ptr(),
1895            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
1896            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
1897            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
1898            clobber_abi("C"),
1899        );
1900        wrap_owned(result, meta, wt)
1901    }
1902}
1903
1904pub fn navigation_split_view_style_automatic(view: &ViewHandle) -> ViewHandle {
1905    navigation_split_view_style(view, SplitViewStyle::Automatic)
1906}
1907
1908/// Which `NavigationSplitViewStyle` to apply.
1909///
1910/// Each is a distinct Swift type with its own conformance, which is why the
1911/// modifier is generic in Swift and takes a value here.
1912#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1913pub enum SplitViewStyle {
1914    /// `.automatic`.
1915    Automatic,
1916    /// `.balanced` — the detail column shares space with the others.
1917    Balanced,
1918    /// `.prominentDetail` — the detail column keeps its size.
1919    ProminentDetail,
1920}
1921
1922impl SplitViewStyle {
1923    fn parts(self) -> (*const c_void, *const c_void, *const c_void) {
1924        match self {
1925            Self::Automatic => (
1926                resolve::auto_nav_split_style_ma(),
1927                resolve::auto_nav_split_style_init(),
1928                resolve::auto_nav_split_style_wt(),
1929            ),
1930            Self::Balanced => (
1931                resolve::balanced_nav_split_style_ma(),
1932                resolve::balanced_nav_split_style_init(),
1933                resolve::balanced_nav_split_style_wt(),
1934            ),
1935            Self::ProminentDetail => (
1936                resolve::prominent_nav_split_style_ma(),
1937                resolve::prominent_nav_split_style_init(),
1938                resolve::prominent_nav_split_style_wt(),
1939            ),
1940        }
1941    }
1942}
1943
1944/// Apply `.navigationSplitViewStyle(_:)`.
1945pub fn navigation_split_view_style(view: &ViewHandle, style: SplitViewStyle) -> ViewHandle {
1946    unsafe {
1947        let (ma, init, wt) = style.parts();
1948        zero_arg_style_modifier(view, ma, init, wt,
1949            resolve::nav_split_style_fn(), resolve::nav_split_style_oret())
1950    }
1951}
1952
1953/// `.navigationViewStyle(.automatic)`
1954pub fn navigation_view_style_automatic(view: &ViewHandle) -> ViewHandle {
1955    unsafe {
1956        zero_arg_style_modifier(view, resolve::default_nav_view_style_ma(),
1957            resolve::default_nav_view_style_init(), resolve::default_nav_view_style_wt(),
1958            resolve::nav_view_style_fn(), resolve::nav_view_style_oret())
1959    }
1960}
1961
1962/// `.navigationViewStyle(.columns)` — the two-column split.
1963///
1964/// The witness is looked up at run time rather than named: a
1965/// `NavigationViewStyle` conformance mangles its substitutions against the
1966/// words in its *own* type name, so `ColumnNavigationViewStyle`'s is not
1967/// `DefaultNavigationViewStyle`'s with a letter changed. Copying one has
1968/// produced a witness for the wrong protocol in this crate before.
1969pub fn navigation_view_style_columns(view: &ViewHandle) -> ViewHandle {
1970    unsafe {
1971        let meta = abi::metadata0(resolve::column_nav_view_style_ma());
1972        let wt = abi::get_view_wt_for_proto(meta, resolve::nav_view_style_proto());
1973        assert!(!wt.is_null(), "ColumnNavigationViewStyle: NavigationViewStyle");
1974        zero_arg_style_modifier(view, resolve::column_nav_view_style_ma(),
1975            resolve::column_nav_view_style_init(), wt,
1976            resolve::nav_view_style_fn(), resolve::nav_view_style_oret())
1977    }
1978}
1979
1980/// `.navigationViewStyle(.doubleColumn)` — sidebar plus detail.
1981pub fn navigation_view_style_double_column(view: &ViewHandle) -> ViewHandle {
1982    unsafe {
1983        let meta = abi::metadata0(resolve::double_column_nav_view_style_ma());
1984        let wt = abi::get_view_wt_for_proto(meta, resolve::nav_view_style_proto());
1985        assert!(!wt.is_null(), "DoubleColumnNavigationViewStyle: NavigationViewStyle");
1986        zero_arg_style_modifier(view, resolve::double_column_nav_view_style_ma(),
1987            resolve::double_column_nav_view_style_init(), wt,
1988            resolve::nav_view_style_fn(), resolve::nav_view_style_oret())
1989    }
1990}
1991
1992/// `.navigationViewStyle(.stack)` — one view at a time, pushed and popped.
1993///
1994/// `@available(macOS, unavailable)`, so iOS only.
1995#[cfg(not(target_os = "macos"))]
1996pub fn navigation_view_style_stack(view: &ViewHandle) -> ViewHandle {
1997    unsafe {
1998        let meta = abi::metadata0(resolve::stack_nav_view_style_ma());
1999        let wt = abi::get_view_wt_for_proto(meta, resolve::nav_view_style_proto());
2000        assert!(!wt.is_null(), "StackNavigationViewStyle: NavigationViewStyle");
2001        zero_arg_style_modifier(view, resolve::stack_nav_view_style_ma(),
2002            resolve::stack_nav_view_style_init(), wt,
2003            resolve::nav_view_style_fn(), resolve::nav_view_style_oret())
2004    }
2005}
2006
2007/// `.tabViewStyle(.automatic)`
2008pub fn tab_view_style_automatic(view: &ViewHandle) -> ViewHandle {
2009    tab_view_style(view, TabStyle::Automatic)
2010}
2011
2012/// Which `TabViewStyle` to apply.
2013///
2014/// Each is a distinct Swift type with its own conformance, which is why the
2015/// modifier is generic in Swift and takes a value here.
2016///
2017/// The cases are the ones a platform can actually use, so a style that does
2018/// not exist here is a compile error rather than a trap at run time:
2019/// `.grouped` is macOS-only and absent on iOS. `.page` is iOS-only and is not
2020/// offered at all — its initialiser takes an `indexDisplayMode`, and nothing
2021/// on this machine could check it.
2022#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2023pub enum TabStyle {
2024    /// `.automatic` — `DefaultTabViewStyle`.
2025    Automatic,
2026    /// `.sidebarAdaptable` — a sidebar where there is room, tabs where not.
2027    SidebarAdaptable,
2028    /// `.tabBarOnly` — the tab bar without the adaptive sidebar.
2029    TabBarOnly,
2030    /// `.grouped` — macOS only.
2031    #[cfg(target_os = "macos")]
2032    Grouped,
2033}
2034
2035impl TabStyle {
2036    fn parts(self) -> (*const c_void, *const c_void, *const c_void) {
2037        match self {
2038            Self::Automatic => (
2039                resolve::default_tab_view_style_ma(),
2040                resolve::default_tab_view_style_init(),
2041                resolve::default_tab_view_style_wt(),
2042            ),
2043            Self::SidebarAdaptable => (
2044                resolve::sidebar_tab_view_style_ma(),
2045                resolve::sidebar_tab_view_style_init(),
2046                resolve::sidebar_tab_view_style_wt(),
2047            ),
2048            Self::TabBarOnly => (
2049                resolve::tabbar_only_tab_view_style_ma(),
2050                resolve::tabbar_only_tab_view_style_init(),
2051                resolve::tabbar_only_tab_view_style_wt(),
2052            ),
2053            #[cfg(target_os = "macos")]
2054            Self::Grouped => (
2055                resolve::grouped_tab_view_style_ma(),
2056                resolve::grouped_tab_view_style_init(),
2057                resolve::grouped_tab_view_style_wt(),
2058            ),
2059        }
2060    }
2061}
2062
2063/// Apply `.tabViewStyle(_:)`.
2064pub fn tab_view_style(view: &ViewHandle, style: TabStyle) -> ViewHandle {
2065    unsafe {
2066        let (ma, init, wt) = style.parts();
2067        zero_arg_style_modifier(view, ma, init, wt,
2068            resolve::tab_view_style_fn(), resolve::tab_view_style_oret())
2069    }
2070}
2071
2072/// `.navigationSplitViewColumnWidth(_:)` — one `CGFloat`, so d0 and nothing else.
2073pub fn navigation_split_view_column_width(view: &ViewHandle, width: f64) -> ViewHandle {
2074    unsafe {
2075        super::modifier_f64(resolve::nav_split_column_width_fn(),
2076            resolve::nav_split_column_width_oret(), view, width)
2077    }
2078}
2079
2080/// `.tabViewSearchActivation(.automatic)` — a resilient struct, so by address.
2081pub fn tab_view_search_activation_automatic(view: &ViewHandle) -> ViewHandle {
2082    tab_view_search_activation(view, TabSearchActivation::Automatic)
2083}
2084
2085/// Which of `TabSearchActivation`'s cases a tab view's search uses.
2086///
2087/// Two, not three: `onSearchPresentation` is in the documentation but exports
2088/// no getter, so there is nothing to call. The export table is the list here.
2089#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2090pub enum TabSearchActivation {
2091    /// Let the system decide.
2092    Automatic,
2093    /// Activate when the search tab itself is selected.
2094    SearchTabSelection,
2095}
2096
2097/// `.tabViewSearchActivation(_:)`.
2098pub fn tab_view_search_activation(
2099    view: &ViewHandle,
2100    activation: TabSearchActivation,
2101) -> ViewHandle {
2102    unsafe {
2103        let meta = abi::metadata0(resolve::tab_search_activation_ma());
2104        let value = super::resilient_static(
2105            meta,
2106            match activation {
2107                TabSearchActivation::Automatic =>
2108                    c"$s7SwiftUI19TabSearchActivationV9automaticACvgZ",
2109                // `searchTabSelection` compresses `search` against the type's
2110                // own `Search`, so the mangling is `06searchC9Selection`, not
2111                // the `18searchTabSelection` a fresh encoding would give.
2112                TabSearchActivation::SearchTabSelection =>
2113                    c"$s7SwiftUI19TabSearchActivationV06searchC9SelectionACvgZ",
2114            },
2115        );
2116        super::modifier_indirect(resolve::tab_search_activation_fn(),
2117            resolve::tab_search_activation_oret(), view, &value)
2118    }
2119}
2120
2121/// `.tabViewCustomization(nil)`.
2122///
2123/// The argument is `Binding<TabViewCustomization>?`. A non-nil one would need a
2124/// binding into customization state a reference binary would have to declare for
2125/// itself, so `nil` is both the reachable spelling and the comparable one.
2126pub fn tab_view_customization_none(view: &ViewHandle) -> ViewHandle {
2127    unsafe {
2128        let payload = abi::resolve_type_by_mangled_name(b"7SwiftUI7BindingVyAA20TabViewCustomizationVG");
2129        assert!(!payload.is_null(), "Binding<TabViewCustomization> metadata");
2130        let bytes = super::opt_resilient_bytes(
2131            payload,
2132            b"7SwiftUI7BindingVyAA20TabViewCustomizationVGSg",
2133            None,
2134        );
2135        super::modifier_indirect(resolve::tab_customization_fn(),
2136            resolve::tab_customization_oret(), view, &bytes)
2137    }
2138}
2139
2140/// `.tabViewCustomization(_:)` with a live customization, not `nil`.
2141///
2142/// [`tab_view_customization_none`] passes `nil`, which is the "do not persist
2143/// anything" case and the only one that needed no value. This builds a real
2144/// `TabViewCustomization` and hands SwiftUI a binding to it, so the user's
2145/// reordering and hiding of tabs is kept — the whole point of the modifier.
2146///
2147/// The value lives in the binding's closures for the life of the process: a
2148/// customization SwiftUI can write back to has to outlive the call that made
2149/// it, and there is nowhere else to put it.
2150pub fn tab_view_customization(view: &ViewHandle) -> ViewHandle {
2151    unsafe {
2152        let meta = abi::metadata0(resolve::tab_customization_ma());
2153        assert!(!meta.is_null(), "TabViewCustomization metadata");
2154        let size = abi::type_bytes(meta);
2155        let mut initial = vec![0u8; size];
2156        core::arch::asm!("blr {f}", f = in(reg) resolve::tab_customization_init(),
2157            in("x8") initial.as_mut_ptr(),
2158            in("x20") meta,
2159            lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
2160            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
2161            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
2162            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
2163            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
2164        );
2165
2166        // Shared between the getter and the setter, and leaked: SwiftUI holds
2167        // the binding, so the storage has to last as long as the view does.
2168        let cell: &'static std::sync::Mutex<Vec<u8>> =
2169            Box::leak(Box::new(std::sync::Mutex::new(initial)));
2170        let get_cell = cell;
2171        let binding = super::binding_get_set(
2172            meta,
2173            b"7SwiftUI7BindingVyAA20TabViewCustomizationVG",
2174            Box::new(move |out| {
2175                let v = get_cell.lock().expect("customization");
2176                core::ptr::copy_nonoverlapping(v.as_ptr(), out, v.len());
2177            }),
2178            Box::new(move |inp| {
2179                let mut v = cell.lock().expect("customization");
2180                let n = v.len();
2181                core::ptr::copy_nonoverlapping(inp, v.as_mut_ptr(), n);
2182            }),
2183        );
2184
2185        let payload =
2186            abi::resolve_type_by_mangled_name(b"7SwiftUI7BindingVyAA20TabViewCustomizationVG");
2187        assert!(!payload.is_null(), "Binding<TabViewCustomization> metadata");
2188        let bytes = super::opt_resilient_bytes(
2189            payload,
2190            b"7SwiftUI7BindingVyAA20TabViewCustomizationVGSg",
2191            Some(&binding),
2192        );
2193        super::modifier_indirect(resolve::tab_customization_fn(),
2194            resolve::tab_customization_oret(), view, &bytes)
2195    }
2196}
2197
2198/// A `View` modifier taking one `@ViewBuilder` content closure.
2199///
2200/// x0/x1 are the closure, then `Self.Type`, `V.Type`, `Self: View`, `V: View`.
2201/// The closure is `@noescape` — SwiftUI resolves the content during the call —
2202/// so the context can live on this stack frame.
2203pub(crate) unsafe fn content_closure_modifier(
2204    view: &ViewHandle,
2205    content: &ViewHandle,
2206    func: *const c_void,
2207    oret: *const c_void,
2208) -> ViewHandle {
2209    unsafe {
2210        let kid_handles = Child::new(content, Content::Concrete);
2211        let handles = kid_handles.element();
2212        let payload = TuplePayload { children: handles.as_ptr(), count: 1 };
2213        let mut ctx =
2214            abi::ClosureCtx::new(emit_tuple, &payload as *const TuplePayload as *mut c_void);
2215
2216        let subst = [view.meta, content.meta,
2217                     view.wt, content.wt];
2218        let (meta, wt, size) = super::opaque_result_type(oret, &subst);
2219        let mut result = vec![0u8; size];
2220        let this = view.value_ptr();
2221        core::arch::asm!("blr {f}", f = in(reg) func,
2222            in("x0") abi::swift_closure_thunk as *const c_void,
2223            in("x1") &mut ctx as *mut abi::ClosureCtx,
2224            in("x2") subst[0], in("x3") subst[1], in("x4") subst[2], in("x5") subst[3],
2225            in("x8") result.as_mut_ptr(),
2226            in("x20") this,
2227            lateout("x6") _, lateout("x7") _, lateout("x9") _,
2228            lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
2229            lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
2230            lateout("lr") _, lateout("x21") _, clobber_abi("C"),
2231        );
2232        wrap_owned(result, meta, wt)
2233    }
2234}
2235
2236/// `.tabViewSidebarHeader { … }`
2237pub fn tab_view_sidebar_header(view: &ViewHandle, content: &ViewHandle) -> ViewHandle {
2238    unsafe {
2239        content_closure_modifier(view, content,
2240            resolve::tab_sidebar_header_fn(), resolve::tab_sidebar_header_oret())
2241    }
2242}
2243
2244/// `.tabViewSidebarFooter { … }`
2245pub fn tab_view_sidebar_footer(view: &ViewHandle, content: &ViewHandle) -> ViewHandle {
2246    unsafe {
2247        content_closure_modifier(view, content,
2248            resolve::tab_sidebar_footer_fn(), resolve::tab_sidebar_footer_oret())
2249    }
2250}
2251
2252/// `.tabViewSidebarBottomBar { … }`
2253pub fn tab_view_sidebar_bottom_bar(view: &ViewHandle, content: &ViewHandle) -> ViewHandle {
2254    unsafe {
2255        content_closure_modifier(view, content,
2256            resolve::tab_sidebar_bottom_bar_fn(), resolve::tab_sidebar_bottom_bar_oret())
2257    }
2258}
2259
2260// ── Modifiers Apple declares `@_alwaysEmitIntoClient` ──
2261//
2262// The framework exports no entry point for these, so each one builds the value
2263// its inlined body builds and goes through the exported `View.modifier(_:)` or
2264// `View.environment(_:_:)`. What the body builds is read out of the deserialised
2265// SIL of the inlinable function — `swiftc -emit-sil` carries it — rather than
2266// guessed from the signature.
2267
2268/// `.geometryGroup()` — inlines to `modifier(_GeometryGroupEffect())`.
2269///
2270/// `_GeometryGroupEffect` has no stored properties, so the value is zero bytes
2271/// and all the work is naming the type.
2272pub fn geometry_group(view: &ViewHandle) -> ViewHandle {
2273    unsafe {
2274        let meta = abi::metadata0(resolve::geometry_group_effect_ma());
2275        assert!(!meta.is_null(), "_GeometryGroupEffect metadata");
2276        let bytes = vec![0u8; abi::type_bytes(meta)];
2277        super::apply_modifier(view, meta, &bytes)
2278    }
2279}
2280
2281/// `.symbolEffectsRemoved(_:)` — inlines to
2282/// `modifier(_SymbolEffectsRemovedModifier(isEnabled:))`, one `Bool`.
2283pub fn symbol_effects_removed(view: &ViewHandle, removed: bool) -> ViewHandle {
2284    unsafe {
2285        let meta = abi::metadata0(resolve::symbol_effects_removed_ma());
2286        assert!(!meta.is_null(), "_SymbolEffectsRemovedModifier metadata");
2287        let mut bytes = vec![0u8; abi::type_bytes(meta).max(1)];
2288        bytes[0] = u8::from(removed);
2289        super::apply_modifier(view, meta, &bytes)
2290    }
2291}
2292
2293/// `.safeAreaPadding(_:)` — inlines to
2294/// `modifier(SafeAreaPaddingModifier(edges: .all, insets: EdgeInsets(_all:)))`.
2295///
2296/// Unlike the two above, both of `SafeAreaPaddingModifier`'s pieces are exported
2297/// initialisers, so the value is built by calling them rather than by writing
2298/// its fields.
2299pub fn safe_area_padding(view: &ViewHandle, length: f64) -> ViewHandle {
2300    unsafe {
2301        let insets_meta = abi::metadata0(resolve::edge_insets_ma());
2302        // `EdgeInsets` is four `CGFloat`s and nothing else, so it comes back in
2303        // d0-d3 rather than through x8 — and it is concrete, so there is no
2304        // metadata in x20 either. Passing x8 instead left the buffer zeroed,
2305        // which rendered identically and only the value comparison caught.
2306        let (top, leading, bottom, trailing): (f64, f64, f64, f64);
2307        core::arch::asm!("blr {f}", f = in(reg) resolve::edge_insets_all_init(),
2308            inout("d0") length => top,
2309            lateout("d1") leading, lateout("d2") bottom, lateout("d3") trailing,
2310            lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
2311            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
2312            lateout("x8") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
2313            lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
2314            lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x20") _,
2315            lateout("x21") _, clobber_abi("C"),
2316        );
2317        let mut insets = vec![0u8; abi::type_bytes(insets_meta)];
2318        for (i, v) in [top, leading, bottom, trailing].iter().enumerate() {
2319            insets[i * 8..i * 8 + 8].copy_from_slice(&v.to_le_bytes());
2320        }
2321        // `insets:` is `EdgeInsets?`, and `EdgeInsets` is four CGFloats with no
2322        // spare bit pattern, so the Optional carries a separate tag byte.
2323        let opt_insets = super::opt_resilient_bytes(
2324            insets_meta,
2325            b"7SwiftUI10EdgeInsetsVSg",
2326            Some(&insets),
2327        );
2328        let edges = super::static_getter(
2329            resolve::edge_set_all(),
2330            abi::metadata0(resolve::edge_set_ma()),
2331        ).0;
2332
2333        let meta = abi::metadata0(resolve::safe_area_padding_ma());
2334        let mut value = vec![0u8; abi::type_bytes(meta)];
2335        core::arch::asm!("blr {f}", f = in(reg) resolve::safe_area_padding_init(),
2336            in("x0") edges,
2337            in("x1") opt_insets.as_ptr(),
2338            in("x8") value.as_mut_ptr(),
2339            in("x20") meta,
2340            lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
2341            lateout("x6") _, lateout("x7") _, lateout("x9") _, lateout("x10") _,
2342            lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
2343            lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
2344            lateout("x21") _, clobber_abi("C"),
2345        );
2346        super::apply_modifier(view, meta, &value)
2347    }
2348}
2349
2350/// `.symbolColorRenderingMode(.flat)` — inlines to
2351/// `environment(\.symbolColorRenderingMode, mode)`.
2352pub fn symbol_color_rendering_mode_flat(view: &ViewHandle) -> ViewHandle {
2353    symbol_color_rendering_mode(view, SymbolColorRendering::Flat)
2354}
2355
2356/// How a multicolour symbol's colours are rendered.
2357#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2358pub enum SymbolColorRendering {
2359    /// `.flat` — each layer one colour.
2360    Flat,
2361    /// `.gradient` — each layer shaded.
2362    Gradient,
2363}
2364
2365/// Apply `.symbolColorRenderingMode(_:)`.
2366pub fn symbol_color_rendering_mode(view: &ViewHandle, mode: SymbolColorRendering) -> ViewHandle {
2367    unsafe {
2368        let getter = match mode {
2369            SymbolColorRendering::Flat => c"$s7SwiftUI24SymbolColorRenderingModeV4flatACvgZ",
2370            SymbolColorRendering::Gradient => {
2371                c"$s7SwiftUI24SymbolColorRenderingModeV8gradientACvgZ"
2372            }
2373        };
2374        symbol_environment_modifier(
2375            view,
2376            resolve::symbol_color_rendering_mode_ma(),
2377            getter,
2378            b"7SwiftUI24SymbolColorRenderingModeVSg",
2379            symbol_color_rendering_key_path(),
2380        )
2381    }
2382}
2383
2384/// `.symbolVariableValueMode(.color)` — inlines to
2385/// `environment(\.symbolVariableValueMode, mode)`.
2386pub fn symbol_variable_value_mode_color(view: &ViewHandle) -> ViewHandle {
2387    symbol_variable_value_mode(view, SymbolVariableValue::Color)
2388}
2389
2390/// How a symbol shows a variable value.
2391#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2392pub enum SymbolVariableValue {
2393    /// `.color` — layers change colour.
2394    Color,
2395    /// `.draw` — layers are drawn in progressively.
2396    Draw,
2397}
2398
2399/// Apply `.symbolVariableValueMode(_:)`.
2400pub fn symbol_variable_value_mode(view: &ViewHandle, mode: SymbolVariableValue) -> ViewHandle {
2401    unsafe {
2402        let getter = match mode {
2403            SymbolVariableValue::Color => c"$s7SwiftUI23SymbolVariableValueModeV5colorACvgZ",
2404            SymbolVariableValue::Draw => c"$s7SwiftUI23SymbolVariableValueModeV4drawACvgZ",
2405        };
2406        symbol_environment_modifier(
2407            view,
2408            resolve::symbol_variable_value_mode_ma(),
2409            getter,
2410            b"7SwiftUI23SymbolVariableValueModeVSg",
2411            symbol_variable_value_key_path(),
2412        )
2413    }
2414}
2415
2416/// The shape both symbol environment modifiers share: a resilient struct's
2417/// static, wrapped in an `Optional`, written through a key path.
2418unsafe fn symbol_environment_modifier(
2419    view: &ViewHandle,
2420    value_ma: *const c_void,
2421    static_getter_sym: &'static core::ffi::CStr,
2422    optional_mangled: &[u8],
2423    key_path: u64,
2424) -> ViewHandle {
2425    unsafe {
2426        let payload = abi::metadata0(value_ma);
2427        let value = super::resilient_static(payload, static_getter_sym);
2428        let boxed = super::opt_resilient_bytes(payload, optional_mangled, Some(&value));
2429        let opt_meta = abi::resolve_type_by_mangled_name(optional_mangled);
2430        assert!(!opt_meta.is_null(), "{}", crate::diag::demangle_type(optional_mangled));
2431        super::generic1_modifier_ints(
2432            resolve::environment_fn(),
2433            resolve::environment_oret(),
2434            view,
2435            opt_meta,
2436            core::ptr::null(),
2437            &[key_path, boxed.as_ptr() as u64],
2438            [0.0; 4],
2439        )
2440    }
2441}
2442
2443/// `\EnvironmentValues.symbolColorRenderingMode`, built once.
2444fn symbol_color_rendering_key_path() -> u64 {
2445    use std::sync::atomic::{AtomicU64, Ordering};
2446    static CACHE: AtomicU64 = AtomicU64::new(0);
2447    static mut SLOT: crate::keypath::Slot = crate::keypath::Slot::new();
2448    let hit = CACHE.load(Ordering::Relaxed);
2449    if hit != 0 {
2450        return hit;
2451    }
2452    unsafe {
2453        let kp = crate::keypath::build(
2454            crate::static_mut(&raw mut SLOT),
2455            crate::keypath::Property {
2456                getter: resolve::env_symbol_color_rendering_getter(),
2457                setter: resolve::env_symbol_color_rendering_setter(),
2458                descriptor: resolve::env_symbol_color_rendering_descriptor(),
2459                root: b"7SwiftUI17EnvironmentValuesV",
2460                value: b"7SwiftUI24SymbolColorRenderingModeVSg",
2461            },
2462            crate::keypath::next_thunks(),
2463        );
2464        CACHE.store(kp, Ordering::Relaxed);
2465        kp
2466    }
2467}
2468
2469/// `\EnvironmentValues.symbolVariableValueMode`, built once.
2470fn symbol_variable_value_key_path() -> u64 {
2471    use std::sync::atomic::{AtomicU64, Ordering};
2472    static CACHE: AtomicU64 = AtomicU64::new(0);
2473    static mut SLOT: crate::keypath::Slot = crate::keypath::Slot::new();
2474    let hit = CACHE.load(Ordering::Relaxed);
2475    if hit != 0 {
2476        return hit;
2477    }
2478    unsafe {
2479        let kp = crate::keypath::build(
2480            crate::static_mut(&raw mut SLOT),
2481            crate::keypath::Property {
2482                getter: resolve::env_symbol_variable_value_getter(),
2483                setter: resolve::env_symbol_variable_value_setter(),
2484                descriptor: resolve::env_symbol_variable_value_descriptor(),
2485                root: b"7SwiftUI17EnvironmentValuesV",
2486                value: b"7SwiftUI23SymbolVariableValueModeVSg",
2487            },
2488            crate::keypath::next_thunks(),
2489        );
2490        CACHE.store(kp, Ordering::Relaxed);
2491        kp
2492    }
2493}
2494
2495/// `.allowedDynamicRange(.high)` on any `View` — inlines to
2496/// `environment(\.allowedDynamicRange, range)`.
2497///
2498/// Not the same modifier as `Image.allowedDynamicRange(_:)`, which is exported
2499/// and already bound: that one is a method on `Image` and produces an `Image`.
2500/// This is the `View` spelling, and it writes the environment instead.
2501pub fn allowed_dynamic_range_high(view: &ViewHandle) -> ViewHandle {
2502    allowed_dynamic_range(view, DynamicRange::High)
2503}
2504
2505/// How much dynamic range an image in this view may use.
2506#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2507pub enum DynamicRange {
2508    /// `.standard` — no more than the display's usual range.
2509    Standard,
2510    /// `.constrainedHigh` — some headroom, bounded.
2511    ConstrainedHigh,
2512    /// `.high` — the display's full range.
2513    High,
2514}
2515
2516/// Apply `.allowedDynamicRange(_:)`.
2517pub fn allowed_dynamic_range(view: &ViewHandle, range: DynamicRange) -> ViewHandle {
2518    unsafe {
2519        let getter = match range {
2520            DynamicRange::Standard => c"$s7SwiftUI5ImageV12DynamicRangeV8standardAEvgZ",
2521            DynamicRange::ConstrainedHigh => {
2522                c"$s7SwiftUI5ImageV12DynamicRangeV15constrainedHighAEvgZ"
2523            }
2524            DynamicRange::High => c"$s7SwiftUI5ImageV12DynamicRangeV4highAEvgZ",
2525        };
2526        symbol_environment_modifier(
2527            view,
2528            resolve::image_dynamic_range_ma(),
2529            getter,
2530            b"7SwiftUI5ImageV12DynamicRangeVSg",
2531            allowed_dynamic_range_key_path(),
2532        )
2533    }
2534}
2535
2536/// `\EnvironmentValues.allowedDynamicRange`, built once.
2537fn allowed_dynamic_range_key_path() -> u64 {
2538    use std::sync::atomic::{AtomicU64, Ordering};
2539    static CACHE: AtomicU64 = AtomicU64::new(0);
2540    static mut SLOT: crate::keypath::Slot = crate::keypath::Slot::new();
2541    let hit = CACHE.load(Ordering::Relaxed);
2542    if hit != 0 {
2543        return hit;
2544    }
2545    unsafe {
2546        let kp = crate::keypath::build(
2547            crate::static_mut(&raw mut SLOT),
2548            crate::keypath::Property {
2549                getter: resolve::env_allowed_dynamic_range_getter(),
2550                setter: resolve::env_allowed_dynamic_range_setter(),
2551                descriptor: resolve::env_allowed_dynamic_range_descriptor(),
2552                root: b"7SwiftUI17EnvironmentValuesV",
2553                value: b"7SwiftUI5ImageV12DynamicRangeVSg",
2554            },
2555            crate::keypath::next_thunks(),
2556        );
2557        CACHE.store(kp, Ordering::Relaxed);
2558        kp
2559    }
2560}
2561
2562/// `Text(verbatim:).equatable()` — an `EquatableView<Text>`.
2563///
2564/// `equatable()` is `@_alwaysEmitIntoClient` and its whole body is
2565/// `EquatableView(content: self)`; `EquatableView` has one stored property, so
2566/// there is nothing to call — the `Text` is the value.
2567///
2568/// It takes a `Text` rather than a `ViewHandle` because the constraint is
2569/// `Self: Equatable`, and `AnyView` — which every handle in this crate is — does
2570/// not conform. A view erased into `AnyView` genuinely cannot be made equatable;
2571/// that is a property of `AnyView`, not a gap in the binding.
2572pub fn equatable_text(text: &str) -> ViewHandle {
2573    unsafe {
2574        let meta = abi::resolve_type_by_mangled_name(b"7SwiftUI13EquatableViewVyAA4TextVG");
2575        assert!(!meta.is_null(), "EquatableView<Text> metadata");
2576        let wt = abi::get_view_wt(meta);
2577        assert!(!wt.is_null(), "EquatableView<Text>: View");
2578        let words = super::text_verbatim_words(text);
2579        let mut bytes = vec![0u8; abi::type_bytes(meta)];
2580        core::ptr::copy_nonoverlapping(words.as_ptr() as *const u8, bytes.as_mut_ptr(), 32);
2581        ViewHandle::wrap(bytes.as_ptr() as *const c_void, meta, wt)
2582    }
2583}
2584
2585/// `.equatable()` — wrap a view in `EquatableView`, whatever its type.
2586///
2587/// [`equatable_text`] does this for a `Text` and nothing else, because the
2588/// mangled name it resolves names `Text`. The constraint is
2589/// `Content: Equatable & View`, and whether a given type satisfies it is a
2590/// question the runtime can answer — so this asks, instead of the binding
2591/// deciding in advance which one type is allowed.
2592///
2593/// The bound is genuine, not a limitation here: a view erased into `AnyView`
2594/// is not `Equatable` and cannot be made so. That is what the panic says, and
2595/// it is the same thing Swift's type checker would say at the call site.
2596///
2597/// Wrapping is the whole of it — `equatable()` is `@inlinable` with the body
2598/// `EquatableView(content: self)`, and `EquatableView` stores only its
2599/// content, so the value is the content's own bytes copied through its value
2600/// witness. A raw `memcpy` would work for a `Text` and leak a reference for
2601/// anything holding one.
2602pub fn equatable(view: &ViewHandle) -> ViewHandle {
2603    unsafe {
2604        let eq_wt = {
2605            type Conforms = unsafe extern "C" fn(*const c_void, *const c_void) -> *const c_void;
2606            let f: Conforms = core::mem::transmute(resolve::conforms_to_protocol());
2607            f(view.meta, resolve::equatable_proto())
2608        };
2609        assert!(
2610            !eq_wt.is_null(),
2611            "{} is not Equatable, so it has no .equatable(); an AnyView never \
2612             is, so build the leaf type directly",
2613            crate::diag::type_name(view.meta)
2614        );
2615        // `EquatableView<Content> where Content: Equatable, Content: View`:
2616        // one parameter then its two witnesses, in the order the requirements
2617        // are declared.
2618        let meta = abi::generic_metadata_from_descriptor(
2619            resolve::equatable_view_desc(),
2620            &[view.meta, eq_wt, view.wt],
2621        );
2622        assert!(!meta.is_null(), "EquatableView metadata");
2623        let wt = abi::get_view_wt(meta);
2624        assert!(!wt.is_null(), "EquatableView<_>: View");
2625
2626        let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(view.meta);
2627        let mut out = vec![0u8; abi::type_bytes(meta)];
2628        if vwt.size > 0 {
2629            (vwt.initialize_with_copy)(
2630                out.as_mut_ptr() as *mut c_void,
2631                view.value_ptr() as *mut c_void,
2632                view.meta,
2633            );
2634        }
2635        wrap_owned(out, meta, wt)
2636    }
2637}
2638
2639/// `.sectionIndexLabel(_:)` — inlines through `_trait` to
2640/// `modifier(_TraitWritingModifier<SectionIndexLabelKey>(value:))`.
2641///
2642/// `SectionIndexLabel` is a resilient enum with a payload, so its `.text` case
2643/// is built the way the crate builds every resilient case: write the payload,
2644/// then inject the tag through the value witness. The whole thing is then an
2645/// `Optional`, because the trait's value type is one.
2646pub fn section_index_label(view: &ViewHandle, label: &str) -> ViewHandle {
2647    unsafe {
2648        let label_meta = abi::metadata0(resolve::section_index_label_ma());
2649        assert!(!label_meta.is_null(), "SectionIndexLabel metadata");
2650        let mut payload = vec![0u8; abi::type_bytes(label_meta)];
2651        let words = super::text_verbatim_words(label);
2652        core::ptr::copy_nonoverlapping(words.as_ptr() as *const u8, payload.as_mut_ptr(), 32);
2653        // `.text(Text)` is the first payload case, and payload cases take the
2654        // low tags.
2655        abi::inject_enum_tag(&mut payload, label_meta, 0);
2656        let value = super::opt_resilient_bytes(
2657            label_meta,
2658            b"7SwiftUI17SectionIndexLabelOSg",
2659            Some(&payload),
2660        );
2661
2662        let meta = abi::resolve_type_by_mangled_name(
2663            b"7SwiftUI21_TraitWritingModifierVyAA20SectionIndexLabelKeyVG",
2664        );
2665        assert!(!meta.is_null(), "_TraitWritingModifier<SectionIndexLabelKey> metadata");
2666        super::apply_modifier(view, meta, &value)
2667    }
2668}
2669
2670/// `.symbolEffect(.pulse)` — inlines to
2671/// `modifier(_IndefiniteSymbolEffectModifier(effect:options:isActive:))`.
2672///
2673/// Three stored properties and no exported initialiser, so the value is
2674/// assembled field by field. `.pulse` is `PulseSymbolEffect()`, whose zero-argument
2675/// initialiser *is* exported — in the `Symbols` framework rather than SwiftUI,
2676/// which `resolve::sym` reaches because it searches every loaded image.
2677///
2678/// `_IndefiniteSymbolEffectModifier` is *not* generic, despite its
2679/// `init<T>(effect:options:isActive:)` — the initialiser folds `T` into a
2680/// `SymbolEffectConfiguration.Effect` case. So the stored fields are
2681/// `{ config, options, isActive }`, which is what the reference's own `dump`
2682/// says and what this assembles.
2683pub fn symbol_effect_pulse(view: &ViewHandle) -> ViewHandle {
2684    symbol_effect(view, SymbolEffect::Pulse)
2685}
2686
2687/// One of the indefinite symbol effects — the ones `.symbolEffect(_:)` takes
2688/// with no `value:`, which run until the view goes away.
2689///
2690/// The discrete and transition effects (`.appear`, `.replace`, `.drawOn` …)
2691/// are a different modifier and are not these.
2692#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2693pub enum SymbolEffect {
2694    /// Fade the layers in and out in turn.
2695    Pulse,
2696    /// A single upward nudge, repeating.
2697    Bounce,
2698    /// Light the layers up one at a time.
2699    VariableColor,
2700    /// Shake side to side.
2701    Wiggle,
2702    /// Spin.
2703    Rotate,
2704    /// Swell and settle, like breathing.
2705    Breathe,
2706}
2707
2708impl SymbolEffect {
2709    /// The effect type's metadata accessor, its initialiser, and the name of
2710    /// the `SymbolEffectConfiguration.Effect` case that wraps it.
2711    fn parts(self) -> (*const c_void, *const c_void, &'static str) {
2712        match self {
2713            Self::Pulse => (
2714                resolve::pulse_symbol_effect_ma(),
2715                resolve::pulse_symbol_effect_init(),
2716                "pulse",
2717            ),
2718            Self::Bounce => (
2719                resolve::bounce_symbol_effect_ma(),
2720                resolve::bounce_symbol_effect_init(),
2721                "bounce",
2722            ),
2723            Self::VariableColor => (
2724                resolve::variable_color_symbol_effect_ma(),
2725                resolve::variable_color_symbol_effect_init(),
2726                "variableColor",
2727            ),
2728            Self::Wiggle => (
2729                resolve::wiggle_symbol_effect_ma(),
2730                resolve::wiggle_symbol_effect_init(),
2731                "wiggle",
2732            ),
2733            Self::Rotate => (
2734                resolve::rotate_symbol_effect_ma(),
2735                resolve::rotate_symbol_effect_init(),
2736                "rotate",
2737            ),
2738            Self::Breathe => (
2739                resolve::breathe_symbol_effect_ma(),
2740                resolve::breathe_symbol_effect_init(),
2741                "breathe",
2742            ),
2743        }
2744    }
2745}
2746
2747/// `.symbolEffect(_:)` with any of the indefinite effects.
2748///
2749/// The case tag is *read* from the enum's reflection rather than written down:
2750/// `SymbolEffectConfiguration.Effect` numbers its cases in declaration order,
2751/// which is not the order the documentation lists them — `.variableColor` is 2
2752/// and `.scale` is 3, where reading down the page would say otherwise. A wrong
2753/// tag builds a valid value of the wrong case, which renders, and renders
2754/// wrongly. See [`abi::enum_case_index`].
2755pub fn symbol_effect(view: &ViewHandle, effect: SymbolEffect) -> ViewHandle {
2756    unsafe {
2757        let (effect_ma, effect_init, case) = effect.parts();
2758        let effect_meta = abi::metadata0(effect_ma);
2759        assert!(!effect_meta.is_null(), "{case} effect metadata");
2760        let mut effect = vec![0u8; abi::type_bytes(effect_meta)];
2761        core::arch::asm!("blr {f}", f = in(reg) effect_init,
2762            in("x8") effect.as_mut_ptr(),
2763            in("x20") effect_meta,
2764            lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
2765            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
2766            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
2767            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
2768            lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
2769        );
2770        let options_meta = abi::metadata0(resolve::symbol_effect_options_ma());
2771        let options = super::static_getter_indirect(
2772            resolve::symbol_effect_options_default(),
2773            options_meta,
2774            abi::type_bytes(options_meta),
2775        );
2776
2777        // `SymbolEffectConfiguration.Effect` is a resilient enum; `.pulse` is its
2778        // first payload case, so the payload goes in at offset zero and the tag
2779        // follows through the value witness.
2780        let effect_enum_meta = abi::metadata0(resolve::symbol_effect_config_effect_ma());
2781        let mut config = vec![0u8; abi::type_bytes(effect_enum_meta)];
2782        config[..abi::type_size(effect_meta)]
2783            .copy_from_slice(&effect[..abi::type_size(effect_meta)]);
2784        let tag = abi::enum_case_index(effect_enum_meta, case)
2785            .unwrap_or_else(|| panic!("SymbolEffectConfiguration.Effect has no case .{case}"));
2786        abi::inject_enum_tag(&mut config, effect_enum_meta, tag as u32);
2787
2788        let meta = abi::resolve_type_by_mangled_name(b"7SwiftUI31_IndefiniteSymbolEffectModifierV");
2789        assert!(!meta.is_null(), "_IndefiniteSymbolEffectModifier metadata");
2790        let config_meta = abi::metadata0(resolve::symbol_effect_config_ma());
2791        let config_size = abi::type_size(config_meta);
2792        let options_size = abi::type_size(options_meta);
2793        let mut bytes = vec![0u8; abi::type_bytes(meta)];
2794        // Read rather than computed. The arithmetic that used to stand in here
2795        // — pad each field to eight — does give the right answers for this
2796        // type, but it is arithmetic agreeing with a layout rather than the
2797        // layout itself: `SymbolEffectConfiguration` is eleven bytes and
2798        // `options` holds a `Double`, so it starts eight-aligned at sixteen,
2799        // and writing it at eleven produced a plausible `SymbolEffectOptions`
2800        // whose speed read 2.07e-317. Only the value comparison caught that.
2801        let offsets = abi::struct_field_offsets(meta).expect("three stored fields");
2802        let &[config_at, options_at, active_at] = offsets.as_slice() else {
2803            panic!("_IndefiniteSymbolEffectModifier is {} fields, not 3", offsets.len());
2804        };
2805        bytes[config_at..config_at + config_size].copy_from_slice(&config[..config_size]);
2806        bytes[options_at..options_at + options_size]
2807            .copy_from_slice(&options[..options_size]);
2808        // `isActive` defaults to true at the call site.
2809        bytes[active_at] = 1;
2810        super::apply_modifier(view, meta, &bytes)
2811    }
2812}
2813
2814
2815/// A `Picker` whose selection cannot change: `Binding.constant` is read-only.
2816/// Use [`picker_bound()`] for one the user can actually pick from.
2817pub fn picker(title: &str, selection: i64, content: &ViewHandle) -> ViewHandle {
2818    unsafe {
2819        let binding = super::constant_binding(
2820            &selection.to_le_bytes(),
2821            resolve::int_meta(),
2822            mangle::bound(&swift_type!(SwiftUI.Binding), &[mangle::INT]).as_bytes(),
2823        );
2824        picker_with_binding(title, content, &binding)
2825    }
2826}
2827
2828/// A `Picker` bound to Rust state: choosing writes the new index back.
2829pub fn picker_bound(
2830    title: &str,
2831    state: &super::BoundState<i64>,
2832    content: &ViewHandle,
2833) -> ViewHandle {
2834    unsafe { picker_with_binding(title, content, &super::i64_binding_of(state)) }
2835}
2836
2837
2838/// A member of `SwiftUI.DatePickerComponents`.
2839///
2840/// `HourMinuteAndSecond` exists in the framework but is `@available(macOS,
2841/// unavailable)` and `@available(iOS, unavailable)`, so it is not here.
2842#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2843pub enum DatePickerComponent {
2844    /// The calendar date — month, day and year.
2845    Date,
2846    /// The time of day, to the minute.
2847    HourAndMinute,
2848}
2849
2850impl DatePickerComponent {
2851    fn getter(self) -> &'static core::ffi::CStr {
2852        match self {
2853            Self::Date => c"$s7SwiftUI20DatePickerComponentsV4dateACvgZ",
2854            Self::HourAndMinute => c"$s7SwiftUI20DatePickerComponentsV13hourAndMinuteACvgZ",
2855        }
2856    }
2857}
2858
2859/// The `DatePickerComponents` value for a set of members.
2860///
2861/// # Safety
2862/// Calls into SwiftUI.
2863pub(crate) unsafe fn date_picker_components(members: &[DatePickerComponent]) -> Vec<u8> {
2864    unsafe {
2865        let meta = abi::metadata0(resolve::dp_components_ma());
2866        assert!(!meta.is_null(), "DatePickerComponents metadata");
2867        let size = abi::type_bytes(meta);
2868
2869        // `rawValue` takes `self` by address in x20 and returns one word;
2870        // `init(rawValue:)` takes the word in x0 and returns through x8.
2871        // Neither is given a metatype — the IR declares them
2872        // `(ptr noalias swiftself) -> i64` and `(sret, i64)`.
2873        let mut raw = 0u64;
2874        for m in members {
2875            let mut one = super::resilient_static(meta, m.getter());
2876            let bits: u64;
2877            core::arch::asm!("blr {f}", f = in(reg) resolve::dp_components_raw(),
2878                in("x20") one.as_mut_ptr(),
2879                lateout("x0") bits,
2880                lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
2881                lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
2882                lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
2883                lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
2884                lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
2885            );
2886            raw |= bits;
2887        }
2888
2889        let mut out = vec![0u8; size];
2890        core::arch::asm!("blr {f}", f = in(reg) resolve::dp_components_init_raw(),
2891            in("x0") raw,
2892            in("x8") out.as_mut_ptr(),
2893            lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
2894            lateout("x5") _, lateout("x6") _, lateout("x7") _,
2895            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
2896            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
2897            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
2898            clobber_abi("C"),
2899        );
2900        out
2901    }
2902}
2903
2904/// A `DatePicker` showing a fixed date, which the user cannot change.
2905/// See [`date_picker_bound`].
2906pub fn date_picker(title: &str, interval: f64) -> ViewHandle {
2907    date_picker_components_shown(title, interval, &[DatePickerComponent::Date])
2908}
2909
2910/// As [`date_picker`], showing the named components.
2911pub fn date_picker_components_shown(
2912    title: &str,
2913    interval: f64,
2914    components: &[DatePickerComponent],
2915) -> ViewHandle {
2916    unsafe {
2917        let date = date_bytes(interval);
2918        let binding = super::constant_binding(
2919            &date,
2920            abi::metadata0(resolve::date_ma()),
2921            mangle::bound(&swift_type!(SwiftUI.Binding), &[mangle::DATE]).as_bytes(),
2922        );
2923        date_picker_with_binding_components(title, &binding, components)
2924    }
2925}
2926
2927/// A `DatePicker` bound to Rust state, held as a `Date`'s seconds-since-2001.
2928pub fn date_picker_bound(title: &str, state: &super::BoundState<f64>) -> ViewHandle {
2929    date_picker_bound_components(title, state, &[DatePickerComponent::Date])
2930}
2931
2932/// As [`date_picker_bound`], showing the named components.
2933pub fn date_picker_bound_components(
2934    title: &str,
2935    state: &super::BoundState<f64>,
2936    components: &[DatePickerComponent],
2937) -> ViewHandle {
2938    unsafe {
2939        // `Date` is one `Double`, so the same eight bytes a slider would write.
2940        let binding = super::scalar_binding::<f64, 8>(
2941            state,
2942            abi::metadata0(resolve::date_ma()),
2943            mangle::bound(&swift_type!(SwiftUI.Binding), &[mangle::DATE]).as_bytes(),
2944            |v| v.to_le_bytes(),
2945            f64::from_le_bytes,
2946        );
2947        date_picker_with_binding_components(title, &binding, components)
2948    }
2949}
2950
2951
2952/// A `ColorPicker` showing a fixed colour, which the user cannot change.
2953/// See [`color_picker_bound`].
2954pub fn color_picker(title: &str, color: crate::Color, supports_opacity: bool) -> ViewHandle {
2955    unsafe {
2956        let value = super::color_value_of(color).to_le_bytes();
2957        let b = super::constant_binding(
2958            &value,
2959            resolve::color_meta(),
2960            mangle::bound(&swift_type!(SwiftUI.Binding), &["7SwiftUI5ColorV"]).as_bytes(),
2961        );
2962        color_picker_with_binding(title, supports_opacity, &b)
2963    }
2964}
2965
2966/// A `ColorPicker` bound to Rust state.
2967///
2968/// The state holds the `Color` as the one reference `Color` is — what
2969/// [`super::color_value_of`] returns — because that is what the binding reads
2970/// and writes. `Color` is a class instance, not a struct of components.
2971pub fn color_picker_bound(
2972    title: &str,
2973    state: &super::BoundState<u64>,
2974    supports_opacity: bool,
2975) -> ViewHandle {
2976    unsafe {
2977        let b = super::scalar_binding::<u64, 8>(
2978            state,
2979            resolve::color_meta(),
2980            mangle::bound(&swift_type!(SwiftUI.Binding), &["7SwiftUI5ColorV"]).as_bytes(),
2981            |v| v.to_le_bytes(),
2982            u64::from_le_bytes,
2983        );
2984        color_picker_with_binding(title, supports_opacity, &b)
2985    }
2986}
2987
2988/// Where a container keeps its content in its generic argument vector.
2989#[derive(Clone, Copy)]
2990pub(crate) struct ContentShape {
2991    /// How many words the argument vector has: parameters, then conformances.
2992    pub(crate) args: usize,
2993    /// Index of the content's metadata.
2994    pub(crate) meta_at: usize,
2995    /// Index of the content's `View` witness table.
2996    pub(crate) wt_at: usize,
2997}
2998
2999/// A container whose only argument is a `() -> Content` closure, keeping the
3000/// content's own type instead of boxing it into an `AnyView`.
3001///
3002/// [`content_container`] names its result by a fixed mangled string, and that
3003/// string has to spell some concrete `Content` — so it spells `AnyView`, and the
3004/// child is boxed to match. The name still gives an *instantiation*, though, and
3005/// every metadata record points at the nominal descriptor it came from: read
3006/// that off and the runtime will build `List<Never, Text>` as readily as
3007/// `List<Never, AnyView>`.
3008///
3009/// The other generic arguments — `Never` for `List`'s selection, `EmptyView` for
3010/// a `Section`'s header — are read off the known instantiation, so they are
3011/// whatever that type was declared with rather than something guessed here.
3012///
3013/// `shape` says where the content sits: how many arguments the vector has, and
3014/// the index of the content's metadata and of its `View` witness. Those cannot
3015/// be derived from each other — a `List` has two type parameters but only one
3016/// conformance, so its witness is at 2 and not at 4 — and a wrong guess would
3017/// build some *other* instantiation rather than fail. So the result is checked
3018/// against what was asked for, and anything unexpected falls back to boxing,
3019/// which is what this always did.
3020///
3021/// # Safety
3022/// `init` must be the container's `init(content:)` and `known` an instantiation
3023/// of it over `AnyView`.
3024pub(crate) unsafe fn content_container_typed(
3025    child: &ViewHandle,
3026    init: *const c_void,
3027    known: &'static CachedType,
3028    shape: ContentShape,
3029) -> ViewHandle {
3030    unsafe {
3031        let (known_meta, _, _) = result_type(known);
3032        // Generic arguments sit after the metadata header.
3033        let args_base = (known_meta as *const *const c_void).add(2);
3034        let mut args: Vec<*const c_void> = (0..shape.args).map(|i| *args_base.add(i)).collect();
3035        args[shape.meta_at] = child.meta;
3036        args[shape.wt_at] = child.wt;
3037        let meta = abi::generic_metadata_from(known_meta, &args);
3038        if meta.is_null() {
3039            return content_container(child, init, known);
3040        }
3041        // Did we build what we meant to? The content's metadata has to be where
3042        // we put it, or `shape` describes a different type than this one.
3043        let built = (meta as *const *const c_void).add(2);
3044        if *built.add(shape.meta_at) != child.meta {
3045            return content_container(child, init, known);
3046        }
3047        let wt = abi::get_view_wt(meta);
3048        if wt.is_null() {
3049            return content_container(child, init, known);
3050        }
3051
3052        let kid = Child::new(child, Content::Concrete);
3053        let handles = kid.element();
3054        let payload = TuplePayload { children: handles.as_ptr(), count: 1 };
3055        let mut ctx =
3056            abi::ClosureCtx::new(emit_tuple, &payload as *const TuplePayload as *mut c_void);
3057
3058        let mut result = vec![0u8; abi::type_bytes(meta)];
3059        core::arch::asm!("blr {f}", f = in(reg) init,
3060            in("x0") abi::swift_closure_thunk as *const c_void,
3061            in("x1") &mut ctx as *mut abi::ClosureCtx,
3062            in("x2") child.meta,
3063            in("x3") child.wt,
3064            in("x8") result.as_mut_ptr(),
3065            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
3066            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
3067            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
3068            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
3069            clobber_abi("C"),
3070        );
3071        wrap_owned(result, meta, wt)
3072    }
3073}
3074
3075// ── Lists with identity ─────────────────────────────────────────────────────
3076
3077/// `(Data.Element) -> Content`, where `Element` is *generic*.
3078///
3079/// [`emit_index_view`] reads x0 as the index itself, which is right for
3080/// `ForEach(0..<n)` — there `Element` is concretely `Int` and arrives in a
3081/// register. Here `Element` is an associated type of a generic `Data`, so it is
3082/// `@in_guaranteed`: x0 is the *address* of the `Int`. Reading it as the value
3083/// gave every row the same number, which was the pointer — caught by the
3084/// rendered pixels, since the type and the value were both already identical to
3085/// Swift's.
3086pub(crate) unsafe extern "C" fn emit_element_view(out: *mut u8, element: i64, payload: *mut c_void) {
3087    unsafe { emit_index_view(out, *(element as *const i64), payload) };
3088}
3089
3090/// The `\Int.self` this list's `id:` is.
3091static mut LIST_ID_SLOT: crate::keypath::IdentitySlot = crate::keypath::IdentitySlot::new();
3092/// And `\String.self`, for the string-keyed one. One slot per key path.
3093static mut LIST_ID_STR_SLOT: crate::keypath::IdentitySlot = crate::keypath::IdentitySlot::new();
3094
3095
3096/// `List(ids, id: \.self) { id in … }` — rows SwiftUI identifies by value.
3097///
3098/// [`for_each`] and everything built on it identify rows *positionally*:
3099/// inserting at the front makes SwiftUI treat every later row as changed rather
3100/// than moved, so nothing animates as a move and per-row state follows the
3101/// index instead of the row. That is the same semantics as writing
3102/// `ForEach(0..<n)` in Swift, and it is the wrong semantics for a list of
3103/// things.
3104///
3105/// This takes the identifiers themselves. `ids` are the rows' identities, and
3106/// `builder` is asked for the row belonging to each — so moving an id moves its
3107/// row, and removing one removes that row rather than the last.
3108///
3109/// The plain `ForEach(_:id:content:)` over `View` content is not reachable:
3110/// it is inlinable and exports no symbol, and the only exported `ForEach`
3111/// `id:` initialisers are the `TabContent`, `PickerContent`, `ToolbarContent`,
3112/// `TableRowContent` and `AccessibilityRotorContent` flavours. `List`'s does
3113/// export one, and builds the identified `ForEach` internally — which is why
3114/// this is a list constructor and not a general `for_each_identified`.
3115pub fn list_identified(ids: &[i64], builder: impl Fn(i64) -> ViewHandle + 'static) -> ViewHandle {
3116    unsafe {
3117        static ID_KP: std::sync::OnceLock<u64> = std::sync::OnceLock::new();
3118        let id_kp = *ID_KP.get_or_init(|| {
3119            crate::keypath::build_identity(crate::static_mut(&raw mut LIST_ID_SLOT), b"Si", b"Si")
3120        });
3121
3122        let int_meta = resolve::int_meta();
3123        let stride = abi::type_bytes(int_meta);
3124        let mut bytes = vec![0u8; stride * ids.len()];
3125        for (i, id) in ids.iter().enumerate() {
3126            bytes[i * stride..i * stride + 8].copy_from_slice(&id.to_le_bytes());
3127        }
3128        let array = abi::swift_array(int_meta, ids.len(), &bytes);
3129
3130        let boxed: IndexBuilder = Box::new(builder);
3131        identified_list(array, b"SaySiG", int_meta, id_kp, &LIST_ID_T, emit_element_view, boxed)
3132    }
3133}
3134
3135/// `List(names, id: \.self) { name in … }` — the same, keyed by `String`.
3136///
3137/// Which is what most lists key on: an identifier is usually a name or a UUID
3138/// string, and turning one into an `Int` to get identity back would defeat the
3139/// point. `String` is `Hashable`, which is all `ForEach`'s `ID` has to be.
3140pub fn list_identified_str(
3141    ids: &[&str],
3142    builder: impl Fn(&str) -> ViewHandle + 'static,
3143) -> ViewHandle {
3144    unsafe {
3145        static ID_KP: std::sync::OnceLock<u64> = std::sync::OnceLock::new();
3146        let id_kp = *ID_KP.get_or_init(|| {
3147            crate::keypath::build_identity(crate::static_mut(&raw mut LIST_ID_STR_SLOT), b"SS", b"SS")
3148        });
3149        let array = swift_string_array(ids);
3150        let string_meta = resolve::string_meta();
3151        let boxed: StringBuilder = Box::new(builder);
3152        let payload = Box::into_raw(Box::new(boxed)) as *mut c_void;
3153        identified_list_raw(
3154            array,
3155            b"SaySSG",
3156            string_meta,
3157            id_kp,
3158            &LIST_ID_STR_T,
3159            emit_string_element_view,
3160            payload,
3161        )
3162    }
3163}
3164
3165/// The Rust side of a string-keyed row builder.
3166pub(crate) type StringBuilder = Box<dyn Fn(&str) -> ViewHandle>;
3167
3168/// `(String) -> Content` — the element arrives by address, as [`emit_element_view`]
3169/// explains, and a `String` is sixteen bytes there rather than one word.
3170pub(crate) unsafe extern "C" fn emit_string_element_view(
3171    out: *mut u8,
3172    element: i64,
3173    payload: *mut c_void,
3174) {
3175    unsafe {
3176        let builder = &*(payload as *const StringBuilder);
3177        let mut raw = [0u8; 16];
3178        core::ptr::copy_nonoverlapping(element as *const u8, raw.as_mut_ptr(), 16);
3179        let view = builder(&abi::read_swift_string(raw));
3180        *(out as *mut u64) = view.into_anyview_word();
3181    }
3182}
3183
3184/// Build the `List` once the element type's pieces are in hand.
3185///
3186/// # Safety
3187/// `array` must be a live Swift array of `element_meta`, `array_mangled` its
3188/// type's mangled name, and `payload` what `body` expects.
3189unsafe fn identified_list_raw(
3190    array: u64,
3191    array_mangled: &[u8],
3192    element_meta: *const c_void,
3193    id_kp: u64,
3194    result: &'static crate::views::types::CachedType,
3195    body: unsafe extern "C" fn(*mut u8, i64, *mut c_void),
3196    payload: *mut c_void,
3197) -> ViewHandle {
3198    unsafe {
3199        // The initialiser *consumes* the key path, so the cached object is
3200        // retained per use — the same balance [`outline_group`] documents.
3201        swift_runtime_sys::RuntimeRaw::swift_retain(id_kp as *mut c_void);
3202        let array_meta = abi::resolve_type_by_mangled_name(array_mangled);
3203        assert!(!array_meta.is_null(), "array metadata");
3204
3205        let ctx = Box::leak(Box::new(abi::IndexClosureCtx::new(body, payload)))
3206            as *mut abi::IndexClosureCtx;
3207
3208        let (meta, wt, size) = result_type(result);
3209        let mut out = vec![0u8; size];
3210        // List.init(_:id:rowContent:)
3211        //   x0 = @in Data, x1 = id KeyPath, x2 = rowContent fn,
3212        //   x3 = rowContent ctx, x4 = Data.Type, x5 = ID.Type,
3213        //   x6 = RowContent.Type, x7 = Data: RandomAccessCollection WT,
3214        //   [sp] = ID: Hashable WT, [sp+8] = RowContent: View WT, x8 = @out
3215        core::arch::asm!(
3216            "sub sp, sp, #16",
3217            "stp {hw}, {vw}, [sp]",
3218            "blr {f}",
3219            "add sp, sp, #16",
3220            f = in(reg) resolve::list_id_init(),
3221            hw = in(reg) conformance_wt(element_meta, resolve::hashable_proto()),
3222            vw = in(reg) resolve::anyview_wt(),
3223            in("x0") &array as *const u64,
3224            in("x1") id_kp,
3225            in("x2") abi::index_closure_thunk as *const c_void,
3226            in("x3") ctx,
3227            in("x4") array_meta,
3228            in("x5") element_meta,
3229            in("x6") resolve::anyview_meta(),
3230            in("x7") conformance_wt(array_meta, resolve::random_access_proto()),
3231            in("x8") out.as_mut_ptr(),
3232            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
3233            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
3234            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
3235            clobber_abi("C"),
3236        );
3237        wrap_owned(out, meta, wt)
3238    }
3239}
3240
3241/// The `Int`-keyed shape, which carries a typed builder rather than a raw one.
3242///
3243/// # Safety
3244/// As [`identified_list_raw`].
3245unsafe fn identified_list(
3246    array: u64,
3247    array_mangled: &[u8],
3248    element_meta: *const c_void,
3249    id_kp: u64,
3250    result: &'static crate::views::types::CachedType,
3251    body: unsafe extern "C" fn(*mut u8, i64, *mut c_void),
3252    builder: IndexBuilder,
3253) -> ViewHandle {
3254    unsafe {
3255        let payload = Box::into_raw(Box::new(TypedIndexBuilder {
3256            builder,
3257            // No type is promised: the rows go in as `AnyView`, which is what
3258            // the result type names.
3259            meta: core::ptr::null(),
3260        })) as *mut c_void;
3261        identified_list_raw(array, array_mangled, element_meta, id_kp, result, body, payload)
3262    }
3263}
3264
3265
3266
3267
3268
3269
3270
3271
3272// ── Tabs ────────────────────────────────────────────────────────────────────
3273
3274/// `Tab<Never, AnyView, DefaultTabLabel>`, spelled without substitutions so it
3275/// can be pasted into a larger name — which the compressed form the runtime
3276/// hands back cannot be.
3277const TAB_MANGLED: &str =
3278    "7SwiftUI3TabVys5NeverO7SwiftUI7AnyViewV7SwiftUI15DefaultTabLabelVG";
3279
3280/// `Tab<Never, AnyView, DefaultTabLabel>`'s metadata, resolved once.
3281fn tab_meta() -> *const c_void {
3282    static CACHE: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
3283    *CACHE.get_or_init(|| unsafe {
3284        let name = mangle::ty("Tab")
3285            .of(vec![
3286                mangle::Type::Verbatim("s5NeverO".into()),
3287                mangle::ty("AnyView"),
3288                mangle::ty("DefaultTabLabel"),
3289            ])
3290            .mangle();
3291        let m = abi::resolve_type_by_mangled_name(name.as_bytes());
3292        assert!(!m.is_null(), "{}", mangle::demangle(&name));
3293        m as usize
3294    }) as *const c_void
3295}
3296
3297/// `Tab("Home", systemImage: "house") { … }` — one tab of a modern `TabView`.
3298///
3299/// The older way to build a tab is `.tabItem { Label(…) }` on the page itself,
3300/// which is what [`tab_item`](super::tab_item) binds. `Tab` is the replacement:
3301/// the tab and its label are one value rather than a modifier on the content,
3302/// which is what lets a `TabView` know its tabs without rendering them.
3303///
3304/// `Tab` is not a `View` — it is a `TabContent`, and only a `TabView` accepts
3305/// one.
3306pub fn tab(title: &str, system_image: &str, content: &ViewHandle) -> ViewHandle {
3307    unsafe {
3308        let t = abi::swift_string(title);
3309        let t0 = u64::from_le_bytes(t[..8].try_into().expect("title lo"));
3310        let t1 = u64::from_le_bytes(t[8..].try_into().expect("title hi"));
3311        let (k0, k1, k2, k3) = super::primitives::localized_string_key(t0, t1);
3312
3313        let img = abi::swift_string(system_image);
3314        let i0 = u64::from_le_bytes(img[..8].try_into().expect("image lo"));
3315        let i1 = u64::from_le_bytes(img[8..].try_into().expect("image hi"));
3316
3317        let kids = Child::new(content, Content::Erased);
3318        let handles = kids.element();
3319        let payload = TuplePayload { children: handles.as_ptr(), count: 1 };
3320        let mut ctx =
3321            abi::ClosureCtx::new(emit_tuple, &payload as *const TuplePayload as *mut c_void);
3322
3323        // Not `result_type`: that asserts the type conforms to `View`, and a
3324        // `Tab` conforms to `TabContent` instead — which is the whole point of
3325        // it. Resolving the name directly skips the witness it does not have.
3326        //
3327        // The content stays boxed, and this is the one container in this file
3328        // where that is not just history. `Tab` is a `TabContent`, so what holds
3329        // it is a `TabView<…, Content: TabContent>` rather than a view, and every
3330        // `TabContent` in the tree — the tab itself, the `TupleTabContent` over
3331        // several, the `TabView` around them — names `Tab<Never, AnyView,
3332        // DefaultTabLabel>` in its own type. Typing one of them and not the rest
3333        // describes a value of one layout to code expecting another: building
3334        // `Tab<Never, Text, …>` here left `tab_view` reading it as the `AnyView`
3335        // form, and `tab_view_one` faulted. Moving them all is a `TabContent`
3336        // composition of its own, not this one-line change.
3337        let meta = tab_meta();
3338        let (content_meta, content_wt) = (resolve::anyview_meta(), resolve::anyview_wt());
3339
3340        let mut result = vec![0u8; abi::type_bytes(meta)];
3341        // Tab.init(_:systemImage:content:)
3342        //   x0-x3 = LocalizedStringKey (thirty-two bytes), x4/x5 = systemImage,
3343        //   x6/x7 = the content closure, [sp] = Content.Type,
3344        //   [sp+8] = Content: View WT, x8 = @out
3345        //
3346        // The `role:` overload next to this one takes a `TabRole?` in between,
3347        // which leaves one register for a two-word closure — AAPCS64 does not
3348        // split an argument, so the closure and everything after it would go to
3349        // the stack. This overload is both what `Tab(_:systemImage:)` compiles
3350        // to and the one that fits.
3351        core::arch::asm!(
3352            "sub sp, sp, #16",
3353            "stp {cm}, {cw}, [sp]",
3354            "blr {f}",
3355            "add sp, sp, #16",
3356            f = in(reg) resolve::tab_init(),
3357            cm = in(reg) content_meta,
3358            cw = in(reg) content_wt,
3359            in("x0") k0, in("x1") k1, in("x2") k2, in("x3") k3,
3360            in("x4") i0, in("x5") i1,
3361            in("x6") abi::swift_closure_thunk as *const c_void,
3362            in("x7") &mut ctx as *mut abi::ClosureCtx,
3363            in("x8") result.as_mut_ptr(),
3364            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
3365            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
3366            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
3367            clobber_abi("C"),
3368        );
3369        // A `Tab` is a `TabContent`, not a `View`. The handle carries that
3370        // witness in the slot a view's `View` witness would use, which is what
3371        // `tab_view_of` reads — and is also why a `Tab` cannot be put anywhere
3372        // a view goes.
3373        wrap_owned(result, meta, conformance_wt(meta, resolve::tab_content_proto()))
3374    }
3375}
3376
3377
3378/// `(…) -> TabContent` — hand back the tab this closure was built with.
3379///
3380/// Unlike [`emit_tuple`], which writes an `AnyView` word, a `TabContent` is a
3381/// value of whatever concrete type it is — fifty-seven bytes for a `Tab` — so
3382/// the whole thing is copied through the result address.
3383unsafe extern "C" fn emit_tab(out: *mut u8, payload: *mut c_void) {
3384    unsafe {
3385        let tab = &*(payload as *const ViewHandle);
3386        let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(tab.meta);
3387        (vwt.initialize_with_copy)(
3388            out as *mut c_void,
3389            tab.value_ptr() as *mut c_void,
3390            tab.meta,
3391        );
3392    }
3393}
3394
3395/// `TabView { Tab(…) }` — the modern tab view, over **one** tab.
3396///
3397/// [`tab_view`](fn@tab_view) takes a `View` and relies on each page carrying a
3398/// `.tabItem`
3399/// modifier; this takes a [`tab`] directly, which is the API that replaced it.
3400///
3401/// The result type is `TabView<Never, TabContentBuilder<Never>.Content<T>>` —
3402/// a generic nested inside a generic, whose mangling puts *both* argument lists
3403/// in one group separated by `_` rather than closing the parent's first. That
3404/// distinction is the difference between resolving and not.
3405///
3406pub fn tab_view_of(tab: &ViewHandle) -> ViewHandle {
3407    unsafe {
3408        assert!(
3409            !tab.wt.is_null(),
3410            "tab_view_of needs a Tab, which carries a TabContent witness",
3411        );
3412        tab_view_from(tab, TAB_MANGLED)
3413    }
3414}
3415
3416/// Wrap any `TabContent` in a `TabView`.
3417///
3418/// `verbose` is the content's mangled name **without substitutions**. It is a
3419/// parameter rather than something to look up because
3420/// `abi::mangled_type_name` hands back the compressed form, whose `AA`
3421/// back-references are numbered against the name they came from — pasting one
3422/// into a larger name shifts every later reference and produces a string that
3423/// resolves to a different type, or to nothing. Here it resolved
3424/// `_TupleTabContent<Never, (Tab, Tab, Tab)>` to
3425/// `_TupleTabContent<Never, (View<…>, Never, Never)>`, which is what caught it.
3426///
3427/// # Safety
3428/// `content` must be a `TabContent` whose witness the handle carries, and
3429/// `verbose` must name its type.
3430unsafe fn tab_view_from(content: &ViewHandle, verbose: &str) -> ViewHandle {
3431    unsafe {
3432        let payload = Box::into_raw(Box::new(content.clone())) as *mut c_void;
3433        let mut ctx = abi::ClosureCtx::new(emit_tab, payload);
3434
3435        // Resolved by name. Every metadata-accessor route to
3436        // `TabContentBuilder<Never>.Content<C>` faulted — its `Ma` with the two
3437        // arguments in registers, `swift_getGenericMetadata` with its `Mn`
3438        // descriptor, and the same with a descriptor taken from a known
3439        // instance — so whatever shape a nested generic's arguments take, it is
3440        // not the flat parent-then-child array a top-level one uses.
3441        let inner = format!("7SwiftUI17TabContentBuilderV7ContentVys5NeverO_{verbose}G");
3442        let full = format!("7SwiftUI7TabViewVys5NeverO{inner}G");
3443        let meta = abi::resolve_type_by_mangled_name(full.as_bytes());
3444        assert!(!meta.is_null(), "{}", crate::diag::demangle_type(full.as_bytes()));
3445        let wt = abi::get_view_wt(meta);
3446        assert!(!wt.is_null(), "TabView !: View");
3447
3448        let mut result = vec![0u8; abi::type_bytes(meta)];
3449        // TabView.init(content: () -> C)
3450        //   x0/x1 = the closure, x2 = C.Type, x3 = C: TabContent WT, x8 = @out
3451        core::arch::asm!("blr {f}", f = in(reg) resolve::tabview_content_init(),
3452            in("x0") abi::swift_closure_thunk as *const c_void,
3453            in("x1") &mut ctx as *mut abi::ClosureCtx,
3454            in("x2") content.meta,
3455            in("x3") content.wt,
3456            in("x8") result.as_mut_ptr(),
3457            lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
3458            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
3459            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
3460            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
3461            clobber_abi("C"),
3462        );
3463        wrap_owned(result, meta, wt)
3464    }
3465}
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477// ── Composing several tabs ──────────────────────────────────────────────────
3478//
3479// `@TabContentBuilder` does more than gather: each `Tab` becomes
3480// `ModifiedContent<Tab.TabIdentifiedView, _TraitWritingModifier<TagValueTraitKey<Never>>>`,
3481// which is what carries its identity, and the block becomes a
3482// `_TupleTabContent` over a tuple of those. `buildBlock` is inlinable and
3483// exports nothing, so both halves are built here instead.
3484//
3485// Every type below is spelled *without substitutions*. The runtime will hand
3486// back a name for any metadata, but only the compressed form, whose `AA`
3487// back-references are numbered against the name they came from — pasting one
3488// into a larger name silently resolves to a different type. Composing from
3489// verbose pieces has nothing to misnumber, and each piece is checked against
3490// the metadata the opaque-result descriptor gives.
3491
3492/// `Tab<Never, AnyView, DefaultTabLabel>.TabIdentifiedView`.
3493///
3494/// Nested in an *extension*, so the module appears twice — once for `Tab` and
3495/// once for the extension — and the generic arguments are the parent's
3496/// followed by the child's (none), separated by `_`.
3497const TAB_IDENTIFIED: &str = "7SwiftUI3TabV7SwiftUIE17TabIdentifiedViewVys5NeverO\
34987SwiftUI7AnyViewV7SwiftUI15DefaultTabLabelV_G";
3499
3500/// `_TraitWritingModifier<TagValueTraitKey<Never>>` — one byte, the tag.
3501const TAB_TRAIT: &str =
3502    "7SwiftUI21_TraitWritingModifierVy7SwiftUI16TagValueTraitKeyVys5NeverOGG";
3503
3504/// One element of the builder's tuple.
3505fn tab_element_name() -> String {
3506    format!("7SwiftUI15ModifiedContentVy{TAB_IDENTIFIED}{TAB_TRAIT}G")
3507}
3508
3509/// A `Tab` as the element `@TabContentBuilder` collects.
3510///
3511/// The type is reached two ways and they are checked against each other: the
3512/// composed name above, and `Tab._identifiedView`'s opaque-result descriptor.
3513/// The value is *built* rather than fetched — the exported `_identifiedView`
3514/// getter faults under every argument layout tried — which is sound because the
3515/// type turns out to be two plain fields: a `TabIdentifiedView` whose single
3516/// field *is* the `Tab`, and a one-byte modifier.
3517///
3518/// # Safety
3519/// `tab` must be a `Tab<Never, AnyView, DefaultTabLabel>`.
3520unsafe fn identified_element(tab: &ViewHandle) -> (Vec<u8>, *const c_void) {
3521    unsafe {
3522        let name = tab_element_name();
3523        let meta = abi::resolve_type_by_mangled_name(name.as_bytes());
3524        assert!(!meta.is_null(), "{}", crate::diag::demangle_type(name.as_bytes()));
3525        debug_assert_eq!(meta, identified_element_from_descriptor(), "composed name is a different type");
3526
3527        let offsets = abi::struct_field_offsets(meta).expect("two stored fields");
3528        let &[content_at, modifier_at] = offsets.as_slice() else {
3529            panic!("the identified view is {} fields, not 2", offsets.len());
3530        };
3531        // The wrapper's one field is the `Tab` itself, at offset zero and the
3532        // same size — checked, since a wrapper that added anything would make
3533        // the copy below silent nonsense.
3534        let wrapper = abi::field_type_metadata(meta, 0);
3535        assert_eq!(abi::field_type_metadata(wrapper, 0), tab.meta, "TabIdentifiedView wraps something else");
3536        assert_eq!(abi::struct_field_offsets(wrapper).as_deref(), Some(&[0][..]));
3537
3538        let mut bytes = vec![0u8; abi::type_bytes(meta)];
3539        let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(tab.meta);
3540        (vwt.initialize_with_copy)(
3541            bytes.as_mut_ptr().add(content_at) as *mut c_void,
3542            tab.value_ptr() as *mut c_void,
3543            tab.meta,
3544        );
3545        // The modifier holds a `Never?`, which is one byte and can only be
3546        // `nil` — `Never` is uninhabited, so there is no other value. Written
3547        // through `Never`'s own witness rather than as a literal, which is the
3548        // rule `store_optional_tag` documents.
3549        abi::store_optional_tag(&mut bytes[modifier_at..], resolve::never_meta(), false);
3550        (bytes, meta)
3551    }
3552}
3553
3554/// The same type by the other route, for the crosscheck in
3555/// [`identified_element`].
3556fn identified_element_from_descriptor() -> *const c_void {
3557    unsafe {
3558        let label = abi::resolve_type_by_mangled_name(b"7SwiftUI15DefaultTabLabelV");
3559        let subst = [
3560            resolve::never_meta(),
3561            resolve::anyview_meta(),
3562            label,
3563            resolve::never_hashable_wt(),
3564            resolve::anyview_wt(),
3565            abi::get_view_wt(label),
3566        ];
3567        super::opaque_result_type(resolve::tab_identified_oret(), &subst).0
3568    }
3569}
3570
3571/// `TabView { Tab(…); Tab(…); … }` — the modern tab view over any number of
3572/// tabs.
3573///
3574/// ```no_run
3575/// # use swiftui_native::views;
3576/// let tabs = [
3577///     views::tab("Home", "house", &views::text("home")),
3578///     views::tab("Settings", "gear", &views::text("settings")),
3579/// ];
3580/// let view = views::tab_view_tabs(&tabs);
3581/// ```
3582pub fn tab_view_tabs(tabs: &[ViewHandle]) -> ViewHandle {
3583    assert!(!tabs.is_empty(), "a TabView needs at least one tab");
3584    unsafe {
3585        let mut elements = Vec::with_capacity(tabs.len());
3586        let mut element_meta = core::ptr::null();
3587        for tab in tabs {
3588            assert!(!tab.wt.is_null(), "tab_view_tabs takes Tabs, which carry a TabContent witness");
3589            let (bytes, meta) = identified_element(tab);
3590            element_meta = meta;
3591            elements.push(bytes);
3592        }
3593
3594        // `(A, B, C, …)` is `A_BC…t`: the `_` appears once, after the *first*
3595        // element, and the rest follow unseparated. Measured — `Si_Sit`
3596        // resolves to a two-element tuple, `Si_Si_Sit` to nothing, and
3597        // `Si_SiSit` is the three-element one.
3598        let one = tab_element_name();
3599        let content_name = if tabs.len() == 1 {
3600            format!("7SwiftUI16_TupleTabContentVys5NeverO{one}G")
3601        } else {
3602            let rest = one.repeat(tabs.len() - 1);
3603            format!("7SwiftUI16_TupleTabContentVys5NeverO{one}_{rest}tG")
3604        };
3605        let content_meta = abi::resolve_type_by_mangled_name(content_name.as_bytes());
3606        assert!(
3607            !content_meta.is_null(),
3608            "{}",
3609            crate::diag::demangle_type(content_name.as_bytes())
3610        );
3611
3612        // The tuple itself, whose element offsets come from tuple metadata —
3613        // not struct metadata: it has no descriptor and no field-offset vector,
3614        // just a count and one `{ type, offset }` pair per element.
3615        let tuple_meta = if tabs.len() == 1 {
3616            element_meta
3617        } else {
3618            abi::tuple_metadata(&vec![element_meta; tabs.len()])
3619        };
3620        let offsets = if tabs.len() == 1 {
3621            vec![0]
3622        } else {
3623            abi::tuple_element_offsets(tuple_meta, tabs.len())
3624        };
3625        let mut tuple = vec![0u8; abi::type_bytes(tuple_meta)];
3626        for (bytes, at) in elements.iter().zip(&offsets) {
3627            tuple[*at..*at + bytes.len()].copy_from_slice(bytes);
3628        }
3629
3630        let mut content = vec![0u8; abi::type_bytes(content_meta)];
3631        // _TupleTabContent.init(_: B)
3632        //   x0 = @in B — generic, so by address — x1 = SelectionValue.Type,
3633        //   x2 = B.Type, x8 = @out
3634        core::arch::asm!("blr {f}", f = in(reg) resolve::tuple_tab_init(),
3635            in("x0") tuple.as_mut_ptr(),
3636            in("x1") resolve::never_meta(),
3637            in("x2") tuple_meta,
3638            in("x8") content.as_mut_ptr(),
3639            lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
3640            lateout("x7") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
3641            lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
3642            lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x20") _,
3643            lateout("x21") _, clobber_abi("C"),
3644        );
3645        // The initialiser consumed the tuple and the elements it holds.
3646        core::mem::forget(tuple);
3647        for bytes in elements {
3648            core::mem::forget(bytes);
3649        }
3650
3651        let wt = conformance_wt(content_meta, resolve::tab_content_proto());
3652        assert!(!wt.is_null(), "_TupleTabContent !: TabContent");
3653        tab_view_from(&wrap_owned(content, content_meta, wt), &content_name)
3654    }
3655}
3656
3657
3658/// `Stepper(_:onIncrement:onDecrement:)` — a stepper driven by callbacks.
3659///
3660/// [`stepper_bound`](super::stepper_bound) binds a number and a range, which is
3661/// right when there is a number to bind. A stepper that adds and removes rows,
3662/// or nudges something the Rust side computes, has no such number — and there
3663/// was no way to ask for one.
3664///
3665/// Both callbacks are `(() -> ())?`, which is two words with `nil` as both
3666/// null. Passing `None` for either greys out that half of the control, which is
3667/// how SwiftUI says "you cannot go further this way".
3668pub fn stepper_actions(
3669    label: &str,
3670    on_increment: Option<Box<dyn Fn()>>,
3671    on_decrement: Option<Box<dyn Fn()>>,
3672) -> ViewHandle {
3673    unsafe {
3674        let ss = abi::swift_string(label);
3675        let s0 = u64::from_le_bytes(ss[..8].try_into().expect("label lo"));
3676        let s1 = u64::from_le_bytes(ss[8..].try_into().expect("label hi"));
3677        let (k0, k1, k2, k3) = super::primitives::localized_string_key(s0, s1);
3678
3679        let words = |f: Option<Box<dyn Fn()>>| match f {
3680            Some(f) => super::closures::boxed_action_words(f),
3681            None => [0, 0],
3682        };
3683        let inc = words(on_increment);
3684        let dec = words(on_decrement);
3685
3686        let (meta, wt, size) = result_type(&STEPPER_T);
3687        let mut result = vec![0u8; size];
3688        // Stepper.init(_:onIncrement:onDecrement:onEditingChanged:)
3689        //   x0-x3 = LocalizedStringKey, x4/x5 = onIncrement,
3690        //   x6/x7 = onDecrement, [sp] = onEditingChanged fn,
3691        //   [sp+8] = its context, x8 = @out
3692        core::arch::asm!(
3693            "sub sp, sp, #16",
3694            "stp {ef}, xzr, [sp]",
3695            "blr {f}",
3696            "add sp, sp, #16",
3697            f = in(reg) resolve::stepper_actions_init(),
3698            ef = in(reg) super::noop_closure as *const c_void,
3699            in("x0") k0, in("x1") k1, in("x2") k2, in("x3") k3,
3700            in("x4") inc[0], in("x5") inc[1],
3701            in("x6") dec[0], in("x7") dec[1],
3702            in("x8") result.as_mut_ptr(),
3703            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
3704            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
3705            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
3706            clobber_abi("C"),
3707        );
3708        wrap_owned(result, meta, wt)
3709    }
3710}
3711
3712/// `ForEach(a...b, id: \.self) { … }` — a closed range, identified by value.
3713///
3714/// [`for_each`] takes a Rust `Range` and reaches the `Range<Int>` overload,
3715/// which builds `ForEach<Range<Int>, Int, Content>`. Apple's documentation
3716/// writes `ForEach(1...10, id: \.self)`, which is a *`ClosedRange`* and a
3717/// different `Data` type argument — so a view transcribed from it could not be
3718/// reproduced until this existed.
3719///
3720/// The `id:` is a real `\Int.self` key path, built at run time, so SwiftUI
3721/// identifies rows by value: a row that moves animates as a move, and its
3722/// state follows it. `for_each`'s positional identity does not.
3723pub fn for_each_closed(
3724    start: i64,
3725    end: i64,
3726    builder: impl Fn(i64) -> ViewHandle + 'static,
3727) -> ViewHandle {
3728    unsafe {
3729        let boxed: IndexBuilder = Box::new(builder);
3730        let probe = boxed(start);
3731        let (content_meta, content_wt) = (probe.meta, probe.wt);
3732
3733        let data_meta = abi::resolve_type_by_mangled_name(b"SNySiG");
3734        assert!(!data_meta.is_null(), "ClosedRange<Int> metadata");
3735        let data_wt = super::conformance_wt(data_meta, resolve::random_access_collection_proto());
3736        let int_meta = resolve::int_meta();
3737
3738        // `ForEach<Data, ID, Content>` — three parameters then three
3739        // conformances, in declaration order.
3740        let (known_meta, _, _) = result_type(&FOREACH_T);
3741        let meta = abi::generic_metadata_from(
3742            known_meta,
3743            &[
3744                data_meta,
3745                int_meta,
3746                content_meta,
3747                data_wt,
3748                resolve::int_hashable_wt(),
3749                content_wt,
3750            ],
3751        );
3752        assert!(!meta.is_null(), "ForEach<ClosedRange<Int>, Int, _> metadata");
3753        let wt = abi::get_view_wt(meta);
3754        assert!(!wt.is_null(), "ForEach !: View");
3755        drop(probe);
3756
3757        // `\Int.self`, built once and *retained per call*.
3758        //
3759        // `ForEach.init(_:id:content:)` consumes the key path — Swift's own
3760        // call site pairs it with a fresh `swift_getKeyPath`, which returns
3761        // `+1`, and never releases afterwards. A cached one handed over
3762        // without a retain is a reference this code does not own, and the
3763        // object is freed a few calls later; the crash lands nowhere near the
3764        // cause. `Environment.init` next door has exactly this note.
3765        static KP: std::sync::OnceLock<u64> = std::sync::OnceLock::new();
3766        let keypath = *KP.get_or_init(|| {
3767            static mut SLOT: crate::keypath::IdentitySlot = crate::keypath::IdentitySlot::new();
3768            crate::keypath::build_identity(crate::static_mut(&raw mut SLOT), b"Si\0", b"Si\0")
3769        });
3770        swift_runtime_sys::RuntimeRaw::swift_retain(keypath as *mut c_void);
3771
3772        let payload =
3773            Box::into_raw(Box::new(TypedIndexBuilder { builder: boxed, meta: content_meta }))
3774                as *mut c_void;
3775        let ctx = Box::leak(Box::new(abi::IndexClosureCtx::new(emit_element_view, payload)))
3776            as *mut abi::IndexClosureCtx;
3777
3778        // `ClosedRange<Int>` is two words: lower then upper, both inclusive.
3779        let mut data = [start, end];
3780        let mut result = vec![0u8; abi::type_bytes(meta)];
3781        // `(sret, @in data, keypath, content fn, content ctx, Data.Type,
3782        //   Content.Type, Data: RandomAccessCollection WT, ID: Hashable WT,
3783        //   [sp] = Content: View WT)` — nine after the result, so one spills.
3784        core::arch::asm!(
3785            "sub sp, sp, #16",
3786            "str {cw}, [sp]",
3787            "blr {f}",
3788            "add sp, sp, #16",
3789            f = in(reg) resolve::foreach_id_init(),
3790            cw = in(reg) content_wt,
3791            in("x0") data.as_mut_ptr(),
3792            in("x1") keypath,
3793            in("x2") abi::index_closure_thunk as *const c_void,
3794            in("x3") ctx,
3795            in("x4") data_meta,
3796            in("x5") content_meta,
3797            in("x6") data_wt,
3798            in("x7") resolve::int_hashable_wt(),
3799            in("x8") result.as_mut_ptr(),
3800            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
3801            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
3802            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
3803            clobber_abi("C"),
3804        );
3805        wrap_owned(result, meta, wt)
3806    }
3807}