[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:
parent
d2f73226bc
commit
7214f407dc
6
BUILD.gn
6
BUILD.gn
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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:
|
||||
|
|
|
@ -35,6 +35,8 @@ enum class DecorationKind {
|
|||
kStruct,
|
||||
kStructMember,
|
||||
/*|*/ kStructMemberOffset,
|
||||
kType,
|
||||
/*|*/ kAccess,
|
||||
kVariable,
|
||||
/*|*/ kBinding,
|
||||
/*|*/ kBuiltin,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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>;
|
||||
|
|
Loading…
Reference in New Issue