Adopt the failable-call standard, and queue its workspace audit - #84
Conversation
The standard: a call that can fail has its failure handled. No per-site
analysis, no exemption for calls that "cannot fail in practice".
It is flat on purpose, because the analysis it replaces is the thing that goes
wrong. Judging per site whether a call is worth checking means the exempted
calls are not the genuinely infallible ones, they are the ones nobody looked at
hard enough -- "trivial enough not to check" is a fact about the reader's
attention, not a property of the call. The cost is a compare and an untaken
branch, which is worth paying outright for a rule that needs no thought.
The measurement that settled it, from `windows-platform-probes`: three timing
loops discarded every `BOOL` while the `SubmitIoRing` loop twelve lines below
carried a careful note explaining why that is the hazard the crate exists to
avoid. Run against a deliberately invalid handle so every event call fails, the
unchecked version reported 212.7 ns for a redundant `SetEvent` (true 205), 422.9
for the set/reset cycle (true 531) and 231.5 for a satisfied wait (true 280).
None looks wrong; the first is within 4%. A discarded status does not produce an
obviously bad result, it produces a plausible one, which is why it survives
review.
The note also records the preferred direction -- discharge the rule in a type,
where no caller can see it -- and what the rule does not cover: a `void` return
or a non-status return like `SetErrorMode`'s previous mode is not discarded
failure information.
Queues M22.1 (classify and fix the failable sites) and M22.2 (the checked owning
handle type). Measured scope: 121 bare `unsafe { Call(...) };` statements across
11 crates, of which most are void-returning and correct; the failable set is
roughly 28 and is dominated by `CloseHandle` at 24 sites. The item records that
the regex saw single-line statements only, so 121 is a floor rather than a total.
Recorded here rather than in the probes crate because the standard is
workspace-wide, and queued as checklist items because a decision recorded only
in a design note schedules no work.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟢 Approval recommended
The changes are documentation-only and consistently record/queue the standard without introducing code or behavioral risk.
Pull request overview
Records a workspace-wide documentation standard that every failable call must have its failure handled, and queues a workspace audit + type-based remediation work to apply the standard to existing code.
Changes:
- Add a new decision section to
DESIGN-NOTES.mddefining the “failable call must be handled” rule, including motivating measurements and scope boundaries. - Add a new
M22checklist section that queues (1) a workspace audit/fix pass and (2) a checked owning handle type to discharge most sites by construction.
File summaries
| File | Description |
|---|---|
| DESIGN-NOTES.md | Adds the new “failable call” decision, rationale/measurement summary, and points to the queued checklist work. |
| CHECKLIST.md | Queues M22.1 (audit/fix) and M22.2 (checked owning handle type) to apply the new standard across the workspace. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…end state The standard needed a mechanism, and the answer is two things rather than one. `unused_results` is a rustc lint, allow-by-default, that fires on any expression statement discarding a non-unit value -- this rule's shape exactly, and indifferent to how many lines the statement spans or whether the callee is `unsafe`. It found four discarded statuses in a `windows-platform-probes` file that had just been fixed by hand and re-swept by regex, which is the argument for it: a sweep reporting a per-crate total is not a list of sites. It is not a gate. Two thirds of its 112 hits on that crate are ordinary Rust -- `HashMap::insert`, `Vec::pop`, `fetch_add`, `black_box` -- so denying it workspace-wide would trade the rule's "no analysis required" property for a permanent triage burden, which is the trade the rule exists to refuse. `#[must_use]` is the enforcement mechanism and is unavailable where it would matter: it cannot be attached to `windows-sys`'s `extern` declarations. It becomes available on our own wrappers, which is a further argument for M22.2 -- a `#[must_use]` wrapper enforces permanently with none of the lint's noise. So the lint is how the audit is run, and a type is how the rule is kept. Both recorded on the decision and on M22.1. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
The new checklist text contains an unverifiable/stale file reference and a POSIX-only RUSTFLAGS=... example that should be corrected for Windows/PowerShell usage.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 2
- Review effort level: Lite
| verdict, and each site needs its callee's return type confirmed. Record the classification so | ||
| the next reader does not redo it. | ||
|
|
||
| **Use the compiler, not a regex: `RUSTFLAGS="-W unused_results"`.** `unused_results` is a |
| every raw Win32 discard the regex found, plus four in `doorbell_cost.rs` the regex had counted | ||
| but nobody had looked at, in a file already believed fixed. A per-crate total is not a list. |
…nst merged main Merges `origin/main` (PR #83) and reconciles this branch's measured claims with what landed, since every figure here describes code that just changed. The measurement table's third column was headed "true", which does not say what it is true *of*. Both columns are now named for the run they came from -- what the unchecked probe reported when every call failed, against what it reports when they succeed. Re-measured against `dc2b463` rather than left as written: - 121 bare `unsafe { Call(...) };` statements became 119, and `windows-platform-probes` 25 became 23, because #83 fixed sites inside it. - `SetEvent` discards fell from 4 to 2 for the same reason. - The `unused_results` count on that crate fell from 112 to 104. - "Two thirds are ordinary Rust" was an overstatement: it is 62 of 104, about sixty percent, with the remaining 42 naming a raw Win32 call -- and not even all of those are violations, since several of the calls return `void`. The failable set is now a table of six calls totalling 30 sites, each confirmed against its `windows-sys` signature rather than assumed. `CancelIoEx`, `RevertToSelf`, `SetCurrentDirectoryW` and `FindCloseChangeNotification` were checked and all return `BOOL`; `CloseIoRing` returns an `HRESULT`. The entry now says plainly that every number in it is stale on arrival and the lint should be re-run instead. That is the honest reading of figures that moved twice in one afternoon from an unrelated merge, and it keeps the item a description of the work's shape rather than an inventory somebody ticks off against a tree that has moved underneath it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Merged The column headerTaken. "true" didn't say what it was true of. Both columns are now named for the run they came from: reported when unchecked and every call fails against reported when the calls succeed. Merging main invalidated most of the numbers, so I re-measured rather than left themThis is the part worth flagging, because the entry's whole value is that a reader can trust its figures. Every measurement in this PR describes code that PR #83 just changed:
And one was an overstatement independent of the merge: "two thirds are ordinary Rust" is really 62 of 104, about sixty percent — with the remaining 42 naming a raw Win32 call, and not even all of those are violations, since several of those calls return The failable set is now a table of six calls totalling 30 sites, each confirmed against its The correction I'd most want keptThe entry now says outright that every number in it is stale on arrival and the lint should be re-run instead. Those figures moved twice in one afternoon purely because an unrelated PR merged. Writing them as an inventory to tick off would guarantee the next reader works against a tree that has moved underneath them; writing them as a description of the work's shape, with the command to regenerate them, does not. That is the same lesson the #83 flake record produced an hour ago — a document written to save someone re-deriving facts is only worth as much as its facts, and prose about code gets no automatic verification the way code does. No conflicts in the merge; the branch remains docs-only. |
There was a problem hiding this comment.
🟡 Changes recommended
The checklist’s suggested RUSTFLAGS spelling (-W unused_results) is likely incorrect for rustc’s command-line lint naming and could cause the documented audit procedure to fail when followed.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Lite
…s back Review read `RUSTFLAGS="-W unused_results"` as likely to be rejected, on the grounds that command-line lint names use dashes. The command is correct as written -- rustc normalises `_` and `-` in lint names, and the audit figures in this very item were produced by running it -- so nothing changes about the instruction. The premise behind the finding is real, though, and worth heading off. rustc accepts the underscore form and then echoes the HYPHENATED form back: a run of `-W unused_results` reports "requested on the command line with `-W unused-results`". To a reader following the instruction that echo reads like a correction, which is presumably where the finding came from. Recorded as a parenthetical so the next reader is not sent looking for a typo that is not there. Verified on 1.98.0 by invoking rustc directly with each spelling: both are accepted, both warn, neither reports an unknown lint. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Declined as stated, but it pointed at something real, so The command works as writtenTested directly on 1.98.0 rather than reasoned about:
rustc normalises But the premise behind the finding is realHere is the actual output, and it explains where the intuition came from: rustc accepts the underscore form and then echoes the hyphenated form back. So a reader who follows the instruction and sees That is a genuine trap in the instructions even though the instructions are correct, so I added a parenthetical saying both spellings work and that the echo reads like a correction but is not one. A finding that is wrong about the code but right about how the code will be read is still worth acting on; it just gets a different fix than the one suggested. No change to the command itself. |
There was a problem hiding this comment.
🟢 Approval recommended
The change is documentation-only and consistently records the standard plus an actionable checklist plan without introducing code or behavioral risk.
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Lite
Records a workspace-wide standard and queues the work it implies. Docs only -- no code changes.
The standard
A call that can fail has its failure handled. No per-site analysis, no exemption for calls that "cannot fail in practice."
It is deliberately flat, because the analysis it replaces is the thing that goes wrong. Judging per site whether a particular call is worth checking means the exempted calls are not the genuinely infallible ones -- they are the ones nobody looked at hard enough. "Trivial enough not to check" is a fact about the reader's attention, not a property of the call. The cost is a compare and an untaken branch, which is worth paying outright for a rule that needs no thought, and overwhelmingly worth it the first time it catches something.
The measurement behind it
From
windows-platform-probes(#83): three timing loops discarded everyBOOL, while theSubmitIoRingloop twelve lines below them carried a careful note explaining why discarding a status is precisely the hazard the crate exists to avoid. The argument was correct and had been applied only to the call that looked expensive enough to deserve it.Run against a deliberately invalid handle so every event call fails:
set_event_already_signalledset_reset_eventwait_zero_signalledNone of those looks wrong -- the first is within 4%. A failing syscall still costs a measurable transition, so the report reads as evidence while describing operations that never happened. That is the core of it: a discarded status does not produce an obviously bad result, it produces a plausible one, which is exactly why it survives review.
Checks cost nothing detectable — 204-206, 528-534, 280.3-280.7 across three runs with them in place, which is run-to-run spread rather than a shift.
Queued work
unsafe { Call(...) };statements across 11 crates, but most are not violations —SubmitThreadpoolWork,SetThreadpoolWait,GetSystemInfo,SetLastErrorand theWaitForThreadpool*Callbacksfamily returnvoid, andSetErrorModereturns the previous mode rather than a status. The failable set is roughly 28, dominated byCloseHandleat 24 sites. The item records that the regex matched single-line statements only, so 121 is a floor, not a total.Dropgiven that panicking during unwind aborts, and whether teardown paths that legitimately expect a close to fail exist here (windows-threadpool-sys's caller-supplied close routines are where they would be).Worth noting:
std'sOwnedHandleis not a discharge on its own. It closes on drop but discardsCloseHandle'sBOOL.Why root, and why a separate PR
The standard is workspace-wide, so burying it in one crate's design notes would orphan it. And per the repo's own rule that design notes are not a work queue, the audit is queued as checklist items rather than left as prose nobody is obliged to act on.
Kept separate from #83 so that peel stays a
windows-platform-probeschange.