v0.35.0 Phase 4 Case-conversion and Owned-mutation Review
Result: complete with no open Phase 4 finding (2026-09-05). Full Unicode 17.0.0 default case conversion and boundary-safe owned String suffix mutation are implemented as ordinary standard source. The implementation adds no Unicode-aware compiler or runtime primitive.
Closed Product
Borrowed str exposes ordinary and recoverable lower- and uppercase conversion. All four methods delegate to two recoverable cores, so allocator selection changes storage policy without creating a second casing algorithm. Each core iterates valid UTF-8 with the existing scalar iterator and asks std/internal/unicode for full mappings. The only contextual rule, Final_Sigma, uses generated Cased and Case_Ignorable properties; it does not inspect table representation or operating-system locale data.
Owned String exposes allocation-free pop and truncate. pop discovers the preceding scalar boundary with the public borrowed-text boundary contract, decodes through the canonical internal UTF-8 decoder, and changes length only after validating the decoded suffix. truncate validates an in-range boundary before changing length. Both operations preserve capacity and therefore preserve the String's allocator affinity.
Only the four casing lookup operations gained package visibility in this phase, when str became their first sibling-module consumer. Generated arrays and lookup representation remain private to the internal Unicode module.
Findings Closed During Implementation
- The first recoverable-casing fixture expected a medial sigma at the end of
İΣ. The generated properties correctly make the preceding dotted capital I cased, so UnicodeFinal_Sigmarequires final sigma. The fixture was corrected before completion; implementation and generated oracle did not change. - The standard dependency audit rejected the new
str -> internal/unicode,str -> mem, and String helper edges until they were explicitly reviewed. The exact allowlist now records those real source dependencies rather than relying on transitive module availability. - Failure tests initially covered successful recoverable allocation only. Native tests now inject an invalid recoverable allocator and verify that both expanding lower- and uppercase operations return the allocator error rather than publish a partial String.
Evidence
- native text tests execute full dotted-I and sharp-S expansion, medial and final sigma, combining
case-ignorable context, reverse multi-byte
pop, successful and rejectedtruncate, and capacity preservation; - allocator-failure native tests execute both recoverable casing directions with a deliberately invalid allocator and observe the stable allocation error;
- MIR cleanup tests prove that propagation destroys initialized local output storage, including partial owned values, without running cleanup on terminating allocation-abort edges;
- standard source checking validates declarations, bodies, public visibility, transitive
noalloc, and the exact new module dependencies; - the Unicode generator's exhaustive oracle remains the authority for all scalar mappings and contextual property membership;
- the complete workspace, warnings-denied Clippy, no-default-features, Rustdoc, generated-data, documentation, and repository-integrity gates pass at the reviewed commit.
Boundary Result
The generator owns Unicode-file interpretation, the internal Unicode module owns mapping lookup, str owns contextual traversal and output construction, and String owns its logical byte length. Each layer calls only the preceding public or package-internal contract. There is one mapping authority, one UTF-8 decoding authority, one conversion core per direction, and no mutation before a fallible suffix boundary has been accepted.