Skip to main content

Module string

Module string 

Source
Expand description

Arc<String> strict-typed carrier FFI for JIT-emitted code (W12-jit-string-carrier-unification, Phase 3 cluster-0 Round 12 T2/T3, 2026-05-13).

ADR-006 §2.7.5 (producing-site classification) names NativeKind::String as the §2.7.5 String carrier with shape Arc::into_raw(Arc<String>) as u64 — the standard Rust Arc layout with refcount at offset -16 of the data pointer. The VM-side consumer (crates/shape-vm/src/executor/objects/ set_methods.rs:136-155::result_slot_to_string_arc, mirrors in hashmap_methods.rs) and KindedSlot::Drop for NativeKind::String (crates/shape-value/src/kinded_slot.rs:500-502) both decode this exact shape via Arc::increment_strong_count::<String> / Arc::from_raw(bits as *const String).

§Carrier-shape rule (binding)

  • NativeKind::String slot: Arc::into_raw(Arc<String>) as u64, refcount at offset -16. Retain/release dispatches through this module’s jit_arc_string_retain / jit_arc_string_release — bumps the Rust Arc control-block refcount.

  • JIT-internal NaN-box string carrier: Box::into_raw(Box::new( UnifiedValue<Arc<String>>)) as u64, refcount at offset +4 inside the UnifiedValue allocation. Retained/released via the legacy jit_arc_retain / jit_arc_release in ffi/arc.rs. Stays for JIT-internal pathways (the dispatch shell’s method-name push at terminators.rs:235, call_string_method returns, etc.) that pair the bits with their own JIT-internal decode contract.

Mixing the two segfaults at every retain/release reclaim:

  • jit_arc_release on an Arc::into_raw(Arc<String>) as u64 slot reads *(bits + 4) as *const AtomicU32 — offset 4 inside the String payload (String’s ptr/cap/len words), corrupting the data on fetch_sub.
  • Arc::decrement_strong_count::<String>(bits) on a Box::into_raw( Box::new(UnifiedValue<Arc<String>>)) slot decrements *(bits - 16) as if it were the Arc control block — but offset -16 from the UnifiedValue start is whatever the allocator placed there. UB.

§Round 7A precedent

The Result/Option Arc carriers in ffi/result.rs::jit_arc_result_retain / _release / jit_arc_option_retain / _release (Round 7A close commit d01d83b7 + 9f27edcd) and the Round 9 typed-Arc collection retain/release pairs in ffi/v2/collection_arc.rs are the bound precedent shape for every body in this module.

§Round 12 T2/T3 surface closures

  • Smoke 4 JIT: let mut s = Set(); s.add("a"); s.add("b"); print( s.size()) → 2 VM == JIT. The "a" / "b" constants flow as MirConstant::Str operands stamped NativeKind::String; the VM trampoline’s KindedSlot::Drop decodes via Arc::from_raw(bits as *const String). Pre-Round-12 box_string returned NaN-box bits → UB at the VM consumer’s Arc::from_raw.
  • print("hello") JIT: was clean SURFACE at the print Call-terminator’s NativeKind::String arm in terminators.rs::466 (Round 8A reopen surfaced). Post-Round-12 the §2.7.5 producer emits the matching carrier shape and jit_print_str reads &String directly.

Functions§

arc_string_constant
Compile-time helper: produce a §2.7.5 Arc::into_raw-shape carrier pointer for a MirConstant::Str / MirConstant::StringId site, content-deduplicated through the program-wide [intern_pool].
jit_arc_string_release
Release an Arc<String> strong-count share. Mirrors jit_arc_string_retain’s increment — uses Arc::decrement_strong_count::<String> per Rust Arc contract. Reaching refcount zero runs String::Drop (drops the inner buffer).
jit_arc_string_retain
Retain (clone) an Arc<String> strong-count share. Bumps the standard Rust Arc refcount at offset -16 of the Arc::into_raw pointer via Arc::increment_strong_count::<String> — NOT the W-series UnifiedValue<T> refcount at offset 4 (jit_arc_retain’s shape).