diff --git a/BUILD.gn b/BUILD.gn index 0610dfc33c..758fa7d3e1 100644 --- a/BUILD.gn +++ b/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", diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8ab0eaac55..47390ee680 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/ast/access_decoration.cc b/src/ast/access_decoration.cc new file mode 100644 index 0000000000..ae0fdef6d6 --- /dev/null +++ b/src/ast/access_decoration.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 diff --git a/src/ast/access_decoration.h b/src/ast/access_decoration.h new file mode 100644 index 0000000000..69a7244e28 --- /dev/null +++ b/src/ast/access_decoration.h @@ -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 + +#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_ diff --git a/src/ast/access_decoration_test.cc b/src/ast/access_decoration_test.cc new file mode 100644 index 0000000000..4a15fa469e --- /dev/null +++ b/src/ast/access_decoration_test.cc @@ -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 + +#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 diff --git a/src/ast/decoration.cc b/src/ast/decoration.cc index 2ce8037ab0..5c8af19aa4 100644 --- a/src/ast/decoration.cc +++ b/src/ast/decoration.cc @@ -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: diff --git a/src/ast/decoration.h b/src/ast/decoration.h index a07c0bf9fc..61c4cff8d2 100644 --- a/src/ast/decoration.h +++ b/src/ast/decoration.h @@ -35,6 +35,8 @@ enum class DecorationKind { kStruct, kStructMember, /*|*/ kStructMemberOffset, + kType, + /*|*/ kAccess, kVariable, /*|*/ kBinding, /*|*/ kBuiltin, diff --git a/src/ast/decoration_test.cc b/src/ast/decoration_test.cc index 3548654dc1..a9beaac1d8 100644 --- a/src/ast/decoration_test.cc +++ b/src/ast/decoration_test.cc @@ -19,6 +19,7 @@ #include #include +#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); diff --git a/src/ast/type_decoration.cc b/src/ast/type_decoration.cc new file mode 100644 index 0000000000..3f13343bbf --- /dev/null +++ b/src/ast/type_decoration.cc @@ -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 + +#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(this); +} + +} // namespace ast +} // namespace tint diff --git a/src/ast/type_decoration.h b/src/ast/type_decoration.h new file mode 100644 index 0000000000..f457e39706 --- /dev/null +++ b/src/ast/type_decoration.h @@ -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 +#include +#include +#include + +#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; + +} // namespace ast +} // namespace tint + +#endif // SRC_AST_TYPE_DECORATION_H_ diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc index cecf9b837a..0d6f4d58df 100644 --- a/src/reader/wgsl/parser_impl.cc +++ b/src/reader/wgsl/parser_impl.cc @@ -17,6 +17,7 @@ #include #include +#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 ParserImpl::expect_image_storage_type( } // variable_ident_decl -// : IDENT COLON type_decl +// : IDENT COLON variable_decoration_list* type_decl Expect ParserImpl::expect_variable_ident_decl( const std::string& use) { auto ident = expect_ident(use); @@ -807,14 +810,47 @@ Expect 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(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( + deco->AsAccess()->value(), ty)); + } + + return TypedIdentifier{ty, ident.value, ident.source}; +} + +Expect 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 ParserImpl::type_alias() { // | MAT4x4 LESS_THAN type_decl GREATER_THAN // | texture_sampler_types Maybe 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 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 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(decos.value); + auto array_decos = cast_decorations(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 ParserImpl::expect_decoration() { Maybe ParserImpl::decoration() { using Result = Maybe; 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(val.value, val.source); + }); + } if (t.IsLocation()) { const char* use = "location decoration"; return expect_paren_block(use, [&]() -> Result { @@ -2817,23 +2873,37 @@ Maybe ParserImpl::decoration() { } template -Expect> ParserImpl::cast_decorations(ast::DecorationList& in) { - bool ok = true; +std::vector ParserImpl::take_decorations(ast::DecorationList& in) { + ast::DecorationList remaining; std::vector out; out.reserve(in.size()); for (auto* deco : in) { - if (!deco->Is()) { - 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()) { + out.emplace_back(ast::As(deco)); + } else { + remaining.emplace_back(deco); } - out.emplace_back(ast::As(deco)); } - // clear in so that we can verify decorations were consumed with - // expect_decorations_consumed() + + in = std::move(remaining); + return out; +} + +template +Expect> ParserImpl::cast_decorations(ast::DecorationList& in) { + auto out = take_decorations(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) diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h index 6604102f66..ab476c979e 100644 --- a/src/reader/wgsl/parser_impl.h +++ b/src/reader/wgsl/parser_impl.h @@ -24,6 +24,7 @@ #include #include +#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 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 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 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 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 > 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 + std::vector 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 - Expect> cast_decorations(ast::DecorationList& in); + Expect> 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); diff --git a/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc b/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc index f4af02700f..2ace2a96dd 100644 --- a/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc +++ b/src/reader/wgsl/parser_impl_variable_ident_decl_test.cc @@ -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 diff --git a/test/compute_boids.wgsl b/test/compute_boids.wgsl index 6bee6d3a9a..99c0549dfc 100644 --- a/test/compute_boids.wgsl +++ b/test/compute_boids.wgsl @@ -56,9 +56,9 @@ fn frag_main() -> void { [[offset(0)]] particles : [[stride(16)]] array; }; -[[binding(0), set(0)]] var params : SimParams; -[[binding(1), set(0)]] var particlesA : Particles; -[[binding(2), set(0)]] var particlesB : Particles; +[[binding(0), set(0)]] var params : [[access(read)]] SimParams; +[[binding(1), set(0)]] var particlesA : [[access(read_write)]] Particles; +[[binding(2), set(0)]] var particlesB : [[access(read_write)]] Particles; [[builtin(global_invocation_id)]] var gl_GlobalInvocationID : vec3; diff --git a/test/cube.wgsl b/test/cube.wgsl index d56f32f1d1..ee2d2cb939 100644 --- a/test/cube.wgsl +++ b/test/cube.wgsl @@ -17,7 +17,7 @@ [[offset(0)]] modelViewProjectionMatrix : mat4x4; }; -[[binding(0), set(0)]] var uniforms : Uniforms; +[[binding(0), set(0)]] var uniforms : [[access(read)]] Uniforms; [[location(0)]] var cur_position : vec4; [[location(1)]] var color : vec4;