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:
parent
81b3948649
commit
ac8975f291
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue