1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
//! Object operation builtin implementations (ADR-006 §2.7.6 / Q8).
//!
//! Wave 5b body migration: `builtin_object_rest` takes `&mut VirtualMachine`
//! (it consults the schema registry to derive a subset schema) plus a
//! `&[KindedSlot]` arg slice and returns `Result<KindedSlot, VMError>`.
//! Heap dispatch goes through `slot.as_heap_value()` + `HeapValue` match,
//! preserving ADR-005 §1's single-discriminator discipline.
use crate::executor::VirtualMachine;
use shape_value::{HeapKind, HeapValue, KindedSlot, NativeKind, TypedObjectStorage, VMError};
use std::sync::Arc;
#[inline]
fn type_error(msg: impl Into<String>) -> VMError {
VMError::RuntimeError(msg.into())
}
impl VirtualMachine {
/// `object_rest(obj, [excluded_keys])` — produce a new object excluding the
/// listed keys. Schema-driven on a `TypedObject` receiver; the subset
/// schema is derived from the schema registry (must be predeclared by
/// the compiler).
pub(in crate::executor) fn builtin_object_rest(
&mut self,
args: &[KindedSlot],
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error("object_rest() requires exactly 2 arguments"));
}
// V3-S5 ckpt-5: extracting exclude keys via the deleted
// `TypedArrayData::String` arm + `HeapValue::TypedArray` arm
// surface-and-stops. Rebuild at ckpt-6 STRICT close per v2-raw
// `TypedArray<*const StringObj>` direct-access target.
match args[1].kind {
NativeKind::Ptr(HeapKind::TypedArray) => {
return Err(VMError::NotImplemented(
"object_rest: SURFACE — V3-S5 ckpt-5 consumer-cascade \
tier 3. The deleted typed-array-data String exclude-keys carrier \
DELETED at ckpt-1..ckpt-4. Rebuild at ckpt-6 STRICT \
close per v2-raw `TypedArray<*const StringObj>` \
direct-access. Refusal #1."
.to_string(),
));
}
_ => {
return Err(type_error(
"object_rest() second argument must be an array",
));
}
}
// Suppress dead-code warnings via fake variable construction.
#[allow(unreachable_code)]
let exclude: std::collections::HashSet<String> = std::collections::HashSet::new();
// Wave 2 Round 4 D4 ckpt-final-prime² (2026-05-14): canonical
// 5-arm receiver-recovery soundness rule for v2-raw TypedObject —
// slot bits are `*const TypedObjectStorage` (NOT
// `Arc::into_raw(Arc<HeapValue>)`). `as_heap_value()` would read
// the storage's HeapHeader bytes as a HeapValue discriminator and
// segfault. Read the raw pointer directly and borrow it for the
// duration of this function (the slot owns the share; we don't
// need to retain).
let receiver_storage_ptr: *const TypedObjectStorage = match args[0].kind {
NativeKind::Ptr(HeapKind::TypedObject) => {
let bits = args[0].slot.raw();
if bits == 0 {
return Err(type_error(
"object_rest() first argument: TypedObject slot bits null",
));
}
bits as *const TypedObjectStorage
}
_ => {
return Err(type_error(
"object_rest() first argument must be an object",
));
}
};
// SAFETY: `receiver_storage_ptr` is a live `*const TypedObjectStorage`
// per the slot-construction-side contract; it's valid for the
// duration of this function (caller's slot holds the strong share).
let receiver_storage: &TypedObjectStorage = unsafe { &*receiver_storage_ptr };
let sid = receiver_storage.schema_id as u32;
// Collect kept field indices before mutable borrow of self.
let kept_indices: Vec<usize> = {
let schema = self.lookup_schema(sid).ok_or_else(|| {
type_error(format!("Schema {} not found", sid))
})?;
schema
.fields
.iter()
.filter(|f| !exclude.contains(&f.name))
.map(|f| f.index as usize)
.collect()
};
let subset_id = self.derive_subset_schema(sid, &exclude)?;
// Build subset slots + heap_mask + field_kinds. Each retained slot
// copies the source bits; for heap slots we bump the matching Arc
// strong-count via per-FieldType clone (the new TypedObjectStorage
// owns its own share).
let orig_slots = &receiver_storage.slots;
let orig_mask = receiver_storage.heap_mask;
let orig_kinds = &receiver_storage.field_kinds;
let mut new_slots: Vec<shape_value::ValueSlot> =
Vec::with_capacity(kept_indices.len());
let mut new_kinds: Vec<NativeKind> = Vec::with_capacity(kept_indices.len());
let mut new_mask: u64 = 0;
for (new_idx, &orig_idx) in kept_indices.iter().enumerate() {
new_slots.push(orig_slots[orig_idx]);
new_kinds.push(orig_kinds[orig_idx]);
if orig_mask & (1u64 << orig_idx) != 0 {
new_mask |= 1u64 << new_idx;
// Bump the source Arc's refcount. Per the per-FieldType
// discipline (ADR-006 §2.4 / §2.5), each kind dictates the
// matching `Arc::increment_strong_count::<T>`. We replicate
// the same pointer in `new_slots`; refcount discipline gets
// enforced when the new TypedObjectStorage drops.
let bits = orig_slots[orig_idx].raw();
if bits != 0 {
unsafe {
match orig_kinds[orig_idx] {
NativeKind::String => {
Arc::increment_strong_count(bits as *const String);
}
NativeKind::Ptr(HeapKind::String) => {
Arc::increment_strong_count(bits as *const String);
}
NativeKind::Ptr(HeapKind::TypedArray) => {
// V3-S5 ckpt-6 STRICT close (2026-05-15):
// slot bits are v2-raw `*mut TypedArray<T>`
// per ADR-006 §2.7.24 Q25.A SUPERSEDED.
// Refcount discipline goes through
// `v2_retain` against the `HeapHeader` at
// offset 0 of the carrier (mirror of
// vm_impl/stack.rs StringV2 / DecimalV2 /
// TypedObject retain dispatch).
let hdr = bits as *const shape_value::v2::heap_header::HeapHeader;
shape_value::v2::refcount::v2_retain(hdr);
}
NativeKind::Ptr(HeapKind::TypedObject) => {
Arc::increment_strong_count(
bits as *const TypedObjectStorage,
);
}
NativeKind::Ptr(HeapKind::HashMap) => {
// Wave 2 Round 3b C2-joint ckpt-2 (2026-05-14):
// bits are `Arc::into_raw(Arc<HashMapKindedRef>)`
// per ADR-006 §2.7.24 Q25.B SUPERSEDED.
Arc::increment_strong_count(
bits as *const shape_value::heap_value::HashMapKindedRef,
);
}
NativeKind::Ptr(HeapKind::Decimal) => {
Arc::increment_strong_count(
bits as *const rust_decimal::Decimal,
);
}
NativeKind::Ptr(HeapKind::BigInt) => {
Arc::increment_strong_count(bits as *const i64);
}
// Other kinds: no Arc payload (Char, Future,
// NativeScalar are inline; Closure has its own
// refcount in OwnedClosureBlock and isn't
// schema-routed today).
_ => {
// No-op for inline / non-Arc kinds. If
// heap_mask was set for one of these, that's
// a construction-side bug; debug_assert in
// tests, silently skip in release.
debug_assert!(
false,
"object_rest: heap_mask set for non-Arc kind {:?}",
orig_kinds[orig_idx]
);
}
}
}
}
}
}
// Wave 2 Round 4 D4 ckpt-1: migrated to v2-raw `_new` + D1's
// `from_typed_object_raw` constructor — no variant signature
// dependency at this site.
let ptr = TypedObjectStorage::_new(
subset_id as u64,
new_slots.into_boxed_slice(),
new_mask,
Arc::from(new_kinds.into_boxed_slice()),
);
Ok(KindedSlot::from_typed_object_raw(ptr))
}
}