seq_runtime/closures/construct.rs
1//! Closure value construction: `make_closure` (wraps a pre-built env) and
2//! `push_closure` (pops captures off the stack, builds the env, pushes the
3//! resulting closure).
4
5use crate::stack::{Stack, pop, push};
6use crate::value::Value;
7use std::sync::Arc;
8
9/// Create a closure value from a function pointer and environment
10///
11/// Takes ownership of the environment (converts raw pointer to Arc).
12/// Arc enables TCO: no cleanup needed after tail calls.
13///
14/// # Safety
15/// - fn_ptr must be a valid function pointer (will be transmuted when called)
16/// - env must be a valid pointer from `create_env`, fully populated via `env_set`
17/// - env ownership is transferred to the Closure value
18// Allow improper_ctypes_definitions: Called from LLVM IR (not C), both sides understand layout
19#[allow(improper_ctypes_definitions)]
20#[unsafe(no_mangle)]
21pub unsafe extern "C" fn patch_seq_make_closure(fn_ptr: u64, env: *mut [Value]) -> Value {
22 if fn_ptr == 0 {
23 panic!("make_closure: null function pointer");
24 }
25
26 if env.is_null() {
27 panic!("make_closure: null environment pointer");
28 }
29
30 // Take ownership of the environment and convert to Arc for TCO support
31 let env_box = unsafe { Box::from_raw(env) };
32 let env_arc: Arc<[Value]> = Arc::from(env_box);
33
34 Value::Closure {
35 fn_ptr: fn_ptr as usize,
36 env: env_arc,
37 }
38}
39
40/// Create closure from function pointer and stack values (all-in-one helper)
41///
42/// Pops `capture_count` values from stack and creates a closure environment
43/// indexed bottom-to-top: env[0] is the caller's deepest capture,
44/// env[N-1] is the caller's shallowest (the value that was on top just
45/// before this call). This matches the typechecker's capture-type vector
46/// and preserves the caller's visual stack order inside the closure body.
47///
48/// # Safety
49/// - fn_ptr must be a valid function pointer
50/// - stack must have at least `capture_count` values
51#[unsafe(no_mangle)]
52pub unsafe extern "C" fn patch_seq_push_closure(
53 mut stack: Stack,
54 fn_ptr: u64,
55 capture_count: i32,
56) -> Stack {
57 if fn_ptr == 0 {
58 panic!("push_closure: null function pointer");
59 }
60
61 if capture_count < 0 {
62 panic!(
63 "push_closure: capture_count cannot be negative: {}",
64 capture_count
65 );
66 }
67
68 let count = capture_count as usize;
69
70 // Pop values from stack top-down, then reverse so env is bottom-to-top.
71 // Index 0 corresponds to the deepest caller capture; the codegen pushes
72 // env[0..N-1] in order at closure entry, leaving the caller's shallowest
73 // capture on top of the body's stack — matching the caller's visual order.
74 let mut captures: Vec<Value> = Vec::with_capacity(count);
75 for _ in 0..count {
76 let (new_stack, value) = unsafe { pop(stack) };
77 captures.push(value);
78 stack = new_stack;
79 }
80 captures.reverse();
81
82 // Create closure value with Arc for TCO support
83 let closure = Value::Closure {
84 fn_ptr: fn_ptr as usize,
85 env: Arc::from(captures.into_boxed_slice()),
86 };
87
88 // Push onto stack
89 unsafe { push(stack, closure) }
90}