[wgsl-reader] Add support for read only storage buffers.

This Cl adds the necessary infrastructure to parse the access decoration
for storage buffers.

This CL incorporates changes from bclayton@ from
https://dawn-review.googlesource.com/c/tint/+/33202 and
https://dawn-review.googlesource.com/c/tint/+/33201

Bug: tint:287
Change-Id: I7479f2cf7ab794b24c682b9927c4c68f6d325839
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/33161
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
Auto-Submit: dan sinclair <dsinclair@chromium.org>
This commit is contained in:
dan sinclair 2020-11-19 18:55:01 +00:00 committed by Commit Bot service account
parent d2f73226bc
commit 7214f407dc
15 changed files with 573 additions and 30 deletions

View File

@ -219,6 +219,8 @@ source_set("libtint_core_src") {
sources = [
"src/ast/access_control.cc",
"src/ast/access_control.h",
"src/ast/access_decoration.cc",
"src/ast/access_decoration.h",
"src/ast/array_accessor_expression.cc",
"src/ast/array_accessor_expression.h",
"src/ast/array_decoration.cc",
@ -367,6 +369,8 @@ source_set("libtint_core_src") {
"src/ast/type/void_type.h",
"src/ast/type_constructor_expression.cc",
"src/ast/type_constructor_expression.h",
"src/ast/type_decoration.cc",
"src/ast/type_decoration.h",
"src/ast/uint_literal.cc",
"src/ast/uint_literal.h",
"src/ast/unary_op.cc",
@ -755,9 +759,9 @@ source_set("tint_unittests_core_src") {
"src/ast/function_test.cc",
"src/ast/identifier_expression_test.cc",
"src/ast/if_statement_test.cc",
"src/ast/int_literal_test.cc",
"src/ast/intrinsic_texture_helper_test.cc",
"src/ast/intrinsic_texture_helper_test.h",
"src/ast/int_literal_test.cc",
"src/ast/location_decoration_test.cc",
"src/ast/loop_statement_test.cc",
"src/ast/member_accessor_expression_test.cc",

View File

@ -40,6 +40,8 @@ set(TINT_LIB_SRCS
../include/tint/tint.h
ast/access_control.cc
ast/access_control.h
ast/access_decoration.cc
ast/access_decoration.h
ast/array_accessor_expression.cc
ast/array_accessor_expression.h
ast/array_decoration.cc
@ -150,6 +152,8 @@ set(TINT_LIB_SRCS
ast/switch_statement.h
ast/type_constructor_expression.h
ast/type_constructor_expression.cc
ast/type_decoration.cc
ast/type_decoration.h
ast/type/access_control_type.cc
ast/type/access_control_type.h
ast/type/alias_type.cc

View File

@ -0,0 +1,41 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/ast/access_decoration.h"
namespace tint {
namespace ast {
constexpr const DecorationKind AccessDecoration::Kind;
AccessDecoration::AccessDecoration(AccessControl val, const Source& source)
: TypeDecoration(Kind, source), value_(val) {}
AccessDecoration::~AccessDecoration() = default;
bool AccessDecoration::IsKind(DecorationKind kind) const {
return kind == Kind || TypeDecoration::IsKind(kind);
}
bool AccessDecoration::IsAccess() const {
return true;
}
void AccessDecoration::to_str(std::ostream& out, size_t indent) const {
make_indent(out, indent);
out << "AccessDecoration{" << value_ << "}" << std::endl;
}
} // namespace ast
} // namespace tint

View File

@ -0,0 +1,61 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_AST_ACCESS_DECORATION_H_
#define SRC_AST_ACCESS_DECORATION_H_
#include <stddef.h>
#include "src/ast/access_control.h"
#include "src/ast/type_decoration.h"
namespace tint {
namespace ast {
/// An access decoration
class AccessDecoration : public TypeDecoration {
public:
/// The kind of decoration that this type represents
static constexpr const DecorationKind Kind = DecorationKind::kAccess;
/// constructor
/// @param value the access value
/// @param source the source of this decoration
explicit AccessDecoration(AccessControl value, const Source& source);
~AccessDecoration() override;
/// @param kind the decoration kind
/// @return true if this Decoration is of the (or derives from) the given
/// kind.
bool IsKind(DecorationKind kind) const override;
/// @returns true if this is an access decoration
bool IsAccess() const override;
/// @returns the access control value
AccessControl value() const { return value_; }
/// Outputs the decoration to the given stream
/// @param out the stream to write to
/// @param indent number of spaces to indent the node when writing
void to_str(std::ostream& out, size_t indent) const override;
private:
AccessControl value_ = AccessControl::kReadWrite;
};
} // namespace ast
} // namespace tint
#endif // SRC_AST_ACCESS_DECORATION_H_

View File

@ -0,0 +1,47 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/ast/access_decoration.h"
#include <sstream>
#include "src/ast/test_helper.h"
namespace tint {
namespace ast {
namespace {
using AccessDecorationTest = TestHelper;
TEST_F(AccessDecorationTest, Creation) {
AccessDecoration d{AccessControl::kWriteOnly, Source{}};
EXPECT_EQ(AccessControl::kWriteOnly, d.value());
}
TEST_F(AccessDecorationTest, Is) {
AccessDecoration d{AccessControl::kReadWrite, Source{}};
EXPECT_FALSE(d.IsAccess());
}
TEST_F(AccessDecorationTest, ToStr) {
AccessDecoration d{AccessControl::kReadOnly, Source{}};
std::ostringstream out;
d.to_str(out, 0);
EXPECT_EQ(out.str(), R"(AccessDecoration{read}
)");
}
} // namespace
} // namespace ast
} // namespace tint

View File

@ -37,6 +37,10 @@ std::ostream& operator<<(std::ostream& out, DecorationKind data) {
return out << "struct member";
case DecorationKind::kStructMemberOffset:
return out << "offset";
case DecorationKind::kType:
return out << "type";
case DecorationKind::kAccess:
return out << "access";
case DecorationKind::kVariable:
return out << "variable";
case DecorationKind::kBinding:

View File

@ -35,6 +35,8 @@ enum class DecorationKind {
kStruct,
kStructMember,
/*|*/ kStructMemberOffset,
kType,
/*|*/ kAccess,
kVariable,
/*|*/ kBinding,
/*|*/ kBuiltin,

View File

@ -19,6 +19,7 @@
#include <unordered_set>
#include <utility>
#include "src/ast/access_decoration.h"
#include "src/ast/array_decoration.h"
#include "src/ast/binding_decoration.h"
#include "src/ast/builtin_decoration.h"
@ -31,6 +32,7 @@
#include "src/ast/struct_member_decoration.h"
#include "src/ast/struct_member_offset_decoration.h"
#include "src/ast/test_helper.h"
#include "src/ast/type_decoration.h"
#include "src/ast/variable_decoration.h"
#include "src/ast/workgroup_decoration.h"
@ -70,6 +72,8 @@ TEST_F(DecorationTest, Kinds) {
EXPECT_EQ(StructMemberDecoration::Kind, DecorationKind::kStructMember);
EXPECT_EQ(StructMemberOffsetDecoration::Kind,
DecorationKind::kStructMemberOffset);
EXPECT_EQ(TypeDecoration::Kind, DecorationKind::kType);
EXPECT_EQ(AccessDecoration::Kind, DecorationKind::kAccess);
EXPECT_EQ(VariableDecoration::Kind, DecorationKind::kVariable);
EXPECT_EQ(BindingDecoration::Kind, DecorationKind::kBinding);
EXPECT_EQ(BuiltinDecoration::Kind, DecorationKind::kBuiltin);
@ -83,6 +87,7 @@ TEST_F(DecorationTest, IsKind) {
DecorationKind::kFunction, DecorationKind::kStage,
DecorationKind::kWorkgroup, DecorationKind::kStruct,
DecorationKind::kStructMember, DecorationKind::kStructMemberOffset,
DecorationKind::kType, DecorationKind::kAccess,
DecorationKind::kVariable, DecorationKind::kBinding,
DecorationKind::kBuiltin, DecorationKind::kConstantId,
DecorationKind::kLocation,
@ -101,6 +106,8 @@ TEST_F(DecorationTest, IsKind) {
// kStruct
// kStructMember
// | kStructMemberOffset
// kType
// | kAccess
// kVariable
// | kBinding
// | kBuiltin
@ -129,6 +136,10 @@ TEST_F(DecorationTest, IsKind) {
{DecorationKind::kStructMember,
DecorationKind::kStructMemberOffset},
},
{
DecorationKind::kAccess,
{DecorationKind::kType, DecorationKind::kAccess},
},
{
DecorationKind::kBinding,
{DecorationKind::kVariable, DecorationKind::kBinding},
@ -159,6 +170,7 @@ TEST_F(DecorationTest, IsKind) {
StageDecoration stage(PipelineStage::kNone, {});
WorkgroupDecoration workgroup(0, {});
StructMemberOffsetDecoration struct_member_offset(0, {});
AccessDecoration access(AccessControl::kReadOnly, {});
BindingDecoration binding(0, {});
BuiltinDecoration builtin(Builtin::kNone, {});
ConstantIdDecoration constant_id(0, {});
@ -168,6 +180,7 @@ TEST_F(DecorationTest, IsKind) {
check(&stage);
check(&workgroup);
check(&struct_member_offset);
check(&access);
check(&binding);
check(&builtin);
check(&constant_id);

View File

@ -0,0 +1,45 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/ast/type_decoration.h"
#include <assert.h>
#include "src/ast/access_decoration.h"
namespace tint {
namespace ast {
constexpr const DecorationKind TypeDecoration::Kind;
TypeDecoration::TypeDecoration(DecorationKind kind, const Source& source)
: Decoration(kind, source) {}
TypeDecoration::~TypeDecoration() = default;
bool TypeDecoration::IsKind(DecorationKind kind) const {
return kind == Kind;
}
bool TypeDecoration::IsAccess() const {
return false;
}
AccessDecoration* TypeDecoration::AsAccess() {
assert(IsAccess());
return static_cast<AccessDecoration*>(this);
}
} // namespace ast
} // namespace tint

62
src/ast/type_decoration.h Normal file
View File

@ -0,0 +1,62 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_AST_TYPE_DECORATION_H_
#define SRC_AST_TYPE_DECORATION_H_
#include <memory>
#include <ostream>
#include <string>
#include <vector>
#include "src/ast/decoration.h"
namespace tint {
namespace ast {
class AccessDecoration;
/// A decoration attached to a type
class TypeDecoration : public Decoration {
public:
/// The kind of decoration that this type represents
static constexpr const DecorationKind Kind = DecorationKind::kType;
~TypeDecoration() override;
/// @param kind the decoration kind
/// @return true if this Decoration is of the (or derives from) the given
/// kind.
bool IsKind(DecorationKind kind) const override;
/// @returns true if this is an access decoration
virtual bool IsAccess() const;
/// @returns the decoration as an access decoration
AccessDecoration* AsAccess();
protected:
/// Constructor
/// @param kind the decoration kind
/// @param source the source of this decoration
explicit TypeDecoration(DecorationKind kind, const Source& source);
};
/// A list of type decorations
using TypeDecorationList = std::vector<TypeDecoration*>;
} // namespace ast
} // namespace tint
#endif // SRC_AST_TYPE_DECORATION_H_

View File

@ -17,6 +17,7 @@
#include <memory>
#include <vector>
#include "src/ast/access_decoration.h"
#include "src/ast/array_accessor_expression.h"
#include "src/ast/binary_expression.h"
#include "src/ast/binding_decoration.h"
@ -45,6 +46,7 @@
#include "src/ast/struct_block_decoration.h"
#include "src/ast/struct_member_offset_decoration.h"
#include "src/ast/switch_statement.h"
#include "src/ast/type/access_control_type.h"
#include "src/ast/type/alias_type.h"
#include "src/ast/type/array_type.h"
#include "src/ast/type/bool_type.h"
@ -61,6 +63,7 @@
#include "src/ast/type/vector_type.h"
#include "src/ast/type/void_type.h"
#include "src/ast/type_constructor_expression.h"
#include "src/ast/type_decoration.h"
#include "src/ast/uint_literal.h"
#include "src/ast/unary_op.h"
#include "src/ast/unary_op_expression.h"
@ -797,7 +800,7 @@ Expect<ast::type::ImageFormat> ParserImpl::expect_image_storage_type(
}
// variable_ident_decl
// : IDENT COLON type_decl
// : IDENT COLON variable_decoration_list* type_decl
Expect<ParserImpl::TypedIdentifier> ParserImpl::expect_variable_ident_decl(
const std::string& use) {
auto ident = expect_ident(use);
@ -807,14 +810,47 @@ Expect<ParserImpl::TypedIdentifier> ParserImpl::expect_variable_ident_decl(
if (!expect(use, Token::Type::kColon))
return Failure::kErrored;
auto decos = decoration_list();
if (decos.errored)
return Failure::kErrored;
auto access_decos = take_decorations<ast::AccessDecoration>(decos.value);
auto t = peek();
auto type = type_decl();
auto type = type_decl(decos.value);
if (type.errored)
return Failure::kErrored;
if (!type.matched)
return add_error(t.source(), "invalid type", use);
return TypedIdentifier{type.value, ident.value, ident.source};
if (!expect_decorations_consumed(decos.value))
return Failure::kErrored;
if (access_decos.size() > 1)
return add_error(ident.source, "multiple access decorations not allowed");
auto* ty = type.value;
for (auto* deco : access_decos) {
// If we have an access control decoration then we take it and wrap our
// type up with that decoration
ty = ctx_.type_mgr().Get(std::make_unique<ast::type::AccessControlType>(
deco->AsAccess()->value(), ty));
}
return TypedIdentifier{ty, ident.value, ident.source};
}
Expect<ast::AccessControl> ParserImpl::expect_access_type() {
auto ident = expect_ident("access_type");
if (ident.errored)
return Failure::kErrored;
if (ident.value == "read")
return {ast::AccessControl::kReadOnly, ident.source};
if (ident.value == "read_write")
return {ast::AccessControl::kReadWrite, ident.source};
return add_error(ident.source, "invalid value for access decoration");
}
// variable_storage_decoration
@ -889,6 +925,23 @@ Maybe<ast::type::Type*> ParserImpl::type_alias() {
// | MAT4x4 LESS_THAN type_decl GREATER_THAN
// | texture_sampler_types
Maybe<ast::type::Type*> ParserImpl::type_decl() {
auto decos = decoration_list();
if (decos.errored)
return Failure::kErrored;
auto type = type_decl(decos.value);
if (type.errored)
return Failure::kErrored;
if (!type.matched)
return Failure::kNoMatch;
if (!expect_decorations_consumed(decos.value))
return Failure::kErrored;
return type.value;
}
Maybe<ast::type::Type*> ParserImpl::type_decl(ast::DecorationList& decos) {
auto t = peek();
if (match(Token::Type::kIdentifier)) {
auto* ty = get_constructed(t.to_str());
@ -918,21 +971,14 @@ Maybe<ast::type::Type*> ParserImpl::type_decl() {
if (match(Token::Type::kPtr))
return expect_type_decl_pointer();
auto decos = decoration_list();
if (decos.errored)
return Failure::kErrored;
if (match(Token::Type::kArray)) {
auto array_decos = cast_decorations<ast::ArrayDecoration>(decos.value);
auto array_decos = cast_decorations<ast::ArrayDecoration>(decos);
if (array_decos.errored)
return Failure::kErrored;
return expect_type_decl_array(std::move(array_decos.value));
}
if (!expect_decorations_consumed(decos.value))
return Failure::kErrored;
if (t.IsMat2x2() || t.IsMat2x3() || t.IsMat2x4() || t.IsMat3x2() ||
t.IsMat3x3() || t.IsMat3x4() || t.IsMat4x2() || t.IsMat4x3() ||
t.IsMat4x4()) {
@ -2714,6 +2760,16 @@ Expect<ast::Decoration*> ParserImpl::expect_decoration() {
Maybe<ast::Decoration*> ParserImpl::decoration() {
using Result = Maybe<ast::Decoration*>;
auto t = next();
if (t.IsIdentifier() && t.to_str() == "access") {
const char* use = "access decoration";
return expect_paren_block(use, [&]() -> Result {
auto val = expect_access_type();
if (val.errored)
return Failure::kErrored;
return create<ast::AccessDecoration>(val.value, val.source);
});
}
if (t.IsLocation()) {
const char* use = "location decoration";
return expect_paren_block(use, [&]() -> Result {
@ -2817,23 +2873,37 @@ Maybe<ast::Decoration*> ParserImpl::decoration() {
}
template <typename T>
Expect<std::vector<T*>> ParserImpl::cast_decorations(ast::DecorationList& in) {
bool ok = true;
std::vector<T*> ParserImpl::take_decorations(ast::DecorationList& in) {
ast::DecorationList remaining;
std::vector<T*> out;
out.reserve(in.size());
for (auto* deco : in) {
if (!deco->Is<T>()) {
std::stringstream msg;
msg << deco->GetKind() << " decoration type cannot be used for "
<< T::Kind;
add_error(deco->source(), msg.str());
ok = false;
continue;
if (deco->Is<T>()) {
out.emplace_back(ast::As<T>(deco));
} else {
remaining.emplace_back(deco);
}
out.emplace_back(ast::As<T>(deco));
}
// clear in so that we can verify decorations were consumed with
// expect_decorations_consumed()
in = std::move(remaining);
return out;
}
template <typename T>
Expect<std::vector<T*>> ParserImpl::cast_decorations(ast::DecorationList& in) {
auto out = take_decorations<T>(in);
bool ok = true;
for (auto* deco : in) {
std::stringstream msg;
msg << deco->GetKind() << " decoration type cannot be used for " << T::Kind;
add_error(deco->source(), msg.str());
ok = false;
}
// clear in so that expect_decorations_consumed() doesn't error again on the
// decorations we've already errored on.
in.clear();
if (!ok)

View File

@ -24,6 +24,7 @@
#include <utility>
#include <vector>
#include "src/ast/access_control.h"
#include "src/ast/array_decoration.h"
#include "src/ast/assignment_statement.h"
#include "src/ast/builtin.h"
@ -320,6 +321,11 @@ class ParserImpl {
/// Parses a `type_decl` grammar element
/// @returns the parsed Type or nullptr if none matched.
Maybe<ast::type::Type*> type_decl();
/// Parses a `type_decl` grammar element with the given pre-parsed
/// decorations.
/// @param decos the list of decorations for the type.
/// @returns the parsed Type or nullptr if none matched.
Maybe<ast::type::Type*> type_decl(ast::DecorationList& decos);
/// Parses a `storage_class` grammar element, erroring on parse failure.
/// @param use a description of what was being parsed if an error was raised.
/// @returns the storage class or StorageClass::kNone if none matched
@ -383,6 +389,10 @@ class ParserImpl {
/// not match a stage name.
/// @returns the pipeline stage.
Expect<ast::PipelineStage> expect_pipeline_stage();
/// Parses an access type identifier, erroring if the next token does not
/// match a valid access type name.
/// @returns the parsed access control.
Expect<ast::AccessControl> expect_access_type();
/// Parses a builtin identifier, erroring if the next token does not match a
/// valid builtin name.
/// @returns the parsed builtin.
@ -720,10 +730,16 @@ class ParserImpl {
template <typename F, typename T = ReturnType<F>>
T without_error(F&& func);
/// Returns all the decorations taken from |list| that matches the type |T|.
/// Those that do not match are kept in |list|.
template <typename T>
std::vector<T*> take_decorations(ast::DecorationList& list);
/// Downcasts all the decorations in |list| to the type |T|, raising a parser
/// error if any of the decorations aren't of the type |T|.
template <typename T>
Expect<std::vector<T*>> cast_decorations(ast::DecorationList& in);
Expect<std::vector<T*>> cast_decorations(ast::DecorationList& list);
/// Reports an error if the decoration list |list| is not empty.
/// Used to ensure that all decorations are consumed.
bool expect_decorations_consumed(const ast::DecorationList& list);

View File

@ -13,6 +13,11 @@
// limitations under the License.
#include "gtest/gtest.h"
#include "src/ast/struct.h"
#include "src/ast/struct_block_decoration.h"
#include "src/ast/type/access_control_type.h"
#include "src/ast/type/i32_type.h"
#include "src/ast/type/struct_type.h"
#include "src/reader/wgsl/parser_impl.h"
#include "src/reader/wgsl/parser_impl_test_helper.h"
@ -24,7 +29,7 @@ namespace {
TEST_F(ParserImplTest, VariableIdentDecl_Parses) {
auto p = parser("my_var : f32");
auto decl = p->expect_variable_ident_decl("test");
ASSERT_FALSE(p->has_error());
ASSERT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(decl.errored);
ASSERT_EQ(decl->name, "my_var");
ASSERT_NE(decl->type, nullptr);
@ -76,6 +81,175 @@ TEST_F(ParserImplTest, VariableIdentDecl_InvalidType) {
ASSERT_EQ(p->error(), "1:10: unknown constructed type 'invalid'");
}
TEST_F(ParserImplTest, VariableIdentDecl_ParsesWithAccessDeco_Read) {
ast::type::I32Type i32;
ast::StructMember mem("a", &i32, ast::StructMemberDecorationList{});
ast::StructMemberList members;
members.push_back(&mem);
ast::StructBlockDecoration block_deco(Source{});
ast::StructDecorationList decos;
decos.push_back(&block_deco);
ast::Struct str(decos, members);
ast::type::StructType s("S", &str);
auto p = parser("my_var : [[access(read)]] S");
p->register_constructed("S", &s);
auto decl = p->expect_variable_ident_decl("test");
ASSERT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(decl.errored);
ASSERT_EQ(decl->name, "my_var");
ASSERT_NE(decl->type, nullptr);
ASSERT_TRUE(decl->type->IsAccessControl());
EXPECT_TRUE(decl->type->AsAccessControl()->IsReadOnly());
}
TEST_F(ParserImplTest, VariableIdentDecl_ParsesWithAccessDeco_ReadWrite) {
ast::type::I32Type i32;
ast::StructMember mem("a", &i32, ast::StructMemberDecorationList{});
ast::StructMemberList members;
members.push_back(&mem);
ast::StructBlockDecoration block_deco(Source{});
ast::StructDecorationList decos;
decos.push_back(&block_deco);
ast::Struct str(decos, members);
ast::type::StructType s("S", &str);
auto p = parser("my_var : [[access(read_write)]] S");
p->register_constructed("S", &s);
auto decl = p->expect_variable_ident_decl("test");
ASSERT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(decl.errored);
ASSERT_EQ(decl->name, "my_var");
ASSERT_NE(decl->type, nullptr);
ASSERT_TRUE(decl->type->IsAccessControl());
EXPECT_TRUE(decl->type->AsAccessControl()->IsReadWrite());
}
TEST_F(ParserImplTest, VariableIdentDecl_MultipleAccessDecoFail) {
ast::type::I32Type i32;
ast::StructMember mem("a", &i32, ast::StructMemberDecorationList{});
ast::StructMemberList members;
members.push_back(&mem);
ast::StructBlockDecoration block_deco(Source{});
ast::StructDecorationList decos;
decos.push_back(&block_deco);
ast::Struct str(decos, members);
ast::type::StructType s("S", &str);
auto p = parser("my_var : [[access(read), access(read_write)]] S");
p->register_constructed("S", &s);
auto decl = p->expect_variable_ident_decl("test");
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(decl.errored);
ASSERT_EQ(p->error(), "1:1: multiple access decorations not allowed");
}
TEST_F(ParserImplTest, VariableIdentDecl_MultipleAccessDeco_MultiBlock_Fail) {
ast::type::I32Type i32;
ast::StructMember mem("a", &i32, ast::StructMemberDecorationList{});
ast::StructMemberList members;
members.push_back(&mem);
ast::StructBlockDecoration block_deco(Source{});
ast::StructDecorationList decos;
decos.push_back(&block_deco);
ast::Struct str(decos, members);
ast::type::StructType s("S", &str);
auto p = parser("my_var : [[access(read)]][[access(read_write)]] S");
p->register_constructed("S", &s);
auto decl = p->expect_variable_ident_decl("test");
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(decl.errored);
ASSERT_EQ(p->error(), "1:1: multiple access decorations not allowed");
}
TEST_F(ParserImplTest, VariableIdentDecl_AccessDecoBadValue) {
auto p = parser("my_var : [[access(unknown)]] S");
auto decl = p->expect_variable_ident_decl("test");
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(decl.errored);
ASSERT_EQ(p->error(), "1:19: invalid value for access decoration");
}
TEST_F(ParserImplTest, VariableIdentDecl_AccessDecoIllegalValue) {
auto p = parser("my_var : [[access(1)]] S");
auto decl = p->expect_variable_ident_decl("test");
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(decl.errored);
ASSERT_EQ(p->error(), "1:19: expected identifier for access_type");
}
TEST_F(ParserImplTest, VariableIdentDecl_NonAccessDecoFail) {
ast::type::I32Type i32;
ast::StructMember mem("a", &i32, ast::StructMemberDecorationList{});
ast::StructMemberList members;
members.push_back(&mem);
ast::StructBlockDecoration block_deco(Source{});
ast::StructDecorationList decos;
decos.push_back(&block_deco);
ast::Struct str(decos, members);
ast::type::StructType s("S", &str);
auto p = parser("my_var : [[stride(1)]] S");
p->register_constructed("S", &s);
auto decl = p->expect_variable_ident_decl("test");
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(decl.errored);
ASSERT_EQ(p->error(), "1:12: unexpected decorations");
}
TEST_F(ParserImplTest, VariableIdentDecl_DecorationMissingRightBlock) {
auto p = parser("my_var : [[access(read) S");
auto decl = p->expect_variable_ident_decl("test");
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(decl.errored);
ASSERT_EQ(p->error(), "1:25: expected ']]' for decoration list");
}
TEST_F(ParserImplTest, VariableIdentDecl_DecorationMissingRightParen) {
auto p = parser("my_var : [[access(read]] S");
auto decl = p->expect_variable_ident_decl("test");
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(decl.errored);
ASSERT_EQ(p->error(), "1:23: expected ')' for access decoration");
}
TEST_F(ParserImplTest, VariableIdentDecl_DecorationMissingLeftParen) {
auto p = parser("my_var : [[access read)]] S");
auto decl = p->expect_variable_ident_decl("test");
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(decl.errored);
ASSERT_EQ(p->error(), "1:19: expected '(' for access decoration");
}
TEST_F(ParserImplTest, VariableIdentDecl_DecorationEmpty) {
auto p = parser("my_var : [[]] S");
auto decl = p->expect_variable_ident_decl("test");
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(decl.errored);
ASSERT_EQ(p->error(), "1:12: empty decoration list");
}
} // namespace
} // namespace wgsl
} // namespace reader

View File

@ -56,9 +56,9 @@ fn frag_main() -> void {
[[offset(0)]] particles : [[stride(16)]] array<Particle, 5>;
};
[[binding(0), set(0)]] var<uniform> params : SimParams;
[[binding(1), set(0)]] var<storage_buffer> particlesA : Particles;
[[binding(2), set(0)]] var<storage_buffer> particlesB : Particles;
[[binding(0), set(0)]] var<uniform> params : [[access(read)]] SimParams;
[[binding(1), set(0)]] var<storage_buffer> particlesA : [[access(read_write)]] Particles;
[[binding(2), set(0)]] var<storage_buffer> particlesB : [[access(read_write)]] Particles;
[[builtin(global_invocation_id)]] var<in> gl_GlobalInvocationID : vec3<u32>;

View File

@ -17,7 +17,7 @@
[[offset(0)]] modelViewProjectionMatrix : mat4x4<f32>;
};
[[binding(0), set(0)]] var<uniform> uniforms : Uniforms;
[[binding(0), set(0)]] var<uniform> uniforms : [[access(read)]] Uniforms;
[[location(0)]] var<in> cur_position : vec4<f32>;
[[location(1)]] var<in> color : vec4<f32>;