Skip to main content

praxis_runtime/
repr.rs

1//! What a live value records about its own type.
2//!
3//! A `GcRef`'s header names a [`TypeDescriptor`], and for most types that is the
4//! whole story: an `Int` object is an `Int`. For the parameterized types it is
5//! not — one `VEC` descriptor serves every `Vec[T]`, and the element type lives
6//! in the *payload* (§11.2). Recovering a static `Type` from a value therefore
7//! means reading per-instance descriptors out of payloads, which is a raw-pointer
8//! operation this module performs once, safely, so the
9//! [`praxis-repr`](https://docs.rs/praxis-repr) bridge never has to.
10//!
11//! The dispatch is a total match over [`BuiltinTypeId`]: a new built-in is a
12//! compile error here, not a silent wrong answer. That is the same discipline
13//! that makes the *forward* map total, and the two are inverses only because
14//! both are exhaustive.
15
16use crate::GcRef;
17use crate::collections::nullable;
18use crate::descriptor::{BuiltinTypeId, TypeDescriptor};
19
20/// One per-instance type argument a value records.
21///
22/// `descriptor` is what the payload stores — `None` when the collection was
23/// never told its element type (a null element descriptor, which every
24/// `praxis_*_new` wrapper accepts as "unknown"). `sample` is a live value that
25/// argument describes, when the payload holds one; recursing into it recovers
26/// nested arguments a descriptor alone cannot (`Vec[Vec[Int]]`).
27#[derive(Clone, Copy)]
28pub struct InstanceArg {
29    /// The descriptor the payload records for this argument position.
30    pub descriptor: Option<&'static TypeDescriptor>,
31    /// A live value of this argument's type, if the value holds one.
32    pub sample: Option<GcRef>,
33}
34
35/// How much of a value's type its own payload determines.
36pub enum InstanceRepr {
37    /// The descriptor is the whole answer: every scalar, `Unit`, `BitSet`.
38    Complete,
39    /// The type takes arguments, and these are what the payload records, in the
40    /// order the type spells them (`Map[K, V]` yields key then value).
41    Args(Vec<InstanceArg>),
42    /// The value's type is not recoverable from the value. The string says why,
43    /// and is what a `NoRuntimeRepr` reports.
44    Unrecorded(&'static str),
45}
46
47/// Read what `value` records about its own type.
48///
49/// # Safety
50/// `value` must be a live `GcRef` whose payload matches its descriptor.
51#[must_use]
52pub unsafe fn instance_repr(value: GcRef) -> InstanceRepr {
53    let Some(builtin) = value.descriptor().as_builtin() else {
54        return InstanceRepr::Unrecorded("not a built-in type");
55    };
56    // SAFETY: forwarded from this function's contract — each arm reads the
57    // payload its descriptor names.
58    unsafe {
59        match builtin {
60            // Scalars and the two nullary collections are their own answer: a
61            // `BitSet` holds `Int`s and a `Range` yields them, so neither has an
62            // element descriptor to recover.
63            BuiltinTypeId::Unit
64            | BuiltinTypeId::Bool
65            | BuiltinTypeId::Int
66            | BuiltinTypeId::Byte
67            | BuiltinTypeId::Char
68            | BuiltinTypeId::Float
69            | BuiltinTypeId::Text
70            | BuiltinTypeId::BitSet
71            | BuiltinTypeId::Range => InstanceRepr::Complete,
72
73            BuiltinTypeId::Vec => {
74                let p = &*value.payload::<crate::collections::VecPayload>();
75                InstanceRepr::Args(vec![InstanceArg {
76                    descriptor: nullable(p.element_descriptor),
77                    sample: p.items.first().copied(),
78                }])
79            }
80            BuiltinTypeId::Deque => {
81                let p = &*value.payload::<crate::collections::DequePayload>();
82                InstanceRepr::Args(vec![InstanceArg {
83                    descriptor: nullable(p.element_descriptor),
84                    sample: p.items.front().copied(),
85                }])
86            }
87            BuiltinTypeId::Grid => {
88                let p = &*value.payload::<crate::collections::GridPayload>();
89                InstanceRepr::Args(vec![InstanceArg {
90                    descriptor: nullable(p.element_descriptor),
91                    sample: p.items.first().copied(),
92                }])
93            }
94            BuiltinTypeId::Set => {
95                let p = &*value.payload::<crate::maps::SetPayload>();
96                InstanceRepr::Args(vec![InstanceArg {
97                    descriptor: nullable(p.element_descriptor),
98                    sample: p.entries.iter().next().map(|k| k.value()),
99                }])
100            }
101            BuiltinTypeId::MinHeap => {
102                let p = &*value.payload::<crate::heaps::MinHeapPayload>();
103                InstanceRepr::Args(vec![InstanceArg {
104                    descriptor: nullable(p.element_descriptor),
105                    sample: p.items.peek().map(|e| e.0.value),
106                }])
107            }
108            BuiltinTypeId::MaxHeap => {
109                let p = &*value.payload::<crate::heaps::MaxHeapPayload>();
110                InstanceRepr::Args(vec![InstanceArg {
111                    descriptor: nullable(p.element_descriptor),
112                    sample: p.items.peek().map(|e| e.value),
113                }])
114            }
115            BuiltinTypeId::Map => {
116                let p = &*value.payload::<crate::maps::MapPayload>();
117                let entry = p.entries.iter().next();
118                InstanceRepr::Args(vec![
119                    InstanceArg {
120                        descriptor: nullable(p.key_descriptor),
121                        sample: entry.map(|(k, _)| k.value()),
122                    },
123                    InstanceArg {
124                        descriptor: nullable(p.value_descriptor),
125                        sample: entry.map(|(_, v)| *v),
126                    },
127                ])
128            }
129            // `Counter[T]` is unary: its values are always `Int` (§6.2).
130            BuiltinTypeId::Counter => {
131                let p = &*value.payload::<crate::maps::CounterPayload>();
132                InstanceRepr::Args(vec![InstanceArg {
133                    descriptor: nullable(p.key_descriptor),
134                    sample: p.entries.keys().next().map(|k| k.value()),
135                }])
136            }
137            BuiltinTypeId::Tuple => {
138                let p = &*value.payload::<crate::tuples::TuplePayload>();
139                if p.schema.is_null() {
140                    return InstanceRepr::Unrecorded("tuple has no schema");
141                }
142                let schema = &*p.schema;
143                InstanceRepr::Args(
144                    schema
145                        .descriptors
146                        .iter()
147                        .enumerate()
148                        .map(|(i, d)| InstanceArg {
149                            descriptor: nullable(*d),
150                            sample: p.items.get(i).copied(),
151                        })
152                        .collect(),
153                )
154            }
155            // A record/enum object carries its *field* schema, not which named
156            // type it is: two records with the same field descriptors are
157            // indistinguishable here.
158            BuiltinTypeId::Record => {
159                InstanceRepr::Unrecorded("a record value does not record its nominal identity")
160            }
161            BuiltinTypeId::Enum => {
162                InstanceRepr::Unrecorded("an enum value does not record its nominal identity")
163            }
164            BuiltinTypeId::Closure => {
165                InstanceRepr::Unrecorded("a closure records no parameter or result types")
166            }
167            // A `VarCell` is the compiler's mutable slot, not a source type.
168            BuiltinTypeId::VarCell => {
169                InstanceRepr::Unrecorded("a VarCell is a compiler-internal slot, not a source type")
170            }
171        }
172    }
173}