diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn index cb309c846f..037793a5dc 100644 --- a/src/tint/BUILD.gn +++ b/src/tint/BUILD.gn @@ -310,6 +310,8 @@ libtint_source_set("libtint_core_all_src") { "ast/stage_attribute.h", "ast/statement.cc", "ast/statement.h", + "ast/static_assert.cc", + "ast/static_assert.h", "ast/storage_class.cc", "ast/storage_class.h", "ast/storage_texture.cc", @@ -1046,6 +1048,7 @@ if (tint_build_unittests) { "ast/sampled_texture_test.cc", "ast/sampler_test.cc", "ast/stage_attribute_test.cc", + "ast/static_assert_test.cc", "ast/storage_class_test.cc", "ast/storage_texture_test.cc", "ast/stride_attribute_test.cc", diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt index b5c32d7c99..5f93189f91 100644 --- a/src/tint/CMakeLists.txt +++ b/src/tint/CMakeLists.txt @@ -180,6 +180,8 @@ set(TINT_LIB_SRCS ast/stage_attribute.h ast/statement.cc ast/statement.h + ast/static_assert.cc + ast/static_assert.h ast/storage_class.cc ast/storage_class.h ast/storage_texture.cc @@ -742,6 +744,7 @@ if(TINT_BUILD_TESTS) ast/sampled_texture_test.cc ast/sampler_test.cc ast/stage_attribute_test.cc + ast/static_assert_test.cc ast/storage_class_test.cc ast/storage_texture_test.cc ast/stride_attribute_test.cc diff --git a/src/tint/ast/module.cc b/src/tint/ast/module.cc index d1a4687b70..3faab6c56e 100644 --- a/src/tint/ast/module.cc +++ b/src/tint/ast/module.cc @@ -75,6 +75,10 @@ void Module::BinGlobalDeclaration(const tint::ast::Node* decl, diag::List& diags TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, enable, program_id); enables_.Push(enable); }, + [&](const StaticAssert* assertion) { + TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, assertion, program_id); + static_asserts_.Push(assertion); + }, [&](Default) { TINT_ICE(AST, diags) << "Unknown global declaration type"; }); } @@ -92,6 +96,13 @@ void Module::AddGlobalVariable(const ast::Variable* var) { global_declarations_.Push(var); } +void Module::AddStaticAssert(const StaticAssert* assertion) { + TINT_ASSERT(AST, assertion); + TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, assertion, program_id); + static_asserts_.Push(assertion); + global_declarations_.Push(assertion); +} + void Module::AddTypeDecl(const ast::TypeDecl* type) { TINT_ASSERT(AST, type); TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, type, program_id); diff --git a/src/tint/ast/module.h b/src/tint/ast/module.h index bd29159247..2818cb827e 100644 --- a/src/tint/ast/module.h +++ b/src/tint/ast/module.h @@ -19,6 +19,7 @@ #include "src/tint/ast/enable.h" #include "src/tint/ast/function.h" +#include "src/tint/ast/static_assert.h" #include "src/tint/ast/type.h" #include "src/tint/utils/vector.h" @@ -53,11 +54,7 @@ class Module final : public Castable { /// @returns the declaration-ordered global declarations for the module const auto& GlobalDeclarations() const { return global_declarations_; } - /// Add a enable directive to the Builder - /// @param ext the enable directive to add - void AddEnable(const Enable* ext); - - /// Add a global variable to the Builder + /// Add a global variable to the module /// @param var the variable to add void AddGlobalVariable(const Variable* var); @@ -72,7 +69,7 @@ class Module final : public Castable { return false; } - /// Adds a global declaration to the Builder. + /// Adds a global declaration to the module. /// @param decl the declaration to add void AddGlobalDeclaration(const tint::ast::Node* decl); @@ -95,10 +92,21 @@ class Module final : public Castable { return out; } + /// Add a enable directive to the module + /// @param ext the enable directive to add + void AddEnable(const Enable* ext); + /// @returns the extension set for the module const auto& Enables() const { return enables_; } - /// Adds a type declaration to the Builder. + /// Add a global static assertion to the module + /// @param assertion the static assert to add + void AddStaticAssert(const StaticAssert* assertion); + + /// @returns the list of global static assertions + const auto& StaticAsserts() const { return static_asserts_; } + + /// Adds a type declaration to the module /// @param decl the type declaration to add void AddTypeDecl(const TypeDecl* decl); @@ -109,7 +117,7 @@ class Module final : public Castable { /// @returns the declared types in the module const auto& TypeDecls() const { return type_decls_; } - /// Add a function to the Builder + /// Add a function to the module /// @param func the function to add void AddFunction(const Function* func); @@ -139,6 +147,7 @@ class Module final : public Castable { FunctionList functions_; utils::Vector global_variables_; utils::Vector enables_; + utils::Vector static_asserts_; }; } // namespace tint::ast diff --git a/src/tint/ast/static_assert.cc b/src/tint/ast/static_assert.cc new file mode 100644 index 0000000000..0609194b88 --- /dev/null +++ b/src/tint/ast/static_assert.cc @@ -0,0 +1,40 @@ +// Copyright 2022 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/tint/ast/static_assert.h" + +#include "src/tint/program_builder.h" + +TINT_INSTANTIATE_TYPEINFO(tint::ast::StaticAssert); + +namespace tint::ast { + +StaticAssert::StaticAssert(ProgramID pid, NodeID nid, const Source& src, const Expression* cond) + : Base(pid, nid, src), condition(cond) { + TINT_ASSERT(AST, cond); + TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, cond, program_id); +} + +StaticAssert::StaticAssert(StaticAssert&&) = default; + +StaticAssert::~StaticAssert() = default; + +const StaticAssert* StaticAssert::Clone(CloneContext* ctx) const { + // Clone arguments outside of create() call to have deterministic ordering + auto src = ctx->Clone(source); + auto* cond = ctx->Clone(condition); + return ctx->dst->create(src, cond); +} + +} // namespace tint::ast diff --git a/src/tint/ast/static_assert.h b/src/tint/ast/static_assert.h new file mode 100644 index 0000000000..f42ad0707b --- /dev/null +++ b/src/tint/ast/static_assert.h @@ -0,0 +1,50 @@ +// Copyright 2022 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_TINT_AST_STATIC_ASSERT_H_ +#define SRC_TINT_AST_STATIC_ASSERT_H_ + +#include "src/tint/ast/statement.h" +#include "src/tint/ast/variable.h" + +namespace tint::ast { + +/// A `static_assert` statement +class StaticAssert final : public Castable { + public: + /// Constructor + /// @param pid the identifier of the program that owns this node + /// @param nid the unique node identifier + /// @param source the variable statement source + /// @param condition the assertion condition + StaticAssert(ProgramID pid, NodeID nid, const Source& source, const Expression* condition); + + /// Move constructor + StaticAssert(StaticAssert&&); + + /// Destructor + ~StaticAssert() override; + + /// Clones this node and all transitive child nodes using the `CloneContext` `ctx`. + /// @param ctx the clone context + /// @return the newly cloned node + const StaticAssert* Clone(CloneContext* ctx) const override; + + /// The assertion condition + const Expression* const condition; +}; + +} // namespace tint::ast + +#endif // SRC_TINT_AST_STATIC_ASSERT_H_ diff --git a/src/tint/ast/static_assert_test.cc b/src/tint/ast/static_assert_test.cc new file mode 100644 index 0000000000..48bee48141 --- /dev/null +++ b/src/tint/ast/static_assert_test.cc @@ -0,0 +1,66 @@ +// Copyright 2022 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/tint/ast/static_assert.h" + +#include "gtest/gtest-spi.h" +#include "src/tint/ast/test_helper.h" + +namespace tint::ast { +namespace { + +using StaticAssertTest = TestHelper; + +TEST_F(StaticAssertTest, Creation) { + auto* cond = Expr(true); + auto* stmt = StaticAssert(cond); + EXPECT_EQ(stmt->condition, cond); +} + +TEST_F(StaticAssertTest, Creation_WithSource) { + auto* cond = Expr(true); + auto* stmt = StaticAssert(Source{{20, 2}}, cond); + auto src = stmt->source; + EXPECT_EQ(src.range.begin.line, 20u); + EXPECT_EQ(src.range.begin.column, 2u); +} + +TEST_F(StaticAssertTest, IsStaticAssert) { + auto* cond = Expr(true); + + auto* stmt = StaticAssert(cond); + EXPECT_TRUE(stmt->Is()); +} + +TEST_F(StaticAssertTest, Assert_Null_Condition) { + EXPECT_FATAL_FAILURE( + { + ProgramBuilder b; + b.StaticAssert(nullptr); + }, + "internal compiler error"); +} + +TEST_F(StaticAssertTest, Assert_DifferentProgramID_Condition) { + EXPECT_FATAL_FAILURE( + { + ProgramBuilder b1; + ProgramBuilder b2; + b1.StaticAssert(b2.Expr(i32(123))); + }, + "internal compiler error"); +} + +} // namespace +} // namespace tint::ast diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h index d67152b6b5..7774a334ae 100644 --- a/src/tint/program_builder.h +++ b/src/tint/program_builder.h @@ -70,6 +70,7 @@ #include "src/tint/ast/sampled_texture.h" #include "src/tint/ast/sampler.h" #include "src/tint/ast/stage_attribute.h" +#include "src/tint/ast/static_assert.h" #include "src/tint/ast/storage_texture.h" #include "src/tint/ast/stride_attribute.h" #include "src/tint/ast/struct_member_align_attribute.h" @@ -1822,6 +1823,42 @@ class ProgramBuilder { return var; } + /// @param source the source information + /// @param condition the assertion condition + /// @returns a new `ast::StaticAssert`, which is automatically registered as a global statement + /// with the ast::Module. + template + const ast::StaticAssert* GlobalStaticAssert(const Source& source, EXPR&& condition) { + auto* sa = StaticAssert(source, std::forward(condition)); + AST().AddStaticAssert(sa); + return sa; + } + + /// @param condition the assertion condition + /// @returns a new `ast::StaticAssert`, which is automatically registered as a global statement + /// with the ast::Module. + template > + const ast::StaticAssert* GlobalStaticAssert(EXPR&& condition) { + auto* sa = StaticAssert(std::forward(condition)); + AST().AddStaticAssert(sa); + return sa; + } + + /// @param source the source information + /// @param condition the assertion condition + /// @returns a new `ast::StaticAssert` with the given assertion condition + template + const ast::StaticAssert* StaticAssert(const Source& source, EXPR&& condition) { + return create(source, Expr(std::forward(condition))); + } + + /// @param condition the assertion condition + /// @returns a new `ast::StaticAssert` with the given assertion condition + template > + const ast::StaticAssert* StaticAssert(EXPR&& condition) { + return create(Expr(std::forward(condition))); + } + /// @param source the source information /// @param expr the expression to take the address of /// @return an ast::UnaryOpExpression that takes the address of `expr`