1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
//! Constructor-object plumbing for product and sum structures.
//!
//! The "structure pattern" lives at two primitives, hand-called at each
//! struct boundary. There is no per-struct trait, no derive, no
//! procedural macro: callers compose [`alloc_ctor_with_objects`] and
//! [`take_ctor_objects`] field by field and let the per-field
//! [`super::traits::IntoLean`] / [`super::traits::TryFromLean`] impls
//! do the actual type marshalling.
//!
//! The module is the only place in `abi` that knows how
//! [`lean_alloc_ctor`], [`lean_ctor_obj_cptr`], and the constructor's
//! `tag`/`num_objs` invariants line up; container modules
//! (`crate::abi::option`, `crate::abi::except`) and downstream
//! handlers ship through these primitives instead of repeating the
//! pointer arithmetic. That keeps a single audited copy of the
//! ctor-allocation rules and centralises the `lean_inc`/`lean_dec`
//! reasoning.
//!
//! ### Lifetime and refcount invariants
//!
//! - [`alloc_ctor_with_objects`] consumes the input array of `Obj<'lean>`
//! handles. Each handle's owned refcount is transferred — via
//! [`Obj::into_raw`] — into the freshly allocated constructor's
//! object-slot, so the new parent owns exactly one count per field plus
//! its own header count. No `Obj::clone` (which would `lean_inc`) runs
//! on the input path.
//! - [`take_ctor_objects`] reads each object slot once, calls `lean_inc`
//! on the field, and wraps the bumped pointer in a fresh `Obj<'lean>`.
//! The parent `Obj` is then dropped; its `lean_dec` walks back through
//! the original per-field counts, leaving each returned handle with the
//! same effective ownership the parent had given that field.
//! - [`ctor_tag`] is a borrow-only read; it never touches the refcount.
// SAFETY DOC: every `unsafe { ... }` block in this file carries its own
// `// SAFETY:` comment naming the invariant; the blanket allow keeps the
// unsafe surface inside the smallest scope that compiles, per
// `docs/architecture/01-safety-model.md`.
use MaybeUninit;
use ;
use ;
use lean_inc;
use crateconversion_error;
use crateLeanResult;
use crateLeanRuntime;
use crateObj;
/// Allocate a freshly-initialised constructor with `N` object-pointer
/// fields and no scalar payload.
///
/// `tag` is the inductive constructor index (Lean's declaration order:
/// `Option.none` = 0, `Option.some` = 1, `Except.error` = 0, `Except.ok`
/// = 1, …). Each entry of `objects` is moved into its slot via
/// [`Obj::into_raw`], so the returned [`Obj`] owns the only live refcount
/// per field plus its own header count. The const-generic `N` matches the
/// number of object-pointer slots the Lean inductive declares, which
/// keeps the call site self-documenting and lets the compiler refuse
/// arity mismatches.
///
/// # Panics
///
/// Panics only via `lean_alloc_ctor`'s `strict_*` arithmetic overflow
/// guard — unreachable for the constructor shapes Lean emits
/// (`LEAN_MAX_CTOR_FIELDS` = 256).
/// Validate that `obj` is a constructor with `expected_tag` and exactly
/// `N` object-pointer fields, then return the `N` owned field handles.
///
/// Each returned [`Obj`] carries one refcount: [`lean_inc`] is called on
/// the slot pointer before wrapping it. The parent `obj` is consumed and
/// its [`Drop`] runs the matching `lean_dec` (which decrements each field
/// once more — balancing the `lean_inc`s and leaving the returned handles
/// with the same effective ownership the parent originally held).
///
/// `label` is embedded in the diagnostic on failure so callers see
/// `"expected Lean Option::some ctor (tag 1, num_objs 1), …"` rather
/// than an anonymous "wrong ctor".
///
/// # Errors
///
/// Returns [`HostStage::Conversion`](crate::error::HostStage::Conversion)
/// if `obj` is scalar-tagged, has a non-constructor heap tag, has a
/// different tag from `expected_tag`, or carries a different
/// object-slot count from `N`.
/// Read the tag byte of a constructor object.
///
/// Used by sum-type decoders (`Option` and `except::Except` (sibling-module sum-type carriers))
/// that need to pick a variant before they know the arity. Borrow-only:
/// leaves the refcount untouched.
///
/// # Errors
///
/// Returns [`HostStage::Conversion`](crate::error::HostStage::Conversion)
/// if `obj` is not a heap-allocated constructor.
/// Shared validator for [`take_ctor_objects`]: ctor kind, matching tag,
/// matching `num_objs`.