use shape_runtime::context::ExecutionContext;
use crate::executor::VirtualMachine;
use shape_value::{HeapKind, KindedSlot, NativeKind, VMError};
#[inline]
fn type_error(msg: impl Into<String>) -> VMError {
VMError::RuntimeError(msg.into())
}
#[cold]
#[inline(never)]
fn ckpt2_surface(op: &'static str, args: &[KindedSlot]) -> VMError {
let receiver_kind = if args.is_empty() {
"<no args>".to_string()
} else {
format!("{:?}", args[0].kind)
};
VMError::NotImplemented(format!(
"{op}: SURFACE — V3-S5 ckpt-2 consumer-cascade tier 1 surface for \
the heap-Arc `Ptr(HeapKind::TypedArray)` set-op arm. \
`TypedArrayData` enum DELETED at ckpt-1 (2026-05-15) per W12-\
typed-array-data-deletion audit §3.5 + ADR-006 §2.7.24 Q25.A \
SUPERSEDED. The previous `Arc<TypedArrayData>` receiver-recovery \
(`as_typed_array`) + per-variant equality helpers \
(`already_seen / lhs_in_rhs / build_from_indices / \
unique_indices`) cascade-broke at the enum deletion site \
(`crates/shape-value/src/heap_value.rs:3944`). Post-deletion \
target is the v2-raw `TypedArray<T>` flat-struct carrier per \
audit §1.2 + §A.3 + §3.1 scalar recipe + §2.2 heap-element \
variants; per-T monomorphization landing across ckpt-3 \
(array_ops/typed_array_methods/iterator_methods/array_sort/\
concat/property_access) + ckpt-4 (TypedBuffer<T> / \
HeapValue::TypedArray arm / HeapKind::TypedArray ordinal) + \
ckpt-5 (wire/json/marshal + 4-table lockstep) + ckpt-6 (JIT \
FFI). The v2-raw `TypedArray<*const StringObj/DecimalObj>` \
path (Wave 2 Agent δ, `set_op_v2_raw_string_decimal`) remains \
live and is independent of `TypedArrayData` — invokable post-\
A2-followup-gate-flip via the per-handler fast-path. Receiver \
kind: {kind}. UNREACHABLE until ckpt-6 STRICT close. REFUSED \
ON SIGHT: TypedArrayData resurrection under any rename \
(Refusal #1, W12 audit §7).",
op = op,
kind = receiver_kind,
))
}
#[derive(Copy, Clone, Eq, PartialEq)]
enum V2RawSetOp {
Union,
Intersect,
Except,
Unique,
}
#[inline]
fn as_v2_raw_string_decimal(
slot: &KindedSlot,
) -> Option<(crate::executor::v2_handlers::v2_array_detect::V2ElemType, u64, u32)> {
use crate::executor::v2_handlers::v2_array_detect::{as_v2_typed_array, V2ElemType};
if slot.kind != NativeKind::Ptr(HeapKind::TypedArray) {
return None;
}
let bits = slot.slot.raw();
let view = as_v2_typed_array(bits, slot.kind)?;
match view.elem_type {
V2ElemType::String | V2ElemType::Decimal => Some((view.elem_type, bits, view.len)),
_ => None,
}
}
fn set_op_v2_raw_string_decimal(
op: V2RawSetOp,
lhs_args: &KindedSlot,
rhs_args: Option<&KindedSlot>,
) -> Result<KindedSlot, VMError> {
use crate::executor::v2_handlers::v2_array_detect::{
stamp_elem_type, V2ElemType, ELEM_TYPE_DECIMAL, ELEM_TYPE_STRING,
};
use shape_value::v2::decimal_obj::DecimalObj;
use shape_value::v2::refcount::v2_retain;
use shape_value::v2::string_obj::StringObj;
use shape_value::v2::typed_array::TypedArray;
use shape_value::ValueSlot;
let (lhs_elem, lhs_bits, lhs_len) = as_v2_raw_string_decimal(lhs_args)
.ok_or_else(|| type_error("set op v2-raw: lhs not a v2-raw String/Decimal array"))?;
let rhs_triple = match rhs_args {
Some(rhs) => {
let (re, rb, rl) = as_v2_raw_string_decimal(rhs)
.ok_or_else(|| type_error("set op v2-raw: rhs not a v2-raw String/Decimal array"))?;
if re != lhs_elem {
return Err(type_error(
"set op v2-raw: lhs/rhs element-type mismatch (String vs Decimal)",
));
}
Some((rb, rl))
}
None => None,
};
fn build_keep<K: PartialEq>(
op: V2RawSetOp,
lhs: &[K],
rhs: Option<&[K]>,
) -> (Vec<u32>, Vec<u32>) {
let mut lhs_keep: Vec<u32> = Vec::new();
let mut rhs_keep: Vec<u32> = Vec::new();
let lhs_dedup = |lhs_keep: &mut Vec<u32>, i: usize| {
if !lhs_keep.iter().any(|&j| lhs[j as usize] == lhs[i]) {
lhs_keep.push(i as u32);
}
};
match op {
V2RawSetOp::Unique => {
for i in 0..lhs.len() {
lhs_dedup(&mut lhs_keep, i);
}
}
V2RawSetOp::Intersect => {
let rhs = rhs.expect("intersect requires rhs");
for i in 0..lhs.len() {
if rhs.iter().any(|x| *x == lhs[i]) {
lhs_dedup(&mut lhs_keep, i);
}
}
}
V2RawSetOp::Except => {
let rhs = rhs.expect("except requires rhs");
for i in 0..lhs.len() {
if !rhs.iter().any(|x| *x == lhs[i]) {
lhs_dedup(&mut lhs_keep, i);
}
}
}
V2RawSetOp::Union => {
let rhs = rhs.expect("union requires rhs");
for i in 0..lhs.len() {
lhs_dedup(&mut lhs_keep, i);
}
for i in 0..rhs.len() {
if lhs_keep.iter().any(|&j| lhs[j as usize] == rhs[i]) {
continue;
}
if !rhs_keep.iter().any(|&j| rhs[j as usize] == rhs[i]) {
rhs_keep.push(i as u32);
}
}
}
}
(lhs_keep, rhs_keep)
}
let out_bits = match lhs_elem {
V2ElemType::String => unsafe {
let lhs_arr = lhs_bits as *const TypedArray<*const StringObj>;
let lhs_keys: Vec<&str> = (0..lhs_len)
.map(|i| StringObj::as_str(TypedArray::<*const StringObj>::get_unchecked(lhs_arr, i)))
.collect();
let rhs_keys_storage: Vec<&str> = match rhs_triple {
Some((rb, rl)) => {
let rhs_arr = rb as *const TypedArray<*const StringObj>;
(0..rl)
.map(|i| StringObj::as_str(TypedArray::<*const StringObj>::get_unchecked(rhs_arr, i)))
.collect()
}
None => Vec::new(),
};
let rhs_keys_opt: Option<&[&str]> = rhs_triple.map(|_| rhs_keys_storage.as_slice());
let (lhs_keep, rhs_keep) = build_keep(op, &lhs_keys, rhs_keys_opt);
let total = lhs_keep.len() + rhs_keep.len();
let out = TypedArray::<*const StringObj>::with_capacity(total as u32);
stamp_elem_type(out as *mut u8, ELEM_TYPE_STRING);
for &i in &lhs_keep {
let p = TypedArray::<*const StringObj>::get_unchecked(lhs_arr, i);
v2_retain(&(*p).header);
TypedArray::<*const StringObj>::push(out, p);
}
if let Some((rb, _)) = rhs_triple {
let rhs_arr = rb as *const TypedArray<*const StringObj>;
for &i in &rhs_keep {
let p = TypedArray::<*const StringObj>::get_unchecked(rhs_arr, i);
v2_retain(&(*p).header);
TypedArray::<*const StringObj>::push(out, p);
}
}
out as u64
},
V2ElemType::Decimal => unsafe {
let lhs_arr = lhs_bits as *const TypedArray<*const DecimalObj>;
let lhs_keys: Vec<rust_decimal::Decimal> = (0..lhs_len)
.map(|i| DecimalObj::value(TypedArray::<*const DecimalObj>::get_unchecked(lhs_arr, i)))
.collect();
let rhs_keys_storage: Vec<rust_decimal::Decimal> = match rhs_triple {
Some((rb, rl)) => {
let rhs_arr = rb as *const TypedArray<*const DecimalObj>;
(0..rl)
.map(|i| DecimalObj::value(TypedArray::<*const DecimalObj>::get_unchecked(rhs_arr, i)))
.collect()
}
None => Vec::new(),
};
let rhs_keys_opt: Option<&[rust_decimal::Decimal]> =
rhs_triple.map(|_| rhs_keys_storage.as_slice());
let (lhs_keep, rhs_keep) = build_keep(op, &lhs_keys, rhs_keys_opt);
let total = lhs_keep.len() + rhs_keep.len();
let out = TypedArray::<*const DecimalObj>::with_capacity(total as u32);
stamp_elem_type(out as *mut u8, ELEM_TYPE_DECIMAL);
for &i in &lhs_keep {
let p = TypedArray::<*const DecimalObj>::get_unchecked(lhs_arr, i);
v2_retain(&(*p).header);
TypedArray::<*const DecimalObj>::push(out, p);
}
if let Some((rb, _)) = rhs_triple {
let rhs_arr = rb as *const TypedArray<*const DecimalObj>;
for &i in &rhs_keep {
let p = TypedArray::<*const DecimalObj>::get_unchecked(rhs_arr, i);
v2_retain(&(*p).header);
TypedArray::<*const DecimalObj>::push(out, p);
}
}
out as u64
},
_ => unreachable!("as_v2_raw_string_decimal filtered other variants"),
};
Ok(KindedSlot::new(
ValueSlot::from_raw(out_bits),
NativeKind::Ptr(HeapKind::TypedArray),
))
}
pub(crate) fn handle_union_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error("union() requires 2 arguments (array, other)"));
}
if as_v2_raw_string_decimal(&args[0]).is_some() {
return set_op_v2_raw_string_decimal(V2RawSetOp::Union, &args[0], Some(&args[1]));
}
Err(ckpt2_surface("union", args))
}
pub(crate) fn handle_intersect_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"intersect() requires 2 arguments (array, other)",
));
}
if as_v2_raw_string_decimal(&args[0]).is_some() {
return set_op_v2_raw_string_decimal(V2RawSetOp::Intersect, &args[0], Some(&args[1]));
}
Err(ckpt2_surface("intersect", args))
}
pub(crate) fn handle_except_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error("except() requires 2 arguments (array, other)"));
}
if as_v2_raw_string_decimal(&args[0]).is_some() {
return set_op_v2_raw_string_decimal(V2RawSetOp::Except, &args[0], Some(&args[1]));
}
Err(ckpt2_surface("except", args))
}
pub(crate) fn handle_unique_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error("unique() requires 1 argument (array)"));
}
if as_v2_raw_string_decimal(&args[0]).is_some() {
return set_op_v2_raw_string_decimal(V2RawSetOp::Unique, &args[0], None);
}
Err(ckpt2_surface("unique", args))
}
pub(crate) fn handle_distinct_v2(
vm: &mut VirtualMachine,
args: &[KindedSlot],
ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
handle_unique_v2(vm, args, ctx)
}
pub(crate) fn handle_distinct_by_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(type_error(
"distinctBy() requires 2 arguments (array, key_fn)",
));
}
match args[1].kind {
NativeKind::Ptr(HeapKind::Closure) | NativeKind::UInt64 => {}
other => {
return Err(type_error(format!(
"distinctBy: key function must be a closure or function ref, got kind {:?}",
other
)));
}
}
Err(ckpt2_surface("distinctBy", args))
}