diff --git a/src/valid/function.rs b/src/valid/function.rs index 60b4310524..0c68d8e04d 100644 --- a/src/valid/function.rs +++ b/src/valid/function.rs @@ -37,6 +37,8 @@ pub enum CallError { #[derive(Clone, Debug, thiserror::Error)] #[cfg_attr(test, derive(PartialEq))] pub enum LocalVariableError { + #[error("Local variable has a type {0:?} that can't be stored in a local variable.")] + InvalidType(Handle), #[error("Initializer doesn't match the variable type")] InitializerType, } @@ -517,6 +519,12 @@ impl super::Validator { constants: &Arena, ) -> Result<(), LocalVariableError> { log::debug!("var {:?}", var); + if !self.types[var.ty.index()] + .flags + .contains(TypeFlags::DATA | TypeFlags::SIZED) + { + return Err(LocalVariableError::InvalidType(var.ty)); + } if let Some(const_handle) = var.init { match constants[const_handle].inner { crate::ConstantInner::Scalar { width, ref value } => { diff --git a/src/valid/type.rs b/src/valid/type.rs index e9f888a63b..abcd268c11 100644 --- a/src/valid/type.rs +++ b/src/valid/type.rs @@ -42,6 +42,8 @@ pub enum TypeError { InvalidWidth(crate::ScalarKind, crate::Bytes), #[error("The base handle {0:?} can not be resolved")] UnresolvedBase(Handle), + #[error("Invalid type for pointer target {0:?}")] + InvalidPointerBase(Handle), #[error("Expected data type, found {0:?}")] InvalidData(Handle), #[error("Structure type {0:?} can not be a block structure")] @@ -197,7 +199,23 @@ impl super::Validator { if base >= handle { return Err(TypeError::UnresolvedBase(base)); } - TypeInfo::new(TypeFlags::DATA | TypeFlags::SIZED, 0) + + // Pointers to dynamically-sized arrays are needed, to serve as + // the type of an `AccessIndex` expression referring to a + // dynamically sized array appearing as the final member of a + // top-level `Struct`. But such pointers cannot be passed to + // functions, stored in varibles, etc. So, we mark them as not + // `DATA`. + let base_info = &self.types[base.index()]; + let data_flag = if base_info.flags.contains(TypeFlags::SIZED) { + TypeFlags::DATA + } else if let crate::TypeInner::Struct { .. } = types[base].inner { + TypeFlags::DATA + } else { + TypeFlags::empty() + }; + + TypeInfo::new(data_flag | TypeFlags::SIZED, 0) } Ti::ValuePointer { size: _, diff --git a/tests/wgsl-errors.rs b/tests/wgsl-errors.rs index fb49a40564..c1acad6ab1 100644 --- a/tests/wgsl-errors.rs +++ b/tests/wgsl-errors.rs @@ -128,10 +128,14 @@ macro_rules! check_validation_error { let error = validation_error($source); if ! matches!(&error, $pattern if $guard) { eprintln!("validation error does not match pattern:\n\ + source code: {}\n\ + \n\ + actual result:\n\ {:#?}\n\ \n\ expected match for pattern:\n\ {}{}", + stringify!($source), error, stringify!($pattern), $guard_string); @@ -227,7 +231,12 @@ fn invalid_structs() { #[test] fn invalid_functions() { check_validation_error! { - "fn bogus(data: array) -> f32 { return data[0]; }": + "fn unacceptable_unsized(arg: array) { }", + "fn unacceptable_unsized(arg: ptr>) { }", + " + struct Unsized { data: array; }; + fn unacceptable_unsized(arg: Unsized) { } + ": Err(naga::valid::ValidationError::Function { name: function_name, error: naga::valid::FunctionError::InvalidArgumentType { @@ -236,7 +245,16 @@ fn invalid_functions() { }, .. }) - if function_name == "bogus" && argument_name == "data" + if function_name == "unacceptable_unsized" && argument_name == "arg" + } + + // A *valid* way to pass an unsized value. + check_validation_error! { + " + struct Unsized { data: array; }; + fn acceptable_ptr_to_unsized(okay: ptr) { } + ": + Ok(_) } } @@ -357,3 +375,24 @@ fn valid_access() { Ok(_) } } + +#[test] +fn invalid_local_vars() { + check_validation_error! { + " + struct Unsized { data: array; }; + fn local_ptr_dynamic_array(okay: ptr) { + var not_okay: ptr> = okay.data; + } + ": + Err(naga::valid::ValidationError::Function { + error: naga::valid::FunctionError::LocalVariable { + name: local_var_name, + error: naga::valid::LocalVariableError::InvalidType(_), + .. + }, + .. + }) + if local_var_name == "not_okay" + } +}