seq_runtime/closures/accessors.rs
1//! Type-specific closure environment readers.
2//!
3//! These exist because the generic `env_get` returns a `Value` by value, which
4//! causes FFI ABI trouble on some platforms for large enum variants. The
5//! specialized readers either return a primitive (`i64`/`f64`) or push the
6//! value directly onto the Seq stack, avoiding the by-value enum return.
7
8use crate::stack::{Stack, push};
9use crate::value::Value;
10
11/// Get an Int value from the closure environment
12///
13/// This is a type-specific helper that avoids passing large Value enums through LLVM IR.
14/// Returns primitive i64 instead of Value to avoid FFI issues with by-value enum passing.
15///
16/// # Safety
17/// - env_data must be a valid pointer to an array of Values
18/// - env_len must match the actual array length
19/// - index must be in bounds [0, env_len)
20/// - The value at index must be Value::Int
21///
22/// # FFI Notes
23/// This function is ONLY called from LLVM-generated code, not from external C code.
24/// The signature is safe for LLVM IR but would be undefined behavior if called from C
25/// with incorrect assumptions about type layout.
26#[unsafe(no_mangle)]
27pub unsafe extern "C" fn patch_seq_env_get_int(
28 env_data: *const Value,
29 env_len: usize,
30 index: i32,
31) -> i64 {
32 if env_data.is_null() {
33 panic!("env_get_int: null environment pointer");
34 }
35
36 if index < 0 {
37 panic!("env_get_int: index cannot be negative: {}", index);
38 }
39
40 let idx = index as usize;
41
42 if idx >= env_len {
43 panic!(
44 "env_get_int: index {} out of bounds for environment of size {}",
45 index, env_len
46 );
47 }
48
49 // Access the value at the index
50 let value = unsafe { &*env_data.add(idx) };
51
52 match value {
53 Value::Int(n) => *n,
54 _ => panic!(
55 "env_get_int: expected Int at index {}, got {:?}",
56 index, value
57 ),
58 }
59}
60
61/// Get a String value from the environment at the given index
62///
63/// # Safety
64/// - env_data must be a valid pointer to an array of Values
65/// - env_len must be the actual length of that array
66/// - index must be within bounds
67/// - The value at index must be a String
68///
69/// This function returns a SeqString by-value.
70/// This is safe for FFI because it's only called from LLVM-generated code, not actual C code.
71#[allow(improper_ctypes_definitions)]
72#[unsafe(no_mangle)]
73pub unsafe extern "C" fn patch_seq_env_get_string(
74 env_data: *const Value,
75 env_len: usize,
76 index: i32,
77) -> crate::seqstring::SeqString {
78 if env_data.is_null() {
79 panic!("env_get_string: null environment pointer");
80 }
81
82 if index < 0 {
83 panic!("env_get_string: index cannot be negative: {}", index);
84 }
85
86 let idx = index as usize;
87
88 if idx >= env_len {
89 panic!(
90 "env_get_string: index {} out of bounds for environment of size {}",
91 index, env_len
92 );
93 }
94
95 // Access the value at the index
96 let value = unsafe { &*env_data.add(idx) };
97
98 match value {
99 Value::String(s) => s.clone(),
100 _ => panic!(
101 "env_get_string: expected String at index {}, got {:?}",
102 index, value
103 ),
104 }
105}
106
107/// Push a String from the closure environment directly onto the stack
108///
109/// This combines getting and pushing in one operation to avoid returning
110/// SeqString by value through FFI, which has calling convention issues on Linux.
111///
112/// # Safety
113/// - Stack pointer must be valid
114/// - env_data must be a valid pointer to an array of Values
115/// - env_len must match the actual array length
116/// - index must be in bounds [0, env_len)
117/// - The value at index must be Value::String
118#[unsafe(no_mangle)]
119pub unsafe extern "C" fn patch_seq_env_push_string(
120 stack: Stack,
121 env_data: *const Value,
122 env_len: usize,
123 index: i32,
124) -> Stack {
125 if env_data.is_null() {
126 panic!("env_push_string: null environment pointer");
127 }
128
129 if index < 0 {
130 panic!("env_push_string: index cannot be negative: {}", index);
131 }
132
133 let idx = index as usize;
134
135 if idx >= env_len {
136 panic!(
137 "env_push_string: index {} out of bounds for environment of size {}",
138 index, env_len
139 );
140 }
141
142 // Access the value at the index
143 let value = unsafe { &*env_data.add(idx) };
144
145 match value {
146 Value::String(s) => unsafe { push(stack, Value::String(s.clone())) },
147 _ => panic!(
148 "env_push_string: expected String at index {}, got {:?}",
149 index, value
150 ),
151 }
152}
153
154/// Push any value from the closure environment onto the stack.
155///
156/// This is the generic capture-push function for types that don't have
157/// specialized getters (Variant, Map, Union, Symbol, Channel). It clones
158/// the Value from the env and pushes it directly, avoiding passing Value
159/// by value through the FFI boundary (which crashes on Linux for some types).
160///
161/// # Safety
162/// - `stack` must be a valid stack pointer
163/// - `env_data` must be a valid pointer to a Value array
164/// - `env_len` must match the actual array length
165/// - `index` must be in bounds [0, env_len)
166#[unsafe(no_mangle)]
167pub unsafe extern "C" fn patch_seq_env_push_value(
168 stack: Stack,
169 env_data: *const Value,
170 env_len: usize,
171 index: i32,
172) -> Stack {
173 if env_data.is_null() {
174 panic!("env_push_value: null environment pointer");
175 }
176
177 if index < 0 {
178 panic!("env_push_value: index cannot be negative: {}", index);
179 }
180
181 let idx = index as usize;
182
183 if idx >= env_len {
184 panic!(
185 "env_push_value: index {} out of bounds for environment of size {}",
186 index, env_len
187 );
188 }
189
190 // Clone the value from the environment and push onto the stack.
191 // This works for any Value variant (Variant, Map, Symbol, Channel, etc.)
192 // The clone is O(1) for Arc-wrapped types (Variant, Map) — just a refcount bump.
193 //
194 // Primitive types (Int, Bool, Float) should use their specialized getters
195 // (env_get_int, etc.) for efficiency. This generic path is for types that
196 // don't have specialized LLVM IR representations.
197 let value = unsafe { (*env_data.add(idx)).clone() };
198 debug_assert!(
199 !matches!(value, Value::Int(_) | Value::Bool(_) | Value::Float(_)),
200 "env_push_value called for primitive type {:?} — use the specialized getter",
201 value
202 );
203 unsafe { push(stack, value) }
204}
205
206/// Get a Bool value from the closure environment
207///
208/// Returns i64 (0 for false, 1 for true) to match LLVM IR representation.
209/// Bools are stored as i64 in the generated code for simplicity.
210///
211/// # Safety
212/// - env_data must be a valid pointer to an array of Values
213/// - env_len must match the actual array length
214/// - index must be in bounds [0, env_len)
215/// - The value at index must be Value::Bool
216#[unsafe(no_mangle)]
217pub unsafe extern "C" fn patch_seq_env_get_bool(
218 env_data: *const Value,
219 env_len: usize,
220 index: i32,
221) -> i64 {
222 if env_data.is_null() {
223 panic!("env_get_bool: null environment pointer");
224 }
225
226 if index < 0 {
227 panic!("env_get_bool: index cannot be negative: {}", index);
228 }
229
230 let idx = index as usize;
231
232 if idx >= env_len {
233 panic!(
234 "env_get_bool: index {} out of bounds for environment of size {}",
235 index, env_len
236 );
237 }
238
239 let value = unsafe { &*env_data.add(idx) };
240
241 match value {
242 Value::Bool(b) => {
243 if *b {
244 1
245 } else {
246 0
247 }
248 }
249 _ => panic!(
250 "env_get_bool: expected Bool at index {}, got {:?}",
251 index, value
252 ),
253 }
254}
255
256/// Get a Float value from the closure environment
257///
258/// Returns f64 directly for efficient LLVM IR integration.
259///
260/// # Safety
261/// - env_data must be a valid pointer to an array of Values
262/// - env_len must match the actual array length
263/// - index must be in bounds [0, env_len)
264/// - The value at index must be Value::Float
265#[unsafe(no_mangle)]
266pub unsafe extern "C" fn patch_seq_env_get_float(
267 env_data: *const Value,
268 env_len: usize,
269 index: i32,
270) -> f64 {
271 if env_data.is_null() {
272 panic!("env_get_float: null environment pointer");
273 }
274
275 if index < 0 {
276 panic!("env_get_float: index cannot be negative: {}", index);
277 }
278
279 let idx = index as usize;
280
281 if idx >= env_len {
282 panic!(
283 "env_get_float: index {} out of bounds for environment of size {}",
284 index, env_len
285 );
286 }
287
288 let value = unsafe { &*env_data.add(idx) };
289
290 match value {
291 Value::Float(f) => *f,
292 _ => panic!(
293 "env_get_float: expected Float at index {}, got {:?}",
294 index, value
295 ),
296 }
297}
298
299/// Get a Quotation impl_ function pointer from the closure environment
300///
301/// Returns i64 (the impl_ function pointer as usize) for LLVM IR.
302/// Returns the tailcc impl_ pointer for TCO when called from compiled code.
303/// Quotations are stateless, so only the function pointer is needed.
304///
305/// # Safety
306/// - env_data must be a valid pointer to an array of Values
307/// - env_len must match the actual array length
308/// - index must be in bounds [0, env_len)
309/// - The value at index must be Value::Quotation
310#[unsafe(no_mangle)]
311pub unsafe extern "C" fn patch_seq_env_get_quotation(
312 env_data: *const Value,
313 env_len: usize,
314 index: i32,
315) -> i64 {
316 if env_data.is_null() {
317 panic!("env_get_quotation: null environment pointer");
318 }
319
320 if index < 0 {
321 panic!("env_get_quotation: index cannot be negative: {}", index);
322 }
323
324 let idx = index as usize;
325
326 if idx >= env_len {
327 panic!(
328 "env_get_quotation: index {} out of bounds for environment of size {}",
329 index, env_len
330 );
331 }
332
333 let value = unsafe { &*env_data.add(idx) };
334
335 match value {
336 Value::Quotation { impl_, .. } => *impl_ as i64,
337 _ => panic!(
338 "env_get_quotation: expected Quotation at index {}, got {:?}",
339 index, value
340 ),
341 }
342}