Functions can take pointers to unsized structs, but no other unsized types.

This commit is contained in:
Jim Blandy 2021-05-27 14:01:40 -07:00 committed by Dzmitry Malyshau
parent fd3b2a93d4
commit 1d0b3f3a75
3 changed files with 68 additions and 3 deletions

View File

@ -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<crate::Type>),
#[error("Initializer doesn't match the variable type")]
InitializerType,
}
@ -517,6 +519,12 @@ impl super::Validator {
constants: &Arena<crate::Constant>,
) -> 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 } => {

View File

@ -42,6 +42,8 @@ pub enum TypeError {
InvalidWidth(crate::ScalarKind, crate::Bytes),
#[error("The base handle {0:?} can not be resolved")]
UnresolvedBase(Handle<crate::Type>),
#[error("Invalid type for pointer target {0:?}")]
InvalidPointerBase(Handle<crate::Type>),
#[error("Expected data type, found {0:?}")]
InvalidData(Handle<crate::Type>),
#[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: _,

View File

@ -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>) -> f32 { return data[0]; }":
"fn unacceptable_unsized(arg: array<f32>) { }",
"fn unacceptable_unsized(arg: ptr<storage, array<f32>>) { }",
"
struct Unsized { data: array<f32>; };
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<f32>; };
fn acceptable_ptr_to_unsized(okay: ptr<storage, Unsized>) { }
":
Ok(_)
}
}
@ -357,3 +375,24 @@ fn valid_access() {
Ok(_)
}
}
#[test]
fn invalid_local_vars() {
check_validation_error! {
"
struct Unsized { data: array<f32>; };
fn local_ptr_dynamic_array(okay: ptr<storage, Unsized>) {
var not_okay: ptr<storage, array<f32>> = 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"
}
}