Skip to main content

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}