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
// SPDX-License-Identifier: MIT OR Apache-2.0
//! [`StorageStatus`] — the **WHY axis** of a per-`(backend, dtype)` storage
//! decline.
//!
//! When a backend cannot hold a [`DType`](crate::DType) in device storage, the
//! *reason* it declines is load-bearing information that Fuel has historically
//! erased: several `fuel-cuda-backend` sites spell the distinction in a comment
//! and then return the identical `UnsupportedDtype` for a dtype that is
//! *representable but unwired* and one whose *encoding is not authored yet*
//! (GAP-161). This enum makes that reason a typed value the compiler and tests
//! can read, instead of prose the return type throws away.
//!
//! ## Two axes, kept separate (GAP-161 / GAP-155)
//!
//! A decline is classified on **two orthogonal axes** and they must not be
//! merged into one enum:
//!
//! * **WHY** (this type) — does the decline *expire*? `Present` /
//! [`Impossible`](StorageStatus::Impossible) /
//! [`UnimplementedYet`](StorageStatus::UnimplementedYet) /
//! [`UndefinedEncoding`](StorageStatus::UndefinedEncoding). Only the latter
//! two expire (when the capability / encoding lands).
//! * **KIND** ([`TokenKind`](crate::TokenKind), *not* this type) — a fact about
//! vocabulary membership: `supported` / `recognized-but-unsupported` /
//! `reserved` / `unknown`. It classifies a `&str` TOKEN (including spellings
//! that are not `DType`s at all), lives at the recognition/emit surface, and
//! never names a backend. A `StorageStatus` cannot even be *computed* for a
//! non-`DType` token, so the two cannot merge — a type-level impossibility,
//! not a discipline. Pair them as `{ kind, why: Option<Self> }`, never a
//! merged enum.
//!
//! The axes *combine*, they do not collapse. The sharpest live case is
//! **`F8E5M2`**: KIND=`Supported` (it has a legal sk4 token and `dtype_token`
//! emits it) while WHY=[`UnimplementedYet`](StorageStatus::UnimplementedYet)
//! (no `CudaStorageSlice` variant) — both axes apply and give DIFFERENT answers,
//! and a single flat enum would have to pick one and be wrong about the other.
//! By contrast `f8e4m3fnuz` is `reserved` in KIND with no WHY at all (it is not
//! a `DType`), and `F4` on a CUDA GEMM is `recognized-but-unsupported` in KIND
//! and [`Impossible`](StorageStatus::Impossible) in WHY. If you find yourself
//! wanting a `Reserved` variant *here*, that is the collapse — stop.
//!
//! Home rationale: this type names only [`DType`](crate::DType) facts and is a
//! pure data enum, so it lives in the lowest crate every backend already
//! depends on (`fuel-ir`) — zero new dependency edges — rather than in
//! `fuel-backend-contract` (which is also reachable, but the type is a mix of
//! dtype-intrinsic reasons like `UndefinedEncoding` and backend-specific ones
//! like `UnimplementedYet`, so "lowest common crate, no inversion" is the
//! cleaner tiebreak).
/// Why a backend can — or cannot — hold a given [`DType`](crate::DType) in
/// device storage. The **WHY axis** of a decline (see the module docs); it does
/// **not** answer the orthogonal KIND question (reserved / recognized /
/// unknown), and must never grow a variant that does.