Expand description
Filesystem intrinsics for std/fs. pub, not private: its own doc
comment explains the boundary it implements; nothing about that requires
hiding the module itself. Branch std-fs-strings’s Task 3 and Task 4
(docs/superpowers/plans/2026-08-11-std-fs-strings.md) built most of this
module, and branch byte-type’s own Task 4 later added read/write
beside them, the same way this crate’s other modules build on task.
Filesystem intrinsics for std/fs.
§The boundary this module implements
Nova has no out-parameters, so an intrinsic returns exactly one word — but
read_to_string must convey a String and a two-field IoError. Rather
than build a Nova sum and record here, which would put Nova value layout in
Rust, the status code returned by each operation is the error kind, and
payloads travel in the slots below. std/fs’s Nova wrappers map a status to
an IoErrorKind and construct every Result and IoError themselves.
§Why SLOTS is a RefCell guarded with try_borrow_mut, not borrow_mut
Every function here is called from inside an async fn’s generated
$poll, which has no landing pads (see nova_mir::async_lower’s module
doc comment), so nothing here may panic. RefCell::borrow_mut panics on an
already-outstanding borrow; try_borrow_mut cannot, so with_slot uses
that instead and falls back to abort_with – which terminates without
unwinding – on the contended case. Each slot still holds nothing more
than a raw already-allocated pointer as a usize, with 0 meaning empty;
the per-task table just adds one layer of indirection to reach it, keyed
by slot_index.
stash itself is pub(crate): std/fs is not its only consumer.
crates/nova-runtime/src/io.rs stashes the three standard streams’
payloads and write byte counts through this identical slot table, and
crates/nova-runtime/src/crypto.rs stashes digests, HMAC tags and random
bytes the same way, rather than either module owning a second one.
take stays private (final review, M4) – io.rs’s own production code
only ever stashes, and its tests reach take through the test-only
take_for_test instead of take itself being widened for their sake.
§Why the pointer is GC-rooted while it sits in a slot
A slot is written by one intrinsic and read by the next, with Nova code in
between. A conservative stack scan cannot see a pointer that lives only in a
thread-local, so the object is registered with gc::add_root while stashed
and released on take — the same balance task::spawn_internal and
take_output_internal keep. Without it the payload could be collected
between the two calls, which is why this does not merely rely on “no
allocation happens in between”.
Constants§
- ALREADY_
EXISTS - CONNECTION_
REFUSED - INTERRUPTED
- INVALID_
DATA - NOT_
FOUND - OK
- Status codes.
0is success; every other value is anIoErrorKind. - OTHER
- PERMISSION_
DENIED - TIMED_
OUT
Functions§
- nova_
rt_ ⚠fs_ create_ dir - Create the directory
path. Its parent must exist; an existingpathisALREADY_EXISTS. - nova_
rt_ ⚠fs_ create_ dir_ all - Create the directory
path, and any missing parent directories along the way. Unlikecreate_dir, an existing directory atpathis success, notALREADY_EXISTS–std::fs::create_dir_all’s own behaviour, carried through unchanged rather than papered over. - nova_
rt_ ⚠fs_ exists - Whether
pathexists. Cannot distinguish absent from unreadable, becausenova-spec/20-STDLIB.md§5 givesexistsaBoolreturn rather than aResult; that is the spec’s choice and is left alone. - nova_
rt_ ⚠fs_ kind - What
pathis: 0 = metadata unavailable (absent, or unreadable), 1 file, 2 directory. - nova_
rt_ fs_ last_ error_ message - Take the pending error message.
- nova_
rt_ ⚠fs_ read - Read
path’s bytes. Returns a status; onOKthe contents are waiting innova_rt_fs_take_bytes. - nova_
rt_ ⚠fs_ read_ dir - List
path’s entry names, sorted. OnOKthe names are waiting innova_rt_fs_take_string_array. - nova_
rt_ ⚠fs_ read_ to_ string - Read
pathas UTF-8. Returns a status; onOKthe contents are waiting innova_rt_fs_take_string. - nova_
rt_ ⚠fs_ remove_ dir_ all - Remove the directory
pathand everything under it. - nova_
rt_ ⚠fs_ remove_ file - Remove the file
path. - nova_
rt_ fs_ take_ bytes - Take the pending payload as
Bytes. Returns an emptyBytesif nothing is pending, which a correct wrapper never asks for. - nova_
rt_ fs_ take_ string - Take the pending payload string. Returns an empty string if nothing is pending, which a correct wrapper never asks for.
- nova_
rt_ fs_ take_ string_ array - Take the array payload staged by a successful
nova_rt_fs_read_dir. - nova_
rt_ fs_ temp_ dir - The OS temporary-directory path, per
std::env::temp_dir(). - nova_
rt_ ⚠fs_ write - Write
content’s bytes topath, truncating an existing file. - nova_
rt_ ⚠fs_ write_ string - Write
contenttopath, truncating an existing file.