Skip to main content

sim_kernel/
class.rs

1//! The class contract: callable factories with subclass relationships.
2//!
3//! This is protocol the libraries implement; the kernel defines the [`Class`]
4//! trait and the subclass test, not concrete class hierarchies.
5
6use crate::{
7    callable::Callable,
8    env::Cx,
9    error::Result,
10    id::{ClassId, Symbol},
11    object::{ClassRef, ReadConstructorRef, ShapeRef, TableRef},
12};
13
14/// Runtime protocol for class objects.
15///
16/// Classes are callable constructor values with stable identity, shape
17/// metadata, read-constructor support, and browseable members.
18pub trait Class: Callable {
19    /// Stable identity of the class.
20    fn id(&self) -> ClassId;
21    /// Display symbol naming the class.
22    fn symbol(&self) -> Symbol;
23    /// Direct parent classes; the default reports no parents.
24    fn parents(&self, _cx: &mut Cx) -> Result<Vec<ClassRef>> {
25        Ok(Vec::new())
26    }
27    /// Whether this class is `expected` or transitively descends from it.
28    fn is_subclass_of(&self, cx: &mut Cx, expected: ClassRef) -> Result<bool>
29    where
30        Self: Sized,
31    {
32        class_is_subclass_of(cx, self, expected)
33    }
34    /// Shape describing the arguments accepted by the constructor.
35    fn constructor_shape(&self, cx: &mut Cx) -> Result<ShapeRef>;
36    /// Shape describing instances produced by the class.
37    fn instance_shape(&self, cx: &mut Cx) -> Result<ShapeRef>;
38    /// Optional read-constructor for decoding instances from data forms.
39    fn read_constructor(&self, cx: &mut Cx) -> Result<Option<ReadConstructorRef>>;
40    /// Browseable member table of the class.
41    fn members(&self, cx: &mut Cx) -> Result<TableRef>;
42}
43
44/// Walk the parent chain of `child` to test whether it is a subclass of
45/// `expected`, returning `false` when either side is not a class object.
46pub fn class_is_subclass_of(cx: &mut Cx, child: &dyn Class, expected: ClassRef) -> Result<bool> {
47    let Some(expected_class) = expected.object().as_class() else {
48        return Ok(false);
49    };
50    if child.id() == expected_class.id() {
51        return Ok(true);
52    }
53    for parent in child.parents(cx)? {
54        let Some(parent) = parent.object().as_class() else {
55            continue;
56        };
57        if parent.id() == expected_class.id() || class_is_subclass_of(cx, parent, expected.clone())?
58        {
59            return Ok(true);
60        }
61    }
62    Ok(false)
63}