use crate::ir_inner::model::program::BufferDecl;
use crate::ir_inner::model::types::{BufferAccess, DataType};
use crate::validate::{err, ValidationError};
use rustc_hash::FxHashMap;
#[inline]
pub(crate) fn check_store(
buffer: &str,
buffers: &FxHashMap<&str, &BufferDecl>,
errors: &mut Vec<ValidationError>,
) {
if let Some(buf) = buffers.get(buffer) {
if buf.access != BufferAccess::ReadWrite && buf.access != BufferAccess::Workgroup {
errors.push(err(format!(
"store to non-writable buffer `{buffer}`. Fix: declare it with BufferAccess::ReadWrite or BufferAccess::Workgroup."
)));
}
if buf.element == DataType::Bytes && !buf.bytes_extraction {
errors.push(err(format!(
"V013: store to buffer `{buffer}` with element type `bytes` is not supported. Fix: use a typed buffer (U32/I32/F32/…) for stores, or declare the buffer with `.with_bytes_extraction(true)` when this is a bytes-producing op such as decode.base64."
)));
}
} else {
errors.push(err(format!(
"store to unknown buffer `{buffer}`. Fix: declare it in Program::buffers."
)));
}
}
#[inline]
pub(crate) fn check_load(
buffer: &str,
buffers: &FxHashMap<&str, &BufferDecl>,
errors: &mut Vec<ValidationError>,
) {
match buffers.get(buffer) {
None => {
errors.push(err(format!(
"load from unknown buffer `{buffer}`. Fix: declare it in Program::buffers."
)));
}
Some(buf) if buf.element == DataType::Bytes && !buf.bytes_extraction => {
errors.push(err(format!(
"V013: load from buffer `{buffer}` with element type `bytes` is not supported. Fix: declare the buffer with a typed element (U32/I32/F32/…) or with `.with_bytes_extraction(true)` when the consuming op is a dedicated bytes-extraction op."
)));
}
Some(_) => {}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ir::BufferDecl;
fn buf_map(decl: &BufferDecl) -> FxHashMap<&str, &BufferDecl> {
let mut m = FxHashMap::default();
m.insert(decl.name(), decl);
m
}
#[test]
fn store_to_unknown_buffer_errors() {
let buffers: FxHashMap<&str, &BufferDecl> = FxHashMap::default();
let mut errors = Vec::new();
check_store("missing", &buffers, &mut errors);
assert_eq!(errors.len(), 1);
assert!(errors[0].message().contains("unknown buffer"));
}
#[test]
fn store_to_readonly_errors() {
let decl = BufferDecl::read("buf", 0, DataType::U32).with_count(4);
let buffers = buf_map(&decl);
let mut errors = Vec::new();
check_store("buf", &buffers, &mut errors);
assert!(errors.iter().any(|e| e.message().contains("non-writable")));
}
#[test]
fn store_to_readwrite_passes() {
let decl =
BufferDecl::storage("buf", 0, BufferAccess::ReadWrite, DataType::U32).with_count(4);
let buffers = buf_map(&decl);
let mut errors = Vec::new();
check_store("buf", &buffers, &mut errors);
assert!(errors.is_empty());
}
#[test]
fn load_from_unknown_buffer_errors() {
let buffers: FxHashMap<&str, &BufferDecl> = FxHashMap::default();
let mut errors = Vec::new();
check_load("missing", &buffers, &mut errors);
assert_eq!(errors.len(), 1);
assert!(errors[0].message().contains("unknown buffer"));
}
#[test]
fn load_from_declared_buffer_passes() {
let decl = BufferDecl::read("buf", 0, DataType::U32).with_count(4);
let buffers = buf_map(&decl);
let mut errors = Vec::new();
check_load("buf", &buffers, &mut errors);
assert!(errors.is_empty());
}
}