1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
//! §16.11's byte-stream face: the two `io::Error` conversions and the four
//! `AsyncRead`/`AsyncWrite` impls.
//!
//! Ungated — `tokio` is already a hard dependency and the two traits
//! themselves need no tokio feature.
//!
//! Nothing here adds a field, a `Drop` or any state. Every impl is one of
//! the handles' existing `pub(crate) poll_*` verbs *with its error mapped*,
//! which is §16.3's *"written once"* rule (ruling 53) spelled out for this
//! surface: *"§16.11's `AsyncRead`/`AsyncWrite` is the **same function**
//! with its error mapped."*
use io;
use Pin;
use ;
use ;
use crate;
use crateHandshake;
use crate;
// ═══════════════════════════════════════════════════════════════════════
// §16.11.1 — the `io::ErrorKind` mapping
// ═══════════════════════════════════════════════════════════════════════
/// §16.11.1's read column.
///
/// **[RATIFIED 2026/08/16 — ruling 227.]** Until that ruling §16.11 read
/// `Reset → ConnectionReset` and `ConnectionLost → NotConnected /
/// BrokenPipe` — a slash between two kinds with no rule for choosing, over a
/// [`ConnectionLost`] with seven variants.
/// §16.11.1's write column.
///
/// The rule, so it can be judged rather than memorised: **on the write side,
/// a peer or transport that went away *under a writer* is `BrokenPipe`; a
/// connection *this side* never had or gave up is `NotConnected`.** That
/// reads §16.11's original slash as a **variant** split rather than a
/// read/write direction split — which is how it was written, the comment
/// having sat on the `WriteError` line alone.
///
/// `TimedOut` is lifted out of both columns because
/// [`io::ErrorKind::TimedOut`] exists and a `DEAD_TIMEOUT` death is exactly
/// what it names; collapsing it would make every death look alike to a
/// consumer whose only view is an [`io::Error`].
//
// **[RATIFIED 2026/08/16 — ruling 238.]** This match is **exhaustive** and
// carries no `_ =>` arm. It read the other way until that ruling: ruling 227
// required a fallback mapping to `Other`, reasoning that `WriteError` is
// `#[non_exhaustive]` (ruling 61 reserves `Stopped`) ***so*** the conversion
// needs one.
//
// The *"so"* is false. **`#[non_exhaustive]` is inert inside the defining
// crate**, and this conversion can only live here — the orphan rule puts
// `impl From<WriteError> for io::Error` in slither or nowhere — so the
// attribute never bites and rustc reports the arm as unreachable.
// `error.rs`'s `write_error_is_exhaustive_in_crate` already proves it.
//
// Ruling 238 moved the **conclusion** as well as the reasoning, which is why
// the arm is gone rather than merely `#[allow]`ed. A `_ =>` arm does not
// future-proof this conversion, it **hides** the future: it would silently
// map `Stopped` to `Other` on the day that variant lands. An exhaustive match
// makes that day a compile error at the one site that must be updated — which
// is what ruling 227's own *"must not be `unreachable!()`"* clause was
// reaching for. **A variant that cannot compile cannot panic.**
/// §16.11's `impl From<ReadError> for std::io::Error`, with §16.11.1's kinds.
///
/// **The original error is preserved as the [`io::Error`]'s inner value.**
/// Every arm is `io::Error::new(kind, err)`, never `io::Error::from(kind)`,
/// so a caller recovers what the [`io::ErrorKind`] projection drops —
/// including a reset's `u64` code, which is data only this conversion's
/// consumer can reach:
///
/// ```
/// use slither::error::ReadError;
///
/// let e: std::io::Error = ReadError::Reset(7).into();
/// assert_eq!(e.kind(), std::io::ErrorKind::ConnectionReset);
/// let inner = e.into_inner().expect("the slither error is preserved");
/// assert_eq!(*inner.downcast::<ReadError>().unwrap(), ReadError::Reset(7));
/// ```
/// §16.11's `impl From<WriteError> for std::io::Error`, with §16.11.1's
/// kinds.
///
/// The original error is preserved as the inner value, exactly as for
/// [`ReadError`].
// ═══════════════════════════════════════════════════════════════════════
// The three shared bodies
// ═══════════════════════════════════════════════════════════════════════
//
// `SendStream`/`RecvStream` and the two halves of a `BiStream` run the
// *same* code: the delegating impls below are two lines each, so there is
// no second implementation of anything (ruling 53, invariant 2).
/// [`AsyncWrite::poll_write`] over [`SendStream`]'s verb.
///
/// **No `WriteZero` guard, and none is wanted.** `SendStream::poll_write`
/// returns `Ok(0)` **only** on its `buf.is_empty()` short-circuit (ruling
/// 110); a non-empty write blocked by flow-control credit parks in
/// `blocked_writers` and returns `Poll::Pending`. So this can only answer
/// `Ok(0)` to a caller that passed an empty buffer, which is what
/// `AsyncWrite` asks for.
/// [`AsyncWrite::poll_shutdown`] over [`SendStream`]'s verbs — **ruling 57**.
///
/// `finish()` **and then** `acked()`. The weaker reading — `finish()` alone
/// — is prior art elsewhere and is rejected here for ruling 47's reason: the
/// natural last act of a transfer is `copy(..).await; shutdown().await`, and
/// under the weak reading it loses its tail at the path's loss rate,
/// silently. That is S28's bug reachable a second time, through the
/// `AsyncWrite` surface, by an application that never touches `close()`.
///
/// # No `shutdown_started` flag, and that is a fact rather than a shortcut
///
/// [`SendStream::poll_finish`] is *"always `Ready` on the first poll"* —
/// §16.7 makes sealing synchronous inside the mutating call that triggers it
/// — and `finish()` is idempotent, *"a second `finish()` is `Ok(())`"*. So
/// re-entering after a `Pending` from `poll_acked` calls `poll_finish` again
/// harmlessly, and there is no state to store.
///
/// It cannot hang past the connection's own death: a dying connection
/// resolves `poll_acked` in error.
/// [`AsyncRead::poll_read`] over [`RecvStream`]'s verb.
///
/// # The empty-buffer short-circuit is load-bearing
///
/// An empty `buf` is answered here, **without touching the handle**. Ruling
/// 119 makes `RecvStream::poll_read` answer `Ok(Some(0))` to an empty slice,
/// whose documented meaning is *park* — and once mapped into a [`ReadBuf`]
/// that filled nothing it is **indistinguishable from EOF**. `Ok(Some(0))` =
/// park and `Ok(None)` = end of stream is the distinction ruling 119 exists
/// to protect, and the `AsyncRead` surface is where the two collide, so the
/// collision is removed *before* the call rather than after it.
///
/// # No scratch buffer
///
/// The tail of the caller's `ReadBuf` is filled directly. `RecvStream::read`
/// pins the reason: *"there is no shell-side scratch buffer (§10.6), so
/// there is nowhere for a dropped future to strand data"* — an impl that
/// read into a `Vec` and copied out would build one (§10.6, invariant 7).
/// The crate forbids `unsafe`, so the uninitialised tail is reached through
/// [`ReadBuf::initialize_unfilled_to`], which is safe.
///
/// # EOF is sticky, and it is already sticky
///
/// Rulings 121 and 124 latch `Ended::Eof` / `Ended::Reset(code)` **in the
/// handle**, ahead of the connection's death latch, and §16.11 is why that
/// matters: *"`AsyncRead` requires a sticky end-of-file, so a connection
/// that dies after the FIN would otherwise surface a spurious `io::Error` to
/// `read_to_end`."* This function must not re-implement it and must not
/// defeat it.
// ═══════════════════════════════════════════════════════════════════════
// The four impls
// ═══════════════════════════════════════════════════════════════════════
//
// All four handles are plain structs of `Rc`s and `Copy` fields, so each is
// `Unpin` and every impl begins `self.get_mut()`. None of them projects a
// pin, and none adds a field, a `Drop` or any state.
/// Delegates to the **receive** half.
///
/// [`BiStream`] gains **no `Drop`** from this impl. Its existing rustdoc
/// records why: it *"has no `Drop` impl of its own — its two fields' do the
/// work, in declaration order"*, and a build that implemented `Drop` on
/// `BiStream` and forgot one half *"would pass any test that checked only
/// the other."*
/// Delegates to the **send** half.
///
/// [`poll_shutdown`](AsyncWrite::poll_shutdown) shuts down the send half
/// **only**: it does not touch, abandon or reset the receive half. A
/// half-closed `BiStream` is the shape `copy_bidirectional` and every
/// request/response protocol relies on — the request ends with a FIN and the
/// response is still to come.