diff --git a/src/proc/mod.rs b/src/proc/mod.rs index c9a66585c5..4c906f6eee 100644 --- a/src/proc/mod.rs +++ b/src/proc/mod.rs @@ -184,6 +184,21 @@ impl super::TypeInner { } } +impl super::StorageClass { + pub fn access(self) -> crate::StorageAccess { + use crate::StorageAccess as Sa; + match self { + crate::StorageClass::Function + | crate::StorageClass::Private + | crate::StorageClass::WorkGroup => Sa::LOAD | Sa::STORE, + crate::StorageClass::Uniform => Sa::LOAD, + crate::StorageClass::Storage { access } => access, + crate::StorageClass::Handle => Sa::LOAD, + crate::StorageClass::PushConstant => Sa::LOAD, + } + } +} + impl super::MathFunction { pub fn argument_count(&self) -> usize { match *self { diff --git a/src/valid/function.rs b/src/valid/function.rs index 053b2d82d2..c2f3cf97cb 100644 --- a/src/valid/function.rs +++ b/src/valid/function.rs @@ -606,10 +606,12 @@ impl super::Validator { } _ => {} } - let good = match *context + + let pointer_ty = context .resolve_pointer_type(pointer) - .map_err(|e| e.with_span())? - { + .map_err(|e| e.with_span())?; + + let good = match *pointer_ty { Ti::Pointer { base, class: _ } => match context.types[base].inner { Ti::Atomic { kind, width } => *value_ty == Ti::Scalar { kind, width }, ref other => value_ty == other, @@ -634,6 +636,16 @@ impl super::Validator { .with_handle(pointer, context.expressions) .with_handle(value, context.expressions)); } + + if let Some(class) = pointer_ty.pointer_class() { + if !class.access().contains(crate::StorageAccess::STORE) { + return Err(FunctionError::InvalidStorePointer(pointer) + .with_span_static( + context.expressions.get_span(pointer), + "writing to this location is not permitted", + )); + } + } } S::ImageStore { image, diff --git a/tests/in/bounds-check-zero.wgsl b/tests/in/bounds-check-zero.wgsl index 74811c12b4..51602cbd65 100644 --- a/tests/in/bounds-check-zero.wgsl +++ b/tests/in/bounds-check-zero.wgsl @@ -7,7 +7,7 @@ struct Globals { m: mat3x4; }; -[[group(0), binding(0)]] var globals: Globals; +[[group(0), binding(0)]] var globals: Globals; fn index_array(i: i32) -> f32 { return globals.a[i]; diff --git a/tests/out/spv/bounds-check-zero.spvasm b/tests/out/spv/bounds-check-zero.spvasm index 156954c8a1..eb3a2da42a 100644 --- a/tests/out/spv/bounds-check-zero.spvasm +++ b/tests/out/spv/bounds-check-zero.spvasm @@ -14,7 +14,6 @@ OpMemberDecorate %9 1 Offset 48 OpMemberDecorate %9 2 Offset 64 OpMemberDecorate %9 2 ColMajor OpMemberDecorate %9 2 MatrixStride 16 -OpDecorate %10 NonWritable OpDecorate %10 DescriptorSet 0 OpDecorate %10 Binding 0 %2 = OpTypeVoid diff --git a/tests/wgsl-errors.rs b/tests/wgsl-errors.rs index 2d1e0f79f1..c85c113db4 100644 --- a/tests/wgsl-errors.rs +++ b/tests/wgsl-errors.rs @@ -1029,3 +1029,45 @@ fn missing_default_case() { ) } } + +#[test] +fn wrong_access_mode() { + // The assignments to `global.i` should be forbidden, because they are in + // variables whose access mode is `read`, not `read_write`. + check_validation_error! { + " + [[block]] + struct Globals { + i: i32; + }; + + [[group(0), binding(0)]] + var globals: Globals; + + fn store(v: i32) { + globals.i = v; + } + ", + " + [[block]] + struct Globals { + i: i32; + }; + + [[group(0), binding(0)]] + var globals: Globals; + + fn store(v: i32) { + globals.i = v; + } + ": + Err( + naga::valid::ValidationError::Function { + name, + error: naga::valid::FunctionError::InvalidStorePointer(_), + .. + }, + ) + if name == "store" + } +}