Skip to main content

oxicuda_driver/
error.rs

1//! Error types for the OxiCUDA driver crate.
2//!
3//! This module provides [`CudaError`], the primary error type returned by
4//! driver API wrappers, [`DriverLoadError`] for library-loading failures,
5//! the [`check`] function for converting raw result codes, and the
6//! `cuda_call!` macro for ergonomic unsafe FFI calls.
7
8use crate::ffi;
9
10// =========================================================================
11// CudaError — one variant per CUDA Driver API error code
12// =========================================================================
13
14/// Primary error type for CUDA driver API calls.
15///
16/// Each variant maps to a specific `CUresult` code from the CUDA Driver API.
17/// The [`Unknown`](CudaError::Unknown) variant is the catch-all for codes not
18/// explicitly listed.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
20pub enum CudaError {
21    // ----- Basic errors (1-8) -----
22    /// `CUDA_ERROR_INVALID_VALUE` (1)
23    #[error("CUDA: invalid value")]
24    InvalidValue,
25
26    /// `CUDA_ERROR_OUT_OF_MEMORY` (2)
27    #[error("CUDA: out of device memory")]
28    OutOfMemory,
29
30    /// `CUDA_ERROR_NOT_INITIALIZED` (3)
31    #[error("CUDA: not initialized")]
32    NotInitialized,
33
34    /// `CUDA_ERROR_DEINITIALIZED` (4)
35    #[error("CUDA: deinitialized")]
36    Deinitialized,
37
38    /// `CUDA_ERROR_PROFILER_DISABLED` (5)
39    #[error("CUDA: profiler disabled")]
40    ProfilerDisabled,
41
42    /// `CUDA_ERROR_PROFILER_NOT_INITIALIZED` (6)
43    #[error("CUDA: profiler not initialized (deprecated)")]
44    ProfilerNotInitialized,
45
46    /// `CUDA_ERROR_PROFILER_ALREADY_STARTED` (7)
47    #[error("CUDA: profiler already started (deprecated)")]
48    ProfilerAlreadyStarted,
49
50    /// `CUDA_ERROR_PROFILER_ALREADY_STOPPED` (8)
51    #[error("CUDA: profiler already stopped (deprecated)")]
52    ProfilerAlreadyStopped,
53
54    // ----- Stub / unavailable (34, 46) -----
55    /// `CUDA_ERROR_STUB_LIBRARY` (34)
56    #[error("CUDA: stub library loaded instead of real driver")]
57    StubLibrary,
58
59    /// `CUDA_ERROR_DEVICE_UNAVAILABLE` (46)
60    #[error("CUDA: device unavailable")]
61    DeviceUnavailable,
62
63    // ----- Device errors (100-102) -----
64    /// `CUDA_ERROR_NO_DEVICE` (100)
65    #[error("CUDA: no device found")]
66    NoDevice,
67
68    /// `CUDA_ERROR_INVALID_DEVICE` (101)
69    #[error("CUDA: invalid device")]
70    InvalidDevice,
71
72    /// `CUDA_ERROR_DEVICE_NOT_LICENSED` (102)
73    #[error("CUDA: device not licensed")]
74    DeviceNotLicensed,
75
76    // ----- Image / context / map errors (200-225) -----
77    /// `CUDA_ERROR_INVALID_IMAGE` (200)
78    #[error("CUDA: invalid PTX/cubin image")]
79    InvalidImage,
80
81    /// `CUDA_ERROR_INVALID_CONTEXT` (201)
82    #[error("CUDA: invalid context")]
83    InvalidContext,
84
85    /// `CUDA_ERROR_CONTEXT_ALREADY_CURRENT` (202)
86    #[error("CUDA: context already current (deprecated)")]
87    ContextAlreadyCurrent,
88
89    /// `CUDA_ERROR_MAP_FAILED` (205)
90    #[error("CUDA: map failed")]
91    MapFailed,
92
93    /// `CUDA_ERROR_UNMAP_FAILED` (206)
94    #[error("CUDA: unmap failed")]
95    UnmapFailed,
96
97    /// `CUDA_ERROR_ARRAY_IS_MAPPED` (207)
98    #[error("CUDA: array is mapped")]
99    ArrayIsMapped,
100
101    /// `CUDA_ERROR_ALREADY_MAPPED` (208)
102    #[error("CUDA: already mapped")]
103    AlreadyMapped,
104
105    /// `CUDA_ERROR_NO_BINARY_FOR_GPU` (209)
106    #[error("CUDA: no binary for GPU")]
107    NoBinaryForGpu,
108
109    /// `CUDA_ERROR_ALREADY_ACQUIRED` (210)
110    #[error("CUDA: already acquired")]
111    AlreadyAcquired,
112
113    /// `CUDA_ERROR_NOT_MAPPED` (211)
114    #[error("CUDA: not mapped")]
115    NotMapped,
116
117    /// `CUDA_ERROR_NOT_MAPPED_AS_ARRAY` (212)
118    #[error("CUDA: not mapped as array")]
119    NotMappedAsArray,
120
121    /// `CUDA_ERROR_NOT_MAPPED_AS_POINTER` (213)
122    #[error("CUDA: not mapped as pointer")]
123    NotMappedAsPointer,
124
125    /// `CUDA_ERROR_ECC_UNCORRECTABLE` (214)
126    #[error("CUDA: uncorrectable ECC error")]
127    EccUncorrectable,
128
129    /// `CUDA_ERROR_UNSUPPORTED_LIMIT` (215)
130    #[error("CUDA: unsupported limit")]
131    UnsupportedLimit,
132
133    /// `CUDA_ERROR_CONTEXT_ALREADY_IN_USE` (216)
134    #[error("CUDA: context already in use")]
135    ContextAlreadyInUse,
136
137    /// `CUDA_ERROR_PEER_ACCESS_UNSUPPORTED` (217)
138    #[error("CUDA: peer access unsupported")]
139    PeerAccessUnsupported,
140
141    /// `CUDA_ERROR_INVALID_PTX` (218)
142    #[error("CUDA: invalid PTX")]
143    InvalidPtx,
144
145    /// `CUDA_ERROR_INVALID_GRAPHICS_CONTEXT` (219)
146    #[error("CUDA: invalid graphics context")]
147    InvalidGraphicsContext,
148
149    /// `CUDA_ERROR_NVLINK_UNCORRECTABLE` (220)
150    #[error("CUDA: NVLINK uncorrectable error")]
151    NvlinkUncorrectable,
152
153    /// `CUDA_ERROR_JIT_COMPILER_NOT_FOUND` (221)
154    #[error("CUDA: JIT compiler not found")]
155    JitCompilerNotFound,
156
157    /// `CUDA_ERROR_UNSUPPORTED_PTX_VERSION` (222)
158    #[error("CUDA: unsupported PTX version")]
159    UnsupportedPtxVersion,
160
161    /// `CUDA_ERROR_JIT_COMPILATION_DISABLED` (223)
162    #[error("CUDA: JIT compilation disabled")]
163    JitCompilationDisabled,
164
165    /// `CUDA_ERROR_UNSUPPORTED_EXEC_AFFINITY` (224)
166    #[error("CUDA: unsupported exec affinity")]
167    UnsupportedExecAffinity,
168
169    /// `CUDA_ERROR_UNSUPPORTED_DEVSIDE_SYNC` (225)
170    #[error("CUDA: unsupported device-side sync")]
171    UnsupportedDevsideSync,
172
173    // ----- Source / file / shared-object errors (300-304) -----
174    /// `CUDA_ERROR_INVALID_SOURCE` (300)
175    #[error("CUDA: invalid source")]
176    InvalidSource,
177
178    /// `CUDA_ERROR_FILE_NOT_FOUND` (301)
179    #[error("CUDA: file not found")]
180    FileNotFound,
181
182    /// `CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND` (302)
183    #[error("CUDA: shared object symbol not found")]
184    SharedObjectSymbolNotFound,
185
186    /// `CUDA_ERROR_SHARED_OBJECT_INIT_FAILED` (303)
187    #[error("CUDA: shared object init failed")]
188    SharedObjectInitFailed,
189
190    /// `CUDA_ERROR_OPERATING_SYSTEM` (304)
191    #[error("CUDA: operating system error")]
192    OperatingSystem,
193
194    // ----- Handle / state errors (400-402) -----
195    /// `CUDA_ERROR_INVALID_HANDLE` (400)
196    #[error("CUDA: invalid handle")]
197    InvalidHandle,
198
199    /// `CUDA_ERROR_ILLEGAL_STATE` (401)
200    #[error("CUDA: illegal state")]
201    IllegalState,
202
203    /// `CUDA_ERROR_LOSSY_QUERY` (402)
204    #[error("CUDA: lossy query")]
205    LossyQuery,
206
207    // ----- Lookup error (500) -----
208    /// `CUDA_ERROR_NOT_FOUND` (500)
209    #[error("CUDA: symbol not found")]
210    NotFound,
211
212    // ----- Readiness error (600) -----
213    /// `CUDA_ERROR_NOT_READY` (600)
214    #[error("CUDA: not ready (async operation pending)")]
215    NotReady,
216
217    // ----- Launch / address / peer errors (700-720) -----
218    /// `CUDA_ERROR_ILLEGAL_ADDRESS` (700)
219    #[error("CUDA: illegal memory address")]
220    IllegalAddress,
221
222    /// `CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES` (701)
223    #[error("CUDA: kernel launch out of resources (registers/shared memory)")]
224    LaunchOutOfResources,
225
226    /// `CUDA_ERROR_LAUNCH_TIMEOUT` (702)
227    #[error("CUDA: kernel launch timeout")]
228    LaunchTimeout,
229
230    /// `CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING` (703)
231    #[error("CUDA: launch incompatible texturing")]
232    LaunchIncompatibleTexturing,
233
234    /// `CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED` (704)
235    #[error("CUDA: peer access already enabled")]
236    PeerAccessAlreadyEnabled,
237
238    /// `CUDA_ERROR_PEER_ACCESS_NOT_ENABLED` (705)
239    #[error("CUDA: peer access not enabled")]
240    PeerAccessNotEnabled,
241
242    /// `CUDA_ERROR_PRIMARY_CONTEXT_ACTIVE` (708)
243    #[error("CUDA: primary context active")]
244    PrimaryContextActive,
245
246    /// `CUDA_ERROR_CONTEXT_IS_DESTROYED` (709)
247    #[error("CUDA: context is destroyed")]
248    ContextIsDestroyed,
249
250    /// `CUDA_ERROR_ASSERT` (710)
251    #[error("CUDA: device-side assert triggered")]
252    Assert,
253
254    /// `CUDA_ERROR_TOO_MANY_PEERS` (711)
255    #[error("CUDA: too many peers")]
256    TooManyPeers,
257
258    /// `CUDA_ERROR_HOST_MEMORY_ALREADY_REGISTERED` (712)
259    #[error("CUDA: host memory already registered")]
260    HostMemoryAlreadyRegistered,
261
262    /// `CUDA_ERROR_HOST_MEMORY_NOT_REGISTERED` (713)
263    #[error("CUDA: host memory not registered")]
264    HostMemoryNotRegistered,
265
266    /// `CUDA_ERROR_HARDWARE_STACK_ERROR` (714)
267    #[error("CUDA: hardware stack error")]
268    HardwareStackError,
269
270    /// `CUDA_ERROR_ILLEGAL_INSTRUCTION` (715)
271    #[error("CUDA: illegal instruction")]
272    IllegalInstruction,
273
274    /// `CUDA_ERROR_MISALIGNED_ADDRESS` (716)
275    #[error("CUDA: misaligned address")]
276    MisalignedAddress,
277
278    /// `CUDA_ERROR_INVALID_ADDRESS_SPACE` (717)
279    #[error("CUDA: invalid address space")]
280    InvalidAddressSpace,
281
282    /// `CUDA_ERROR_INVALID_PC` (718)
283    #[error("CUDA: invalid program counter")]
284    InvalidPc,
285
286    /// `CUDA_ERROR_LAUNCH_FAILED` (719)
287    #[error("CUDA: kernel launch failed")]
288    LaunchFailed,
289
290    /// `CUDA_ERROR_COOPERATIVE_LAUNCH_TOO_LARGE` (720)
291    #[error("CUDA: cooperative launch too large")]
292    CooperativeLaunchTooLarge,
293
294    // ----- Permission / support errors (800-812) -----
295    /// `CUDA_ERROR_NOT_PERMITTED` (800)
296    #[error("CUDA: not permitted")]
297    NotPermitted,
298
299    /// `CUDA_ERROR_NOT_SUPPORTED` (801)
300    #[error("CUDA: not supported")]
301    NotSupported,
302
303    /// `CUDA_ERROR_SYSTEM_NOT_READY` (802)
304    #[error("CUDA: system not ready")]
305    SystemNotReady,
306
307    /// `CUDA_ERROR_SYSTEM_DRIVER_MISMATCH` (803)
308    #[error("CUDA: system driver mismatch")]
309    SystemDriverMismatch,
310
311    /// `CUDA_ERROR_COMPAT_NOT_SUPPORTED_ON_DEVICE` (804)
312    #[error("CUDA: compat not supported on device")]
313    CompatNotSupportedOnDevice,
314
315    /// `CUDA_ERROR_MPS_CONNECTION_FAILED` (805)
316    #[error("CUDA: MPS connection failed")]
317    MpsConnectionFailed,
318
319    /// `CUDA_ERROR_MPS_RPC_FAILURE` (806)
320    #[error("CUDA: MPS RPC failure")]
321    MpsRpcFailure,
322
323    /// `CUDA_ERROR_MPS_SERVER_NOT_READY` (807)
324    #[error("CUDA: MPS server not ready")]
325    MpsServerNotReady,
326
327    /// `CUDA_ERROR_MPS_MAX_CLIENTS_REACHED` (808)
328    #[error("CUDA: MPS max clients reached")]
329    MpsMaxClientsReached,
330
331    /// `CUDA_ERROR_MPS_MAX_CONNECTIONS_REACHED` (809)
332    #[error("CUDA: MPS max connections reached")]
333    MpsMaxConnectionsReached,
334
335    /// `CUDA_ERROR_MPS_CLIENT_TERMINATED` (810)
336    #[error("CUDA: MPS client terminated")]
337    MpsClientTerminated,
338
339    /// `CUDA_ERROR_CDP_NOT_SUPPORTED` (811)
340    #[error("CUDA: CDP not supported")]
341    CdpNotSupported,
342
343    /// `CUDA_ERROR_CDP_VERSION_MISMATCH` (812)
344    #[error("CUDA: CDP version mismatch")]
345    CdpVersionMismatch,
346
347    // ----- Stream capture / graph errors (900-915) -----
348    /// `CUDA_ERROR_STREAM_CAPTURE_UNSUPPORTED` (900)
349    #[error("CUDA: stream capture unsupported")]
350    StreamCaptureUnsupported,
351
352    /// `CUDA_ERROR_STREAM_CAPTURE_INVALIDATED` (901)
353    #[error("CUDA: stream capture invalidated")]
354    StreamCaptureInvalidated,
355
356    /// `CUDA_ERROR_STREAM_CAPTURE_MERGE` (902)
357    #[error("CUDA: stream capture merge not permitted")]
358    StreamCaptureMerge,
359
360    /// `CUDA_ERROR_STREAM_CAPTURE_UNMATCHED` (903)
361    #[error("CUDA: stream capture unmatched")]
362    StreamCaptureUnmatched,
363
364    /// `CUDA_ERROR_STREAM_CAPTURE_UNJOINED` (904)
365    #[error("CUDA: stream capture unjoined")]
366    StreamCaptureUnjoined,
367
368    /// `CUDA_ERROR_STREAM_CAPTURE_ISOLATION` (905)
369    #[error("CUDA: stream capture isolation violation")]
370    StreamCaptureIsolation,
371
372    /// `CUDA_ERROR_STREAM_CAPTURE_IMPLICIT` (906)
373    #[error("CUDA: implicit stream in graph capture")]
374    StreamCaptureImplicit,
375
376    /// `CUDA_ERROR_CAPTURED_EVENT` (907)
377    #[error("CUDA: captured event error")]
378    CapturedEvent,
379
380    /// `CUDA_ERROR_STREAM_CAPTURE_WRONG_THREAD` (908)
381    #[error("CUDA: stream capture wrong thread")]
382    StreamCaptureWrongThread,
383
384    /// `CUDA_ERROR_TIMEOUT` (909)
385    #[error("CUDA: async operation timed out")]
386    Timeout,
387
388    /// `CUDA_ERROR_GRAPH_EXEC_UPDATE_FAILURE` (910)
389    #[error("CUDA: graph exec update failure")]
390    GraphExecUpdateFailure,
391
392    /// `CUDA_ERROR_EXTERNAL_DEVICE` (911)
393    #[error("CUDA: external device error")]
394    ExternalDevice,
395
396    /// `CUDA_ERROR_INVALID_CLUSTER_SIZE` (912)
397    #[error("CUDA: invalid cluster size")]
398    InvalidClusterSize,
399
400    /// `CUDA_ERROR_FUNCTION_NOT_LOADED` (913)
401    #[error("CUDA: function not loaded")]
402    FunctionNotLoaded,
403
404    /// `CUDA_ERROR_INVALID_RESOURCE_TYPE` (914)
405    #[error("CUDA: invalid resource type")]
406    InvalidResourceType,
407
408    /// `CUDA_ERROR_INVALID_RESOURCE_CONFIGURATION` (915)
409    #[error("CUDA: invalid resource configuration")]
410    InvalidResourceConfiguration,
411
412    // ----- Catch-all -----
413    /// Unknown error code not covered by any other variant.
414    #[error("CUDA: unknown error (code {0})")]
415    Unknown(u32),
416}
417
418impl CudaError {
419    /// Convert a raw `CUresult` code into the corresponding [`CudaError`] variant.
420    ///
421    /// This should not be called with `CUDA_SUCCESS` (0); prefer using [`check`]
422    /// which returns `Ok(())` for success.
423    #[allow(clippy::too_many_lines)]
424    pub fn from_raw(code: u32) -> Self {
425        match code {
426            ffi::CUDA_ERROR_INVALID_VALUE => Self::InvalidValue,
427            ffi::CUDA_ERROR_OUT_OF_MEMORY => Self::OutOfMemory,
428            ffi::CUDA_ERROR_NOT_INITIALIZED => Self::NotInitialized,
429            ffi::CUDA_ERROR_DEINITIALIZED => Self::Deinitialized,
430            ffi::CUDA_ERROR_PROFILER_DISABLED => Self::ProfilerDisabled,
431            ffi::CUDA_ERROR_PROFILER_NOT_INITIALIZED => Self::ProfilerNotInitialized,
432            ffi::CUDA_ERROR_PROFILER_ALREADY_STARTED => Self::ProfilerAlreadyStarted,
433            ffi::CUDA_ERROR_PROFILER_ALREADY_STOPPED => Self::ProfilerAlreadyStopped,
434            ffi::CUDA_ERROR_STUB_LIBRARY => Self::StubLibrary,
435            ffi::CUDA_ERROR_DEVICE_UNAVAILABLE => Self::DeviceUnavailable,
436            ffi::CUDA_ERROR_NO_DEVICE => Self::NoDevice,
437            ffi::CUDA_ERROR_INVALID_DEVICE => Self::InvalidDevice,
438            ffi::CUDA_ERROR_DEVICE_NOT_LICENSED => Self::DeviceNotLicensed,
439            ffi::CUDA_ERROR_INVALID_IMAGE => Self::InvalidImage,
440            ffi::CUDA_ERROR_INVALID_CONTEXT => Self::InvalidContext,
441            ffi::CUDA_ERROR_CONTEXT_ALREADY_CURRENT => Self::ContextAlreadyCurrent,
442            ffi::CUDA_ERROR_MAP_FAILED => Self::MapFailed,
443            ffi::CUDA_ERROR_UNMAP_FAILED => Self::UnmapFailed,
444            ffi::CUDA_ERROR_ARRAY_IS_MAPPED => Self::ArrayIsMapped,
445            ffi::CUDA_ERROR_ALREADY_MAPPED => Self::AlreadyMapped,
446            ffi::CUDA_ERROR_NO_BINARY_FOR_GPU => Self::NoBinaryForGpu,
447            ffi::CUDA_ERROR_ALREADY_ACQUIRED => Self::AlreadyAcquired,
448            ffi::CUDA_ERROR_NOT_MAPPED => Self::NotMapped,
449            ffi::CUDA_ERROR_NOT_MAPPED_AS_ARRAY => Self::NotMappedAsArray,
450            ffi::CUDA_ERROR_NOT_MAPPED_AS_POINTER => Self::NotMappedAsPointer,
451            ffi::CUDA_ERROR_ECC_UNCORRECTABLE => Self::EccUncorrectable,
452            ffi::CUDA_ERROR_UNSUPPORTED_LIMIT => Self::UnsupportedLimit,
453            ffi::CUDA_ERROR_CONTEXT_ALREADY_IN_USE => Self::ContextAlreadyInUse,
454            ffi::CUDA_ERROR_PEER_ACCESS_UNSUPPORTED => Self::PeerAccessUnsupported,
455            ffi::CUDA_ERROR_INVALID_PTX => Self::InvalidPtx,
456            ffi::CUDA_ERROR_INVALID_GRAPHICS_CONTEXT => Self::InvalidGraphicsContext,
457            ffi::CUDA_ERROR_NVLINK_UNCORRECTABLE => Self::NvlinkUncorrectable,
458            ffi::CUDA_ERROR_JIT_COMPILER_NOT_FOUND => Self::JitCompilerNotFound,
459            ffi::CUDA_ERROR_UNSUPPORTED_PTX_VERSION => Self::UnsupportedPtxVersion,
460            ffi::CUDA_ERROR_JIT_COMPILATION_DISABLED => Self::JitCompilationDisabled,
461            ffi::CUDA_ERROR_UNSUPPORTED_EXEC_AFFINITY => Self::UnsupportedExecAffinity,
462            ffi::CUDA_ERROR_UNSUPPORTED_DEVSIDE_SYNC => Self::UnsupportedDevsideSync,
463            ffi::CUDA_ERROR_INVALID_SOURCE => Self::InvalidSource,
464            ffi::CUDA_ERROR_FILE_NOT_FOUND => Self::FileNotFound,
465            ffi::CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND => Self::SharedObjectSymbolNotFound,
466            ffi::CUDA_ERROR_SHARED_OBJECT_INIT_FAILED => Self::SharedObjectInitFailed,
467            ffi::CUDA_ERROR_OPERATING_SYSTEM => Self::OperatingSystem,
468            ffi::CUDA_ERROR_INVALID_HANDLE => Self::InvalidHandle,
469            ffi::CUDA_ERROR_ILLEGAL_STATE => Self::IllegalState,
470            ffi::CUDA_ERROR_LOSSY_QUERY => Self::LossyQuery,
471            ffi::CUDA_ERROR_NOT_FOUND => Self::NotFound,
472            ffi::CUDA_ERROR_NOT_READY => Self::NotReady,
473            ffi::CUDA_ERROR_ILLEGAL_ADDRESS => Self::IllegalAddress,
474            ffi::CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES => Self::LaunchOutOfResources,
475            ffi::CUDA_ERROR_LAUNCH_TIMEOUT => Self::LaunchTimeout,
476            ffi::CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING => Self::LaunchIncompatibleTexturing,
477            ffi::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED => Self::PeerAccessAlreadyEnabled,
478            ffi::CUDA_ERROR_PEER_ACCESS_NOT_ENABLED => Self::PeerAccessNotEnabled,
479            ffi::CUDA_ERROR_PRIMARY_CONTEXT_ACTIVE => Self::PrimaryContextActive,
480            ffi::CUDA_ERROR_CONTEXT_IS_DESTROYED => Self::ContextIsDestroyed,
481            ffi::CUDA_ERROR_ASSERT => Self::Assert,
482            ffi::CUDA_ERROR_TOO_MANY_PEERS => Self::TooManyPeers,
483            ffi::CUDA_ERROR_HOST_MEMORY_ALREADY_REGISTERED => Self::HostMemoryAlreadyRegistered,
484            ffi::CUDA_ERROR_HOST_MEMORY_NOT_REGISTERED => Self::HostMemoryNotRegistered,
485            ffi::CUDA_ERROR_HARDWARE_STACK_ERROR => Self::HardwareStackError,
486            ffi::CUDA_ERROR_ILLEGAL_INSTRUCTION => Self::IllegalInstruction,
487            ffi::CUDA_ERROR_MISALIGNED_ADDRESS => Self::MisalignedAddress,
488            ffi::CUDA_ERROR_INVALID_ADDRESS_SPACE => Self::InvalidAddressSpace,
489            ffi::CUDA_ERROR_INVALID_PC => Self::InvalidPc,
490            ffi::CUDA_ERROR_LAUNCH_FAILED => Self::LaunchFailed,
491            ffi::CUDA_ERROR_COOPERATIVE_LAUNCH_TOO_LARGE => Self::CooperativeLaunchTooLarge,
492            ffi::CUDA_ERROR_NOT_PERMITTED => Self::NotPermitted,
493            ffi::CUDA_ERROR_NOT_SUPPORTED => Self::NotSupported,
494            ffi::CUDA_ERROR_SYSTEM_NOT_READY => Self::SystemNotReady,
495            ffi::CUDA_ERROR_SYSTEM_DRIVER_MISMATCH => Self::SystemDriverMismatch,
496            ffi::CUDA_ERROR_COMPAT_NOT_SUPPORTED_ON_DEVICE => Self::CompatNotSupportedOnDevice,
497            ffi::CUDA_ERROR_MPS_CONNECTION_FAILED => Self::MpsConnectionFailed,
498            ffi::CUDA_ERROR_MPS_RPC_FAILURE => Self::MpsRpcFailure,
499            ffi::CUDA_ERROR_MPS_SERVER_NOT_READY => Self::MpsServerNotReady,
500            ffi::CUDA_ERROR_MPS_MAX_CLIENTS_REACHED => Self::MpsMaxClientsReached,
501            ffi::CUDA_ERROR_MPS_MAX_CONNECTIONS_REACHED => Self::MpsMaxConnectionsReached,
502            ffi::CUDA_ERROR_MPS_CLIENT_TERMINATED => Self::MpsClientTerminated,
503            ffi::CUDA_ERROR_CDP_NOT_SUPPORTED => Self::CdpNotSupported,
504            ffi::CUDA_ERROR_CDP_VERSION_MISMATCH => Self::CdpVersionMismatch,
505            ffi::CUDA_ERROR_STREAM_CAPTURE_UNSUPPORTED => Self::StreamCaptureUnsupported,
506            ffi::CUDA_ERROR_STREAM_CAPTURE_INVALIDATED => Self::StreamCaptureInvalidated,
507            ffi::CUDA_ERROR_STREAM_CAPTURE_MERGE => Self::StreamCaptureMerge,
508            ffi::CUDA_ERROR_STREAM_CAPTURE_UNMATCHED => Self::StreamCaptureUnmatched,
509            ffi::CUDA_ERROR_STREAM_CAPTURE_UNJOINED => Self::StreamCaptureUnjoined,
510            ffi::CUDA_ERROR_STREAM_CAPTURE_ISOLATION => Self::StreamCaptureIsolation,
511            ffi::CUDA_ERROR_STREAM_CAPTURE_IMPLICIT => Self::StreamCaptureImplicit,
512            ffi::CUDA_ERROR_CAPTURED_EVENT => Self::CapturedEvent,
513            ffi::CUDA_ERROR_STREAM_CAPTURE_WRONG_THREAD => Self::StreamCaptureWrongThread,
514            ffi::CUDA_ERROR_TIMEOUT => Self::Timeout,
515            ffi::CUDA_ERROR_GRAPH_EXEC_UPDATE_FAILURE => Self::GraphExecUpdateFailure,
516            ffi::CUDA_ERROR_EXTERNAL_DEVICE => Self::ExternalDevice,
517            ffi::CUDA_ERROR_INVALID_CLUSTER_SIZE => Self::InvalidClusterSize,
518            ffi::CUDA_ERROR_FUNCTION_NOT_LOADED => Self::FunctionNotLoaded,
519            ffi::CUDA_ERROR_INVALID_RESOURCE_TYPE => Self::InvalidResourceType,
520            ffi::CUDA_ERROR_INVALID_RESOURCE_CONFIGURATION => Self::InvalidResourceConfiguration,
521            ffi::CUDA_ERROR_UNKNOWN => Self::Unknown(999),
522            other => Self::Unknown(other),
523        }
524    }
525
526    /// Returns `true` for errors that indicate a **fatal**, non-recoverable
527    /// GPU state — meaning the current CUDA context is likely corrupted and
528    /// should be destroyed before attempting further GPU work.
529    ///
530    /// # Rationale
531    ///
532    /// Most CUDA errors are caused by incorrect API usage (e.g. bad argument,
533    /// unsupported feature) and do not invalidate the GPU context. A small
534    /// subset — hardware faults, illegal instructions, or illegal memory
535    /// accesses — leave the device in an indeterminate state.
536    ///
537    /// # Examples
538    ///
539    /// ```
540    /// use oxicuda_driver::CudaError;
541    ///
542    /// // Hardware fault — fatal
543    /// assert!(CudaError::IllegalAddress.is_fatal());
544    /// assert!(CudaError::LaunchFailed.is_fatal());
545    /// assert!(CudaError::HardwareStackError.is_fatal());
546    ///
547    /// // Bad argument — recoverable
548    /// assert!(!CudaError::InvalidValue.is_fatal());
549    /// assert!(!CudaError::OutOfMemory.is_fatal());
550    /// assert!(!CudaError::NotReady.is_fatal());
551    /// ```
552    #[must_use]
553    pub fn is_fatal(&self) -> bool {
554        matches!(
555            self,
556            // Hardware / device-side faults that corrupt context state.
557            Self::IllegalAddress
558                | Self::LaunchFailed
559                | Self::HardwareStackError
560                | Self::IllegalInstruction
561                | Self::MisalignedAddress
562                | Self::InvalidAddressSpace
563                | Self::InvalidPc
564                | Self::Assert
565                | Self::EccUncorrectable
566                | Self::NvlinkUncorrectable
567                // Driver / context state is unrecoverable.
568                | Self::ContextIsDestroyed
569                | Self::Deinitialized
570        )
571    }
572
573    /// Returns `true` for errors that are clearly caused by incorrect API
574    /// usage rather than hardware faults or resource exhaustion.
575    ///
576    /// These errors indicate the caller passed bad arguments and the GPU
577    /// state is intact.
578    ///
579    /// ```
580    /// use oxicuda_driver::CudaError;
581    ///
582    /// assert!(CudaError::InvalidValue.is_usage_error());
583    /// assert!(CudaError::InvalidDevice.is_usage_error());
584    /// assert!(!CudaError::OutOfMemory.is_usage_error());
585    /// ```
586    #[must_use]
587    pub fn is_usage_error(&self) -> bool {
588        matches!(
589            self,
590            Self::InvalidValue
591                | Self::InvalidDevice
592                | Self::InvalidContext
593                | Self::InvalidHandle
594                | Self::InvalidImage
595                | Self::InvalidPtx
596                | Self::InvalidSource
597                | Self::InvalidClusterSize
598                | Self::NoDevice
599                | Self::UnsupportedLimit
600                | Self::NotSupported
601                | Self::NotPermitted
602        )
603    }
604
605    /// Convert this error back to its raw `CUresult` code.
606    #[allow(clippy::too_many_lines)]
607    pub fn as_raw(&self) -> u32 {
608        match self {
609            Self::InvalidValue => ffi::CUDA_ERROR_INVALID_VALUE,
610            Self::OutOfMemory => ffi::CUDA_ERROR_OUT_OF_MEMORY,
611            Self::NotInitialized => ffi::CUDA_ERROR_NOT_INITIALIZED,
612            Self::Deinitialized => ffi::CUDA_ERROR_DEINITIALIZED,
613            Self::ProfilerDisabled => ffi::CUDA_ERROR_PROFILER_DISABLED,
614            Self::ProfilerNotInitialized => ffi::CUDA_ERROR_PROFILER_NOT_INITIALIZED,
615            Self::ProfilerAlreadyStarted => ffi::CUDA_ERROR_PROFILER_ALREADY_STARTED,
616            Self::ProfilerAlreadyStopped => ffi::CUDA_ERROR_PROFILER_ALREADY_STOPPED,
617            Self::StubLibrary => ffi::CUDA_ERROR_STUB_LIBRARY,
618            Self::DeviceUnavailable => ffi::CUDA_ERROR_DEVICE_UNAVAILABLE,
619            Self::NoDevice => ffi::CUDA_ERROR_NO_DEVICE,
620            Self::InvalidDevice => ffi::CUDA_ERROR_INVALID_DEVICE,
621            Self::DeviceNotLicensed => ffi::CUDA_ERROR_DEVICE_NOT_LICENSED,
622            Self::InvalidImage => ffi::CUDA_ERROR_INVALID_IMAGE,
623            Self::InvalidContext => ffi::CUDA_ERROR_INVALID_CONTEXT,
624            Self::ContextAlreadyCurrent => ffi::CUDA_ERROR_CONTEXT_ALREADY_CURRENT,
625            Self::MapFailed => ffi::CUDA_ERROR_MAP_FAILED,
626            Self::UnmapFailed => ffi::CUDA_ERROR_UNMAP_FAILED,
627            Self::ArrayIsMapped => ffi::CUDA_ERROR_ARRAY_IS_MAPPED,
628            Self::AlreadyMapped => ffi::CUDA_ERROR_ALREADY_MAPPED,
629            Self::NoBinaryForGpu => ffi::CUDA_ERROR_NO_BINARY_FOR_GPU,
630            Self::AlreadyAcquired => ffi::CUDA_ERROR_ALREADY_ACQUIRED,
631            Self::NotMapped => ffi::CUDA_ERROR_NOT_MAPPED,
632            Self::NotMappedAsArray => ffi::CUDA_ERROR_NOT_MAPPED_AS_ARRAY,
633            Self::NotMappedAsPointer => ffi::CUDA_ERROR_NOT_MAPPED_AS_POINTER,
634            Self::EccUncorrectable => ffi::CUDA_ERROR_ECC_UNCORRECTABLE,
635            Self::UnsupportedLimit => ffi::CUDA_ERROR_UNSUPPORTED_LIMIT,
636            Self::ContextAlreadyInUse => ffi::CUDA_ERROR_CONTEXT_ALREADY_IN_USE,
637            Self::PeerAccessUnsupported => ffi::CUDA_ERROR_PEER_ACCESS_UNSUPPORTED,
638            Self::InvalidPtx => ffi::CUDA_ERROR_INVALID_PTX,
639            Self::InvalidGraphicsContext => ffi::CUDA_ERROR_INVALID_GRAPHICS_CONTEXT,
640            Self::NvlinkUncorrectable => ffi::CUDA_ERROR_NVLINK_UNCORRECTABLE,
641            Self::JitCompilerNotFound => ffi::CUDA_ERROR_JIT_COMPILER_NOT_FOUND,
642            Self::UnsupportedPtxVersion => ffi::CUDA_ERROR_UNSUPPORTED_PTX_VERSION,
643            Self::JitCompilationDisabled => ffi::CUDA_ERROR_JIT_COMPILATION_DISABLED,
644            Self::UnsupportedExecAffinity => ffi::CUDA_ERROR_UNSUPPORTED_EXEC_AFFINITY,
645            Self::UnsupportedDevsideSync => ffi::CUDA_ERROR_UNSUPPORTED_DEVSIDE_SYNC,
646            Self::InvalidSource => ffi::CUDA_ERROR_INVALID_SOURCE,
647            Self::FileNotFound => ffi::CUDA_ERROR_FILE_NOT_FOUND,
648            Self::SharedObjectSymbolNotFound => ffi::CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND,
649            Self::SharedObjectInitFailed => ffi::CUDA_ERROR_SHARED_OBJECT_INIT_FAILED,
650            Self::OperatingSystem => ffi::CUDA_ERROR_OPERATING_SYSTEM,
651            Self::InvalidHandle => ffi::CUDA_ERROR_INVALID_HANDLE,
652            Self::IllegalState => ffi::CUDA_ERROR_ILLEGAL_STATE,
653            Self::LossyQuery => ffi::CUDA_ERROR_LOSSY_QUERY,
654            Self::NotFound => ffi::CUDA_ERROR_NOT_FOUND,
655            Self::NotReady => ffi::CUDA_ERROR_NOT_READY,
656            Self::IllegalAddress => ffi::CUDA_ERROR_ILLEGAL_ADDRESS,
657            Self::LaunchOutOfResources => ffi::CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES,
658            Self::LaunchTimeout => ffi::CUDA_ERROR_LAUNCH_TIMEOUT,
659            Self::LaunchIncompatibleTexturing => ffi::CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING,
660            Self::PeerAccessAlreadyEnabled => ffi::CUDA_ERROR_PEER_ACCESS_ALREADY_ENABLED,
661            Self::PeerAccessNotEnabled => ffi::CUDA_ERROR_PEER_ACCESS_NOT_ENABLED,
662            Self::PrimaryContextActive => ffi::CUDA_ERROR_PRIMARY_CONTEXT_ACTIVE,
663            Self::ContextIsDestroyed => ffi::CUDA_ERROR_CONTEXT_IS_DESTROYED,
664            Self::Assert => ffi::CUDA_ERROR_ASSERT,
665            Self::TooManyPeers => ffi::CUDA_ERROR_TOO_MANY_PEERS,
666            Self::HostMemoryAlreadyRegistered => ffi::CUDA_ERROR_HOST_MEMORY_ALREADY_REGISTERED,
667            Self::HostMemoryNotRegistered => ffi::CUDA_ERROR_HOST_MEMORY_NOT_REGISTERED,
668            Self::HardwareStackError => ffi::CUDA_ERROR_HARDWARE_STACK_ERROR,
669            Self::IllegalInstruction => ffi::CUDA_ERROR_ILLEGAL_INSTRUCTION,
670            Self::MisalignedAddress => ffi::CUDA_ERROR_MISALIGNED_ADDRESS,
671            Self::InvalidAddressSpace => ffi::CUDA_ERROR_INVALID_ADDRESS_SPACE,
672            Self::InvalidPc => ffi::CUDA_ERROR_INVALID_PC,
673            Self::LaunchFailed => ffi::CUDA_ERROR_LAUNCH_FAILED,
674            Self::CooperativeLaunchTooLarge => ffi::CUDA_ERROR_COOPERATIVE_LAUNCH_TOO_LARGE,
675            Self::NotPermitted => ffi::CUDA_ERROR_NOT_PERMITTED,
676            Self::NotSupported => ffi::CUDA_ERROR_NOT_SUPPORTED,
677            Self::SystemNotReady => ffi::CUDA_ERROR_SYSTEM_NOT_READY,
678            Self::SystemDriverMismatch => ffi::CUDA_ERROR_SYSTEM_DRIVER_MISMATCH,
679            Self::CompatNotSupportedOnDevice => ffi::CUDA_ERROR_COMPAT_NOT_SUPPORTED_ON_DEVICE,
680            Self::MpsConnectionFailed => ffi::CUDA_ERROR_MPS_CONNECTION_FAILED,
681            Self::MpsRpcFailure => ffi::CUDA_ERROR_MPS_RPC_FAILURE,
682            Self::MpsServerNotReady => ffi::CUDA_ERROR_MPS_SERVER_NOT_READY,
683            Self::MpsMaxClientsReached => ffi::CUDA_ERROR_MPS_MAX_CLIENTS_REACHED,
684            Self::MpsMaxConnectionsReached => ffi::CUDA_ERROR_MPS_MAX_CONNECTIONS_REACHED,
685            Self::MpsClientTerminated => ffi::CUDA_ERROR_MPS_CLIENT_TERMINATED,
686            Self::CdpNotSupported => ffi::CUDA_ERROR_CDP_NOT_SUPPORTED,
687            Self::CdpVersionMismatch => ffi::CUDA_ERROR_CDP_VERSION_MISMATCH,
688            Self::StreamCaptureUnsupported => ffi::CUDA_ERROR_STREAM_CAPTURE_UNSUPPORTED,
689            Self::StreamCaptureInvalidated => ffi::CUDA_ERROR_STREAM_CAPTURE_INVALIDATED,
690            Self::StreamCaptureMerge => ffi::CUDA_ERROR_STREAM_CAPTURE_MERGE,
691            Self::StreamCaptureUnmatched => ffi::CUDA_ERROR_STREAM_CAPTURE_UNMATCHED,
692            Self::StreamCaptureUnjoined => ffi::CUDA_ERROR_STREAM_CAPTURE_UNJOINED,
693            Self::StreamCaptureIsolation => ffi::CUDA_ERROR_STREAM_CAPTURE_ISOLATION,
694            Self::StreamCaptureImplicit => ffi::CUDA_ERROR_STREAM_CAPTURE_IMPLICIT,
695            Self::CapturedEvent => ffi::CUDA_ERROR_CAPTURED_EVENT,
696            Self::StreamCaptureWrongThread => ffi::CUDA_ERROR_STREAM_CAPTURE_WRONG_THREAD,
697            Self::Timeout => ffi::CUDA_ERROR_TIMEOUT,
698            Self::GraphExecUpdateFailure => ffi::CUDA_ERROR_GRAPH_EXEC_UPDATE_FAILURE,
699            Self::ExternalDevice => ffi::CUDA_ERROR_EXTERNAL_DEVICE,
700            Self::InvalidClusterSize => ffi::CUDA_ERROR_INVALID_CLUSTER_SIZE,
701            Self::FunctionNotLoaded => ffi::CUDA_ERROR_FUNCTION_NOT_LOADED,
702            Self::InvalidResourceType => ffi::CUDA_ERROR_INVALID_RESOURCE_TYPE,
703            Self::InvalidResourceConfiguration => ffi::CUDA_ERROR_INVALID_RESOURCE_CONFIGURATION,
704            Self::Unknown(code) => *code,
705        }
706    }
707}
708
709// =========================================================================
710// CudaResult type alias
711// =========================================================================
712
713/// Convenience result alias used throughout the crate.
714pub type CudaResult<T> = Result<T, CudaError>;
715
716// =========================================================================
717// check() — convert raw CUresult to CudaResult
718// =========================================================================
719
720/// Convert a raw [`CUresult`](ffi::CUresult) into a [`CudaResult<()>`].
721///
722/// Returns `Ok(())` for `CUDA_SUCCESS` (0), otherwise wraps the code in the
723/// corresponding [`CudaError`] variant.
724#[inline(always)]
725pub fn check(result: u32) -> CudaResult<()> {
726    if result == 0 {
727        Ok(())
728    } else {
729        Err(CudaError::from_raw(result))
730    }
731}
732
733// =========================================================================
734// cuda_call! macro
735// =========================================================================
736
737/// Invoke a raw CUDA Driver API function and convert the result to [`CudaResult`].
738///
739/// The expression is evaluated inside an `unsafe` block and its `CUresult`
740/// return value is passed through [`check`].
741///
742/// # Examples
743///
744/// ```ignore
745/// cuda_call!(cuInit(0))?;
746/// ```
747#[macro_export]
748macro_rules! cuda_call {
749    ($expr:expr) => {
750        $crate::error::check(unsafe { $expr })
751    };
752}
753
754// =========================================================================
755// DriverLoadError
756// =========================================================================
757
758/// Errors that can occur while dynamically loading `libcuda.so` / `nvcuda.dll`.
759#[derive(Debug, thiserror::Error)]
760pub enum DriverLoadError {
761    /// The CUDA driver library was not found on the system.
762    #[error("CUDA driver library not found (tried: {candidates:?}): {last_error}")]
763    LibraryNotFound {
764        /// Library names that were attempted.
765        candidates: Vec<String>,
766        /// OS-level error from the last attempt.
767        last_error: String,
768    },
769
770    /// The shared library was loaded but a required symbol was missing.
771    #[error("failed to load symbol '{symbol}': {reason}")]
772    SymbolNotFound {
773        /// Name of the missing symbol (e.g. `"cuInit"`).
774        symbol: &'static str,
775        /// OS-level error description.
776        reason: String,
777    },
778
779    /// The CUDA driver library loaded and all symbols resolved, but `cuInit(0)`
780    /// returned a non-zero error code.
781    ///
782    /// This typically happens when there is no CUDA-capable GPU installed
783    /// (`CUDA_ERROR_NO_DEVICE` = 100) or when the installed driver is
784    /// incompatible with the hardware.
785    #[error("cuInit(0) failed with CUDA error code {code}")]
786    InitializationFailed {
787        /// Raw CUDA error code returned by `cuInit`.
788        code: u32,
789    },
790
791    /// CUDA is not supported on this operating system (e.g. macOS).
792    #[error("CUDA driver not supported on this platform")]
793    UnsupportedPlatform,
794}
795
796// =========================================================================
797// Tests
798// =========================================================================
799
800#[cfg(test)]
801mod tests {
802    use super::*;
803
804    #[test]
805    fn test_check_success() {
806        assert!(check(0).is_ok());
807    }
808
809    #[test]
810    fn test_check_error() {
811        let result = check(1);
812        assert!(result.is_err());
813        assert_eq!(result.err(), Some(CudaError::InvalidValue));
814    }
815
816    #[test]
817    fn test_check_out_of_memory() {
818        let result = check(2);
819        assert_eq!(result.err(), Some(CudaError::OutOfMemory));
820    }
821
822    #[test]
823    fn test_check_not_initialized() {
824        let result = check(3);
825        assert_eq!(result.err(), Some(CudaError::NotInitialized));
826    }
827
828    #[test]
829    fn test_from_raw_roundtrip() {
830        // Test a representative sample of error codes round-trip correctly
831        let codes: &[u32] = &[
832            1, 2, 3, 4, 5, 6, 7, 8, 34, 46, 100, 101, 102, 200, 201, 202, 205, 206, 207, 208, 209,
833            210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 300,
834            301, 302, 303, 304, 400, 401, 402, 500, 600, 700, 701, 702, 703, 704, 705, 708, 709,
835            710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 800, 801, 802, 803, 804, 805,
836            806, 807, 808, 809, 810, 811, 812, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909,
837            910, 911, 912, 913, 914, 915, 999,
838        ];
839        for &code in codes {
840            let err = CudaError::from_raw(code);
841            assert_eq!(
842                err.as_raw(),
843                code,
844                "round-trip failed for code {code}: got {:?}",
845                err
846            );
847        }
848    }
849
850    #[test]
851    fn test_unknown_code() {
852        let err = CudaError::from_raw(12345);
853        assert_eq!(err, CudaError::Unknown(12345));
854        assert_eq!(err.as_raw(), 12345);
855    }
856
857    #[test]
858    fn test_unknown_code_display() {
859        let err = CudaError::from_raw(12345);
860        let msg = format!("{err}");
861        assert!(
862            msg.contains("12345"),
863            "display should contain the code: {msg}"
864        );
865    }
866
867    #[test]
868    fn test_error_display_messages() {
869        assert_eq!(
870            format!("{}", CudaError::InvalidValue),
871            "CUDA: invalid value"
872        );
873        assert_eq!(
874            format!("{}", CudaError::OutOfMemory),
875            "CUDA: out of device memory"
876        );
877        assert_eq!(
878            format!("{}", CudaError::LaunchFailed),
879            "CUDA: kernel launch failed"
880        );
881        assert_eq!(
882            format!("{}", CudaError::NotReady),
883            "CUDA: not ready (async operation pending)"
884        );
885    }
886
887    #[test]
888    fn test_error_is_copy() {
889        let err = CudaError::InvalidValue;
890        let copy = err;
891        assert_eq!(err, copy);
892    }
893
894    #[test]
895    fn test_error_implements_std_error() {
896        fn assert_error<T: std::error::Error>() {}
897        assert_error::<CudaError>();
898        assert_error::<DriverLoadError>();
899    }
900
901    #[test]
902    fn test_driver_load_error_display() {
903        let err = DriverLoadError::LibraryNotFound {
904            candidates: vec!["libcuda.so".to_string()],
905            last_error: "no such file or directory".to_string(),
906        };
907        let msg = format!("{err}");
908        assert!(msg.contains("libcuda.so"));
909
910        let err = DriverLoadError::SymbolNotFound {
911            symbol: "cuInit",
912            reason: "not found".to_string(),
913        };
914        let msg = format!("{err}");
915        assert!(msg.contains("cuInit"));
916
917        let err = DriverLoadError::UnsupportedPlatform;
918        let msg = format!("{err}");
919        assert!(msg.contains("not supported"));
920    }
921
922    #[test]
923    fn test_cuda_call_macro() {
924        // Simulate a successful call via a function that returns CUresult
925        unsafe fn fake_success() -> u32 {
926            0
927        }
928        unsafe fn fake_launch_failed() -> u32 {
929            719
930        }
931
932        let result = cuda_call!(fake_success());
933        assert!(result.is_ok());
934
935        let result = cuda_call!(fake_launch_failed());
936        assert_eq!(result.err(), Some(CudaError::LaunchFailed));
937    }
938
939    #[test]
940    fn test_error_hash() {
941        use std::collections::HashSet;
942        let mut set = HashSet::new();
943        set.insert(CudaError::InvalidValue);
944        set.insert(CudaError::OutOfMemory);
945        set.insert(CudaError::InvalidValue); // duplicate
946        assert_eq!(set.len(), 2);
947    }
948
949    #[test]
950    fn test_cuda_result_type_alias() {
951        fn returns_ok() -> CudaResult<i32> {
952            Ok(42)
953        }
954        fn returns_err() -> CudaResult<i32> {
955            Err(CudaError::NoDevice)
956        }
957        assert_eq!(returns_ok().ok(), Some(42));
958        assert_eq!(returns_err().err(), Some(CudaError::NoDevice));
959    }
960
961    // =========================================================================
962    // Error injection test suite
963    // =========================================================================
964
965    /// Verifies every commonly-used CudaError variant has a non-empty Display.
966    #[test]
967    fn test_all_common_cuda_error_variants_have_display() {
968        let errors: &[CudaError] = &[
969            // Basic errors (1-8)
970            CudaError::InvalidValue,
971            CudaError::OutOfMemory,
972            CudaError::NotInitialized,
973            CudaError::Deinitialized,
974            CudaError::ProfilerDisabled,
975            CudaError::ProfilerNotInitialized,
976            CudaError::ProfilerAlreadyStarted,
977            CudaError::ProfilerAlreadyStopped,
978            // Stub / unavailable
979            CudaError::StubLibrary,
980            CudaError::DeviceUnavailable,
981            // Device errors
982            CudaError::NoDevice,
983            CudaError::InvalidDevice,
984            CudaError::DeviceNotLicensed,
985            // Image / context errors
986            CudaError::InvalidImage,
987            CudaError::InvalidContext,
988            CudaError::ContextAlreadyCurrent,
989            CudaError::MapFailed,
990            CudaError::UnmapFailed,
991            CudaError::EccUncorrectable,
992            // Source / file errors
993            CudaError::InvalidSource,
994            CudaError::FileNotFound,
995            CudaError::SharedObjectSymbolNotFound,
996            // Handle errors
997            CudaError::InvalidHandle,
998            CudaError::IllegalState,
999            CudaError::NotFound,
1000            // Readiness
1001            CudaError::NotReady,
1002            // Launch / address errors
1003            CudaError::IllegalAddress,
1004            CudaError::LaunchOutOfResources,
1005            CudaError::LaunchTimeout,
1006            CudaError::LaunchFailed,
1007            CudaError::CooperativeLaunchTooLarge,
1008            // Permission errors
1009            CudaError::NotPermitted,
1010            CudaError::NotSupported,
1011            // Hardware faults
1012            CudaError::HardwareStackError,
1013            CudaError::IllegalInstruction,
1014            CudaError::MisalignedAddress,
1015            CudaError::InvalidAddressSpace,
1016            CudaError::InvalidPc,
1017            // Peer access
1018            CudaError::PeerAccessAlreadyEnabled,
1019            CudaError::PeerAccessNotEnabled,
1020            CudaError::ContextIsDestroyed,
1021            // Stream capture
1022            CudaError::StreamCaptureUnsupported,
1023            CudaError::StreamCaptureInvalidated,
1024            CudaError::InvalidClusterSize,
1025            CudaError::FunctionNotLoaded,
1026            // Catch-all
1027            CudaError::Unknown(99999),
1028        ];
1029        for err in errors {
1030            let display = format!("{err}");
1031            assert!(
1032                !display.is_empty(),
1033                "CudaError::{err:?} has an empty Display string"
1034            );
1035        }
1036    }
1037
1038    /// Verifies the code→error→code round-trip for the most critical codes.
1039    #[test]
1040    fn test_cuda_error_from_result_code() {
1041        let pairs: &[(u32, CudaError)] = &[
1042            (1, CudaError::InvalidValue),
1043            (2, CudaError::OutOfMemory),
1044            (3, CudaError::NotInitialized),
1045            (4, CudaError::Deinitialized),
1046            (100, CudaError::NoDevice),
1047            (101, CudaError::InvalidDevice),
1048            (200, CudaError::InvalidImage),
1049            (201, CudaError::InvalidContext),
1050            (400, CudaError::InvalidHandle),
1051            (500, CudaError::NotFound),
1052            (600, CudaError::NotReady),
1053            (700, CudaError::IllegalAddress),
1054            (701, CudaError::LaunchOutOfResources),
1055            (702, CudaError::LaunchTimeout),
1056            (719, CudaError::LaunchFailed),
1057            (800, CudaError::NotPermitted),
1058            (801, CudaError::NotSupported),
1059            (912, CudaError::InvalidClusterSize),
1060            (999, CudaError::Unknown(999)),
1061        ];
1062        for &(code, ref expected) in pairs {
1063            let got = CudaError::from_raw(code);
1064            assert_eq!(
1065                &got, expected,
1066                "from_raw({code}) should be {expected:?}, got {got:?}"
1067            );
1068            assert_eq!(
1069                got.as_raw(),
1070                code,
1071                "as_raw() round-trip failed for code {code}"
1072            );
1073        }
1074    }
1075
1076    /// Verifies the `is_fatal` classification for a representative sample.
1077    #[test]
1078    fn test_cuda_error_is_fatal() {
1079        // Fatal — hardware fault / context corruption.
1080        let fatal: &[CudaError] = &[
1081            CudaError::IllegalAddress,
1082            CudaError::LaunchFailed,
1083            CudaError::HardwareStackError,
1084            CudaError::IllegalInstruction,
1085            CudaError::MisalignedAddress,
1086            CudaError::InvalidAddressSpace,
1087            CudaError::InvalidPc,
1088            CudaError::Assert,
1089            CudaError::EccUncorrectable,
1090            CudaError::NvlinkUncorrectable,
1091            CudaError::ContextIsDestroyed,
1092            CudaError::Deinitialized,
1093        ];
1094        for err in fatal {
1095            assert!(
1096                err.is_fatal(),
1097                "CudaError::{err:?} should be classified as fatal"
1098            );
1099        }
1100
1101        // Non-fatal — recoverable errors or usage mistakes.
1102        let non_fatal: &[CudaError] = &[
1103            CudaError::InvalidValue,
1104            CudaError::OutOfMemory,
1105            CudaError::NotInitialized,
1106            CudaError::NoDevice,
1107            CudaError::NotReady,
1108            CudaError::LaunchOutOfResources,
1109            CudaError::LaunchTimeout,
1110            CudaError::NotPermitted,
1111            CudaError::NotSupported,
1112            CudaError::InvalidClusterSize,
1113            CudaError::Unknown(99),
1114        ];
1115        for err in non_fatal {
1116            assert!(
1117                !err.is_fatal(),
1118                "CudaError::{err:?} should NOT be classified as fatal"
1119            );
1120        }
1121    }
1122
1123    /// Verifies the `is_usage_error` classification.
1124    #[test]
1125    fn test_cuda_error_is_usage_error() {
1126        let usage: &[CudaError] = &[
1127            CudaError::InvalidValue,
1128            CudaError::InvalidDevice,
1129            CudaError::InvalidContext,
1130            CudaError::InvalidHandle,
1131            CudaError::NoDevice,
1132        ];
1133        for err in usage {
1134            assert!(
1135                err.is_usage_error(),
1136                "CudaError::{err:?} should be a usage error"
1137            );
1138        }
1139
1140        let non_usage: &[CudaError] = &[
1141            CudaError::OutOfMemory,
1142            CudaError::LaunchFailed,
1143            CudaError::IllegalAddress,
1144            CudaError::NotReady,
1145        ];
1146        for err in non_usage {
1147            assert!(
1148                !err.is_usage_error(),
1149                "CudaError::{err:?} should NOT be a usage error"
1150            );
1151        }
1152    }
1153
1154    /// Verifies that `is_fatal` and `is_usage_error` are mutually exclusive.
1155    #[test]
1156    fn test_fatal_and_usage_error_are_disjoint() {
1157        // A sample of errors that should not be both fatal AND usage errors.
1158        let all_errors: &[CudaError] = &[
1159            CudaError::InvalidValue,
1160            CudaError::OutOfMemory,
1161            CudaError::LaunchFailed,
1162            CudaError::IllegalAddress,
1163            CudaError::HardwareStackError,
1164            CudaError::NotReady,
1165            CudaError::InvalidDevice,
1166        ];
1167        for err in all_errors {
1168            assert!(
1169                !(err.is_fatal() && err.is_usage_error()),
1170                "CudaError::{err:?} cannot be both fatal and a usage error"
1171            );
1172        }
1173    }
1174
1175    /// Simulates error injection: build a Result from a raw error code and
1176    /// verify downstream handling branches on the correct classification.
1177    #[test]
1178    fn test_error_injection_simulation() {
1179        // Inject CUDA_ERROR_ILLEGAL_ADDRESS (700) — should trigger fatal handling.
1180        let injected = check(700);
1181        let err = injected.expect_err("error code 700 must be an Err");
1182        assert_eq!(err, CudaError::IllegalAddress);
1183        assert!(err.is_fatal(), "IllegalAddress must be fatal");
1184        assert!(!err.is_usage_error());
1185
1186        // Inject CUDA_ERROR_INVALID_VALUE (1) — should trigger usage-error handling.
1187        let injected = check(1);
1188        let err = injected.expect_err("error code 1 must be an Err");
1189        assert_eq!(err, CudaError::InvalidValue);
1190        assert!(!err.is_fatal(), "InvalidValue must not be fatal");
1191        assert!(err.is_usage_error());
1192
1193        // Inject unknown code — should propagate as Unknown.
1194        let injected = check(55555);
1195        let err = injected.expect_err("unknown code must be an Err");
1196        assert!(matches!(err, CudaError::Unknown(55555)));
1197        assert!(!err.is_fatal());
1198        assert!(!err.is_usage_error());
1199    }
1200}