validation: Remove requirement for block attribute

Replace all validation rules that rely on the block attribute with the
new rules based on fixed-footprint types.

Bug: tint:1324
Change-Id: I02656537bee66e6e1af95875e503a37bf23d4a6b
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/72081
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
James Price 2021-12-09 15:45:03 +00:00
parent 81b3948649
commit ac8975f291
6 changed files with 81 additions and 141 deletions

View File

@ -963,41 +963,6 @@ TEST_F(ArrayStrideTest, DuplicateDecoration) {
} // namespace
} // namespace ArrayStrideTests
namespace StructBlockTests {
namespace {
using StructBlockTest = ResolverTest;
TEST_F(StructBlockTest, StructUsedAsArrayElement) {
auto* s = Structure("S", {Member("x", ty.i32())},
{create<ast::StructBlockDecoration>()});
auto* a = ty.array(ty.Of(s), 4);
Global("G", a, ast::StorageClass::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"error: A structure type with a [[block]] decoration cannot be "
"used as an element of an array");
}
TEST_F(StructBlockTest, StructWithNestedBlockMember_Invalid) {
auto* inner =
Structure("Inner", {Member("x", ty.i32())},
{create<ast::StructBlockDecoration>(Source{{56, 78}})});
auto* outer =
Structure("Outer", {Member(Source{{12, 34}}, "y", ty.Of(inner))});
Global("G", ty.Of(outer), ast::StorageClass::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(12:34 error: structs must not contain [[block]] decorated struct members
56:78 note: see member's struct decoration here)");
}
} // namespace
} // namespace StructBlockTests
namespace ResourceTests {
namespace {

View File

@ -43,7 +43,6 @@
#include "src/ast/sampled_texture.h"
#include "src/ast/sampler.h"
#include "src/ast/storage_texture.h"
#include "src/ast/struct_block_decoration.h"
#include "src/ast/switch_statement.h"
#include "src/ast/traverse_expressions.h"
#include "src/ast/type_name.h"
@ -2707,6 +2706,34 @@ bool Resolver::IsPlain(const sem::Type* type) const {
sem::Struct>();
}
// https://gpuweb.github.io/gpuweb/wgsl/#fixed-footprint-types
bool Resolver::IsFixedFootprint(const sem::Type* type) const {
if (type->is_scalar()) {
return true;
}
if (type->Is<sem::Vector>()) {
return true;
}
if (type->Is<sem::Matrix>()) {
return true;
}
if (type->Is<sem::Atomic>()) {
return true;
}
if (auto* arr = type->As<sem::Array>()) {
return !arr->IsRuntimeSized() && IsFixedFootprint(arr->ElemType());
}
if (auto* str = type->As<sem::Struct>()) {
for (auto* member : str->Members()) {
if (!IsFixedFootprint(member->Type())) {
return false;
}
}
return true;
}
return false;
}
// https://gpuweb.github.io/gpuweb/wgsl.html#storable-types
bool Resolver::IsStorable(const sem::Type* type) const {
return IsPlain(type) || type->IsAnyOf<sem::Texture, sem::Sampler>();

View File

@ -92,6 +92,10 @@ class Resolver {
/// @returns true if the given type is a plain type
bool IsPlain(const sem::Type* type) const;
/// @param type the given type
/// @returns true if the given type is a fixed-footprint type
bool IsFixedFootprint(const sem::Type* type) const;
/// @param type the given type
/// @returns true if the given type is storable
bool IsStorable(const sem::Type* type) const;

View File

@ -43,7 +43,6 @@
#include "src/ast/sampled_texture.h"
#include "src/ast/sampler.h"
#include "src/ast/storage_texture.h"
#include "src/ast/struct_block_decoration.h"
#include "src/ast/switch_statement.h"
#include "src/ast/traverse_expressions.h"
#include "src/ast/type_name.h"
@ -541,7 +540,6 @@ bool Resolver::ValidateGlobalVariable(const sem::Variable* var) {
// attribute, satisfying the storage class constraints.
auto* str = var->Type()->UnwrapRef()->As<sem::Struct>();
if (!str) {
AddError(
"variables declared in the <storage> storage class must be of a "
@ -549,17 +547,6 @@ bool Resolver::ValidateGlobalVariable(const sem::Variable* var) {
decl->source);
return false;
}
if (!str->IsBlockDecorated()) {
AddError(
"structure used as a storage buffer must be declared with the "
"[[block]] decoration",
str->Declaration()->source);
if (decl->source.range.begin.line) {
AddNote("structure used as storage buffer here", decl->source);
}
return false;
}
break;
}
case ast::StorageClass::kUniform: {
@ -575,18 +562,6 @@ bool Resolver::ValidateGlobalVariable(const sem::Variable* var) {
decl->source);
return false;
}
if (!str->IsBlockDecorated()) {
AddError(
"structure used as a uniform buffer must be declared with the "
"[[block]] decoration",
str->Declaration()->source);
if (decl->source.range.begin.line) {
AddNote("structure used as uniform buffer here", decl->source);
}
return false;
}
for (auto* member : str->Members()) {
if (auto* arr = member->Type()->As<sem::Array>()) {
if (arr->IsRuntimeSized()) {
@ -2050,18 +2025,10 @@ bool Resolver::ValidatePipelineStages() {
bool Resolver::ValidateArray(const sem::Array* arr, const Source& source) {
auto* el_ty = arr->ElemType();
if (auto* el_str = el_ty->As<sem::Struct>()) {
if (el_str->IsBlockDecorated()) {
// https://gpuweb.github.io/gpuweb/wgsl/#attributes
// A structure type with the block attribute must not be:
// * the element type of an array type
// * the member type in another structure
AddError(
"A structure type with a [[block]] decoration cannot be used as an "
"element of an array",
source);
return false;
}
if (!IsFixedFootprint(el_ty)) {
AddError("an array element type cannot contain a runtime-sized array",
source);
return false;
}
return true;
}
@ -2123,15 +2090,13 @@ bool Resolver::ValidateStructure(const sem::Struct* str) {
member->Declaration()->source);
return false;
}
if (!str->IsBlockDecorated()) {
AddError(
"a struct containing a runtime-sized array "
"requires the [[block]] attribute: '" +
builder_->Symbols().NameFor(str->Declaration()->name) + "'",
member->Declaration()->source);
return false;
}
}
} else if (!IsFixedFootprint(member->Type())) {
AddError(
"a struct that contains a runtime array cannot be nested inside "
"another struct",
member->Declaration()->source);
return false;
}
auto has_location = false;
@ -2192,18 +2157,6 @@ bool Resolver::ValidateStructure(const sem::Struct* str) {
interpolate_attribute->source);
return false;
}
if (auto* member_struct_type = member->Type()->As<sem::Struct>()) {
if (auto* member_struct_type_block_decoration =
ast::GetDecoration<ast::StructBlockDecoration>(
member_struct_type->Declaration()->decorations)) {
AddError("structs must not contain [[block]] decorated struct members",
member->Declaration()->source);
AddNote("see member's struct decoration here",
member_struct_type_block_decoration->source);
return false;
}
}
}
for (auto* deco : str->Declaration()->decorations) {

View File

@ -112,25 +112,6 @@ TEST_F(ResolverStorageClassValidationTest, NotStorage_AccessMode) {
R"(56:78 error: only variables in <storage> storage class may declare an access mode)");
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferNoBlockDecoration) {
// struct S { x : i32 };
// var<storage, read> g : S;
auto* s = Structure(Source{{12, 34}}, "S", {Member("x", ty.i32())});
Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
ast::Access::kRead,
ast::DecorationList{
create<ast::BindingDecoration>(0),
create<ast::GroupDecoration>(0),
});
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(12:34 error: structure used as a storage buffer must be declared with the [[block]] decoration
56:78 note: structure used as storage buffer here)");
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Basic) {
// [[block]] struct S { x : i32 };
// var<storage, read> g : S;
@ -249,24 +230,6 @@ TEST_F(ResolverStorageClassValidationTest, UniformBufferBoolAlias) {
56:78 note: while instantiating variable g)");
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferNoBlockDecoration) {
// struct S { x : i32 };
// var<uniform> g : S;
auto* s = Structure(Source{{12, 34}}, "S", {Member("x", ty.i32())});
Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kUniform,
ast::DecorationList{
create<ast::BindingDecoration>(0),
create<ast::GroupDecoration>(0),
});
ASSERT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(12:34 error: structure used as a uniform buffer must be declared with the [[block]] decoration
56:78 note: structure used as uniform buffer here)");
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferNoError_Basic) {
// [[block]] struct S { x : i32 };
// var<uniform> g : S;

View File

@ -499,23 +499,51 @@ TEST_F(ResolverTypeValidationTest, RuntimeArrayIsLast_Pass) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTypeValidationTest, RuntimeArrayIsLastNoBlock_Fail) {
TEST_F(ResolverTypeValidationTest, RuntimeArrayInArray) {
// struct Foo {
// vf: f32;
// rt: array<f32>;
// rt : array<array<f32>, 4>;
// };
Structure("Foo", {
Member("vf", ty.f32()),
Member(Source{{12, 34}}, "rt", ty.array<f32>()),
});
WrapInFunction();
Structure("Foo",
{Member("rt", ty.array(Source{{12, 34}}, ty.array<f32>(), 4))});
EXPECT_FALSE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(),
"12:34 error: a struct containing a runtime-sized array requires "
"the [[block]] attribute: 'Foo'");
"12:34 error: an array element type cannot contain a runtime-sized "
"array");
}
TEST_F(ResolverTypeValidationTest, RuntimeArrayInStructInArray) {
// struct Foo {
// rt : array<f32>;
// };
// var<private> a : array<Foo, 4>;
auto* foo = Structure("Foo", {Member("rt", ty.array<f32>())});
Global("v", ty.array(Source{{12, 34}}, ty.Of(foo), 4),
ast::StorageClass::kPrivate);
EXPECT_FALSE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(),
"12:34 error: an array element type cannot contain a runtime-sized "
"array");
}
TEST_F(ResolverTypeValidationTest, RuntimeArrayInStructInStruct) {
// struct Foo {
// rt : array<f32>;
// };
// struct Outer {
// inner : Foo;
// };
auto* foo = Structure("Foo", {Member("rt", ty.array<f32>())});
Structure("Outer", {Member(Source{{12, 34}}, "inner", ty.Of(foo))});
EXPECT_FALSE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(),
"12:34 error: a struct that contains a runtime array cannot be "
"nested inside another struct");
}
TEST_F(ResolverTypeValidationTest, RuntimeArrayIsNotLast_Fail) {