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>
diff --git a/src/resolver/decoration_validation_test.cc b/src/resolver/decoration_validation_test.cc
index 1f41c7a..8f043d6 100644
--- a/src/resolver/decoration_validation_test.cc
+++ b/src/resolver/decoration_validation_test.cc
@@ -963,41 +963,6 @@
} // 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 {
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index d211dde..65820f9 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -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 @@
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>();
diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h
index ff4cf96..8aed028 100644
--- a/src/resolver/resolver.h
+++ b/src/resolver/resolver.h
@@ -93,6 +93,10 @@
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;
diff --git a/src/resolver/resolver_validation.cc b/src/resolver/resolver_validation.cc
index 917c8db..1d1199a 100644
--- a/src/resolver/resolver_validation.cc
+++ b/src/resolver/resolver_validation.cc
@@ -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 @@
// 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 @@
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 @@
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::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 @@
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 @@
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) {
diff --git a/src/resolver/storage_class_validation_test.cc b/src/resolver/storage_class_validation_test.cc
index f902a9e..9391edc 100644
--- a/src/resolver/storage_class_validation_test.cc
+++ b/src/resolver/storage_class_validation_test.cc
@@ -112,25 +112,6 @@
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 @@
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;
diff --git a/src/resolver/type_validation_test.cc b/src/resolver/type_validation_test.cc
index 82037b3..aa9a7be 100644
--- a/src/resolver/type_validation_test.cc
+++ b/src/resolver/type_validation_test.cc
@@ -499,23 +499,51 @@
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) {