Skip to main content

scheme_rs/
records.rs

1//! Records (also known as structs).
2//!
3//! [`Records`](Record) are the mechanism by which new types are introduced to
4//! scheme and the method by which custom Rust types are stored and accessible
5//! to scheme code.
6//!
7//! Each records is described by its [`RecordTypeDescriptor`], which includes
8//! the names of its name and fields among other properties.
9//!
10//! # Implementing [`SchemeCompatible`]
11//!
12//! Any type that implements [`Trace`] and [`Debug`](std::fmt::Debug) is
13//! eligible to implement `SchemeCompatible`. Once this criteria is fulfilled,
14//! we first need to use the [`rtd`] proc macro to fill in the type descriptor.
15//!
16//! For example, let's say that we have `Enemy` struct that we want to have two
17//! immutable fields and one mutable field:
18//!
19//! ```rust
20//! # use std::sync::Mutex;
21//! # use scheme_rs::gc::Trace;
22//! #[derive(Trace, Debug)]
23//! struct Enemy {
24//!   // pos_x and pos_y will be immutable
25//!   pos_x: f64,
26//!   pos_y: f64,
27//!   // health will be mutable (thus the mutex)
28//!   health: Mutex<f64>,
29//! }
30//! ```
31//!
32//! We can now fill in the `rtd` for the type:
33//!
34//! ```rust
35//! # use std::sync::{Arc, Mutex};
36//! # use scheme_rs::{gc::Trace, records::{rtd, SchemeCompatible, RecordTypeDescriptor},
37//! # exceptions::Exception };
38//! # #[derive(Debug, Trace)]
39//! # struct Enemy {
40//! #   pos_x: f64,
41//! #   pos_y: f64,
42//! #   health: Mutex<f64>,
43//! # }
44//! impl SchemeCompatible for Enemy {
45//!     fn rtd() -> Arc<RecordTypeDescriptor> {
46//!         rtd!(
47//!             name: "enemy",
48//!             fields: [ "pos-x", "pos-y", mutable("health") ],
49//!             constructor: |pos_x, pos_y, health| {
50//!                 Ok(Enemy {
51//!                     pos_x: pos_x.try_to_scheme_type()?,
52//!                     pos_y: pos_y.try_to_scheme_type()?,
53//!                     health: Mutex::new(health.try_to_scheme_type()?),
54//!                 })
55//!             }
56//!         )
57//!     }
58//! }
59//! ```
60//!
61//! It's important to note that you need to provide an argument in the
62//! constructor for every field specified in `fields` and every parent field;
63//! however, this does not preclude you from omitting fields that are present in
64//! your data type from the `fields` list.
65//!
66//! Technically, [`rtd`](SchemeCompatible::rtd) is the only required method to
67//! implement `SchemeCompatible`, but since we populated `fields` it will be
68//! possible for the [`get_field`](SchemeCompatible::get_field) and
69//! [`set_field`](SchemeCompatible::set_field) functions to be called, which by
70//! default panic.
71//!
72//! Thus, we need to provide getters and setters for each field. We only need to
73//! provide setters for the mutable fields. Fields are indexed by their position
74//! in the `fields` array passed to `rtd`:
75//!
76//! ```rust
77//! # use std::sync::{Arc, Mutex};
78//! # use scheme_rs::{gc::Trace, value::Value, records::{rtd, SchemeCompatible, RecordTypeDescriptor}, exceptions::Exception};
79//! # #[derive(Debug, Trace)]
80//! # struct Enemy {
81//! #   pos_x: f64,
82//! #   pos_y: f64,
83//! #   health: Mutex<f64>,
84//! # }
85//! impl SchemeCompatible for Enemy {
86//! #    fn rtd() -> Arc<RecordTypeDescriptor> {
87//! #        rtd!(name: "enemy", sealed: true)
88//! #    }
89//!     fn get_field(&self, k: usize) -> Result<Value, Exception> {
90//!         match k {
91//!             0 => Ok(Value::from(self.pos_x)),
92//!             1 => Ok(Value::from(self.pos_y)),
93//!             2 => Ok(Value::from(*self.health.lock().unwrap())),
94//!             _ => Err(Exception::invalid_record_index(k)),
95//!         }
96//!     }
97//!
98//!     fn set_field(&self, k: usize, new_health: Value) -> Result<(), Exception> {
99//!         if k != 2 { return Err(Exception::invalid_record_index(k)); }
100//!         let new_health = f64::try_from(new_health)?;
101//!         *self.health.lock().unwrap() = new_health;
102//!         Ok(())
103//!     }
104//! }
105//! ```
106//!
107//! ## Expressing subtyping relationships
108//!
109//! It is possible to express the classic child/parent relationship in structs
110//! by embedding the parent in the child and implementing the
111//! [`extract_embedded_record`](SchemeCompatible::extract_embedded_record)
112//! function with the [`into_scheme_compatible`] function:
113//!
114//! ```rust
115//! # use std::sync::Arc;
116//! # use scheme_rs::{gc::{Trace, Gc}, value::Value, records::{rtd, SchemeCompatible, RecordTypeDescriptor, into_scheme_compatible}, exceptions::Exception};
117//! # #[derive(Debug, Trace)]
118//! # struct Enemy {
119//! #   pos_x: f64,
120//! #   pos_y: f64,
121//! #   health: f64,
122//! # }
123//! # impl SchemeCompatible for Enemy {
124//! #    fn rtd() -> Arc<RecordTypeDescriptor> {
125//! #        rtd!(name: "enemy", sealed: true)
126//! #    }
127//! # }
128//! #[derive(Debug, Trace)]
129//! struct SpecialEnemy {
130//!     parent: Gc<Enemy>,
131//!     special: u64,
132//! }
133//!
134//! impl SchemeCompatible for SpecialEnemy {
135//!     fn rtd() -> Arc<RecordTypeDescriptor> {
136//!         rtd!(
137//!             name: "enemy",
138//!             parent: Enemy,
139//!             fields: ["special"],
140//!             // The constructor must take all of the arguments
141//!             // required by all of the parent objects, in order.
142//!             constructor: |pos_x, pos_y, health, special| {
143//!                 Ok(SpecialEnemy {
144//!                     parent: Gc::new(Enemy {
145//!                         pos_x: pos_x.try_to_scheme_type()?,
146//!                         pos_y: pos_y.try_to_scheme_type()?,
147//!                         health: health.try_to_scheme_type()?,
148//!                     }),
149//!                     special: special.try_to_scheme_type()?,
150//!                 })
151//!             }
152//!         )
153//!     }
154//!
155//!     fn get_field(&self, _k: usize) -> Result<Value, Exception> {
156//!         Ok(Value::from(self.special))
157//!     }
158//!
159//!     fn extract_embedded_record(
160//!         &self,
161//!         rtd: &Arc<RecordTypeDescriptor>
162//!     ) -> Option<Gc<dyn SchemeCompatible>> {
163//!         Enemy::rtd()
164//!             .is_subtype_of(rtd)
165//!             .then(|| into_scheme_compatible(self.parent.clone()))
166//!     }
167//! }
168//! ```
169//!
170//! ## Defining Rust types as Scheme records
171//!
172//! There is still a little bit more work to do in order to have our Rust type
173//! appear fully as a record in scheme. First, we can use the `lib` keyword in
174//! the `rtd!` macro to specify a location to put a procedure that returns our
175//! type's rtd:
176//!
177//! ```rust
178//! # use std::sync::Arc;
179//! # use scheme_rs::{gc::Trace, records::{rtd, SchemeCompatible, RecordTypeDescriptor},
180//! # exceptions::Exception };
181//! # #[derive(Debug, Trace)]
182//! # struct Enemy {}
183//! impl SchemeCompatible for Enemy {
184//!     fn rtd() -> Arc<RecordTypeDescriptor> {
185//!         rtd!(
186//!             lib: "(enemies (1))",
187//!             // ...
188//! #           name: "enemy",
189//! #           sealed: true, opaque: true,
190//!         )
191//!     }
192//! }
193//! ```
194//!
195//! This will register the procedure `enemy-rtd` in the `(enemies (1))` scheme
196//! library. We can expand that library using the `define-rust-type` macro
197//! provided by the `(rust (1))` library to define enemy fully as a scheme
198//! record:
199//!
200//! ```scheme
201//! (library (enemies (1))
202//!  (export enemy make-enemy enemy?)
203//!  (import (rust (1)))
204//!
205//!  (define-rust-type enemy (enemy-rtd) make-enemy enemy?))
206//! ```
207
208use std::{
209    any::Any,
210    collections::HashMap,
211    fmt,
212    mem::ManuallyDrop,
213    ptr::NonNull,
214    sync::{Arc, LazyLock, Mutex},
215};
216
217use by_address::ByAddress;
218use parking_lot::RwLock;
219
220use crate::{
221    exceptions::Exception,
222    gc::{Gc, GcInner, Trace},
223    proc::{Application, ContBarrier, FuncPtr, Procedure},
224    registry::{bridge, cps_bridge},
225    runtime::{Runtime, RuntimeInner},
226    symbols::Symbol,
227    value::{UnpackedValue, Value, ValueType},
228    vectors::Vector,
229};
230
231pub use scheme_rs_macros::rtd;
232
233/// Type declaration for a record.
234#[derive(Trace, Clone)]
235#[repr(align(16))]
236pub struct RecordTypeDescriptor {
237    /// The name of the record.
238    pub name: Symbol,
239    /// Whether or not the record is "sealed". Sealed records cannot be made the
240    /// parent of other records.
241    pub sealed: bool,
242    /// Whether or not the record is "opaque". Opaque records are not considered
243    /// to be records proper and fail the `record?` predicate.
244    pub opaque: bool,
245    /// An optional universal identifier for the record. Prevents the record
246    /// from being "generative," i.e. unique upon each call to
247    /// `define-record-type`.
248    pub uid: Option<Symbol>,
249    /// Whether or not the type being described is a Rust type.
250    pub rust_type: bool,
251    /// The Rust parent of the record type, if it exists.
252    pub rust_parent_constructor: Option<RustParentConstructor>,
253    /// Parent is most recently inserted record type, if one exists.
254    pub inherits: indexmap::IndexSet<ByAddress<Arc<RecordTypeDescriptor>>>,
255    /// The index into `fields` where this record's fields proper begin. All of
256    /// the previous fields belong to a parent.
257    pub field_index_offset: usize,
258    /// The fields of the record, not including any of the ones inherited from
259    /// parents.
260    pub fields: Vec<Field>,
261}
262
263impl RecordTypeDescriptor {
264    pub fn is_base_record_type(&self) -> bool {
265        self.inherits.is_empty()
266    }
267
268    pub fn is_subtype_of(self: &Arc<Self>, rtd: &Arc<Self>) -> bool {
269        Arc::ptr_eq(self, rtd) || self.inherits.contains(&ByAddress(rtd.clone()))
270    }
271
272    pub fn num_fields(&self) -> usize {
273        self.fields.len()
274            + self
275                .inherits
276                .iter()
277                .map(|parent| parent.fields.len())
278                .sum::<usize>()
279    }
280}
281
282impl fmt::Debug for RecordTypeDescriptor {
283    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284        write!(
285            f,
286            "#<rtd name: {} sealed: {} opaque: {} rust: {} ",
287            self.name, self.sealed, self.opaque, self.rust_type,
288        )?;
289        if !self.inherits.is_empty() {
290            let parent = self.inherits.last().unwrap();
291            write!(f, "parent: {} ", parent.name)?;
292        }
293        write!(f, "fields: (")?;
294        for (i, field) in self.fields.iter().enumerate() {
295            if i > 0 {
296                write!(f, " ")?;
297            }
298            field.fmt(f)?;
299        }
300        write!(f, ")>")?;
301        Ok(())
302    }
303}
304
305/// Description of a Record field.
306#[derive(Trace, Clone)]
307pub enum Field {
308    Immutable(Symbol),
309    Mutable(Symbol),
310}
311
312impl Field {
313    fn parse(field: &Value) -> Result<Self, Exception> {
314        let (mutability, field_name) = field.clone().try_into()?;
315        let mutability: Symbol = mutability.try_into()?;
316        let (field_name, _) = field_name.clone().try_into()?;
317        let field_name: Symbol = field_name.try_into()?;
318        match &*mutability.to_str() {
319            "mutable" => Ok(Field::Mutable(field_name)),
320            "immutable" => Ok(Field::Immutable(field_name)),
321            _ => Err(Exception::error(
322                "mutability specifier must be mutable or immutable".to_string(),
323            )),
324        }
325    }
326
327    fn parse_fields(fields: &Value) -> Result<Vec<Self>, Exception> {
328        let fields: Vector = fields.clone().try_into()?;
329        fields.0.vec.read().iter().map(Self::parse).collect()
330    }
331
332    fn name(&self) -> Symbol {
333        match self {
334            Self::Immutable(sym) | Self::Mutable(sym) => *sym,
335        }
336    }
337}
338
339impl fmt::Debug for Field {
340    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341        match self {
342            Self::Immutable(sym) => write!(f, "(immutable {sym})"),
343            Self::Mutable(sym) => write!(f, "(mutable {sym})"),
344        }
345    }
346}
347
348type NonGenerativeStore = LazyLock<Arc<Mutex<HashMap<Symbol, Arc<RecordTypeDescriptor>>>>>;
349
350static NONGENERATIVE: NonGenerativeStore = LazyLock::new(|| Arc::new(Mutex::new(HashMap::new())));
351
352#[bridge(
353    name = "make-record-type-descriptor",
354    lib = "(rnrs records procedural (6))"
355)]
356pub fn make_record_type_descriptor(
357    name: &Value,
358    parent: &Value,
359    uid: &Value,
360    sealed: &Value,
361    opaque: &Value,
362    fields: &Value,
363) -> Result<Vec<Value>, Exception> {
364    let uid: Option<Symbol> = if uid.is_true() {
365        Some(uid.clone().try_into()?)
366    } else {
367        None
368    };
369
370    // If the record is non-generative, check to see if it has already been
371    // instanciated.
372    if let Some(ref uid) = uid
373        && let Some(rtd) = NONGENERATIVE.lock().unwrap().get(uid)
374    {
375        return Ok(vec![Value::from(rtd.clone())]);
376    }
377
378    let name: Symbol = name.clone().try_into()?;
379    let parent: Option<Arc<RecordTypeDescriptor>> = parent
380        .is_true()
381        .then(|| parent.clone().try_into())
382        .transpose()?;
383    let inherits = if let Some(parent) = parent {
384        let mut inherits = parent.inherits.clone();
385        inherits.insert(ByAddress(parent));
386        inherits
387    } else {
388        indexmap::IndexSet::new()
389    };
390    let field_index_offset = inherits.last().map_or(0, |last_parent| {
391        last_parent.field_index_offset + last_parent.fields.len()
392    });
393    let sealed = sealed.is_true();
394    let opaque = opaque.is_true();
395    let fields = Field::parse_fields(fields)?;
396    let rtd = Arc::new(RecordTypeDescriptor {
397        name,
398        sealed,
399        opaque,
400        uid,
401        rust_type: false,
402        rust_parent_constructor: None,
403        inherits,
404        field_index_offset,
405        fields,
406    });
407
408    if let Some(uid) = uid {
409        NONGENERATIVE.lock().unwrap().insert(uid, rtd.clone());
410    }
411
412    Ok(vec![Value::from(rtd)])
413}
414
415#[bridge(
416    name = "record-type-descriptor?",
417    lib = "(rnrs records procedural (6))"
418)]
419pub fn record_type_descriptor_pred(obj: &Value) -> Result<Vec<Value>, Exception> {
420    Ok(vec![Value::from(
421        obj.type_of() == ValueType::RecordTypeDescriptor,
422    )])
423}
424
425/// A description of a record's constructor.
426#[derive(Trace, Clone)]
427pub struct RecordConstructorDescriptor {
428    parent: Option<Gc<RecordConstructorDescriptor>>,
429    rtd: Arc<RecordTypeDescriptor>,
430    protocol: Procedure,
431}
432
433impl SchemeCompatible for RecordConstructorDescriptor {
434    fn rtd() -> Arc<RecordTypeDescriptor> {
435        rtd!(name: "record-constructor-descriptor", sealed: true, opaque: true)
436    }
437}
438
439impl fmt::Debug for RecordConstructorDescriptor {
440    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
441        Ok(())
442    }
443}
444
445fn make_default_record_constructor_descriptor(
446    runtime: Runtime,
447    rtd: Arc<RecordTypeDescriptor>,
448) -> Gc<RecordConstructorDescriptor> {
449    let parent = rtd.inherits.last().map(|parent| {
450        make_default_record_constructor_descriptor(runtime.clone(), parent.0.clone())
451    });
452    let protocol = Procedure::new(
453        runtime,
454        vec![Value::from(rtd.clone())],
455        FuncPtr::Bridge(default_protocol),
456        1,
457        false,
458    );
459    Gc::new(RecordConstructorDescriptor {
460        parent,
461        rtd,
462        protocol,
463    })
464}
465
466#[cps_bridge(
467    def = "make-record-constructor-descriptor rtd parent-constructor-descriptor protocol",
468    lib = "(rnrs records procedural (6))"
469)]
470pub fn make_record_constructor_descriptor(
471    runtime: &Runtime,
472    _env: &[Value],
473    args: &[Value],
474    _rest_args: &[Value],
475    _barrier: &mut ContBarrier,
476    k: Value,
477) -> Result<Application, Exception> {
478    let k: Procedure = k.try_into()?;
479    let [rtd, parent_rcd, protocol] = args else {
480        unreachable!();
481    };
482
483    let rtd: Arc<RecordTypeDescriptor> = rtd.clone().try_into()?;
484
485    if rtd.rust_type && rtd.rust_parent_constructor.is_none() {
486        return Err(Exception::error(format!(
487            "cannot create a record-constructor-descriptor for rust type without a constructor {}",
488            rtd.name
489        )));
490    }
491
492    let parent_rcd = if parent_rcd.is_true() {
493        let Some(parent_rtd) = rtd.inherits.last() else {
494            return Err(Exception::error("rtd is a base type"));
495        };
496        let parent_rcd = parent_rcd.try_to_rust_type::<RecordConstructorDescriptor>()?;
497        if !Arc::ptr_eq(&parent_rcd.rtd, parent_rtd) {
498            return Err(Exception::error("parent rtd does not match parent rcd"));
499        }
500        Some(parent_rcd)
501    } else if !rtd.is_base_record_type() {
502        Some(make_default_record_constructor_descriptor(
503            runtime.clone(),
504            rtd.inherits.last().unwrap().clone().0,
505        ))
506    } else {
507        None
508    };
509
510    let protocol = if protocol.is_true() {
511        protocol.clone().try_into()?
512    } else {
513        Procedure::new(
514            runtime.clone(),
515            vec![Value::from(rtd.clone())],
516            FuncPtr::Bridge(default_protocol),
517            1,
518            false,
519        )
520    };
521
522    let rcd = RecordConstructorDescriptor {
523        parent: parent_rcd,
524        rtd,
525        protocol,
526    };
527
528    Ok(Application::new(
529        k,
530        vec![Value::from(Record::from_rust_type(rcd))],
531    ))
532}
533
534#[cps_bridge(def = "record-constructor rcd", lib = "(rnrs records procedural (6))")]
535pub fn record_constructor(
536    runtime: &Runtime,
537    _env: &[Value],
538    args: &[Value],
539    _rest_args: &[Value],
540    barrier: &mut ContBarrier,
541    k: Value,
542) -> Result<Application, Exception> {
543    let [rcd] = args else {
544        unreachable!();
545    };
546    let rcd = rcd.try_to_rust_type::<RecordConstructorDescriptor>()?;
547
548    let (protocols, rtds) = rcd_to_protocols_and_rtds(&rcd);
549
550    // See if there is a rust constructor available
551    let rust_constructor = rtds
552        .iter()
553        .rev()
554        .find(|rtd| rtd.rust_parent_constructor.is_some())
555        .map_or_else(|| Value::from(false), |rtd| Value::from(rtd.clone()));
556
557    let protocols = protocols.into_iter().map(Value::from).collect::<Vec<_>>();
558    let rtds = rtds.into_iter().map(Value::from).collect::<Vec<_>>();
559    let chain_protocols = Value::from(barrier.new_k(
560        runtime.clone(),
561        vec![Value::from(protocols), k],
562        chain_protocols,
563        1,
564        false,
565    ));
566
567    Ok(chain_constructors(
568        runtime,
569        &[Value::from(rtds), rust_constructor],
570        &[],
571        &[],
572        barrier,
573        chain_protocols,
574    ))
575}
576
577fn rcd_to_protocols_and_rtds(
578    rcd: &Gc<RecordConstructorDescriptor>,
579) -> (Vec<Procedure>, Vec<Arc<RecordTypeDescriptor>>) {
580    let (mut protocols, mut rtds) = if let Some(ref parent) = rcd.parent {
581        rcd_to_protocols_and_rtds(parent)
582    } else {
583        (Vec::new(), Vec::new())
584    };
585    protocols.push(rcd.protocol.clone());
586    rtds.push(rcd.rtd.clone());
587    (protocols, rtds)
588}
589
590pub(crate) unsafe extern "C" fn chain_protocols(
591    runtime: *mut GcInner<RwLock<RuntimeInner>>,
592    env: *const Value,
593    args: *const Value,
594    barrier: *mut ContBarrier,
595) -> *mut Application {
596    unsafe {
597        // env[0] is a vector of protocols
598        let protocols: Vector = env.as_ref().unwrap().clone().try_into().unwrap();
599        // env[1] is k, the continuation
600        let k = env.add(1).as_ref().unwrap().clone();
601
602        let mut protocols = protocols.0.vec.read().clone();
603        let remaining_protocols = protocols.split_off(1);
604        let curr_protocol: Procedure = protocols[0].clone().try_into().unwrap();
605
606        // If there are no more remaining protocols after the current, call the
607        // protocol with arg[0] and the continuation.
608        if remaining_protocols.is_empty() {
609            return Box::into_raw(Box::new(Application::new(
610                curr_protocol,
611                vec![args.as_ref().unwrap().clone(), k.clone()],
612            )));
613        }
614
615        // Otherwise, turn the remaining chain into the continuation:
616        let new_k = barrier.as_mut().unwrap().new_k(
617            Runtime::from_raw_inc_rc(runtime),
618            vec![Value::from(remaining_protocols), k],
619            chain_protocols,
620            1,
621            false,
622        );
623
624        Box::into_raw(Box::new(Application::new(
625            curr_protocol,
626            vec![args.as_ref().unwrap().clone(), Value::from(new_k)],
627        )))
628    }
629}
630
631#[cps_bridge]
632fn chain_constructors(
633    runtime: &Runtime,
634    env: &[Value],
635    args: &[Value],
636    _rest_args: &[Value],
637    _barrier: &mut ContBarrier,
638    k: Value,
639) -> Result<Application, Exception> {
640    let k: Procedure = k.try_into()?;
641    // env[0] is a vector of RTDs
642    let rtds: Vector = env[0].clone().try_into()?;
643    // env[1] is the possible rust constructor
644    let rust_constructor = env[1].clone();
645    let mut rtds = rtds.0.vec.read().clone();
646    let remaining_rtds = rtds.split_off(1);
647    let curr_rtd: Arc<RecordTypeDescriptor> = rtds[0].clone().try_into()?;
648    let rtds_remain = !remaining_rtds.is_empty();
649    let num_args = curr_rtd.fields.len();
650    let env = if rtds_remain {
651        vec![Value::from(remaining_rtds), rust_constructor]
652    } else {
653        vec![Value::from(curr_rtd), rust_constructor]
654    }
655    .into_iter()
656    // Chain the current environment:
657    .chain(env[2..].iter().cloned())
658    // Chain the arguments passed to this function:
659    .chain(args.iter().cloned())
660    .collect::<Vec<_>>();
661    let next_proc = Procedure::new(
662        runtime.clone(),
663        env,
664        if rtds_remain {
665            FuncPtr::Bridge(chain_constructors)
666        } else {
667            FuncPtr::Bridge(constructor)
668        },
669        num_args,
670        false,
671    );
672    Ok(Application::new(k, vec![Value::from(next_proc)]))
673}
674
675#[cps_bridge]
676fn constructor(
677    _runtime: &Runtime,
678    env: &[Value],
679    args: &[Value],
680    _rest_args: &[Value],
681    _barrier: &mut ContBarrier,
682    k: Value,
683) -> Result<Application, Exception> {
684    let k: Procedure = k.try_into()?;
685    let rtd: Arc<RecordTypeDescriptor> = env[0].clone().try_into()?;
686    // The fields of the record are all of the env variables chained with
687    // the arguments to this function.
688    let mut fields = env[2..]
689        .iter()
690        .cloned()
691        .chain(args.iter().cloned())
692        .collect::<Vec<_>>();
693    // Check for a rust constructor
694    let rust_constructor = env[1].clone();
695    let (rust_parent, fields) = if rust_constructor.is_true() {
696        let rust_rtd: Arc<RecordTypeDescriptor> = rust_constructor.try_into()?;
697        let num_fields: usize = rust_rtd
698            .inherits
699            .iter()
700            .map(|parent| parent.fields.len())
701            .sum();
702        let remaining_fields = fields.split_off(num_fields + rust_rtd.fields.len());
703        (
704            Some((rust_rtd.rust_parent_constructor.unwrap().constructor)(
705                &fields,
706            )?),
707            remaining_fields,
708        )
709    } else {
710        (None, fields)
711    };
712    let record = Value::from(Record(Gc::new(RecordInner {
713        rust_parent,
714        rtd,
715        fields: fields.into_iter().map(RwLock::new).collect(),
716    })));
717    Ok(Application::new(k, vec![record]))
718}
719
720#[cps_bridge]
721fn default_protocol(
722    runtime: &Runtime,
723    env: &[Value],
724    args: &[Value],
725    _rest_args: &[Value],
726    _barrier: &mut ContBarrier,
727    k: Value,
728) -> Result<Application, Exception> {
729    let k: Procedure = k.try_into()?;
730    let rtd: Arc<RecordTypeDescriptor> = env[0].clone().try_into()?;
731    let num_args = rtd.num_fields(); // rtd.field_index_offset + rtd.fields.len();
732
733    let constructor = Procedure::new(
734        runtime.clone(),
735        vec![args[0].clone(), Value::from(rtd)],
736        FuncPtr::Bridge(default_protocol_constructor),
737        num_args,
738        false,
739    );
740
741    Ok(Application::new(k, vec![Value::from(constructor)]))
742}
743
744#[cps_bridge]
745fn default_protocol_constructor(
746    runtime: &Runtime,
747    env: &[Value],
748    args: &[Value],
749    _rest_args: &[Value],
750    barrier: &mut ContBarrier,
751    k: Value,
752) -> Result<Application, Exception> {
753    let constructor: Procedure = env[0].clone().try_into()?;
754    let rtd: Arc<RecordTypeDescriptor> = env[1].clone().try_into()?;
755    let mut args = args.to_vec();
756
757    let k = if let Some(parent) = rtd.inherits.last() {
758        let remaining = args.split_off(parent.num_fields());
759        Value::from(barrier.new_k(
760            runtime.clone(),
761            vec![Value::from(remaining), k],
762            call_constructor_continuation,
763            1,
764            false,
765        ))
766    } else {
767        k
768    };
769
770    args.push(k);
771    Ok(Application::new(constructor, args))
772}
773
774pub(crate) unsafe extern "C" fn call_constructor_continuation(
775    _runtime: *mut GcInner<RwLock<RuntimeInner>>,
776    env: *const Value,
777    args: *const Value,
778    _barrier: *mut ContBarrier,
779) -> *mut Application {
780    unsafe {
781        let constructor: Procedure = args.as_ref().unwrap().clone().try_into().unwrap();
782        let args: Vector = env.as_ref().unwrap().clone().try_into().unwrap();
783        let mut args = args.0.vec.read().clone();
784        let cont = env.add(1).as_ref().unwrap().clone();
785        args.push(cont);
786
787        // Call the constructor
788        Box::into_raw(Box::new(Application::new(constructor, args)))
789    }
790}
791
792/// A Scheme record type. Effectively a tuple of a fixed size array and some type
793/// information.
794#[derive(Trace, Clone)]
795pub struct Record(pub(crate) Gc<RecordInner>);
796
797impl Record {
798    pub fn rtd(&self) -> Arc<RecordTypeDescriptor> {
799        self.0.rtd.clone()
800    }
801
802    /// Convert any Rust type that implements [SchemeCompatible] into an opaque
803    /// record.
804    pub fn from_rust_type<T: SchemeCompatible>(t: T) -> Self {
805        let opaque_parent = Some(into_scheme_compatible(Gc::new(t)));
806        let rtd = T::rtd();
807        Self(Gc::new(RecordInner {
808            rust_parent: opaque_parent,
809            rtd,
810            fields: Vec::new(),
811        }))
812    }
813
814    pub fn from_rust_gc_type<T: SchemeCompatible>(t: Gc<T>) -> Self {
815        let opaque_parent = Some(into_scheme_compatible(t));
816        let rtd = T::rtd();
817        Self(Gc::new(RecordInner {
818            rust_parent: opaque_parent,
819            rtd,
820            fields: Vec::new(),
821        }))
822    }
823
824    /// Attempt to convert the record into a Rust type that implements
825    /// [SchemeCompatible].
826    pub fn cast<T: SchemeCompatible>(&self) -> Option<Gc<T>> {
827        let rust_parent = self.0.rust_parent.as_ref()?;
828
829        // Attempt to extract any embedded records
830        let rtd = T::rtd();
831        let mut t = rust_parent.clone();
832        while let Some(embedded) = { t.extract_embedded_record(&rtd) } {
833            t = embedded;
834        }
835
836        let t = ManuallyDrop::new(t);
837
838        // Second, convert the opaque_parent type into a Gc<dyn Any>
839        let any: NonNull<GcInner<dyn Any + Send + Sync>> = t.ptr;
840        let gc_any = Gc {
841            ptr: any,
842            marker: std::marker::PhantomData,
843        };
844
845        // Then, convert that back into the desired type
846        Gc::downcast::<T>(gc_any).ok()
847    }
848
849    /// Get the kth field of the Record
850    pub fn get_field(&self, k: usize) -> Result<Value, Exception> {
851        self.get_parent_field(&self.rtd(), k)
852    }
853
854    /// Get the kth field of a parent Record
855    pub fn get_parent_field(
856        &self,
857        rtd: &Arc<RecordTypeDescriptor>,
858        k: usize,
859    ) -> Result<Value, Exception> {
860        if !self.0.rtd.is_subtype_of(rtd) {
861            Err(Exception::error(format!("not a subtype of {}", rtd.name)))
862        } else if let Some(mut t) = self.0.rust_parent.clone() {
863            while let Some(embedded) = { t.extract_embedded_record(rtd) } {
864                t = embedded;
865            }
866            t.get_field(rtd.field_index_offset + k)
867        } else {
868            Ok(self.0.fields[rtd.field_index_offset + k].read().clone())
869        }
870    }
871
872    /// Set the kth field of the Record
873    pub fn set_field(&self, k: usize, new_value: Value) -> Result<(), Exception> {
874        self.set_parent_field(&self.rtd(), k, new_value)
875    }
876
877    /// Set the kth field of a parent Record
878    pub fn set_parent_field(
879        &self,
880        rtd: &Arc<RecordTypeDescriptor>,
881        k: usize,
882        new_value: Value,
883    ) -> Result<(), Exception> {
884        if !self.0.rtd.is_subtype_of(rtd) {
885            Err(Exception::error(format!("not a subtype of {}", rtd.name)))
886        } else if let Some(mut t) = self.0.rust_parent.clone() {
887            while let Some(embedded) = { t.extract_embedded_record(rtd) } {
888                t = embedded;
889            }
890            t.set_field(rtd.field_index_offset + k, new_value)
891        } else {
892            *self.0.fields[rtd.field_index_offset + k].write() = new_value;
893            Ok(())
894        }
895    }
896}
897
898impl fmt::Debug for Record {
899    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
900        self.0.fmt(f)
901    }
902}
903
904#[derive(Trace)]
905#[repr(align(16))]
906pub(crate) struct RecordInner {
907    pub(crate) rust_parent: Option<Gc<dyn SchemeCompatible>>,
908    pub(crate) rtd: Arc<RecordTypeDescriptor>,
909    pub(crate) fields: Vec<RwLock<Value>>,
910}
911
912impl fmt::Debug for RecordInner {
913    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
914        write!(f, "#<{}", self.rtd.name)?;
915        if let Some(parent) = &self.rust_parent {
916            write!(f, "{parent:?}")?;
917        }
918        let mut field_names = self
919            .rtd
920            .inherits
921            .iter()
922            .cloned()
923            .chain(Some(ByAddress(self.rtd.clone())))
924            .flat_map(|rtd| rtd.fields.clone());
925        for field in &self.fields {
926            let field = field.read();
927            let name = field_names.next().unwrap().name();
928            write!(f, " {name}: {field:?}")?;
929        }
930        write!(f, ">")
931    }
932}
933
934/// A Rust value that can present itself as a Scheme record.
935pub trait SchemeCompatible: fmt::Debug + Trace + Any + Send + Sync + 'static {
936    /// The Record Type Descriptor of the value. Can be constructed at runtime,
937    /// but cannot change.
938    fn rtd() -> Arc<RecordTypeDescriptor>
939    where
940        Self: Sized;
941
942    /// Extract the embedded record type with the matching record type
943    /// descriptor if it exists.
944    fn extract_embedded_record(
945        &self,
946        _rtd: &Arc<RecordTypeDescriptor>,
947    ) -> Option<Gc<dyn SchemeCompatible>> {
948        None
949    }
950
951    /// Fetch the kth field of the record.
952    fn get_field(&self, k: usize) -> Result<Value, Exception> {
953        Err(Exception::error(format!("invalid record field: {k}")))
954    }
955
956    /// Set the kth field of the record.
957    fn set_field(&self, k: usize, _val: Value) -> Result<(), Exception> {
958        Err(Exception::error(format!("invalid record field: {k}")))
959    }
960}
961
962/// Convenience function for converting a `Gc<T>` into a
963/// `Gc<dyn SchemeCompatible>`.
964///
965/// This isn't as simple as using the `as` keyword in Rust due to the
966/// instability of the `CoerceUnsized` trait.
967pub fn into_scheme_compatible(t: Gc<impl SchemeCompatible>) -> Gc<dyn SchemeCompatible> {
968    // Convert t into a Gc<dyn SchemeCompatible>. This has to be done
969    // manually since [CoerceUnsized] is unstable.
970    let t = ManuallyDrop::new(t);
971    let any: NonNull<GcInner<dyn SchemeCompatible>> = t.ptr;
972    Gc {
973        ptr: any,
974        marker: std::marker::PhantomData,
975    }
976}
977
978#[derive(Copy, Clone, Debug, Trace)]
979pub struct RustParentConstructor {
980    #[trace(skip)]
981    constructor: ParentConstructor,
982}
983
984impl RustParentConstructor {
985    pub fn new(constructor: ParentConstructor) -> Self {
986        Self { constructor }
987    }
988}
989
990type ParentConstructor = fn(&[Value]) -> Result<Gc<dyn SchemeCompatible>, Exception>;
991
992pub(crate) fn is_subtype_of(val: &Value, rt: Arc<RecordTypeDescriptor>) -> Result<bool, Exception> {
993    let UnpackedValue::Record(rec) = val.clone().unpack() else {
994        return Ok(false);
995    };
996    Ok(Arc::ptr_eq(&rec.0.rtd, &rt) || rec.0.rtd.inherits.contains(&ByAddress::from(rt)))
997}
998
999#[cps_bridge]
1000fn record_predicate_fn(
1001    _runtime: &Runtime,
1002    env: &[Value],
1003    args: &[Value],
1004    _rest_args: &[Value],
1005    _barrier: &mut ContBarrier,
1006    k: Value,
1007) -> Result<Application, Exception> {
1008    let k: Procedure = k.try_into()?;
1009    let [val] = args else {
1010        unreachable!();
1011    };
1012    // RTD is the first environment variable:
1013    let rtd: Arc<RecordTypeDescriptor> = env[0].try_to_scheme_type()?;
1014    Ok(Application::new(
1015        k,
1016        vec![Value::from(is_subtype_of(val, rtd)?)],
1017    ))
1018}
1019
1020#[cps_bridge(def = "record-predicate rtd", lib = "(rnrs records procedural (6))")]
1021pub fn record_predicate(
1022    runtime: &Runtime,
1023    _env: &[Value],
1024    args: &[Value],
1025    _rest_args: &[Value],
1026    _barrier: &mut ContBarrier,
1027    k: Value,
1028) -> Result<Application, Exception> {
1029    let k: Procedure = k.try_into()?;
1030    let [rtd] = args else {
1031        unreachable!();
1032    };
1033    // TODO: Check if RTD is a record type.
1034    let pred_fn = Procedure::new(
1035        runtime.clone(),
1036        vec![rtd.clone()],
1037        FuncPtr::Bridge(record_predicate_fn),
1038        1,
1039        false,
1040    );
1041    Ok(Application::new(k, vec![Value::from(pred_fn)]))
1042}
1043
1044#[cps_bridge]
1045fn record_accessor_fn(
1046    _runtime: &Runtime,
1047    env: &[Value],
1048    args: &[Value],
1049    _rest_args: &[Value],
1050    _barrier: &mut ContBarrier,
1051    k: Value,
1052) -> Result<Application, Exception> {
1053    let k: Procedure = k.try_into()?;
1054    let [val] = args else {
1055        unreachable!();
1056    };
1057    let record: Record = val.clone().try_into()?;
1058    // RTD is the first environment variable, field index is the second
1059    let rtd: Arc<RecordTypeDescriptor> = env[0].try_to_scheme_type()?;
1060    if !is_subtype_of(val, rtd.clone())? {
1061        return Err(Exception::error("not a child of this record type"));
1062    }
1063    let idx: usize = env[1].clone().try_into()?;
1064    let val = if let Some(rust_parent) = &record.0.rust_parent
1065        && rtd.rust_type
1066    {
1067        let mut t = rust_parent.clone();
1068        while let Some(embedded) = { t.extract_embedded_record(&rtd) } {
1069            t = embedded;
1070        }
1071        t.get_field(idx)?
1072    } else {
1073        record.0.fields[idx].read().clone()
1074    };
1075    if val.is_undefined() {
1076        return Err(Exception::error(format!(
1077            "failed to get field: {}, {idx}",
1078            rtd.name
1079        )));
1080    }
1081    Ok(Application::new(k, vec![val]))
1082}
1083
1084#[cps_bridge(def = "record-accessor rtd k", lib = "(rnrs records procedural (6))")]
1085pub fn record_accessor(
1086    runtime: &Runtime,
1087    _env: &[Value],
1088    args: &[Value],
1089    _rest_args: &[Value],
1090    _barrier: &mut ContBarrier,
1091    k: Value,
1092) -> Result<Application, Exception> {
1093    let k: Procedure = k.try_into()?;
1094    let [rtd, idx] = args else {
1095        unreachable!();
1096    };
1097    let rtd: Arc<RecordTypeDescriptor> = rtd.clone().try_into()?;
1098    let idx: usize = idx.clone().try_into()?;
1099    if idx >= rtd.fields.len() {
1100        return Err(Exception::error(format!(
1101            "{idx} is out of range 0..{}",
1102            rtd.fields.len()
1103        )));
1104    }
1105    let idx = idx + rtd.field_index_offset;
1106    let accessor_fn = Procedure::new(
1107        runtime.clone(),
1108        vec![Value::from(rtd), Value::from(idx)],
1109        FuncPtr::Bridge(record_accessor_fn),
1110        1,
1111        false,
1112    );
1113    Ok(Application::new(k, vec![Value::from(accessor_fn)]))
1114}
1115
1116#[cps_bridge]
1117fn record_mutator_fn(
1118    _runtime: &Runtime,
1119    env: &[Value],
1120    args: &[Value],
1121    _rest_args: &[Value],
1122    _barrier: &mut ContBarrier,
1123    k: Value,
1124) -> Result<Application, Exception> {
1125    let k: Procedure = k.try_into()?;
1126    let [rec, new_val] = args else {
1127        unreachable!();
1128    };
1129    let record: Record = rec.clone().try_into()?;
1130    // RTD is the first environment variable, field index is the second
1131    let rtd: Arc<RecordTypeDescriptor> = env[0].try_to_scheme_type()?;
1132    if !is_subtype_of(rec, rtd.clone())? {
1133        return Err(Exception::error("not a child of this record type"));
1134    }
1135    let idx: usize = env[1].clone().try_into()?;
1136    if let Some(rust_parent) = &record.0.rust_parent
1137        && rtd.rust_type
1138    {
1139        let mut t = rust_parent.clone();
1140        while let Some(embedded) = { t.extract_embedded_record(&rtd) } {
1141            t = embedded;
1142        }
1143        t.set_field(idx, new_val.clone())?;
1144    } else {
1145        *record.0.fields[idx].write() = new_val.clone();
1146    }
1147    Ok(Application::new(k, vec![]))
1148}
1149
1150#[cps_bridge(def = "record-mutator rtd k", lib = "(rnrs records procedural (6))")]
1151pub fn record_mutator(
1152    runtime: &Runtime,
1153    _env: &[Value],
1154    args: &[Value],
1155    _rest_args: &[Value],
1156    _barrier: &mut ContBarrier,
1157    k: Value,
1158) -> Result<Application, Exception> {
1159    let k: Procedure = k.try_into()?;
1160    let [rtd, idx] = args else {
1161        unreachable!();
1162    };
1163    let rtd: Arc<RecordTypeDescriptor> = rtd.clone().try_into()?;
1164    let idx: usize = idx.clone().try_into()?;
1165    if idx >= rtd.fields.len() {
1166        return Err(Exception::error(format!(
1167            "{idx} is out of range {}",
1168            rtd.fields.len()
1169        )));
1170    }
1171    if matches!(rtd.fields[idx], Field::Immutable(_)) {
1172        return Err(Exception::error(format!("{idx} is immutable")));
1173    }
1174    let idx = idx + rtd.field_index_offset;
1175    let mutator_fn = Procedure::new(
1176        runtime.clone(),
1177        vec![Value::from(rtd), Value::from(idx)],
1178        FuncPtr::Bridge(record_mutator_fn),
1179        2,
1180        false,
1181    );
1182    Ok(Application::new(k, vec![Value::from(mutator_fn)]))
1183}
1184
1185// Inspection library:
1186
1187#[bridge(name = "record?", lib = "(rnrs records inspection (6))")]
1188pub fn record_pred(obj: &Value) -> Result<Vec<Value>, Exception> {
1189    match &*obj.unpacked_ref() {
1190        UnpackedValue::Record(rec) => Ok(vec![Value::from(!rec.0.rtd.opaque)]),
1191        _ => Ok(vec![Value::from(false)]),
1192    }
1193}
1194
1195#[bridge(name = "record-rtd", lib = "(rnrs records inspection (6))")]
1196pub fn record_rtd(record: &Value) -> Result<Vec<Value>, Exception> {
1197    match &*record.unpacked_ref() {
1198        UnpackedValue::Record(rec) if !rec.0.rtd.opaque => Ok(vec![Value::from(rec.0.rtd.clone())]),
1199        _ => Err(Exception::error(
1200            "expected a non-opaque record type".to_string(),
1201        )),
1202    }
1203}
1204
1205#[bridge(name = "record-type-name", lib = "(rnrs records inspection (6))")]
1206pub fn record_type_name(rtd: &Value) -> Result<Vec<Value>, Exception> {
1207    let rtd: Arc<RecordTypeDescriptor> = rtd.clone().try_into()?;
1208    Ok(vec![Value::from(rtd.name)])
1209}
1210
1211#[bridge(name = "record-type-parent", lib = "(rnrs records inspection (6))")]
1212pub fn record_type_parent(rtd: &Value) -> Result<Vec<Value>, Exception> {
1213    let rtd: Arc<RecordTypeDescriptor> = rtd.clone().try_into()?;
1214    if let Some(parent) = rtd.inherits.last() {
1215        Ok(vec![Value::from(parent.0.clone())])
1216    } else {
1217        Ok(vec![Value::from(false)])
1218    }
1219}
1220
1221#[bridge(name = "record-type-uid", lib = "(rnrs records inspection (6))")]
1222pub fn record_type_uid(rtd: &Value) -> Result<Vec<Value>, Exception> {
1223    let rtd: Arc<RecordTypeDescriptor> = rtd.clone().try_into()?;
1224    if let Some(uid) = rtd.uid {
1225        Ok(vec![Value::from(uid)])
1226    } else {
1227        Ok(vec![Value::from(false)])
1228    }
1229}
1230
1231#[bridge(
1232    name = "record-type-generative?",
1233    lib = "(rnrs records inspection (6))"
1234)]
1235pub fn record_type_generative_pred(rtd: &Value) -> Result<Vec<Value>, Exception> {
1236    let rtd: Arc<RecordTypeDescriptor> = rtd.clone().try_into()?;
1237    Ok(vec![Value::from(rtd.uid.is_none())])
1238}
1239
1240#[bridge(name = "record-type-sealed?", lib = "(rnrs records inspection (6))")]
1241pub fn record_type_sealed_pred(rtd: &Value) -> Result<Vec<Value>, Exception> {
1242    let rtd: Arc<RecordTypeDescriptor> = rtd.clone().try_into()?;
1243    Ok(vec![Value::from(rtd.sealed)])
1244}
1245
1246#[bridge(name = "record-type-opaque?", lib = "(rnrs records inspection (6))")]
1247pub fn record_type_opaque_pred(rtd: &Value) -> Result<Vec<Value>, Exception> {
1248    let rtd: Arc<RecordTypeDescriptor> = rtd.clone().try_into()?;
1249    Ok(vec![Value::from(rtd.opaque)])
1250}
1251
1252#[bridge(
1253    name = "record-type-field-names",
1254    lib = "(rnrs records inspection (6))"
1255)]
1256pub fn record_type_field_names(rtd: &Value) -> Result<Vec<Value>, Exception> {
1257    let rtd: Arc<RecordTypeDescriptor> = rtd.clone().try_into()?;
1258    let fields = rtd
1259        .fields
1260        .iter()
1261        .map(Field::name)
1262        .map(Value::from)
1263        .collect::<Vec<_>>();
1264    Ok(vec![Value::from(fields)])
1265}
1266
1267#[bridge(name = "record-field-mutable?", lib = "(rnrs records inspection (6))")]
1268pub fn record_field_mutable_pred(rtd: &Value, k: &Value) -> Result<Vec<Value>, Exception> {
1269    let rtd: Arc<RecordTypeDescriptor> = rtd.clone().try_into()?;
1270    let k: usize = k.try_to_scheme_type()?;
1271
1272    if k >= rtd.fields.len() {
1273        return Err(Exception::invalid_index(k, rtd.fields.len()));
1274    }
1275
1276    Ok(vec![Value::from(matches!(
1277        rtd.fields[k],
1278        Field::Mutable(_)
1279    ))])
1280}