Native Owned-Data Internal String Settlement v1
September 10, 2026 ยท View on GitHub
Status: implemented bounded profile; HOSTED GREEN under the v0.4.0 release baseline. Historical local, authoring-time, ignored, device/simulator, or separately provisioned evidence below retains its narrower scope; public promotion, registry publication and broader product completion remain separately gated.
Audience: compiler contributors and standalone native SDK reviewers.
Defect and affected route
The standalone owned-data SDK derives and independently replays its public
descriptor before generating the native provider. That descriptor excludes
public owned String parameters/results, effects, imports, and contracts; it
does not exclude internal String locals or internal String-valued calls.
with_native_owned_data_sdk_subject does not activate Project v8 or run its
Wasm admission. The public standalone builder calls the real
emit_native_owned_data_provider with that retained HIR and descriptor.
Previously, OwnedDataProvider selected terminator-based String helpers and
omitted the inline owner ledger. A String local followed by checked arithmetic
failure could bypass its lexical drop. Context close observes provider handles,
not these internal allocations, so successful close did not prove settlement.
Terminator-based clone and query helpers also lost content after U+0000.
This is not evidence that the example passes activated Project v8/v9 admission: those routes also apply their existing Wasm restrictions. Neither admission is widened or silently replaced by the standalone SDK route.
Correction and ownership
OwnedDataProvider now selects the same length-delimited String helpers,
materialized-instance discovery, and per-String-function owner ledger as
ordinary/stdout C generation. That selector change retains its distinct profile
identity and existing Bytes/result lowering. The complete emitted translation
unit uses one String representation, including emitted but unselected functions.
The inline settlement contract governs local, temporary, parameter, and provisional-result ownership. Arguments stage left to right and transfer their complete String owner group at call commit; checked failure settles all remaining cells without changing the chosen status or caller output slot. String cleanup and resource CleanupPlan ownership remain separate. The Bytes cleanup contract and public handle lifetime are unchanged; the subsequent native lowering correction below realizes existing transfers.
The contents contract governs exact UTF-8 bytes,
including embedded NUL and empty strings. Header-backed String pointers are
internal implementation values, not a new public ABI or permission to adopt
foreign allocations. Allocation exhaustion, runtime invariant failure, signals,
foreign unwind, and longjmp remain outside recoverable settlement.
Explicit compatibility amendment
This supersedes the earlier inline-settlement/contents documents' frozen
OwnedDataProvider exclusion. V8/v9 provider C intentionally changes whenever
the complete emitted inventory uses Strings, even if those functions are not
selected exports. Corresponding object/archive bytes and native artifact
lengths, digests, and integrity bindings may therefore change. An older emitter
must not be used to retain stale integrity facts.
For the original String-selector amendment, String-free whole-provider output and budget accounting remain unchanged. Existing v10 selection/runtime, all three command-profile selectors, the scalar Rust SDK renderer, and private callable prelude are not redirected by that amendment. Its public signatures, descriptor/manifest schemas, HIR, Graph, CleanupPlan, Wasm/npm bytes, and target admission are unchanged. These preservation statements do not exclude the separate shared Bytes-lowering correction below: affected native code can change even without Strings or when an existing command profile uses that path. This is not a public owned-String API, a v10 backport, or full cross-backend settlement support.
Follow-up inventory and native Bytes corrections
Selecting the authored physical fixture exposed two distinct compiler defects.
The structural cleanup inventory walker reserved an extra binding step after
every statement, but only let supplied a binding action. Assignment, while
and audited unsafe statements therefore stopped discovery before later owned
initializers or the block tail. The corrected walker reserves that step only
for let, preserving child visitation and structural ownership order. Scalar
loop admission is unchanged; the fixture's scalar-signature helper owns its
internal String separately. Formerly rejected source can now receive complete
inventory, plan and Graph projections; this is not a claim that its defective
metadata was already correct or remains byte-identical.
The independent native defect replayed an argument's complete transition group after its producer had already moved Bytes into the call-argument slot. Other argument forms needed their move before evaluating a later fallible argument. Native lowering now authenticates the exact expression, source and destination against the existing plan immediately after argument evaluation. It carries the canonical destination identity forward instead of replaying a transfer. Owned conditional consumers likewise select the exact result transfer, and a temporary-record Bytes projection returns the plan-owned projected result slot, not a stale aggregate-field transport expression.
Owned-result scalar and Copy-variant matches remain outside mandatory cleanup
admission. Source typing alone is insufficient: Copy-variant match resolution
rejects the droppable result, while scalar-match mandatory independent cleanup
replay also rejects it during resolution, both with SPX-H006. Neither path is
enabled by this correction.
This does not add runtime deduplication, repair liveness flags, sort cleanup vectors, recompute ownership from C syntax, or change the atomic call commit. The emitter correction alone changes no source admission, HIR, Graph, CleanupPlan, Wasm/npm bytes, public signatures or schemas. Affected native C and its object/archive bytes, lengths, digests and evidence bindings do change; current production emission must supply those bindings. The owning byte-data contract remains Portable Indexed Byte Data v1.
Evidence and limits
Focused emitter units cover function-signature/body/contract String discovery, materialized-instance helper discovery, String-free byte/budget preservation, and unchanged v10, command, and private callable selectors. Contracts and generic-instance discovery are whole-emitter evidence, not selected SDK closure admission.
tests/native/owned_data_string_settlement.rs derives and replays the real standalone
descriptor, generates the real provider, and instruments its allocations with
the existing fixed-table test allocator. Context-close success alone is not
the oracle. Required cases include checked failure before and after String
argument commit, local and mixed Bytes ownership, exact NUL/Unicode values,
legal scalar-loop helpers, poisoned failure outputs, and same-context reuse.
The C fixture keeps one native context across 32 rounds for each descriptor
route, checks the v9 scalar carrier as well as its byte handle, and requires
exact allocation/free balance after every call. The clone case checks length
as well as equality so terminated clone/equality cannot jointly hide truncation.
The separate negative fixture retains SPX-W110 for the existing v8/v9 Wasm
String-literal exclusion.
The separate private-toolchain test
crates/semaprax-toolchain/tests/native_owned_data_string_sdk_v1.rs supplies the
replayed descriptor and real emitted provider to the actual lower
build_and_publish authority in StandaloneEvidence mode, then consumes the
generated package locked and offline without repository source dependencies.
This exercises the renderer/tool/publication path used by the standalone
builder, not its convenience wrapper itself. It adds no private dependency to
the registry compiler. The package authority compiles its archive at O2; the
separate instrumented C fixture covers both O0 and O2. Neither consumer success
nor context close substitutes for physical allocation accounting.
The consumer reuses one safe SDK object, whose context is closed and
reinitialized between calls; this is distinct from the C fixture's reuse of one
initialized native context.
tests/native/bytes_call_staging.rs isolates the shared native defect with
eight real standalone-provider call shapes: direct allocation, owned place,
record-field place, temporary-record projection, nested first-argument calls,
block result, conditional result, and multiple owned arguments
with a later place. Each observes checked failure before a later allocation and
successful call commit, including both conditional selections. Literal
allocation/free counts, exact distinct binary payloads, guarded copy bounds,
poisoned failure outputs, double-drop rejection and empty allocation tables
are checked through 32 rounds on one initialized context at O0/O2. The separate
sanitizer selection adds ASan/UBSan; no Project activation or allocator-OOM
recovery follows from these standalone observations.
Separate diagnostic cases retain the closed scalar-match boundary for both
fresh-copy arms and repeated references to the same owned place, and the closed
Copy-variant match boundary; they are not positive native runtime cases.
The lower package's crates/semaprax-native-rust-owned-data-package/src/tests/ffi_boundaries.rs also strengthens its generated
safe-Rust fail-stop oracle. A flushed call-completed witness precedes harness
assertions after any returned result or caught panic. Fatal cases must not
reach it. A separate test-only generated-file mutation keeps the real failing
provider close but deliberately ignores its failure; the same oracle must
reject this control even when a later harness assertion exits unsuccessfully.
This calibration prevents a harness panic from masquerading as runtime
fail-stop. It neither modifies production FFI nor proves containment of
arbitrary malicious native code.
Focused corrective execution gates:
cargo test --locked -p semaprax --lib codegen::native_emit::owned_strings::tests
cargo test --locked -p semaprax --test native owned_data_string_settlement::
cargo test --locked -p semaprax --test native owned_data_string_settlement::standalone_owned_data_strings_settle_at_o0_and_o2 -- --exact
cargo test --locked -p semaprax --test native bytes_call_staging::bytes_call_arguments_settle_once_at_o0_and_o2 -- --exact
cargo test --locked -p semaprax-toolchain --test native_owned_data_string_sdk_v1 provisioned_standalone_owned_data_string_sdk_consumer -- --ignored --exact
The ordinary String regression is no longer ignored: its historical H006 blocker was corrected and physical execution passed. The new Bytes fixture retains its printed scratch directory and has no intrinsic process deadline. Execute these trusted compiler/target fixtures under independently bounded process and memory limits; do not infer descendant settlement from direct-child completion.
The C fixture requires CLANG or clang and does not silently skip when it is
absent. The explicitly ignored SDK gate must be deliberately selected after
provisioning absolute CLANG and SEMAPRAX_ARCHIVER paths admitted by the
existing held-tool rules. Windows additionally needs SEMAPRAX_VCTOOLS,
SEMAPRAX_LINKER, INCLUDE, and LIB for the existing MSVC toolchain contract.
These are invocation prerequisites, not newly granted tool authority. A default
run that leaves the SDK gate ignored does not establish consumer evidence.
Explicit sanitizer execution remains independently required; static inspection
does not substitute for either gate.
For the ignored sanitizer gate, provision an absolute
SEMAPRAX_STRING_SANITIZER_CLANG with ASan/UBSan runtimes and run:
cargo test --locked -p semaprax --test native owned_data_string_settlement::provisioned_owned_data_strings_asan_ubsan -- --ignored --exact
cargo test --locked -p semaprax --test native bytes_call_staging::provisioned_bytes_call_arguments_asan_ubsan -- --ignored --exact
The fixed allocation table supplies the leak oracle even where LeakSanitizer is unavailable. ASan/UBSan are additional memory/undefined-behavior checks, not a claim of LeakSanitizer coverage.
Local corrective validation has passed the original String fixture's ordinary
O0/O2 execution, separately selected ASan/UBSan O0/O2 execution, and SPX-W110
negative case on Linux AArch64 with Rust 1.88 and Clang 14. On macOS with Rust
1.98, 57 cleanup/loop tests, eight String-emitter units, 16 preservation tests,
the frozen scalar-C known answer, and 46 lower-package tests pass. These are
local, focused observations, not hosted or Windows evidence.
The eight-shape Bytes corpus passes ordinary O0/O2 and separately selected ASan/UBSan O0/O2 execution on Linux AArch64 with Rust 1.88 and Clang 14. The two new closed-match diagnostic tests also pass, completing all four tests in that fixture. Three existing owned-byte record tests and two owned-byte variant tests pass there, as do the existing frame corpus's ordinary native gate and calibrated isolated/retained ASan/UBSan O0/O2 gate. These selected checks do not establish the full suite or Windows support.
The actual generated String SDK consumer also passes on Linux AArch64 with
Rust 1.88, Clang 14 and the held archiver /usr/bin/aarch64-linux-gnu-ar.
The real package is compiled at O2 and consumed locked/offline in a fresh nested
target directory, with publication on the container's /tmp tmpfs. The same
test binary and tools failed with PackageError::Publication when its empty
fixture was placed on the Docker Desktop /target bind mount. The precise
failing filesystem operation has not been established; this is a
filesystem-dependent validation limit, not evidence of universal filesystem
support.
The initial focused Clippy attempt was blocked by an unrelated field_place
lint. After integrating the CI owner's correction, strict compiler-library
Clippy passes on macOS. No successful full quality profile or hosted run is
claimed by this corrective evidence.
Existing SDK, Project v8/v9/v10, frame-payload, and artifact known answers remain required alongside the new tests; they are not replaced with convenient expected hashes. Ordinary Wasm String settlement and the remaining frozen command/callable limitations are separate gaps. No completion-matrix status is promoted.