#[cfg(feature = "validate")] use crate::{ arena::{Arena, UniqueArena}, proc::TypeResolution, }; use crate::arena::{BadHandle, Handle}; #[derive(Clone, Debug, thiserror::Error)] #[cfg_attr(test, derive(PartialEq))] pub enum ComposeError { #[error(transparent)] BadHandle(#[from] BadHandle), #[error("Composing of type {0:?} can't be done")] Type(Handle), #[error("Composing expects {expected} components but {given} were given")] ComponentCount { given: u32, expected: u32 }, #[error("Composing {index}'s component type is not expected")] ComponentType { index: u32 }, } #[cfg(feature = "validate")] pub fn validate_compose( self_ty_handle: Handle, constant_arena: &Arena, type_arena: &UniqueArena, component_resolutions: impl ExactSizeIterator, ) -> Result<(), ComposeError> { use crate::TypeInner as Ti; let self_ty = type_arena.get_handle(self_ty_handle)?; match self_ty.inner { // vectors are composed from scalars or other vectors Ti::Vector { size, kind, width } => { let mut total = 0; for (index, comp_res) in component_resolutions.enumerate() { total += match *comp_res.inner_with(type_arena) { Ti::Scalar { kind: comp_kind, width: comp_width, } if comp_kind == kind && comp_width == width => 1, Ti::Vector { size: comp_size, kind: comp_kind, width: comp_width, } if comp_kind == kind && comp_width == width => comp_size as u32, ref other => { log::error!("Vector component[{}] type {:?}", index, other); return Err(ComposeError::ComponentType { index: index as u32, }); } }; } if size as u32 != total { return Err(ComposeError::ComponentCount { expected: size as u32, given: total, }); } } // matrix are composed from column vectors Ti::Matrix { columns, rows, width, } => { let inner = Ti::Vector { size: rows, kind: crate::ScalarKind::Float, width, }; if columns as usize != component_resolutions.len() { return Err(ComposeError::ComponentCount { expected: columns as u32, given: component_resolutions.len() as u32, }); } for (index, comp_res) in component_resolutions.enumerate() { if comp_res.inner_with(type_arena) != &inner { log::error!("Matrix component[{}] type {:?}", index, comp_res); return Err(ComposeError::ComponentType { index: index as u32, }); } } } Ti::Array { base, size: crate::ArraySize::Constant(handle), stride: _, } => { let count = constant_arena[handle].to_array_length().unwrap(); if count as usize != component_resolutions.len() { return Err(ComposeError::ComponentCount { expected: count, given: component_resolutions.len() as u32, }); } for (index, comp_res) in component_resolutions.enumerate() { let base_inner = &type_arena[base].inner; let comp_res_inner = comp_res.inner_with(type_arena); // We don't support arrays of pointers, but it seems best not to // embed that assumption here, so use `TypeInner::equivalent`. if !base_inner.equivalent(comp_res_inner, type_arena) { log::error!("Array component[{}] type {:?}", index, comp_res); return Err(ComposeError::ComponentType { index: index as u32, }); } } } Ti::Struct { ref members, .. } => { if members.len() != component_resolutions.len() { return Err(ComposeError::ComponentCount { given: component_resolutions.len() as u32, expected: members.len() as u32, }); } for (index, (member, comp_res)) in members.iter().zip(component_resolutions).enumerate() { let member_inner = &type_arena[member.ty].inner; let comp_res_inner = comp_res.inner_with(type_arena); // We don't support pointers in structs, but it seems best not to embed // that assumption here, so use `TypeInner::equivalent`. if !comp_res_inner.equivalent(member_inner, type_arena) { log::error!("Struct component[{}] type {:?}", index, comp_res); return Err(ComposeError::ComponentType { index: index as u32, }); } } } ref other => { log::error!("Composing of {:?}", other); return Err(ComposeError::Type(self_ty_handle)); } } Ok(()) }