Skip to main content

scheme_rs/
proc.rs

1//! Scheme Procedures.
2//!
3//! Scheme procedures, more commonly known as [`closures`](https://en.wikipedia.org/wiki/Closure_(computer_programming))
4//! as they capture their environment, are the fundamental and only way to
5//! transfer control from a Rust context to a Scheme context.
6//!
7//! # Calling procedures from Rust
8//!
9//! # Manually creating closures
10//!
11//! Generally procedures are created in Scheme contexts. However, it is
12//! occasionally desirable to create a closure in Rust contexts. This can be
13//! done with a [`cps_bridge`] function and a call to [`Procedure::new`]. The
14//! `env` argument to the CPS function is a reference to the vector passed to
15//! the `new` function:
16//!
17//! ```
18//! # use scheme_rs::{proc::{Procedure, BridgePtr, Application, ContBarrier},
19//! # registry::cps_bridge, value::Value, runtime::Runtime, exceptions::Exception};
20//! #[cps_bridge]
21//! fn closure(
22//!     _runtime: &Runtime,
23//!     env: &[Value],
24//!     _args: &[Value],
25//!     _rest_args: &[Value],
26//!     _barrier: &mut ContBarrier,
27//!     k: Value,
28//! ) -> Result<Application, Exception> {
29//!     Ok(Application::new(k.try_into()?, vec![ env[0].clone() ]))
30//! }
31//!
32//! # fn main() {
33//! # let runtime = Runtime::new();
34//! let closure = Procedure::new(
35//!     runtime,
36//!     vec![ Value::from(3.1415) ],
37//!     closure as BridgePtr,
38//!     0,
39//!     false,
40//! );
41//! # }
42//! ```
43//!
44//! By default the environment is immutable. If the environment needs to be
45//! modified, a [`Cell`](scheme_rs::value::Cell) can be used:
46//!
47//! ```
48//! # use scheme_rs::{
49//! #     proc::{Procedure, BridgePtr, Application, ContBarrier},
50//! #     registry::cps_bridge, value::{Value, Cell}, runtime::Runtime,
51//! #     exceptions::Exception,
52//! #     num::Number,
53//! # };
54//! #[cps_bridge]
55//! fn next_num(
56//!     _runtime: &Runtime,
57//!     env: &[Value],
58//!     _args: &[Value],
59//!     _rest_args: &[Value],
60//!     _barrier: &mut ContBarrier,
61//!     k: Value,
62//! ) -> Result<Application, Exception> {
63//!     // Fetch the cell from the environment:
64//!     let cell: Cell = env[0].try_to_scheme_type()?;
65//!     let curr: Number = cell.get().try_into()?;
66//!
67//!     // Increment the cell
68//!     cell.set(Value::from(curr.clone() + Number::from(1)));
69//!
70//!     // Return the previous value:
71//!     Ok(Application::new(k.try_into()?, vec![ Value::from(curr) ]))
72//! }
73//!
74//! # fn main() {
75//! # let runtime = Runtime::new();
76//! let next_num = Procedure::new(
77//!     runtime,
78//!     // Cells must be converted to values:
79//!     vec![ Value::from(Cell::new(Value::from(3.1415))) ],
80//!     next_num as BridgePtr,
81//!     0,
82//!     false,
83//! );
84//! # }
85//! ```
86//!
87//! # Categories of procedures
88//!
89//! In scheme-rs, procedures can be placed into a few different categories, the
90//! most obvious is that procedures are either _user_ functions or
91//! [_continuations_](https://en.wikipedia.org/wiki/Continuation). This
92//! categorization is mostly transparent to the user.
93
94use crate::{
95    env::Local,
96    exceptions::{Exception, raise},
97    gc::{Gc, GcInner, Trace},
98    lists::{self, Pair, list_to_vec},
99    ports::{BufferMode, Port, Transcoder},
100    records::{Record, RecordTypeDescriptor, SchemeCompatible, rtd},
101    registry::BridgeFnDebugInfo,
102    runtime::{Runtime, RuntimeInner},
103    symbols::Symbol,
104    syntax::Span,
105    value::Value,
106    vectors::Vector,
107};
108use parking_lot::RwLock;
109use scheme_rs_macros::{cps_bridge, maybe_async, maybe_await};
110use std::{
111    any::Any,
112    collections::HashMap,
113    fmt,
114    ops::DerefMut,
115    sync::{
116        Arc, OnceLock,
117        atomic::{AtomicUsize, Ordering},
118    },
119};
120
121/// A function pointer to a generated continuation.
122pub(crate) type ContinuationPtr = unsafe extern "C" fn(
123    runtime: *mut GcInner<RwLock<RuntimeInner>>,
124    env: *const Value,
125    args: *const Value,
126    barrier: *mut ContBarrier<'_>,
127) -> *mut Application;
128
129/// A function pointer to a generated user function.
130pub(crate) type UserPtr = unsafe extern "C" fn(
131    runtime: *mut GcInner<RwLock<RuntimeInner>>,
132    env: *const Value,
133    args: *const Value,
134    barrier: *mut ContBarrier<'_>,
135    k: Value,
136) -> *mut Application;
137
138/// A function pointer to a sync Rust bridge function.
139pub type BridgePtr = fn(
140    runtime: &Runtime,
141    env: &[Value],
142    args: &[Value],
143    rest_args: &[Value],
144    barrier: &mut ContBarrier<'_>,
145    k: Value,
146) -> Application;
147
148/// A function pointer to an async Rust bridge function.
149#[cfg(feature = "async")]
150pub type AsyncBridgePtr = for<'a> fn(
151    runtime: &'a Runtime,
152    env: &'a [Value],
153    args: &'a [Value],
154    rest_args: &'a [Value],
155    barrier: &'a mut ContBarrier<'_>,
156    k: Value,
157) -> futures::future::BoxFuture<'a, Application>;
158
159#[derive(Copy, Clone, Debug)]
160pub(crate) enum FuncPtr {
161    /// A function defined in Rust
162    Bridge(BridgePtr),
163    #[cfg(feature = "async")]
164    /// An async function defined in Rust
165    AsyncBridge(AsyncBridgePtr),
166    /// A JIT compiled user function
167    User(UserPtr),
168    /// A JIT compiled (or occasionally defined in Rust) continuation
169    Continuation(ContinuationPtr),
170    /// A continuation that exits a prompt. Can be dynamically replaced
171    PromptBarrier {
172        barrier_id: usize,
173        k: ContinuationPtr,
174    },
175}
176
177impl From<BridgePtr> for FuncPtr {
178    fn from(ptr: BridgePtr) -> Self {
179        Self::Bridge(ptr)
180    }
181}
182
183#[cfg(feature = "async")]
184impl From<AsyncBridgePtr> for FuncPtr {
185    fn from(ptr: AsyncBridgePtr) -> Self {
186        Self::AsyncBridge(ptr)
187    }
188}
189
190impl From<UserPtr> for FuncPtr {
191    fn from(ptr: UserPtr) -> Self {
192        Self::User(ptr)
193    }
194}
195
196enum JitFuncPtr {
197    Continuation(ContinuationPtr),
198    User(UserPtr),
199}
200
201#[derive(Clone, Trace)]
202#[repr(align(16))]
203pub(crate) struct ProcedureInner {
204    /// The runtime the Procedure is defined in. This is necessary to ensure that
205    /// dropping the runtime does not de-allocate the function pointer for this
206    /// procedure.
207    // TODO: Do we make this optional in the case of bridge functions?
208    pub(crate) runtime: Runtime,
209    /// Environmental variables used by the procedure.
210    pub(crate) env: Vec<Value>,
211    /// Fuction pointer to the body of the procecure.
212    #[trace(skip)]
213    pub(crate) func: FuncPtr,
214    /// Number of required arguments to this procedure.
215    pub(crate) num_required_args: usize,
216    /// Whether or not this is a variadic function.
217    pub(crate) variadic: bool,
218    /// Whether or not this function is a variable transformer.
219    pub(crate) is_variable_transformer: bool,
220    /// Debug information for this function. Only applicable if the function is
221    /// a user function, i.e. not a continuation.
222    pub(crate) debug_info: Option<Arc<ProcDebugInfo>>,
223}
224
225impl ProcedureInner {
226    pub(crate) fn new(
227        runtime: Runtime,
228        env: Vec<Value>,
229        func: FuncPtr,
230        num_required_args: usize,
231        variadic: bool,
232        debug_info: Option<Arc<ProcDebugInfo>>,
233    ) -> Self {
234        Self {
235            runtime,
236            env,
237            func,
238            num_required_args,
239            variadic,
240            is_variable_transformer: false,
241            debug_info,
242        }
243    }
244
245    pub fn is_continuation(&self) -> bool {
246        matches!(
247            self.func,
248            FuncPtr::Continuation(_) | FuncPtr::PromptBarrier { .. }
249        )
250    }
251
252    pub(crate) fn prepare_args(
253        &self,
254        mut args: Vec<Value>,
255        barrier: &mut ContBarrier,
256    ) -> Result<(Vec<Value>, Option<Value>), Application> {
257        // Extract the continuation, if it is required
258        let cont = (!self.is_continuation()).then(|| args.pop().unwrap());
259
260        // Error if the number of arguments provided is incorrect
261        if args.len() < self.num_required_args {
262            return Err(raise(
263                self.runtime.clone(),
264                Exception::wrong_num_of_args(self.num_required_args, args.len()).into(),
265                barrier,
266            ));
267        }
268
269        if !self.variadic && args.len() > self.num_required_args {
270            return Err(raise(
271                self.runtime.clone(),
272                Exception::wrong_num_of_args(self.num_required_args, args.len()).into(),
273                barrier,
274            ));
275        }
276
277        Ok((args, cont))
278    }
279
280    #[cfg(feature = "async")]
281    async fn apply_async_bridge(
282        &self,
283        func: AsyncBridgePtr,
284        args: &[Value],
285        barrier: &mut ContBarrier<'_>,
286        k: Value,
287    ) -> Application {
288        let (args, rest_args) = if self.variadic {
289            args.split_at(self.num_required_args)
290        } else {
291            (args, &[] as &[Value])
292        };
293
294        (func)(&self.runtime, &self.env, args, rest_args, barrier, k).await
295    }
296
297    fn apply_sync_bridge(
298        &self,
299        func: BridgePtr,
300        args: &[Value],
301        barrier: &mut ContBarrier,
302        k: Value,
303    ) -> Application {
304        let (args, rest_args) = if self.variadic {
305            args.split_at(self.num_required_args)
306        } else {
307            (args, &[] as &[Value])
308        };
309
310        (func)(&self.runtime, &self.env, args, rest_args, barrier, k)
311    }
312
313    fn apply_jit(
314        &self,
315        func: JitFuncPtr,
316        mut args: Vec<Value>,
317        barrier: &mut ContBarrier,
318        k: Option<Value>,
319    ) -> Application {
320        if self.variadic {
321            let mut rest_args = Value::null();
322            let extra_args = args.len() - self.num_required_args;
323            for _ in 0..extra_args {
324                rest_args = Value::from(Pair::immutable(args.pop().unwrap(), rest_args));
325            }
326            args.push(rest_args);
327        }
328
329        let app = match func {
330            JitFuncPtr::Continuation(sync_fn) => unsafe {
331                (sync_fn)(
332                    Gc::as_ptr(&self.runtime.0),
333                    self.env.as_ptr(),
334                    args.as_ptr(),
335                    barrier as *mut ContBarrier<'_>,
336                )
337            },
338            JitFuncPtr::User(sync_fn) => unsafe {
339                (sync_fn)(
340                    Gc::as_ptr(&self.runtime.0),
341                    self.env.as_ptr(),
342                    args.as_ptr(),
343                    barrier as *mut ContBarrier<'_>,
344                    Value::from_raw(Value::as_raw(k.as_ref().unwrap())),
345                )
346            },
347        };
348
349        unsafe { *Box::from_raw(app) }
350    }
351
352    /// Apply the arguments to the function, returning the next application.
353    #[maybe_async]
354    pub fn apply(&self, args: Vec<Value>, barrier: &mut ContBarrier<'_>) -> Application {
355        if let FuncPtr::PromptBarrier { barrier_id: id, .. } = self.func {
356            barrier.pop_marks();
357            match barrier.pop_dyn_stack() {
358                Some(DynStackElem::PromptBarrier(PromptBarrier {
359                    barrier_id,
360                    replaced_k,
361                })) if barrier_id == id => {
362                    let (args, _) = match replaced_k.0.prepare_args(args, barrier) {
363                        Ok(args) => args,
364                        Err(raised) => return raised,
365                    };
366                    return Application::new(replaced_k, args);
367                }
368                Some(other) => barrier.push_dyn_stack(other),
369                _ => (),
370            }
371        }
372
373        let (args, k) = match self.prepare_args(args, barrier) {
374            Ok(args) => args,
375            Err(raised) => return raised,
376        };
377
378        match self.func {
379            FuncPtr::Bridge(sbridge) => self.apply_sync_bridge(sbridge, &args, barrier, k.unwrap()),
380            #[cfg(feature = "async")]
381            FuncPtr::AsyncBridge(abridge) => {
382                self.apply_async_bridge(abridge, &args, barrier, k.unwrap())
383                    .await
384            }
385            FuncPtr::User(user) => self.apply_jit(JitFuncPtr::User(user), args, barrier, k),
386            FuncPtr::Continuation(k) => {
387                barrier.pop_marks();
388                self.apply_jit(JitFuncPtr::Continuation(k), args, barrier, None)
389            }
390            FuncPtr::PromptBarrier { k, .. } => {
391                self.apply_jit(JitFuncPtr::Continuation(k), args, barrier, None)
392            }
393        }
394    }
395
396    #[cfg(feature = "async")]
397    /// Attempt to call the function, and throw an error if is async
398    pub fn apply_sync(&self, args: Vec<Value>, barrier: &mut ContBarrier) -> Application {
399        if let FuncPtr::PromptBarrier { barrier_id: id, .. } = self.func {
400            barrier.pop_marks();
401            match barrier.pop_dyn_stack() {
402                Some(DynStackElem::PromptBarrier(PromptBarrier {
403                    barrier_id,
404                    replaced_k,
405                })) if barrier_id == id => {
406                    let (args, _) = match replaced_k.0.prepare_args(args, barrier) {
407                        Ok(args) => args,
408                        Err(raised) => return raised,
409                    };
410                    return Application::new(replaced_k, args);
411                }
412                Some(other) => barrier.push_dyn_stack(other),
413                _ => (),
414            }
415        }
416
417        let (args, k) = match self.prepare_args(args, barrier) {
418            Ok(args) => args,
419            Err(raised) => return raised,
420        };
421
422        match self.func {
423            FuncPtr::Bridge(sbridge) => self.apply_sync_bridge(sbridge, &args, barrier, k.unwrap()),
424            FuncPtr::AsyncBridge(_) => raise(
425                self.runtime.clone(),
426                Exception::error("attempt to apply async function in a sync-only context").into(),
427                barrier,
428            ),
429            FuncPtr::User(user) => self.apply_jit(JitFuncPtr::User(user), args, barrier, k),
430            FuncPtr::Continuation(k) => {
431                barrier.pop_marks();
432                self.apply_jit(JitFuncPtr::Continuation(k), args, barrier, None)
433            }
434            FuncPtr::PromptBarrier { k, .. } => {
435                self.apply_jit(JitFuncPtr::Continuation(k), args, barrier, None)
436            }
437        }
438    }
439}
440
441impl fmt::Debug for ProcedureInner {
442    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
443        if self.is_continuation() {
444            return write!(f, "continuation");
445        }
446
447        let Some(ref debug_info) = self.debug_info else {
448            write!(f, "(<lambda>")?;
449            for i in 0..self.num_required_args {
450                write!(f, " ${i}")?;
451            }
452            if self.variadic {
453                write!(f, " . ${}", self.num_required_args)?;
454            }
455            return write!(f, ")");
456        };
457
458        write!(f, "({}", debug_info.name)?;
459
460        if let Some((last, args)) = debug_info.args.split_last() {
461            for arg in args {
462                write!(f, " {arg}")?;
463            }
464            if self.variadic {
465                write!(f, " .")?;
466            }
467            write!(f, " {last}")?;
468        }
469
470        write!(f, ") at {}", debug_info.location)
471    }
472}
473
474/// The runtime representation of a Procedure, which can be either a user
475/// function or a continuation. Contains a reference to all of the environmental
476/// variables used in the body, along with a function pointer to the body of the
477/// procedure.
478#[derive(Clone, Trace)]
479pub struct Procedure(pub(crate) Gc<ProcedureInner>);
480
481impl Procedure {
482    #[allow(private_bounds)]
483    /// Creates a new procedure. `func` must be a [`BridgePtr`] or an
484    /// `AsyncBridgePtr` if `async` is enabled.
485    pub fn new(
486        runtime: Runtime,
487        env: Vec<Value>,
488        func: impl Into<FuncPtr>,
489        num_required_args: usize,
490        variadic: bool,
491    ) -> Self {
492        Self::with_debug_info(runtime, env, func.into(), num_required_args, variadic, None)
493    }
494
495    pub(crate) fn with_debug_info(
496        runtime: Runtime,
497        env: Vec<Value>,
498        func: FuncPtr,
499        num_required_args: usize,
500        variadic: bool,
501        debug_info: Option<Arc<ProcDebugInfo>>,
502    ) -> Self {
503        Self(Gc::new(ProcedureInner {
504            runtime,
505            env,
506            func,
507            num_required_args,
508            variadic,
509            is_variable_transformer: false,
510            debug_info,
511        }))
512    }
513
514    /// Get the runtime associated with the procedure
515    pub fn get_runtime(&self) -> Runtime {
516        self.0.runtime.clone()
517    }
518
519    /// Return the number of required arguments and whether or not this function
520    /// is variadic
521    pub fn get_formals(&self) -> (usize, bool) {
522        (self.0.num_required_args, self.0.variadic)
523    }
524
525    /// Return the debug information associated with procedure, if it exists.
526    pub fn get_debug_info(&self) -> Option<Arc<ProcDebugInfo>> {
527        self.0.debug_info.clone()
528    }
529
530    /// # Safety
531    /// `args` must be a valid pointer and contain num_required_args + variadic entries.
532    pub(crate) unsafe fn collect_args(&self, args: *const Value) -> Vec<Value> {
533        // I don't really like this, but what are you gonna do?
534        let (num_required_args, variadic) = self.get_formals();
535
536        unsafe {
537            let mut collected_args: Vec<_> = (0..num_required_args)
538                .map(|i| args.add(i).as_ref().unwrap().clone())
539                .collect();
540
541            if variadic {
542                let rest_args = args.add(num_required_args).as_ref().unwrap().clone();
543                let mut vec = Vec::new();
544                lists::list_to_vec(&rest_args, &mut vec);
545                collected_args.extend(vec);
546            }
547
548            collected_args
549        }
550    }
551
552    pub fn is_variable_transformer(&self) -> bool {
553        self.0.is_variable_transformer
554    }
555
556    /// Return whether or not the procedure is a continuation
557    pub fn is_continuation(&self) -> bool {
558        self.0.is_continuation()
559    }
560
561    /// Applies `args` to the procedure and returns the values it evaluates to.
562    #[maybe_async]
563    pub fn call(
564        &self,
565        args: &[Value],
566        barrier: &mut ContBarrier<'_>,
567    ) -> Result<Vec<Value>, Exception> {
568        let mut args = args.to_vec();
569
570        args.push(halt_continuation(self.get_runtime()));
571
572        maybe_await!(Application::new(self.clone(), args).eval(barrier))
573    }
574
575    #[cfg(feature = "async")]
576    pub fn call_sync(
577        &self,
578        args: &[Value],
579        barrier: &mut ContBarrier<'_>,
580    ) -> Result<Vec<Value>, Exception> {
581        let mut args = args.to_vec();
582
583        args.push(halt_continuation(self.get_runtime()));
584
585        Application::new(self.clone(), args).eval_sync(barrier)
586    }
587}
588
589static HALT_CONTINUATION: OnceLock<Value> = OnceLock::new();
590
591/// Return a continuation that returns its expressions to the Rust program.
592pub fn halt_continuation(runtime: Runtime) -> Value {
593    unsafe extern "C" fn halt(
594        _runtime: *mut GcInner<RwLock<RuntimeInner>>,
595        _env: *const Value,
596        args: *const Value,
597        _barrier: *mut ContBarrier,
598    ) -> *mut Application {
599        unsafe { crate::runtime::halt(Value::into_raw(args.read())) }
600    }
601
602    HALT_CONTINUATION
603        .get_or_init(move || {
604            Value::from(Procedure(Gc::new(ProcedureInner::new(
605                runtime,
606                Vec::new(),
607                FuncPtr::Continuation(halt),
608                0,
609                true,
610                None,
611            ))))
612        })
613        .clone()
614}
615
616impl fmt::Debug for Procedure {
617    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
618        self.0.fmt(f)
619    }
620}
621
622impl PartialEq for Procedure {
623    fn eq(&self, rhs: &Procedure) -> bool {
624        Gc::ptr_eq(&self.0, &rhs.0)
625    }
626}
627
628pub(crate) enum OpType {
629    Proc(Procedure),
630    HaltOk,
631    HaltErr,
632}
633
634/// An application of a function to a given set of values.
635pub struct Application {
636    /// The operator being applied to.
637    op: OpType,
638    /// The arguments being applied to the operator.
639    args: Vec<Value>,
640}
641
642impl Application {
643    pub fn new(op: Procedure, args: Vec<Value>) -> Self {
644        Self {
645            op: OpType::Proc(op),
646            args,
647        }
648    }
649
650    pub fn halt_ok(args: Vec<Value>) -> Self {
651        Self {
652            op: OpType::HaltOk,
653            args,
654        }
655    }
656
657    pub fn halt_err(arg: Value) -> Self {
658        Self {
659            op: OpType::HaltErr,
660            args: vec![arg],
661        }
662    }
663
664    /// Evaluate the application - and all subsequent application - until all that
665    /// remains are values. This is the main trampoline of the evaluation engine.
666    #[maybe_async]
667    pub fn eval(mut self, barrier: &mut ContBarrier<'_>) -> Result<Vec<Value>, Exception> {
668        loop {
669            let op = match self.op {
670                OpType::Proc(proc) => proc,
671                OpType::HaltOk => return Ok(self.args),
672                OpType::HaltErr => {
673                    return Err(Exception(self.args.pop().unwrap()));
674                }
675            };
676            self = maybe_await!(op.0.apply(self.args, barrier));
677        }
678    }
679
680    #[cfg(feature = "async")]
681    /// Just like [eval] but throws an error if we encounter an async function.
682    pub fn eval_sync(mut self, barrier: &mut ContBarrier) -> Result<Vec<Value>, Exception> {
683        loop {
684            let op = match self.op {
685                OpType::Proc(proc) => proc,
686                OpType::HaltOk => return Ok(self.args),
687                OpType::HaltErr => {
688                    return Err(Exception(self.args.pop().unwrap()));
689                }
690            };
691            self = op.0.apply_sync(self.args, barrier);
692        }
693    }
694}
695
696/// Debug information associated with a procedure, including its name, argument
697/// names, and source location.
698#[derive(Debug)]
699pub struct ProcDebugInfo {
700    /// The name of the function.
701    pub name: Symbol,
702    /// Named arguments for the function.
703    pub args: Vec<Local>,
704    /// Location of the function definition
705    pub location: Span,
706}
707
708impl ProcDebugInfo {
709    pub fn new(name: Option<Symbol>, args: Vec<Local>, location: Span) -> Self {
710        Self {
711            name: name.unwrap_or_else(|| Symbol::intern("<lambda>")),
712            args,
713            location,
714        }
715    }
716
717    pub fn from_bridge_fn(name: &'static str, debug_info: BridgeFnDebugInfo) -> Self {
718        Self {
719            name: Symbol::intern(name),
720            args: debug_info
721                .args
722                .iter()
723                .map(|arg| Local::gensym_with_name(Symbol::intern(arg)))
724                .collect(),
725            location: Span {
726                line: debug_info.line,
727                column: debug_info.column as usize,
728                offset: debug_info.offset,
729                file: std::sync::Arc::from(debug_info.file.to_string()),
730            },
731        }
732    }
733}
734
735#[cps_bridge(def = "apply arg1 . args", lib = "(rnrs base builtins (6))")]
736pub fn apply(
737    _runtime: &Runtime,
738    _env: &[Value],
739    args: &[Value],
740    rest_args: &[Value],
741    _barrier: &mut ContBarrier,
742    k: Value,
743) -> Result<Application, Exception> {
744    if rest_args.is_empty() {
745        return Err(Exception::wrong_num_of_args(2, args.len()));
746    }
747    let op: Procedure = args[0].clone().try_into()?;
748    let (last, args) = rest_args.split_last().unwrap();
749    let mut args = args.to_vec();
750    list_to_vec(last, &mut args);
751    args.push(k);
752    Ok(Application::new(op.clone(), args))
753}
754
755////////////////////////////////////////////////////////////////////////////////
756//
757// Continuation barriers
758//
759
760#[cfg(feature = "async")]
761type Param<'a> = &'a mut (dyn Any + Send + Sync);
762
763#[cfg(not(feature = "async"))]
764type Param<'a> = &'a mut dyn Any;
765
766/// A continuation barrier. Escape procedures created within a continuation
767/// barrier cannot be called within another barrier.
768///
769/// This structure also contains the dynamic state of the running program
770/// including winders, exception handlers, continuation marks, and parameters.
771pub struct ContBarrier<'a> {
772    /// The id of the barrier. Checked when calling an escape procedure
773    id: usize,
774    /// The active dynamic stack
775    dyn_stack: Vec<DynStackElem>,
776    /// The active [continuation marks](https://srfi.schemers.org/srfi-157/srfi-157.html)
777    cont_marks: Vec<HashMap<Symbol, Value>>,
778    /// The active installed mutable parameters
779    params: HashMap<Symbol, Param<'a>>,
780}
781
782impl<'a> ContBarrier<'a> {
783    pub fn new() -> Self {
784        static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
785
786        Self {
787            id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
788            dyn_stack: Vec::new(),
789            // Procedures returned by the JIT compiler are delimited
790            // continuations (of sorts), and therefore we need to preallocate
791            // the initial marks for them since there's no mechanism to allocate
792            // for them when they're run.
793            cont_marks: vec![HashMap::new()],
794            params: HashMap::new(),
795        }
796    }
797
798    /// This is the only method you can use to create continuations, in order to
799    /// ensure that a continuation isn't allocated without a corresponding push
800    /// to cont_marks
801    pub(crate) fn new_k(
802        &mut self,
803        runtime: Runtime,
804        env: Vec<Value>,
805        k: ContinuationPtr,
806        num_required_args: usize,
807        variadic: bool,
808    ) -> Procedure {
809        self.push_marks();
810        Procedure::with_debug_info(
811            runtime,
812            env,
813            FuncPtr::Continuation(k),
814            num_required_args,
815            variadic,
816            None,
817        )
818    }
819
820    pub fn save(&self) -> SavedDynamicState {
821        SavedDynamicState {
822            id: self.id,
823            dyn_stack: self.dyn_stack.clone(),
824            cont_marks: self.cont_marks.clone(),
825        }
826    }
827
828    pub fn add_param(
829        &mut self,
830        key: impl Into<Symbol>,
831        #[cfg(feature = "async")] val: &'a mut (impl Any + Send + Sync),
832        #[cfg(not(feature = "async"))] val: &'a mut impl Any,
833    ) {
834        self.params.insert(key.into(), val);
835    }
836
837    pub fn get_param<'b>(&'b mut self, key: impl Into<Symbol>) -> Option<Param<'b>> {
838        self.params.get_mut(&key.into()).map(|v| v.deref_mut())
839    }
840
841    pub fn get_params_disjoint<'b, const N: usize>(
842        &'b mut self,
843        keys: [&Symbol; N],
844    ) -> [Option<Param<'b>>; N] {
845        self.params
846            .get_disjoint_mut(keys)
847            .map(|v| v.map(|v| v.deref_mut()))
848    }
849
850    pub fn iter_params<'b>(&'b mut self) -> impl Iterator<Item = (Symbol, Param<'b>)> {
851        self.params.iter_mut().map(|(k, v)| (*k, v.deref_mut()))
852    }
853
854    /// Constructs a child barrier from the current barrier, extracting an array
855    /// of parameters that are not automatically passed onto the child.
856    pub fn child_barrier<'b, 'c, const N: usize>(
857        &'b mut self,
858        params: [impl Into<Symbol>; N],
859    ) -> ([Option<Param<'b>>; N], ContBarrier<'c>)
860    where
861        'b: 'c,
862    {
863        let param_to_index = params
864            .into_iter()
865            .enumerate()
866            .map(|(idx, param)| (param.into(), idx))
867            .collect::<HashMap<_, _>>();
868        let mut params = [const { None }; N];
869        let mut child_barrier = ContBarrier::from(self.save());
870        for (key, value) in self.params.iter_mut() {
871            let value = value.deref_mut();
872            if let Some(idx) = param_to_index.get(key) {
873                params[*idx] = Some(value);
874            } else {
875                child_barrier.params.insert(*key, value);
876            }
877        }
878        (params, child_barrier)
879    }
880
881    pub(crate) fn push_marks(&mut self) {
882        self.cont_marks.push(HashMap::new());
883    }
884
885    pub(crate) fn pop_marks(&mut self) {
886        self.cont_marks.pop();
887    }
888
889    pub(crate) fn current_marks(&self, tag: Symbol) -> Vec<Value> {
890        self.cont_marks
891            .iter()
892            .rev()
893            .flat_map(|marks| marks.get(&tag).cloned())
894            .collect()
895    }
896
897    pub(crate) fn set_continuation_mark(&mut self, tag: Symbol, val: Value) {
898        self.cont_marks.last_mut().unwrap().insert(tag, val);
899    }
900
901    // TODO: We should certainly try to optimize these functions. Linear
902    // searching isn't _great_, although in practice I can't imagine this stack
903    // will ever get very large.
904
905    pub fn current_exception_handler(&self) -> Option<Procedure> {
906        self.dyn_stack.iter().rev().find_map(|elem| match elem {
907            DynStackElem::ExceptionHandler(proc) => Some(proc.clone()),
908            _ => None,
909        })
910    }
911
912    pub fn current_input_port(&self) -> Port {
913        self.dyn_stack
914            .iter()
915            .rev()
916            .find_map(|elem| match elem {
917                DynStackElem::CurrentInputPort(port) => Some(port.clone()),
918                _ => None,
919            })
920            .unwrap_or_else(|| {
921                Port::new(
922                    "<stdin>",
923                    #[cfg(not(feature = "async"))]
924                    std::io::stdin(),
925                    #[cfg(feature = "tokio")]
926                    tokio::io::stdin(),
927                    BufferMode::Line,
928                    Some(Transcoder::native()),
929                )
930            })
931    }
932
933    pub fn current_output_port(&self) -> Port {
934        self.dyn_stack
935            .iter()
936            .rev()
937            .find_map(|elem| match elem {
938                DynStackElem::CurrentOutputPort(port) => Some(port.clone()),
939                _ => None,
940            })
941            .unwrap_or_else(|| {
942                Port::new(
943                    "<stdout>",
944                    #[cfg(not(feature = "async"))]
945                    std::io::stdout(),
946                    #[cfg(feature = "tokio")]
947                    tokio::io::stdout(),
948                    // TODO: Probably should change this to line, but that
949                    // doesn't play nicely with rustyline
950                    BufferMode::None,
951                    Some(Transcoder::native()),
952                )
953            })
954    }
955
956    pub(crate) fn push_dyn_stack(&mut self, elem: DynStackElem) {
957        self.dyn_stack.push(elem);
958    }
959
960    pub(crate) fn pop_dyn_stack(&mut self) -> Option<DynStackElem> {
961        self.dyn_stack.pop()
962    }
963
964    pub(crate) fn dyn_stack_last(&self) -> Option<&DynStackElem> {
965        self.dyn_stack.last()
966    }
967
968    pub(crate) fn dyn_stack_len(&self) -> usize {
969        self.dyn_stack.len()
970    }
971
972    pub(crate) fn dyn_stack_is_empty(&self) -> bool {
973        self.dyn_stack.is_empty()
974    }
975}
976
977impl Default for ContBarrier<'_> {
978    fn default() -> Self {
979        Self::new()
980    }
981}
982
983impl<'a, 'b, 'c> From<&'b mut ContBarrier<'a>> for ContBarrier<'c>
984where
985    'b: 'c,
986{
987    fn from(value: &'b mut ContBarrier<'a>) -> Self {
988        let mut new_barrier = ContBarrier::from(value.save());
989        for (key, value) in value.params.iter_mut() {
990            new_barrier.params.insert(*key, value.deref_mut());
991        }
992        new_barrier
993    }
994}
995
996/// A copy of [`DynamicState`] without mutable parameters
997#[derive(Clone, Debug, Trace)]
998pub struct SavedDynamicState {
999    id: usize,
1000    dyn_stack: Vec<DynStackElem>,
1001    cont_marks: Vec<HashMap<Symbol, Value>>,
1002}
1003
1004impl SavedDynamicState {
1005    pub(crate) fn dyn_stack_get(&self, idx: usize) -> Option<&DynStackElem> {
1006        self.dyn_stack.get(idx)
1007    }
1008
1009    pub(crate) fn dyn_stack_last(&self) -> Option<&DynStackElem> {
1010        self.dyn_stack.last()
1011    }
1012
1013    pub(crate) fn dyn_stack_len(&self) -> usize {
1014        self.dyn_stack.len()
1015    }
1016
1017    pub(crate) fn dyn_stack_is_empty(&self) -> bool {
1018        self.dyn_stack.is_empty()
1019    }
1020}
1021
1022impl From<SavedDynamicState> for ContBarrier<'_> {
1023    fn from(value: SavedDynamicState) -> Self {
1024        ContBarrier {
1025            dyn_stack: value.dyn_stack,
1026            cont_marks: value.cont_marks,
1027            ..Default::default()
1028        }
1029    }
1030}
1031
1032impl SchemeCompatible for SavedDynamicState {
1033    fn rtd() -> Arc<RecordTypeDescriptor> {
1034        rtd!(name: "$dynamic-state", sealed: true, opaque: true)
1035    }
1036}
1037
1038#[derive(Clone, Debug, PartialEq, Trace)]
1039pub(crate) enum DynStackElem {
1040    Prompt(Prompt),
1041    PromptBarrier(PromptBarrier),
1042    Winder(Winder),
1043    ExceptionHandler(Procedure),
1044    CurrentInputPort(Port),
1045    CurrentOutputPort(Port),
1046}
1047
1048pub(crate) unsafe extern "C" fn pop_dyn_stack(
1049    _runtime: *mut GcInner<RwLock<RuntimeInner>>,
1050    env: *const Value,
1051    args: *const Value,
1052    barrier: *mut ContBarrier,
1053) -> *mut Application {
1054    unsafe {
1055        // env[0] is the continuation
1056        let k: Procedure = env.as_ref().unwrap().clone().try_into().unwrap();
1057
1058        barrier.as_mut().unwrap_unchecked().pop_dyn_stack();
1059
1060        let args = k.collect_args(args);
1061        let app = Application::new(k, args);
1062
1063        Box::into_raw(Box::new(app))
1064    }
1065}
1066
1067#[cps_bridge(def = "print-trace", lib = "(rnrs base builtins (6))")]
1068pub fn print_trace(
1069    _runtime: &Runtime,
1070    _env: &[Value],
1071    _args: &[Value],
1072    _rest_args: &[Value],
1073    barrier: &mut ContBarrier,
1074    k: Value,
1075) -> Result<Application, Exception> {
1076    println!(
1077        "trace: {:#?}",
1078        barrier.current_marks(Symbol::intern("trace"))
1079    );
1080    Ok(Application::new(k.try_into()?, vec![]))
1081}
1082
1083////////////////////////////////////////////////////////////////////////////////
1084//
1085// Call with current continuation
1086//
1087
1088#[cps_bridge(
1089    def = "call-with-current-continuation proc",
1090    lib = "(rnrs base builtins (6))"
1091)]
1092pub fn call_with_current_continuation(
1093    runtime: &Runtime,
1094    _env: &[Value],
1095    args: &[Value],
1096    _rest_args: &[Value],
1097    barrier: &mut ContBarrier,
1098    k: Value,
1099) -> Result<Application, Exception> {
1100    let [proc] = args else { unreachable!() };
1101    let proc: Procedure = proc.clone().try_into()?;
1102
1103    let (req_args, variadic) = {
1104        let k: Procedure = k.clone().try_into()?;
1105        k.get_formals()
1106    };
1107
1108    let barrier = Value::from(Record::from_rust_type(barrier.save()));
1109
1110    let escape_procedure = Procedure::new(
1111        runtime.clone(),
1112        vec![k.clone(), barrier],
1113        FuncPtr::Bridge(escape_procedure),
1114        req_args,
1115        variadic,
1116    );
1117
1118    let app = Application::new(proc, vec![Value::from(escape_procedure), k]);
1119
1120    Ok(app)
1121}
1122
1123/// Prepare the continuation for call/cc. Clones the continuation environment
1124/// and creates a closure that calls the appropriate winders.
1125#[cps_bridge]
1126fn escape_procedure(
1127    runtime: &Runtime,
1128    env: &[Value],
1129    args: &[Value],
1130    rest_args: &[Value],
1131    barrier: &mut ContBarrier,
1132    _k: Value,
1133) -> Result<Application, Exception> {
1134    // env[0] is the continuation
1135    let k = env[0].clone();
1136
1137    // env[1] is the dyn stack of the continuation
1138    let saved_barrier_val = env[1].clone();
1139    let saved_barrier = saved_barrier_val
1140        .try_to_rust_type::<SavedDynamicState>()
1141        .unwrap();
1142    let saved_barrier_read = saved_barrier.as_ref();
1143
1144    if saved_barrier_read.id != barrier.id {
1145        return Err(Exception::error("attempt to cross continuation barrier"));
1146    }
1147
1148    barrier.cont_marks = saved_barrier_read.cont_marks.clone();
1149
1150    // Clone the continuation
1151    let k: Procedure = k.try_into().unwrap();
1152
1153    let args = args.iter().chain(rest_args).cloned().collect::<Vec<_>>();
1154
1155    // Simple optimization: check if we're in the same dyn stack
1156    if barrier.dyn_stack_len() == saved_barrier_read.dyn_stack_len()
1157        && barrier.dyn_stack_last() == saved_barrier_read.dyn_stack_last()
1158    {
1159        Ok(Application::new(k, args))
1160    } else {
1161        let args = Value::from(args);
1162        let k = barrier.new_k(
1163            runtime.clone(),
1164            vec![Value::from(k), args, saved_barrier_val],
1165            unwind,
1166            0,
1167            false,
1168        );
1169        Ok(Application::new(k, Vec::new()))
1170    }
1171}
1172
1173unsafe extern "C" fn unwind(
1174    runtime: *mut GcInner<RwLock<RuntimeInner>>,
1175    env: *const Value,
1176    _args: *const Value,
1177    barrier: *mut ContBarrier,
1178) -> *mut Application {
1179    unsafe {
1180        // env[0] is the ultimate continuation
1181        let k = env.as_ref().unwrap().clone();
1182
1183        // env[1] are the arguments to pass to k
1184        let args = env.add(1).as_ref().unwrap().clone();
1185
1186        // env[2] is the stack we are trying to reach
1187        let dest_stack_val = env.add(2).as_ref().unwrap().clone();
1188        let dest_stack = dest_stack_val
1189            .clone()
1190            .try_to_rust_type::<SavedDynamicState>()
1191            .unwrap();
1192        let dest_stack_read = dest_stack.as_ref();
1193
1194        let barrier = barrier.as_mut().unwrap_unchecked();
1195
1196        while !barrier.dyn_stack_is_empty()
1197            && (barrier.dyn_stack_len() > dest_stack_read.dyn_stack_len()
1198                || barrier.dyn_stack_last()
1199                    != dest_stack_read.dyn_stack_get(barrier.dyn_stack_len() - 1))
1200        {
1201            match barrier.pop_dyn_stack() {
1202                None => {
1203                    break;
1204                }
1205                Some(DynStackElem::Winder(winder)) => {
1206                    // Call the out winder while unwinding
1207                    let app = Application::new(
1208                        winder.out_thunk,
1209                        vec![Value::from(barrier.new_k(
1210                            Runtime::from_raw_inc_rc(runtime),
1211                            vec![k, args, dest_stack_val],
1212                            unwind,
1213                            0,
1214                            false,
1215                        ))],
1216                    );
1217                    return Box::into_raw(Box::new(app));
1218                }
1219                _ => (),
1220            };
1221        }
1222
1223        // Begin winding
1224        let app = Application::new(
1225            barrier.new_k(
1226                Runtime::from_raw_inc_rc(runtime),
1227                vec![k, args, dest_stack_val, Value::from(false)],
1228                wind,
1229                0,
1230                false,
1231            ),
1232            Vec::new(),
1233        );
1234
1235        Box::into_raw(Box::new(app))
1236    }
1237}
1238
1239unsafe extern "C" fn wind(
1240    runtime: *mut GcInner<RwLock<RuntimeInner>>,
1241    env: *const Value,
1242    _args: *const Value,
1243    barrier: *mut ContBarrier,
1244) -> *mut Application {
1245    unsafe {
1246        // env[0] is the ultimate continuation
1247        let k = env.as_ref().unwrap().clone();
1248
1249        // env[1] are the arguments to pass to k
1250        let args = env.add(1).as_ref().unwrap().clone();
1251
1252        // env[2] is the stack we are trying to reach
1253        let dest_stack_val = env.add(2).as_ref().unwrap().clone();
1254        let dest_stack = dest_stack_val
1255            .try_to_rust_type::<SavedDynamicState>()
1256            .unwrap();
1257        let dest_stack_read = dest_stack.as_ref();
1258
1259        let barrier = barrier.as_mut().unwrap_unchecked();
1260
1261        // env[3] is potentially a winder that we should push onto the dyn stack
1262        let winder = env.add(3).as_ref().unwrap().clone();
1263        if winder.is_true() {
1264            let winder = winder.try_to_rust_type::<Winder>().unwrap();
1265            barrier.push_dyn_stack(DynStackElem::Winder(winder.as_ref().clone()));
1266        }
1267
1268        while barrier.dyn_stack_len() < dest_stack_read.dyn_stack_len() {
1269            match dest_stack_read
1270                .dyn_stack_get(barrier.dyn_stack_len())
1271                .cloned()
1272            {
1273                None => {
1274                    break;
1275                }
1276                Some(DynStackElem::Winder(winder)) => {
1277                    // Call the in winder while winding
1278                    let app = Application::new(
1279                        winder.in_thunk.clone(),
1280                        vec![Value::from(barrier.new_k(
1281                            Runtime::from_raw_inc_rc(runtime),
1282                            vec![
1283                                k,
1284                                args,
1285                                dest_stack_val,
1286                                Value::from(Record::from_rust_type(winder)),
1287                            ],
1288                            wind,
1289                            0,
1290                            false,
1291                        ))],
1292                    );
1293                    return Box::into_raw(Box::new(app));
1294                }
1295                Some(elem) => barrier.push_dyn_stack(elem),
1296            }
1297        }
1298
1299        let args: Vector = args.try_into().unwrap();
1300        let args = args.0.vec.read().to_vec();
1301
1302        Box::into_raw(Box::new(Application::new(k.try_into().unwrap(), args)))
1303    }
1304}
1305
1306unsafe extern "C" fn call_consumer_with_values(
1307    runtime: *mut GcInner<RwLock<RuntimeInner>>,
1308    env: *const Value,
1309    args: *const Value,
1310    barrier: *mut ContBarrier,
1311) -> *mut Application {
1312    unsafe {
1313        // env[0] is the consumer
1314        let consumer = env.as_ref().unwrap().clone();
1315        let type_name = consumer.type_name();
1316
1317        let consumer: Procedure = match consumer.try_into() {
1318            Ok(consumer) => consumer,
1319            _ => {
1320                let raised = raise(
1321                    Runtime::from_raw_inc_rc(runtime),
1322                    Exception::invalid_operator(type_name).into(),
1323                    barrier.as_mut().unwrap_unchecked(),
1324                );
1325                return Box::into_raw(Box::new(raised));
1326            }
1327        };
1328
1329        // env[1] is the continuation
1330        let k = env.add(1).as_ref().unwrap().clone();
1331
1332        let mut collected_args: Vec<_> = (0..consumer.0.num_required_args)
1333            .map(|i| args.add(i).as_ref().unwrap().clone())
1334            .collect();
1335
1336        // I hate this constant going back and forth from variadic to list. I have
1337        // to figure out a way to make it consistent
1338        if consumer.0.variadic {
1339            let rest_args = args
1340                .add(consumer.0.num_required_args)
1341                .as_ref()
1342                .unwrap()
1343                .clone();
1344            let mut vec = Vec::new();
1345            list_to_vec(&rest_args, &mut vec);
1346            collected_args.extend(vec);
1347        }
1348
1349        collected_args.push(k);
1350
1351        Box::into_raw(Box::new(Application::new(consumer.clone(), collected_args)))
1352    }
1353}
1354
1355#[cps_bridge(
1356    def = "call-with-values producer consumer",
1357    lib = "(rnrs base builtins (6))"
1358)]
1359pub fn call_with_values(
1360    runtime: &Runtime,
1361    _env: &[Value],
1362    args: &[Value],
1363    _rest_args: &[Value],
1364    barrier: &mut ContBarrier,
1365    k: Value,
1366) -> Result<Application, Exception> {
1367    let [producer, consumer] = args else {
1368        return Err(Exception::wrong_num_of_args(2, args.len()));
1369    };
1370
1371    let producer: Procedure = producer.clone().try_into()?;
1372    let consumer: Procedure = consumer.clone().try_into()?;
1373
1374    // Get the details of the consumer:
1375    let (num_required_args, variadic) = { (consumer.0.num_required_args, consumer.0.variadic) };
1376
1377    let call_consumer_closure = barrier.new_k(
1378        runtime.clone(),
1379        vec![Value::from(consumer), k],
1380        call_consumer_with_values,
1381        num_required_args,
1382        variadic,
1383    );
1384
1385    Ok(Application::new(
1386        producer,
1387        vec![Value::from(call_consumer_closure)],
1388    ))
1389}
1390
1391////////////////////////////////////////////////////////////////////////////////
1392//
1393// Dynamic wind
1394//
1395
1396#[derive(Clone, Debug, Trace, PartialEq)]
1397pub(crate) struct Winder {
1398    pub(crate) in_thunk: Procedure,
1399    pub(crate) out_thunk: Procedure,
1400}
1401
1402impl SchemeCompatible for Winder {
1403    fn rtd() -> Arc<RecordTypeDescriptor> {
1404        rtd!(name: "$winder", sealed: true, opaque: true)
1405    }
1406}
1407
1408#[cps_bridge(def = "dynamic-wind in body out", lib = "(rnrs base builtins (6))")]
1409pub fn dynamic_wind(
1410    runtime: &Runtime,
1411    _env: &[Value],
1412    args: &[Value],
1413    _rest_args: &[Value],
1414    barrier: &mut ContBarrier,
1415    k: Value,
1416) -> Result<Application, Exception> {
1417    let [in_thunk_val, body_thunk_val, out_thunk_val] = args else {
1418        return Err(Exception::wrong_num_of_args(3, args.len()));
1419    };
1420
1421    let in_thunk: Procedure = in_thunk_val.clone().try_into()?;
1422    let _: Procedure = body_thunk_val.clone().try_into()?;
1423
1424    let call_body_thunk_cont = barrier.new_k(
1425        runtime.clone(),
1426        vec![
1427            in_thunk_val.clone(),
1428            body_thunk_val.clone(),
1429            out_thunk_val.clone(),
1430            k,
1431        ],
1432        call_body_thunk,
1433        0,
1434        true,
1435    );
1436
1437    Ok(Application::new(
1438        in_thunk,
1439        vec![Value::from(call_body_thunk_cont)],
1440    ))
1441}
1442
1443pub(crate) unsafe extern "C" fn call_body_thunk(
1444    runtime: *mut GcInner<RwLock<RuntimeInner>>,
1445    env: *const Value,
1446    _args: *const Value,
1447    barrier: *mut ContBarrier,
1448) -> *mut Application {
1449    unsafe {
1450        // env[0] is the in thunk
1451        let in_thunk = env.as_ref().unwrap().clone();
1452
1453        // env[1] is the body thunk
1454        let body_thunk: Procedure = env.add(1).as_ref().unwrap().clone().try_into().unwrap();
1455
1456        // env[2] is the out thunk
1457        let out_thunk = env.add(2).as_ref().unwrap().clone();
1458
1459        // env[3] is k, the continuation
1460        let k = env.add(3).as_ref().unwrap().clone();
1461
1462        let barrier = barrier.as_mut().unwrap_unchecked();
1463
1464        barrier.push_dyn_stack(DynStackElem::Winder(Winder {
1465            in_thunk: in_thunk.clone().try_into().unwrap(),
1466            out_thunk: out_thunk.clone().try_into().unwrap(),
1467        }));
1468
1469        let k = barrier.new_k(
1470            Runtime::from_raw_inc_rc(runtime),
1471            vec![out_thunk, k],
1472            call_out_thunks,
1473            0,
1474            true,
1475        );
1476
1477        let app = Application::new(body_thunk, vec![Value::from(k)]);
1478
1479        Box::into_raw(Box::new(app))
1480    }
1481}
1482
1483pub(crate) unsafe extern "C" fn call_out_thunks(
1484    runtime: *mut GcInner<RwLock<RuntimeInner>>,
1485    env: *const Value,
1486    args: *const Value,
1487    barrier: *mut ContBarrier,
1488) -> *mut Application {
1489    unsafe {
1490        // env[0] is the out thunk
1491        let out_thunk: Procedure = env.as_ref().unwrap().clone().try_into().unwrap();
1492
1493        // env[1] is k, the remaining continuation
1494        let k = env.add(1).as_ref().unwrap().clone();
1495
1496        // args[0] is the result of the body thunk
1497        let body_thunk_res = args.as_ref().unwrap().clone();
1498
1499        let barrier = barrier.as_mut().unwrap_unchecked();
1500        barrier.pop_dyn_stack();
1501
1502        let cont = barrier.new_k(
1503            Runtime::from_raw_inc_rc(runtime),
1504            vec![body_thunk_res, k],
1505            forward_body_thunk_result,
1506            0,
1507            true,
1508        );
1509
1510        let app = Application::new(out_thunk, vec![Value::from(cont)]);
1511
1512        Box::into_raw(Box::new(app))
1513    }
1514}
1515
1516unsafe extern "C" fn forward_body_thunk_result(
1517    _runtime: *mut GcInner<RwLock<RuntimeInner>>,
1518    env: *const Value,
1519    _args: *const Value,
1520    _barrier: *mut ContBarrier,
1521) -> *mut Application {
1522    unsafe {
1523        // env[0] is the result of the body thunk
1524        let body_thunk_res = env.as_ref().unwrap().clone();
1525        // env[1] is k, the continuation.
1526        let k: Procedure = env.add(1).as_ref().unwrap().clone().try_into().unwrap();
1527
1528        let mut args = Vec::new();
1529        list_to_vec(&body_thunk_res, &mut args);
1530
1531        Box::into_raw(Box::new(Application::new(k, args)))
1532    }
1533}
1534
1535////////////////////////////////////////////////////////////////////////////////
1536//
1537// Prompts and delimited continuations
1538//
1539
1540#[derive(Clone, Debug, PartialEq, Trace)]
1541pub(crate) struct Prompt {
1542    tag: Symbol,
1543    barrier_id: usize,
1544    handler: Procedure,
1545    handler_k: Procedure,
1546}
1547
1548#[derive(Clone, Debug, PartialEq, Trace)]
1549pub(crate) struct PromptBarrier {
1550    barrier_id: usize,
1551    replaced_k: Procedure,
1552}
1553
1554static BARRIER_ID: AtomicUsize = AtomicUsize::new(0);
1555
1556#[cps_bridge(def = "call-with-prompt tag thunk handler", lib = "(prompts)")]
1557pub fn call_with_prompt(
1558    runtime: &Runtime,
1559    _env: &[Value],
1560    args: &[Value],
1561    _rest_args: &[Value],
1562    barrier: &mut ContBarrier,
1563    k: Value,
1564) -> Result<Application, Exception> {
1565    let [tag, thunk, handler] = args else {
1566        unreachable!()
1567    };
1568
1569    let k_proc: Procedure = k.clone().try_into().unwrap();
1570    let (req_args, variadic) = k_proc.get_formals();
1571    let tag: Symbol = tag.clone().try_into().unwrap();
1572
1573    let barrier_id = BARRIER_ID.fetch_add(1, Ordering::Relaxed);
1574
1575    barrier.push_dyn_stack(DynStackElem::Prompt(Prompt {
1576        tag,
1577        handler: handler.clone().try_into().unwrap(),
1578        barrier_id,
1579        handler_k: k.clone().try_into()?,
1580    }));
1581
1582    barrier.push_marks();
1583
1584    let prompt_barrier = Procedure::new(
1585        runtime.clone(),
1586        vec![k],
1587        FuncPtr::PromptBarrier {
1588            barrier_id,
1589            k: pop_dyn_stack,
1590        },
1591        req_args,
1592        variadic,
1593    );
1594
1595    Ok(Application::new(
1596        thunk.clone().try_into().unwrap(),
1597        vec![Value::from(prompt_barrier)],
1598    ))
1599}
1600
1601#[cps_bridge(def = "abort-to-prompt tag . values", lib = "(prompts)")]
1602pub fn abort_to_prompt(
1603    runtime: &Runtime,
1604    _env: &[Value],
1605    args: &[Value],
1606    rest_args: &[Value],
1607    barrier: &mut ContBarrier,
1608    k: Value,
1609) -> Result<Application, Exception> {
1610    let [tag] = args else { unreachable!() };
1611
1612    let unwind_to_prompt = barrier.new_k(
1613        runtime.clone(),
1614        vec![
1615            k,
1616            Value::from(rest_args.to_vec()),
1617            tag.clone(),
1618            Value::from_rust_type(barrier.save()),
1619        ],
1620        unwind_to_prompt,
1621        0,
1622        false,
1623    );
1624
1625    Ok(Application::new(unwind_to_prompt, Vec::new()))
1626}
1627
1628unsafe extern "C" fn unwind_to_prompt(
1629    runtime: *mut GcInner<RwLock<RuntimeInner>>,
1630    env: *const Value,
1631    _args: *const Value,
1632    barrier: *mut ContBarrier,
1633) -> *mut Application {
1634    unsafe {
1635        // env[0] is continuation
1636        let k = env.as_ref().unwrap().clone();
1637        // env[1] is the arguments passed to abort-to-prompt:
1638        let args = env.add(1).as_ref().unwrap().clone();
1639        // env[2] is the prompt tag
1640        let tag: Symbol = env.add(2).as_ref().unwrap().clone().try_into().unwrap();
1641        // env[3] is the saved dyn stack
1642        let saved_barrier = env.add(3).as_ref().unwrap().clone();
1643
1644        let barrier = barrier.as_mut().unwrap_unchecked();
1645
1646        loop {
1647            let app = match barrier.pop_dyn_stack() {
1648                None => {
1649                    // If the stack is empty, we should return the error
1650                    Application::halt_err(Value::from(Exception::error(format!(
1651                        "no prompt tag {tag} found"
1652                    ))))
1653                }
1654                Some(DynStackElem::Prompt(Prompt {
1655                    tag: prompt_tag,
1656                    barrier_id,
1657                    handler,
1658                    handler_k,
1659                })) if prompt_tag == tag => {
1660                    let saved_barrier = saved_barrier
1661                        .try_to_rust_type::<SavedDynamicState>()
1662                        .unwrap();
1663                    let prompt_delimited_barrier = SavedDynamicState {
1664                        id: saved_barrier.id,
1665                        dyn_stack: saved_barrier.as_ref().dyn_stack[barrier.dyn_stack_len() + 1..]
1666                            .to_vec(),
1667                        cont_marks: saved_barrier.cont_marks.clone(),
1668                    };
1669                    let (req_args, var) = {
1670                        let k_proc: Procedure = k.clone().try_into().unwrap();
1671                        k_proc.get_formals()
1672                    };
1673                    // Construct the arguments
1674                    let mut handler_args = vec![Value::from(Procedure::new(
1675                        Runtime::from_raw_inc_rc(runtime),
1676                        vec![
1677                            k,
1678                            Value::from(barrier_id),
1679                            Value::from_rust_type(prompt_delimited_barrier),
1680                        ],
1681                        FuncPtr::Bridge(delimited_continuation),
1682                        req_args,
1683                        var,
1684                    ))];
1685                    handler_args.extend(args.cast_to_scheme_type::<Vector>().unwrap().iter());
1686                    handler_args.push(Value::from(handler_k));
1687                    Application::new(handler, handler_args)
1688                }
1689                Some(DynStackElem::Winder(winder)) => {
1690                    // If this is a winder, we should call the out winder while unwinding
1691                    Application::new(
1692                        winder.out_thunk,
1693                        vec![Value::from(barrier.new_k(
1694                            Runtime::from_raw_inc_rc(runtime),
1695                            vec![k, args, Value::from(tag), saved_barrier],
1696                            unwind_to_prompt,
1697                            0,
1698                            false,
1699                        ))],
1700                    )
1701                }
1702                _ => continue,
1703            };
1704            return Box::into_raw(Box::new(app));
1705        }
1706    }
1707}
1708
1709#[cps_bridge]
1710fn delimited_continuation(
1711    runtime: &Runtime,
1712    env: &[Value],
1713    args: &[Value],
1714    rest_args: &[Value],
1715    barrier: &mut ContBarrier,
1716    k: Value,
1717) -> Result<Application, Exception> {
1718    // env[0] is the delimited continuation
1719    let dk = env[0].clone();
1720
1721    // env[1] is the barrier Id
1722    let barrier_id: usize = env[1].try_to_scheme_type()?;
1723
1724    // env[2] is the dyn stack of the continuation
1725    let saved_barrier_val = env[2].clone();
1726    let saved_barrier = saved_barrier_val
1727        .try_to_rust_type::<SavedDynamicState>()
1728        .unwrap();
1729    let saved_barrier_read = saved_barrier.as_ref();
1730    // Restore continuation marks
1731    barrier.cont_marks = saved_barrier_read.cont_marks.clone();
1732
1733    let args = args.iter().chain(rest_args).cloned().collect::<Vec<_>>();
1734
1735    barrier.push_dyn_stack(DynStackElem::PromptBarrier(PromptBarrier {
1736        barrier_id,
1737        replaced_k: k.try_into()?,
1738    }));
1739
1740    // Simple optimization: if the saved dyn stack is empty, we
1741    // can just call the delimited continuation
1742    if saved_barrier_read.dyn_stack_is_empty() {
1743        Ok(Application::new(dk.try_into()?, args))
1744    } else {
1745        let args = Value::from(args);
1746        let k = barrier.new_k(
1747            runtime.clone(),
1748            vec![
1749                dk,
1750                args,
1751                saved_barrier_val,
1752                Value::from(0),
1753                Value::from(false),
1754            ],
1755            wind_delim,
1756            0,
1757            false,
1758        );
1759        Ok(Application::new(k, Vec::new()))
1760    }
1761}
1762
1763unsafe extern "C" fn wind_delim(
1764    runtime: *mut GcInner<RwLock<RuntimeInner>>,
1765    env: *const Value,
1766    _args: *const Value,
1767    barrier: *mut ContBarrier,
1768) -> *mut Application {
1769    unsafe {
1770        // env[0] is the ultimate continuation
1771        let k = env.as_ref().unwrap().clone();
1772
1773        // env[1] are the arguments to pass to k
1774        let args = env.add(1).as_ref().unwrap().clone();
1775
1776        // env[2] is the stack we are trying to reach
1777        let dest_stack_val = env.add(2).as_ref().unwrap().clone();
1778        let dest_stack = dest_stack_val
1779            .try_to_rust_type::<SavedDynamicState>()
1780            .unwrap();
1781        let dest_stack_read = dest_stack.as_ref();
1782
1783        // env[3] is the index into the dest stack we're at
1784        let mut idx: usize = env.add(3).as_ref().unwrap().cast_to_scheme_type().unwrap();
1785
1786        let barrier = barrier.as_mut().unwrap_unchecked();
1787
1788        // env[4] is potentially a winder that we should push onto the dyn stack
1789        let winder = env.add(4).as_ref().unwrap().clone();
1790        if winder.is_true() {
1791            let winder = winder.try_to_rust_type::<Winder>().unwrap();
1792            barrier.push_dyn_stack(DynStackElem::Winder(winder.as_ref().clone()));
1793        }
1794
1795        while let Some(elem) = dest_stack_read.dyn_stack_get(idx) {
1796            idx += 1;
1797
1798            if let DynStackElem::Winder(winder) = elem {
1799                // Call the in winder while winding
1800                let app = Application::new(
1801                    winder.in_thunk.clone(),
1802                    vec![Value::from(barrier.new_k(
1803                        Runtime::from_raw_inc_rc(runtime),
1804                        vec![
1805                            k,
1806                            args,
1807                            dest_stack_val,
1808                            Value::from(Record::from_rust_type(winder.clone())),
1809                        ],
1810                        wind,
1811                        0,
1812                        false,
1813                    ))],
1814                );
1815                return Box::into_raw(Box::new(app));
1816            }
1817            barrier.push_dyn_stack(elem.clone());
1818        }
1819
1820        let args: Vector = args.try_into().unwrap();
1821        let args = args.0.vec.read().to_vec();
1822
1823        Box::into_raw(Box::new(Application::new(k.try_into().unwrap(), args)))
1824    }
1825}