Skip to main content

vyre_spec/data_type/
validation.rs

1//! Recursive well-formedness checks for data-type layout contracts.
2
3use super::{DataType, QuantizationScale, QuantizationZeroPoint};
4
5impl DataType {
6    /// Validate recursively that this data-type value is a well-formed spec
7    /// contract, not merely a constructible enum value.
8    ///
9    /// This rejects zero-lane vectors, zero-byte arrays, zero-sized BSR blocks,
10    /// empty/non-positive device meshes, invalid quantized storage families, and
11    /// zero-sized quantization groups. The enum remains constructible for
12    /// migration and fuzzing, but release paths should call this before freezing
13    /// signatures or allocating backend buffers.
14    ///
15    /// # Errors
16    ///
17    /// Returns an actionable diagnostic for the first malformed layout field.
18    pub fn validate_layout(&self) -> Result<(), String> {
19        self.validate_layout_at("DataType")
20    }
21
22    fn validate_layout_at(&self, path: &str) -> Result<(), String> {
23        match self {
24            Self::Array { element_size } => {
25                if *element_size == 0 {
26                    return Err(format!(
27                        "Fix: {path}::Array element_size must be > 0 for a frozen layout contract."
28                    ));
29                }
30                Ok(())
31            }
32            Self::Vec { element, count } => {
33                if *count == 0 {
34                    return Err(format!(
35                        "Fix: {path}::Vec count must be > 0 for a frozen layout contract."
36                    ));
37                }
38                element.validate_layout_at("DataType::Vec.element")
39            }
40            Self::TensorShaped { element, shape } => {
41                for (axis, &dim) in shape.iter().enumerate() {
42                    if dim == 0 {
43                        return Err(format!(
44                            "Fix: {path}::TensorShaped shape[{axis}] must be > 0 for a frozen layout contract."
45                        ));
46                    }
47                }
48                element.validate_layout_at("DataType::TensorShaped.element")
49            }
50            Self::SparseCsr { element } => {
51                element.validate_layout_at("DataType::SparseCsr.element")
52            }
53            Self::SparseCoo { element } => {
54                element.validate_layout_at("DataType::SparseCoo.element")
55            }
56            Self::SparseBsr {
57                element,
58                block_rows,
59                block_cols,
60            } => {
61                if *block_rows == 0 {
62                    return Err(format!(
63                        "Fix: {path}::SparseBsr block_rows must be > 0 for a frozen layout contract."
64                    ));
65                }
66                if *block_cols == 0 {
67                    return Err(format!(
68                        "Fix: {path}::SparseBsr block_cols must be > 0 for a frozen layout contract."
69                    ));
70                }
71                element.validate_layout_at("DataType::SparseBsr.element")
72            }
73            Self::DeviceMesh { axes } => {
74                if axes.is_empty() {
75                    return Err(format!(
76                        "Fix: {path}::DeviceMesh axes must not be empty for a frozen layout contract."
77                    ));
78                }
79                for (axis, &extent) in axes.iter().enumerate() {
80                    if extent == 0 {
81                        return Err(format!(
82                            "Fix: {path}::DeviceMesh axes[{axis}] must be > 0 for a frozen layout contract."
83                        ));
84                    }
85                }
86                Ok(())
87            }
88            Self::Quantized {
89                storage,
90                scale,
91                zero_point,
92            } => {
93                if !storage.is_quantized_storage() {
94                    return Err(format!(
95                        "Fix: {path}::Quantized storage {storage} is not a supported packed quantized storage type."
96                    ));
97                }
98                validate_quantization_scale(scale, path)?;
99                validate_quantization_zero_point(zero_point, path)
100            }
101            _ => Ok(()),
102        }
103    }
104}
105
106fn validate_quantization_scale(scale: &QuantizationScale, path: &str) -> Result<(), String> {
107    match scale {
108        QuantizationScale::PerGroup { group_size } if *group_size == 0 => Err(format!(
109            "Fix: {path}::Quantized scale PerGroup group_size must be > 0."
110        )),
111        _ => Ok(()),
112    }
113}
114
115fn validate_quantization_zero_point(
116    zero_point: &QuantizationZeroPoint,
117    path: &str,
118) -> Result<(), String> {
119    match zero_point {
120        QuantizationZeroPoint::PerGroup { group_size } if *group_size == 0 => Err(format!(
121            "Fix: {path}::Quantized zero_point PerGroup group_size must be > 0."
122        )),
123        _ => Ok(()),
124    }
125}