Skip to content

Commit 6b759a2

Browse files
alexnodelandclaude
andauthored
fix(checkpoint): file-based persistence is native-only, and now says so (#22)
`cargo check --target wasm32-unknown-unknown` fails on the **default** feature set with a dozen "`?` couldn't convert the error to `CheckpointError`". The cause is a gate that drifted out of step with the error type it feeds. `CheckpointError::Io` is `#[cfg(not(target_arch = "wasm32"))]` and its doc reads "native only", with `Storage(String)` offered as the wasm alternative -- so the error enum already encodes that file I/O is not a wasm concern. But `checkpoint::recovery` and `InteractiveSession::{save,load}` were gated on `feature = "checkpoint"` alone, which is on by default and says nothing about the target. On wasm32 the file-I/O code was therefore still compiled while the variant its `?` operators desugar into was not. Both are now gated on `all(feature = "checkpoint", not(target_arch = "wasm32"))`, matching the intent the error type had already declared. Gating rather than adding a wasm `From<io::Error>` is deliberate: `wasm32-unknown-unknown` has no filesystem, so `File::create` there could only ever be a compile-time promise of a runtime failure. Not a `parallel` problem, which is worth recording because it is the natural guess: `--features parallel --no-default-features` already checks clean on wasm32. `checkpoint` was the only blocker, so a downstream crate that reached for `default-features = false` to get a wasm build no longer needs to. wasm32 now checks clean with default features -- zero errors, zero warnings. Native untouched: 681 tests pass, fmt and clippy clean. Claude-Session: https://claude.ai/code/session_01SHNw2k18vFYcaTukhmvbAQ Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 51dc9fa commit 6b759a2

3 files changed

Lines changed: 28 additions & 11 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/checkpoint/mod.rs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,36 @@
44
//! enabling long-running experiments to be paused and resumed.
55
//!
66
//! The `state` submodule (data structures) is always available.
7-
//! The `recovery` submodule (file I/O) requires the `checkpoint` feature.
8-
9-
#[cfg(feature = "checkpoint")]
7+
//! The `recovery` submodule (file I/O) requires the `checkpoint` feature **and
8+
//! a target with a filesystem**.
9+
//!
10+
//! The second half of that condition is not a new restriction, it is the one
11+
//! [`CheckpointError`](crate::error::CheckpointError) already encodes: its
12+
//! `Io` variant is `#[cfg(not(target_arch = "wasm32"))]` and its docs read
13+
//! "native only", with `Storage(String)` offered as the wasm alternative. The
14+
//! module gate had not been kept in step, so on `wasm32` the file-I/O code was
15+
//! still compiled while the error variant its `?` operators desugar into was
16+
//! not — `cargo check --target wasm32-unknown-unknown` failed on the default
17+
//! feature set with a dozen "`?` couldn't convert the error to
18+
//! `CheckpointError`".
19+
//!
20+
//! Gating the module rather than adding a wasm `From<io::Error>` is the fix
21+
//! that matches the intent: `wasm32-unknown-unknown` has no filesystem, so
22+
//! `File::create` there could only ever be a compile-time promise of a
23+
//! runtime failure.
24+
#[cfg(all(feature = "checkpoint", not(target_arch = "wasm32")))]
1025
mod recovery;
1126
mod rng;
1227
mod state;
1328

14-
#[cfg(feature = "checkpoint")]
29+
#[cfg(all(feature = "checkpoint", not(target_arch = "wasm32")))]
1530
pub use recovery::*;
1631
pub use rng::*;
1732
pub use state::*;
1833

1934
/// Prelude for checkpoint module
2035
pub mod prelude {
21-
#[cfg(feature = "checkpoint")]
36+
#[cfg(all(feature = "checkpoint", not(target_arch = "wasm32")))]
2237
pub use super::recovery::*;
2338
pub use super::rng::*;
2439
pub use super::state::*;

src/interactive/session.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
use serde::{Deserialize, Serialize};
77
use std::collections::HashMap;
88

9-
#[cfg(feature = "checkpoint")]
9+
#[cfg(all(feature = "checkpoint", not(target_arch = "wasm32")))]
1010
use std::fs::File;
11-
#[cfg(feature = "checkpoint")]
11+
#[cfg(all(feature = "checkpoint", not(target_arch = "wasm32")))]
1212
use std::io::{BufReader, BufWriter};
13-
#[cfg(feature = "checkpoint")]
13+
#[cfg(all(feature = "checkpoint", not(target_arch = "wasm32")))]
1414
use std::path::Path;
1515

1616
use super::aggregation::FitnessAggregator;
@@ -384,8 +384,10 @@ where
384384
}
385385
}
386386

387-
/// File-based session persistence (requires `checkpoint` feature)
388-
#[cfg(feature = "checkpoint")]
387+
/// File-based session persistence (requires the `checkpoint` feature and a
388+
/// target with a filesystem — see [`crate::checkpoint`] for why the second
389+
/// half of that is not a new restriction).
390+
#[cfg(all(feature = "checkpoint", not(target_arch = "wasm32")))]
389391
impl<G> InteractiveSession<G>
390392
where
391393
G: EvolutionaryGenome + Serialize + for<'de> Deserialize<'de>,

0 commit comments

Comments
 (0)