Native String Contents 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, or separately provisioned observations below retain their narrower scope; public promotion and broader product completion remain separately gated.

Audience: compiler contributors and native-runtime reviewers.

Contract and defect

Owned string values contain exact UTF-8 bytes. U+0000 is data, not an end marker. Byte length, scalar count, equality, cloning, concatenation, prefix and substring operations must observe the whole value, including content after a NUL byte. Empty text and a single NUL scalar are distinct values.

The source lexer, canonical formatter, HIR literals, and native literal escaping already preserve those bytes. The defective ordinary native runtime copied literal bytes but later used C terminator searches to determine value length. Changing escaping or rejecting NUL would not correct that defect.

Representation selection

Ordinary C11 (emit_c and emit_hir_c) and stdout-transcript generation reuse the existing length-header String runtime introduced by the v10 provider. Construction, clone, equality, both intrinsic helper groups, and drop all use the same representation within a generated translation unit. The trailing terminator remains a convenience; it never determines semantic length.

The header and helper names retain their historical v10 spelling to reuse the exact runtime bytes. This does not select Project v10, widen its public closure, or confer provider-handle authority. Ordinary String selection is separate from the v10 provider's borrowed-status and byte-carrier runtime selection. This correction does not add either carrier runtime to String-free ordinary output; existing source-driven carrier selection remains unchanged.

The existing inline owner ledger continues to govern allocation ownership, argument commit, scope exit, failure settlement, and result publication. No cleanup transition or resource CleanupPlan interpretation changes. Nonempty concatenation in the reused runtime uses one temporary allocation in addition to the result allocation; physical fixtures count and settle both rather than hiding this cost.

Compatibility and authority

String-bearing ordinary and stdout-transcript C bytes intentionally change. Dependent Target Evidence and Patch Evidence v2 bind those current production bytes and therefore change their corresponding digests and lengths. They do not use an older emitter to retain stale artifact bindings.

The existing v10 provider runtime constants and outputs remain unchanged. The subsequent owned-data internal String correction explicitly adopts this representation for v8/v9 providers, including unselected String functions. All three versioned command profiles and the private callable prelude remain on their existing paths. String-free native output and its budget accounting remain unchanged. Source, Graph, HIR, CleanupPlan, diagnostic, manifest, descriptor, and evidence schemas do not change. Wasm output, host imports, and interpreter admission do not change.

Project v1-v4 native routes use ordinary emission, but their Phase-A profiles reject owned Strings throughout the retained compiled function inventory. Project v5-v7 use the unchanged command profiles. This correction therefore does not change admitted Project v1-v7 native artifacts.

Generated internal String char * values now point into a header allocation. They must originate from that generated runtime and be released through its spx_string_drop. Foreign C literals, malloc/strdup pointers, or values from a different runtime/profile are not valid String inputs. Raw free of a String result is invalid. These internal signatures are not a supported public String ABI; the existing public C/C++ projections exclude them.

No new filesystem, process, network, callback, or publication authority is introduced. Allocation failure and runtime invariant failure remain fail-stop; no signal, unwind, or longjmp recovery guarantee is added.

Evidence and remaining gates

Authored regressions cover NUL positions, unequal suffixes after NUL, exact byte and scalar lengths, Unicode beside NUL, cloning, consuming calls and results, concatenation, prefix/substring matches, and string_from_char for U+0000. Native physical fixtures check explicit lengths and memcmp, exact allocation/free accounting, failure-slot poison, and reuse after failure. Terminator-based C assertions cannot establish the contents contract.

The generic String-return fixture also checks the exact four-byte length of a + U+0000 + é after the generic bridge. Equality alone could let a terminator-based clone and comparison hide the same truncation. The additional length assertion preserves the existing success value and allocation-balance checks; it is implemented, like the separate test-only binary-stdout setup described in the inline settlement contract.

The value corpus compares interpreter, native O0/O2, and Core-Wasm/Node only where those existing profiles admit it. Owned user String signatures remain outside the interpreter profile. Node value equality is not physical Wasm String settlement; its ordinary host API still lacks a drop operation. The separate Internal String Interpreter v1 adds an implemented opt-in route for String helpers, without changing this corpus's ordinary-interpreter rejection or adding external String values. Frozen command/callable emitted-but-unselected String functions retain their separate representation and cleanup limitations; the owned-data provider correction has separately scoped hosted-green release evidence.

The focused gates (reproducible release regression selectors) are:

cargo test --locked -p semaprax --test native string_settlement::
cargo test --locked -p semaprax --test language string_ops
cargo test --locked -p semaprax --test semantic target_evidence::string_cleanup_evidence_binds_current_production_c_and_rejects_foreign_binding

Physical native cases require CLANG or clang; the cross-backend contents case additionally requires Node. These new cases fail when a required tool is absent rather than skipping conformance. For the explicitly ignored sanitizer gate, set SEMAPRAX_STRING_SANITIZER_CLANG to an absolute existing Clang executable with ASan/UBSan runtimes provisioned, then run:

cargo test --locked -p semaprax --test native string_settlement::provisioned_ordinary_native_string_asan_ubsan -- --ignored --exact
cargo test --locked -p semaprax --test native string_settlement::contents::provisioned_embedded_nul_native_values_asan_ubsan -- --ignored --exact

The implemented release regression corpus is HOSTED GREEN. Required target and sanitizer execution, ordinary Wasm settlement, full interpreter admission, and exact-head package promotion remain open; this correction alone is not production readiness.