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
|
||||||
} // namespace ArrayStrideTests
|
} // 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 ResourceTests {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,6 @@
|
||||||
#include "src/ast/sampled_texture.h"
|
#include "src/ast/sampled_texture.h"
|
||||||
#include "src/ast/sampler.h"
|
#include "src/ast/sampler.h"
|
||||||
#include "src/ast/storage_texture.h"
|
#include "src/ast/storage_texture.h"
|
||||||
#include "src/ast/struct_block_decoration.h"
|
|
||||||
#include "src/ast/switch_statement.h"
|
#include "src/ast/switch_statement.h"
|
||||||
#include "src/ast/traverse_expressions.h"
|
#include "src/ast/traverse_expressions.h"
|
||||||
#include "src/ast/type_name.h"
|
#include "src/ast/type_name.h"
|
||||||
|
@ -2707,6 +2706,34 @@ bool Resolver::IsPlain(const sem::Type* type) const {
|
||||||
sem::Struct>();
|
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
|
// https://gpuweb.github.io/gpuweb/wgsl.html#storable-types
|
||||||
bool Resolver::IsStorable(const sem::Type* type) const {
|
bool Resolver::IsStorable(const sem::Type* type) const {
|
||||||
return IsPlain(type) || type->IsAnyOf<sem::Texture, sem::Sampler>();
|
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
|
/// @returns true if the given type is a plain type
|
||||||
bool IsPlain(const sem::Type* type) const;
|
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
|
/// @param type the given type
|
||||||
/// @returns true if the given type is storable
|
/// @returns true if the given type is storable
|
||||||
bool IsStorable(const sem::Type* type) const;
|
bool IsStorable(const sem::Type* type) const;
|
||||||
|
|
|
@ -43,7 +43,6 @@
|
||||||
#include "src/ast/sampled_texture.h"
|
#include "src/ast/sampled_texture.h"
|
||||||
#include "src/ast/sampler.h"
|
#include "src/ast/sampler.h"
|
||||||
#include "src/ast/storage_texture.h"
|
#include "src/ast/storage_texture.h"
|
||||||
#include "src/ast/struct_block_decoration.h"
|
|
||||||
#include "src/ast/switch_statement.h"
|
#include "src/ast/switch_statement.h"
|
||||||
#include "src/ast/traverse_expressions.h"
|
#include "src/ast/traverse_expressions.h"
|
||||||
#include "src/ast/type_name.h"
|
#include "src/ast/type_name.h"
|
||||||
|
@ -541,7 +540,6 @@ bool Resolver::ValidateGlobalVariable(const sem::Variable* var) {
|
||||||
// attribute, satisfying the storage class constraints.
|
// attribute, satisfying the storage class constraints.
|
||||||
|
|
||||||
auto* str = var->Type()->UnwrapRef()->As<sem::Struct>();
|
auto* str = var->Type()->UnwrapRef()->As<sem::Struct>();
|
||||||
|
|
||||||
if (!str) {
|
if (!str) {
|
||||||
AddError(
|
AddError(
|
||||||
"variables declared in the <storage> storage class must be of a "
|
"variables declared in the <storage> storage class must be of a "
|
||||||
|
@ -549,17 +547,6 @@ bool Resolver::ValidateGlobalVariable(const sem::Variable* var) {
|
||||||
decl->source);
|
decl->source);
|
||||||
return false;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case ast::StorageClass::kUniform: {
|
case ast::StorageClass::kUniform: {
|
||||||
|
@ -575,18 +562,6 @@ bool Resolver::ValidateGlobalVariable(const sem::Variable* var) {
|
||||||
decl->source);
|
decl->source);
|
||||||
return false;
|
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()) {
|
for (auto* member : str->Members()) {
|
||||||
if (auto* arr = member->Type()->As<sem::Array>()) {
|
if (auto* arr = member->Type()->As<sem::Array>()) {
|
||||||
if (arr->IsRuntimeSized()) {
|
if (arr->IsRuntimeSized()) {
|
||||||
|
@ -2050,18 +2025,10 @@ bool Resolver::ValidatePipelineStages() {
|
||||||
bool Resolver::ValidateArray(const sem::Array* arr, const Source& source) {
|
bool Resolver::ValidateArray(const sem::Array* arr, const Source& source) {
|
||||||
auto* el_ty = arr->ElemType();
|
auto* el_ty = arr->ElemType();
|
||||||
|
|
||||||
if (auto* el_str = el_ty->As<sem::Struct>()) {
|
if (!IsFixedFootprint(el_ty)) {
|
||||||
if (el_str->IsBlockDecorated()) {
|
AddError("an array element type cannot contain a runtime-sized array",
|
||||||
// https://gpuweb.github.io/gpuweb/wgsl/#attributes
|
source);
|
||||||
// A structure type with the block attribute must not be:
|
return false;
|
||||||
// * 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2123,15 +2090,13 @@ bool Resolver::ValidateStructure(const sem::Struct* str) {
|
||||||
member->Declaration()->source);
|
member->Declaration()->source);
|
||||||
return false;
|
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;
|
auto has_location = false;
|
||||||
|
@ -2192,18 +2157,6 @@ bool Resolver::ValidateStructure(const sem::Struct* str) {
|
||||||
interpolate_attribute->source);
|
interpolate_attribute->source);
|
||||||
return false;
|
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) {
|
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)");
|
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) {
|
TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Basic) {
|
||||||
// [[block]] struct S { x : i32 };
|
// [[block]] struct S { x : i32 };
|
||||||
// var<storage, read> g : S;
|
// var<storage, read> g : S;
|
||||||
|
@ -249,24 +230,6 @@ TEST_F(ResolverStorageClassValidationTest, UniformBufferBoolAlias) {
|
||||||
56:78 note: while instantiating variable g)");
|
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) {
|
TEST_F(ResolverStorageClassValidationTest, UniformBufferNoError_Basic) {
|
||||||
// [[block]] struct S { x : i32 };
|
// [[block]] struct S { x : i32 };
|
||||||
// var<uniform> g : S;
|
// var<uniform> g : S;
|
||||||
|
|
|
@ -499,23 +499,51 @@ TEST_F(ResolverTypeValidationTest, RuntimeArrayIsLast_Pass) {
|
||||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverTypeValidationTest, RuntimeArrayIsLastNoBlock_Fail) {
|
TEST_F(ResolverTypeValidationTest, RuntimeArrayInArray) {
|
||||||
// struct Foo {
|
// struct Foo {
|
||||||
// vf: f32;
|
// rt : array<array<f32>, 4>;
|
||||||
// rt: array<f32>;
|
|
||||||
// };
|
// };
|
||||||
|
|
||||||
Structure("Foo", {
|
Structure("Foo",
|
||||||
Member("vf", ty.f32()),
|
{Member("rt", ty.array(Source{{12, 34}}, ty.array<f32>(), 4))});
|
||||||
Member(Source{{12, 34}}, "rt", ty.array<f32>()),
|
|
||||||
});
|
|
||||||
|
|
||||||
WrapInFunction();
|
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve()) << r()->error();
|
EXPECT_FALSE(r()->Resolve()) << r()->error();
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: a struct containing a runtime-sized array requires "
|
"12:34 error: an array element type cannot contain a runtime-sized "
|
||||||
"the [[block]] attribute: 'Foo'");
|
"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) {
|
TEST_F(ResolverTypeValidationTest, RuntimeArrayIsNotLast_Fail) {
|
||||||
|
|
Loading…
Reference in New Issue