Import Tint changes from Dawn
Changes:
- bb0229002db5cd1c58df4ff846020dcc180f9258 tint/uniformity: Violations are now errors by James Price <jrprice@google.com>
- f38ee3e07392eb1d66b8f9fb0702ccef3d062926 tint/exe: Add --allow-non-uniform-derivatives flag by James Price <jrprice@google.com>
- e657c470bdc66ba10468ed494a4cda0a7258f982 tint/uniformity: Add derivative_uniformity filter by James Price <jrprice@google.com>
- 5853205342386f296377f3276a76d36181558ca3 tint/sem: Record diagnostic severity modifications by James Price <jrprice@google.com>
- 613fbcf79ad0b366711f144dbd1d1c07132b910d tint: Add diagnostic and enable to ModuleCloneTest by James Price <jrprice@google.com>
- c744a23d77d53b8e8e7b9d5c7d7439e2622034da tint/reader/wgsl: Parse @diagnostic attributes by James Price <jrprice@google.com>
- 75326f88e64cad9bbf3e469e61ac1427fc8f43d8 tint/writer/wgsl: Handle diagnostic attributes by James Price <jrprice@google.com>
- 8dd35110c28830d2d6f81145b142860d57bfc845 wgsl/reader: Parse diagnostic directives by James Price <jrprice@google.com>
- 02855b5060831b7fa2dedf1a62567a3dc0c6207b tint/resolver: Handle diagnostic attributes on functions by James Price <jrprice@google.com>
- e8ea579bceafe593964e6110559925362eb80842 tint/writer: Handle diagnostic directives by James Price <jrprice@google.com>
- 81e55754e148eb27ec835fceebac577003df8a6b tint/resolver: Handle diagnostic directives by James Price <jrprice@google.com>
- 098e3d8f90800a8a2f1f0a5aff054fcdb4bca146 tint/dependency_graph: Handle diagnostic controls by James Price <jrprice@google.com>
- 15bf15d55f5f90e08c912d70be5de207cd34a80f tint/ast: Add global diagnostic controls to Module by James Price <jrprice@google.com>
- 7d2b192439cb4a0380a2eb60947540a79a157847 tint: rename template argument token strings by Ben Clayton <bclayton@google.com>
- f0d8edecee77d05fb3e670ca5a77ad211116e78a tint: Fix x86 build by Ben Clayton <bclayton@google.com>
- 355b70d2be291a5d069df330810d0b484592618d tint/reader/wgsl: Use ClassifyTemplateArguments() by Ben Clayton <bclayton@google.com>
- ef5434dcd17a18369084fc8b6b315bf3ddb609e8 tint/ast: Add DiagnosticAttribute AST node by James Price <jrprice@google.com>
- 4ff2645d1620d65aae6d847f191ae0ca3ee27e22 tint/reader/wgsl: Add ClassifyTemplateArguments() by Ben Clayton <bclayton@google.com>
- 6c8aa8aff9d13050080ce3e7c471d5a72273ade7 Resolve some reader/writer dependency issues by Brandon Jones <bajones@chromium.org>
- c98d57d662b7fdd0c82fa8169cd98f87007530be tint: Rename 'static_assert' to 'const_assert' by Ben Clayton <bclayton@google.com>
- 9dc48bcef3c390ac55eb52b76f932092c7c79f0a tint: Rename 'type' to 'alias' by Ben Clayton <bclayton@google.com>
GitOrigin-RevId: bb0229002db5cd1c58df4ff846020dcc180f9258
Change-Id: I2d8e330bd6e37c5bd974a4bafa259a3aa947c163
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/117530
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 9a14d8a..0473d49 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -224,12 +224,16 @@
"ast/compound_assignment_statement.h",
"ast/const.cc",
"ast/const.h",
+ "ast/const_assert.cc",
+ "ast/const_assert.h",
"ast/continue_statement.cc",
"ast/continue_statement.h",
"ast/depth_multisampled_texture.cc",
"ast/depth_multisampled_texture.h",
"ast/depth_texture.cc",
"ast/depth_texture.h",
+ "ast/diagnostic_attribute.cc",
+ "ast/diagnostic_attribute.h",
"ast/diagnostic_control.cc",
"ast/diagnostic_control.h",
"ast/disable_validation_attribute.cc",
@@ -315,8 +319,6 @@
"ast/stage_attribute.h",
"ast/statement.cc",
"ast/statement.h",
- "ast/static_assert.cc",
- "ast/static_assert.h",
"ast/storage_texture.cc",
"ast/storage_texture.h",
"ast/stride_attribute.cc",
@@ -908,6 +910,8 @@
libtint_source_set("libtint_wgsl_reader_src") {
sources = [
+ "reader/wgsl/classify_template_args.cc",
+ "reader/wgsl/classify_template_args.h",
"reader/wgsl/lexer.cc",
"reader/wgsl/lexer.h",
"reader/wgsl/parser.cc",
@@ -1179,7 +1183,9 @@
"ast/case_selector_test.cc",
"ast/case_statement_test.cc",
"ast/compound_assignment_statement_test.cc",
+ "ast/const_assert_test.cc",
"ast/continue_statement_test.cc",
+ "ast/diagnostic_attribute_test.cc",
"ast/diagnostic_control_test.cc",
"ast/discard_statement_test.cc",
"ast/enable_test.cc",
@@ -1204,7 +1210,6 @@
"ast/loop_statement_test.cc",
"ast/matrix_test.cc",
"ast/member_accessor_expression_test.cc",
- "ast/module_clone_test.cc",
"ast/module_test.cc",
"ast/multisampled_texture_test.cc",
"ast/phony_expression_test.cc",
@@ -1213,7 +1218,6 @@
"ast/sampled_texture_test.cc",
"ast/sampler_test.cc",
"ast/stage_attribute_test.cc",
- "ast/static_assert_test.cc",
"ast/storage_texture_test.cc",
"ast/stride_attribute_test.cc",
"ast/struct_member_align_attribute_test.cc",
@@ -1234,6 +1238,11 @@
"ast/workgroup_attribute_test.cc",
]
deps = [ ":libtint_transform_src" ]
+
+ if (tint_build_wgsl_reader && tint_build_wgsl_writer) {
+ # This AST test relies on the WGSL reader and writer
+ sources += [ "ast/module_clone_test.cc" ]
+ }
}
tint_unittests_source_set("tint_unittests_diagnostic_src") {
@@ -1272,6 +1281,7 @@
"resolver/call_validation_test.cc",
"resolver/compound_assignment_validation_test.cc",
"resolver/compound_statement_test.cc",
+ "resolver/const_assert_test.cc",
"resolver/const_eval_binary_op_test.cc",
"resolver/const_eval_bitcast_test.cc",
"resolver/const_eval_builtin_test.cc",
@@ -1283,6 +1293,7 @@
"resolver/const_eval_unary_op_test.cc",
"resolver/control_block_validation_test.cc",
"resolver/dependency_graph_test.cc",
+ "resolver/diagnostic_control_test.cc",
"resolver/entry_point_validation_test.cc",
"resolver/evaluation_stage_test.cc",
"resolver/f16_extension_test.cc",
@@ -1304,7 +1315,6 @@
"resolver/resolver_test_helper.h",
"resolver/root_identifier_test.cc",
"resolver/side_effects_test.cc",
- "resolver/static_assert_test.cc",
"resolver/struct_address_space_use_test.cc",
"resolver/struct_layout_test.cc",
"resolver/struct_pipeline_stage_use_test.cc",
@@ -1325,6 +1335,7 @@
tint_unittests_source_set("tint_unittests_sem_src") {
sources = [
"sem/builtin_test.cc",
+ "sem/diagnostic_severity_test.cc",
"sem/expression_test.cc",
"sem/struct_test.cc",
]
@@ -1432,7 +1443,11 @@
"transform/zero_init_workgroup_memory_test.cc",
]
- deps = [ ":libtint_transform_src" ]
+ deps = [
+ ":libtint_transform_src",
+ ":libtint_wgsl_reader_src",
+ ":libtint_wgsl_writer_src",
+ ]
}
tint_unittests_source_set("tint_unittests_utils_src") {
@@ -1511,6 +1526,7 @@
deps = [
":libtint_spv_reader_src",
+ ":libtint_wgsl_writer_src",
"${tint_spirv_tools_dir}/:spvtools_opt",
]
}
@@ -1526,6 +1542,7 @@
"writer/spirv/builder_builtin_test.cc",
"writer/spirv/builder_builtin_texture_test.cc",
"writer/spirv/builder_call_test.cc",
+ "writer/spirv/builder_const_assert_test.cc",
"writer/spirv/builder_discard_test.cc",
"writer/spirv/builder_entry_point_test.cc",
"writer/spirv/builder_format_conversion_test.cc",
@@ -1539,7 +1556,6 @@
"writer/spirv/builder_literal_test.cc",
"writer/spirv/builder_loop_test.cc",
"writer/spirv/builder_return_test.cc",
- "writer/spirv/builder_static_assert_test.cc",
"writer/spirv/builder_switch_test.cc",
"writer/spirv/builder_test.cc",
"writer/spirv/builder_type_test.cc",
@@ -1561,6 +1577,7 @@
tint_unittests_source_set("tint_unittests_wgsl_reader_src") {
sources = [
+ "reader/wgsl/classify_template_args_test.cc",
"reader/wgsl/lexer_test.cc",
"reader/wgsl/parser_impl_additive_expression_test.cc",
"reader/wgsl/parser_impl_address_space_test.cc",
@@ -1578,6 +1595,9 @@
"reader/wgsl/parser_impl_continuing_stmt_test.cc",
"reader/wgsl/parser_impl_core_lhs_expression_test.cc",
"reader/wgsl/parser_impl_depth_texture_test.cc",
+ "reader/wgsl/parser_impl_diagnostic_attribute_test.cc",
+ "reader/wgsl/parser_impl_diagnostic_control_test.cc",
+ "reader/wgsl/parser_impl_diagnostic_directive_test.cc",
"reader/wgsl/parser_impl_element_count_expression_test.cc",
"reader/wgsl/parser_impl_enable_directive_test.cc",
"reader/wgsl/parser_impl_error_msg_test.cc",
@@ -1654,7 +1674,9 @@
"writer/wgsl/generator_impl_call_test.cc",
"writer/wgsl/generator_impl_case_test.cc",
"writer/wgsl/generator_impl_cast_test.cc",
+ "writer/wgsl/generator_impl_const_assert_test.cc",
"writer/wgsl/generator_impl_continue_test.cc",
+ "writer/wgsl/generator_impl_diagnostic_test.cc",
"writer/wgsl/generator_impl_discard_test.cc",
"writer/wgsl/generator_impl_enable_test.cc",
"writer/wgsl/generator_impl_function_test.cc",
@@ -1666,7 +1688,6 @@
"writer/wgsl/generator_impl_loop_test.cc",
"writer/wgsl/generator_impl_member_accessor_test.cc",
"writer/wgsl/generator_impl_return_test.cc",
- "writer/wgsl/generator_impl_static_assert_test.cc",
"writer/wgsl/generator_impl_switch_test.cc",
"writer/wgsl/generator_impl_test.cc",
"writer/wgsl/generator_impl_type_test.cc",
@@ -1695,6 +1716,7 @@
"writer/msl/generator_impl_call_test.cc",
"writer/msl/generator_impl_case_test.cc",
"writer/msl/generator_impl_cast_test.cc",
+ "writer/msl/generator_impl_const_assert_test.cc",
"writer/msl/generator_impl_continue_test.cc",
"writer/msl/generator_impl_discard_test.cc",
"writer/msl/generator_impl_function_test.cc",
@@ -1707,7 +1729,6 @@
"writer/msl/generator_impl_module_constant_test.cc",
"writer/msl/generator_impl_return_test.cc",
"writer/msl/generator_impl_sanitizer_test.cc",
- "writer/msl/generator_impl_static_assert_test.cc",
"writer/msl/generator_impl_switch_test.cc",
"writer/msl/generator_impl_test.cc",
"writer/msl/generator_impl_type_test.cc",
@@ -1735,6 +1756,7 @@
"writer/hlsl/generator_impl_call_test.cc",
"writer/hlsl/generator_impl_case_test.cc",
"writer/hlsl/generator_impl_cast_test.cc",
+ "writer/hlsl/generator_impl_const_assert_test.cc",
"writer/hlsl/generator_impl_continue_test.cc",
"writer/hlsl/generator_impl_discard_test.cc",
"writer/hlsl/generator_impl_function_test.cc",
@@ -1747,7 +1769,6 @@
"writer/hlsl/generator_impl_module_constant_test.cc",
"writer/hlsl/generator_impl_return_test.cc",
"writer/hlsl/generator_impl_sanitizer_test.cc",
- "writer/hlsl/generator_impl_static_assert_test.cc",
"writer/hlsl/generator_impl_switch_test.cc",
"writer/hlsl/generator_impl_test.cc",
"writer/hlsl/generator_impl_type_test.cc",
@@ -1804,7 +1825,6 @@
":libtint_glsl_writer_src",
":libtint_transform_src",
":tint_unittests_ast_src",
- ":tint_unittests_transform_src",
]
}
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 438ab85..58d28cf 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -119,12 +119,16 @@
ast/compound_assignment_statement.h
ast/const.cc
ast/const.h
+ ast/const_assert.cc
+ ast/const_assert.h
ast/continue_statement.cc
ast/continue_statement.h
ast/depth_multisampled_texture.cc
ast/depth_multisampled_texture.h
ast/depth_texture.cc
ast/depth_texture.h
+ ast/diagnostic_attribute.cc
+ ast/diagnostic_attribute.h
ast/diagnostic_control.cc
ast/diagnostic_control.h
ast/disable_validation_attribute.cc
@@ -206,8 +210,6 @@
ast/stage_attribute.h
ast/statement.cc
ast/statement.h
- ast/static_assert.cc
- ast/static_assert.h
ast/storage_texture.cc
ast/storage_texture.h
ast/stride_attribute.cc
@@ -616,6 +618,8 @@
if(${TINT_BUILD_WGSL_READER})
list(APPEND TINT_LIB_SRCS
+ reader/wgsl/classify_template_args.cc
+ reader/wgsl/classify_template_args.h
reader/wgsl/lexer.cc
reader/wgsl/lexer.h
reader/wgsl/parser.cc
@@ -829,9 +833,11 @@
ast/case_selector_test.cc
ast/case_statement_test.cc
ast/compound_assignment_statement_test.cc
+ ast/const_assert_test.cc
ast/continue_statement_test.cc
ast/depth_multisampled_texture_test.cc
ast/depth_texture_test.cc
+ ast/diagnostic_attribute_test.cc
ast/diagnostic_control_test.cc
ast/discard_statement_test.cc
ast/enable_test.cc
@@ -854,7 +860,6 @@
ast/loop_statement_test.cc
ast/matrix_test.cc
ast/member_accessor_expression_test.cc
- ast/module_clone_test.cc
ast/module_test.cc
ast/multisampled_texture_test.cc
ast/phony_expression_test.cc
@@ -863,7 +868,6 @@
ast/sampled_texture_test.cc
ast/sampler_test.cc
ast/stage_attribute_test.cc
- ast/static_assert_test.cc
ast/storage_texture_test.cc
ast/stride_attribute_test.cc
ast/struct_member_align_attribute_test.cc
@@ -910,6 +914,7 @@
resolver/call_validation_test.cc
resolver/compound_assignment_validation_test.cc
resolver/compound_statement_test.cc
+ resolver/const_assert_test.cc
resolver/const_eval_binary_op_test.cc
resolver/const_eval_bitcast_test.cc
resolver/const_eval_builtin_test.cc
@@ -921,6 +926,7 @@
resolver/const_eval_unary_op_test.cc
resolver/control_block_validation_test.cc
resolver/dependency_graph_test.cc
+ resolver/diagnostic_control_test.cc
resolver/entry_point_validation_test.cc
resolver/evaluation_stage_test.cc
resolver/f16_extension_test.cc
@@ -941,7 +947,6 @@
resolver/resolver_test_helper.h
resolver/resolver_test.cc
resolver/side_effects_test.cc
- resolver/static_assert_test.cc
resolver/root_identifier_test.cc
resolver/address_space_layout_validation_test.cc
resolver/address_space_validation_test.cc
@@ -956,6 +961,7 @@
resolver/variable_validation_test.cc
scope_stack_test.cc
sem/builtin_test.cc
+ sem/diagnostic_severity_test.cc
sem/expression_test.cc
sem/struct_test.cc
source_test.cc
@@ -1074,6 +1080,7 @@
if(${TINT_BUILD_WGSL_READER})
list(APPEND TINT_TEST_SRCS
+ reader/wgsl/classify_template_args_test.cc
reader/wgsl/lexer_test.cc
reader/wgsl/parser_test.cc
reader/wgsl/parser_impl_additive_expression_test.cc
@@ -1091,6 +1098,9 @@
reader/wgsl/parser_impl_continuing_stmt_test.cc
reader/wgsl/parser_impl_core_lhs_expression_test.cc
reader/wgsl/parser_impl_depth_texture_test.cc
+ reader/wgsl/parser_impl_diagnostic_attribute_test.cc
+ reader/wgsl/parser_impl_diagnostic_control_test.cc
+ reader/wgsl/parser_impl_diagnostic_directive_test.cc
reader/wgsl/parser_impl_element_count_expression_test.cc
reader/wgsl/parser_impl_enable_directive_test.cc
reader/wgsl/parser_impl_error_msg_test.cc
@@ -1164,7 +1174,7 @@
writer/spirv/builder_builtin_test.cc
writer/spirv/builder_builtin_texture_test.cc
writer/spirv/builder_call_test.cc
- writer/spirv/builder_initializer_expression_test.cc
+ writer/spirv/builder_const_assert_test.cc
writer/spirv/builder_discard_test.cc
writer/spirv/builder_entry_point_test.cc
writer/spirv/builder_format_conversion_test.cc
@@ -1174,10 +1184,10 @@
writer/spirv/builder_global_variable_test.cc
writer/spirv/builder_ident_expression_test.cc
writer/spirv/builder_if_test.cc
+ writer/spirv/builder_initializer_expression_test.cc
writer/spirv/builder_literal_test.cc
writer/spirv/builder_loop_test.cc
writer/spirv/builder_return_test.cc
- writer/spirv/builder_static_assert_test.cc
writer/spirv/builder_switch_test.cc
writer/spirv/builder_test.cc
writer/spirv/builder_type_test.cc
@@ -1204,19 +1214,20 @@
writer/wgsl/generator_impl_call_test.cc
writer/wgsl/generator_impl_case_test.cc
writer/wgsl/generator_impl_cast_test.cc
- writer/wgsl/generator_impl_initializer_test.cc
+ writer/wgsl/generator_impl_const_assert_test.cc
writer/wgsl/generator_impl_continue_test.cc
+ writer/wgsl/generator_impl_diagnostic_test.cc
writer/wgsl/generator_impl_discard_test.cc
writer/wgsl/generator_impl_enable_test.cc
writer/wgsl/generator_impl_function_test.cc
writer/wgsl/generator_impl_global_decl_test.cc
writer/wgsl/generator_impl_identifier_test.cc
writer/wgsl/generator_impl_if_test.cc
+ writer/wgsl/generator_impl_initializer_test.cc
writer/wgsl/generator_impl_loop_test.cc
writer/wgsl/generator_impl_literal_test.cc
writer/wgsl/generator_impl_member_accessor_test.cc
writer/wgsl/generator_impl_return_test.cc
- writer/wgsl/generator_impl_static_assert_test.cc
writer/wgsl/generator_impl_switch_test.cc
writer/wgsl/generator_impl_type_test.cc
writer/wgsl/generator_impl_unary_op_test.cc
@@ -1228,6 +1239,7 @@
if(${TINT_BUILD_WGSL_READER} AND ${TINT_BUILD_WGSL_WRITER})
list(APPEND TINT_TEST_SRCS
+ ast/module_clone_test.cc
transform/add_empty_entry_point_test.cc
transform/add_block_attribute_test.cc
transform/array_length_from_uniform_test.cc
@@ -1298,19 +1310,19 @@
writer/msl/generator_impl_call_test.cc
writer/msl/generator_impl_case_test.cc
writer/msl/generator_impl_cast_test.cc
- writer/msl/generator_impl_initializer_test.cc
+ writer/msl/generator_impl_const_assert_test.cc
writer/msl/generator_impl_continue_test.cc
writer/msl/generator_impl_discard_test.cc
writer/msl/generator_impl_function_test.cc
writer/msl/generator_impl_identifier_test.cc
writer/msl/generator_impl_if_test.cc
+ writer/msl/generator_impl_initializer_test.cc
writer/msl/generator_impl_import_test.cc
writer/msl/generator_impl_loop_test.cc
writer/msl/generator_impl_member_accessor_test.cc
writer/msl/generator_impl_module_constant_test.cc
writer/msl/generator_impl_return_test.cc
writer/msl/generator_impl_sanitizer_test.cc
- writer/msl/generator_impl_static_assert_test.cc
writer/msl/generator_impl_switch_test.cc
writer/msl/generator_impl_test.cc
writer/msl/generator_impl_type_test.cc
@@ -1370,19 +1382,19 @@
writer/hlsl/generator_impl_call_test.cc
writer/hlsl/generator_impl_case_test.cc
writer/hlsl/generator_impl_cast_test.cc
- writer/hlsl/generator_impl_initializer_test.cc
+ writer/hlsl/generator_impl_const_assert_test.cc
writer/hlsl/generator_impl_continue_test.cc
writer/hlsl/generator_impl_discard_test.cc
writer/hlsl/generator_impl_function_test.cc
writer/hlsl/generator_impl_identifier_test.cc
writer/hlsl/generator_impl_if_test.cc
+ writer/hlsl/generator_impl_initializer_test.cc
writer/hlsl/generator_impl_import_test.cc
writer/hlsl/generator_impl_loop_test.cc
writer/hlsl/generator_impl_member_accessor_test.cc
writer/hlsl/generator_impl_module_constant_test.cc
writer/hlsl/generator_impl_return_test.cc
writer/hlsl/generator_impl_sanitizer_test.cc
- writer/hlsl/generator_impl_static_assert_test.cc
writer/hlsl/generator_impl_switch_test.cc
writer/hlsl/generator_impl_test.cc
writer/hlsl/generator_impl_type_test.cc
diff --git a/src/tint/ast/static_assert.cc b/src/tint/ast/const_assert.cc
similarity index 71%
rename from src/tint/ast/static_assert.cc
rename to src/tint/ast/const_assert.cc
index 0609194..5f9978d 100644
--- a/src/tint/ast/static_assert.cc
+++ b/src/tint/ast/const_assert.cc
@@ -12,29 +12,29 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/tint/ast/static_assert.h"
+#include "src/tint/ast/const_assert.h"
#include "src/tint/program_builder.h"
-TINT_INSTANTIATE_TYPEINFO(tint::ast::StaticAssert);
+TINT_INSTANTIATE_TYPEINFO(tint::ast::ConstAssert);
namespace tint::ast {
-StaticAssert::StaticAssert(ProgramID pid, NodeID nid, const Source& src, const Expression* cond)
+ConstAssert::ConstAssert(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;
+ConstAssert::ConstAssert(ConstAssert&&) = default;
-StaticAssert::~StaticAssert() = default;
+ConstAssert::~ConstAssert() = default;
-const StaticAssert* StaticAssert::Clone(CloneContext* ctx) const {
+const ConstAssert* ConstAssert::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<StaticAssert>(src, cond);
+ return ctx->dst->create<ConstAssert>(src, cond);
}
} // namespace tint::ast
diff --git a/src/tint/ast/static_assert.h b/src/tint/ast/const_assert.h
similarity index 73%
rename from src/tint/ast/static_assert.h
rename to src/tint/ast/const_assert.h
index f42ad07..8f0aae5 100644
--- a/src/tint/ast/static_assert.h
+++ b/src/tint/ast/const_assert.h
@@ -12,34 +12,34 @@
// 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_
+#ifndef SRC_TINT_AST_CONST_ASSERT_H_
+#define SRC_TINT_AST_CONST_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<StaticAssert, Statement> {
+/// A `const_assert` statement
+class ConstAssert final : public Castable<ConstAssert, Statement> {
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);
+ ConstAssert(ProgramID pid, NodeID nid, const Source& source, const Expression* condition);
/// Move constructor
- StaticAssert(StaticAssert&&);
+ ConstAssert(ConstAssert&&);
/// Destructor
- ~StaticAssert() override;
+ ~ConstAssert() 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;
+ const ConstAssert* Clone(CloneContext* ctx) const override;
/// The assertion condition
const Expression* const condition;
@@ -47,4 +47,4 @@
} // namespace tint::ast
-#endif // SRC_TINT_AST_STATIC_ASSERT_H_
+#endif // SRC_TINT_AST_CONST_ASSERT_H_
diff --git a/src/tint/ast/static_assert_test.cc b/src/tint/ast/const_assert_test.cc
similarity index 68%
rename from src/tint/ast/static_assert_test.cc
rename to src/tint/ast/const_assert_test.cc
index 48bee48..ec938b7 100644
--- a/src/tint/ast/static_assert_test.cc
+++ b/src/tint/ast/const_assert_test.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "src/tint/ast/static_assert.h"
+#include "src/tint/ast/const_assert.h"
#include "gtest/gtest-spi.h"
#include "src/tint/ast/test_helper.h"
@@ -20,44 +20,44 @@
namespace tint::ast {
namespace {
-using StaticAssertTest = TestHelper;
+using ConstAssertTest = TestHelper;
-TEST_F(StaticAssertTest, Creation) {
+TEST_F(ConstAssertTest, Creation) {
auto* cond = Expr(true);
- auto* stmt = StaticAssert(cond);
+ auto* stmt = ConstAssert(cond);
EXPECT_EQ(stmt->condition, cond);
}
-TEST_F(StaticAssertTest, Creation_WithSource) {
+TEST_F(ConstAssertTest, Creation_WithSource) {
auto* cond = Expr(true);
- auto* stmt = StaticAssert(Source{{20, 2}}, cond);
+ auto* stmt = ConstAssert(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) {
+TEST_F(ConstAssertTest, IsConstAssert) {
auto* cond = Expr(true);
- auto* stmt = StaticAssert(cond);
- EXPECT_TRUE(stmt->Is<ast::StaticAssert>());
+ auto* stmt = ConstAssert(cond);
+ EXPECT_TRUE(stmt->Is<ast::ConstAssert>());
}
-TEST_F(StaticAssertTest, Assert_Null_Condition) {
+TEST_F(ConstAssertTest, Assert_Null_Condition) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
- b.StaticAssert(nullptr);
+ b.ConstAssert(nullptr);
},
"internal compiler error");
}
-TEST_F(StaticAssertTest, Assert_DifferentProgramID_Condition) {
+TEST_F(ConstAssertTest, Assert_DifferentProgramID_Condition) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b1;
ProgramBuilder b2;
- b1.StaticAssert(b2.Expr(i32(123)));
+ b1.ConstAssert(b2.Expr(i32(123)));
},
"internal compiler error");
}
diff --git a/src/tint/ast/diagnostic_attribute.cc b/src/tint/ast/diagnostic_attribute.cc
new file mode 100644
index 0000000..21f698e
--- /dev/null
+++ b/src/tint/ast/diagnostic_attribute.cc
@@ -0,0 +1,44 @@
+// Copyright 2023 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/diagnostic_attribute.h"
+
+#include <string>
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::DiagnosticAttribute);
+
+namespace tint::ast {
+
+DiagnosticAttribute::DiagnosticAttribute(ProgramID pid,
+ NodeID nid,
+ const Source& src,
+ const ast::DiagnosticControl* dc)
+ : Base(pid, nid, src), control(dc) {}
+
+DiagnosticAttribute::~DiagnosticAttribute() = default;
+
+std::string DiagnosticAttribute::Name() const {
+ return "diagnostic";
+}
+
+const DiagnosticAttribute* DiagnosticAttribute::Clone(CloneContext* ctx) const {
+ // Clone arguments outside of create() call to have deterministic ordering
+ auto src = ctx->Clone(source);
+ auto dc = ctx->Clone(control);
+ return ctx->dst->create<DiagnosticAttribute>(src, dc);
+}
+
+} // namespace tint::ast
diff --git a/src/tint/ast/diagnostic_attribute.h b/src/tint/ast/diagnostic_attribute.h
new file mode 100644
index 0000000..7e0c514
--- /dev/null
+++ b/src/tint/ast/diagnostic_attribute.h
@@ -0,0 +1,53 @@
+// Copyright 2023 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_DIAGNOSTIC_ATTRIBUTE_H_
+#define SRC_TINT_AST_DIAGNOSTIC_ATTRIBUTE_H_
+
+#include <string>
+
+#include "src/tint/ast/attribute.h"
+#include "src/tint/ast/diagnostic_control.h"
+
+namespace tint::ast {
+
+/// A diagnostic attribute
+class DiagnosticAttribute final : public Castable<DiagnosticAttribute, Attribute> {
+ public:
+ /// constructor
+ /// @param pid the identifier of the program that owns this node
+ /// @param nid the unique node identifier
+ /// @param src the source of this node
+ /// @param dc the diagnostic control
+ DiagnosticAttribute(ProgramID pid,
+ NodeID nid,
+ const Source& src,
+ const ast::DiagnosticControl* dc);
+ ~DiagnosticAttribute() override;
+
+ /// @returns the WGSL name for the attribute
+ std::string Name() const override;
+
+ /// Clones this node and all transitive child nodes using the `CloneContext` `ctx`.
+ /// @param ctx the clone context
+ /// @return the newly cloned node
+ const DiagnosticAttribute* Clone(CloneContext* ctx) const override;
+
+ /// The diagnostic control expression.
+ const ast::DiagnosticControl* const control;
+};
+
+} // namespace tint::ast
+
+#endif // SRC_TINT_AST_DIAGNOSTIC_ATTRIBUTE_H_
diff --git a/src/tint/ast/diagnostic_attribute_test.cc b/src/tint/ast/diagnostic_attribute_test.cc
new file mode 100644
index 0000000..d7f0779
--- /dev/null
+++ b/src/tint/ast/diagnostic_attribute_test.cc
@@ -0,0 +1,32 @@
+// Copyright 2023 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/test_helper.h"
+
+namespace tint::ast {
+namespace {
+
+using namespace tint::number_suffixes; // NOLINT
+using DiagnosticAttributeTest = TestHelper;
+
+TEST_F(DiagnosticAttributeTest, Creation) {
+ auto* name = Expr("foo");
+ auto* d = DiagnosticAttribute(DiagnosticSeverity::kWarning, name);
+ EXPECT_EQ(d->Name(), "diagnostic");
+ EXPECT_EQ(d->control->severity, DiagnosticSeverity::kWarning);
+ EXPECT_EQ(d->control->rule_name, name);
+}
+
+} // namespace
+} // namespace tint::ast
diff --git a/src/tint/ast/diagnostic_control.cc b/src/tint/ast/diagnostic_control.cc
index d83e9c2..a814d58 100644
--- a/src/tint/ast/diagnostic_control.cc
+++ b/src/tint/ast/diagnostic_control.cc
@@ -38,6 +38,19 @@
return ctx->dst->create<DiagnosticControl>(src, severity, rule);
}
+diag::Severity ToSeverity(DiagnosticSeverity sc) {
+ switch (sc) {
+ case DiagnosticSeverity::kError:
+ return diag::Severity::Error;
+ case DiagnosticSeverity::kWarning:
+ return diag::Severity::Warning;
+ case DiagnosticSeverity::kInfo:
+ return diag::Severity::Note;
+ default:
+ return diag::Severity::InternalCompilerError;
+ }
+}
+
/// ParseDiagnosticSeverity parses a DiagnosticSeverity from a string.
/// @param str the string to parse
/// @returns the parsed enum, or DiagnosticSeverity::kUndefined if the string could not be parsed.
@@ -73,4 +86,29 @@
return out << "<unknown>";
}
+/// ParseDiagnosticRule parses a DiagnosticRule from a string.
+/// @param str the string to parse
+/// @returns the parsed enum, or DiagnosticRule::kUndefined if the string could not be parsed.
+DiagnosticRule ParseDiagnosticRule(std::string_view str) {
+ if (str == "chromium_unreachable_code") {
+ return DiagnosticRule::kChromiumUnreachableCode;
+ }
+ if (str == "derivative_uniformity") {
+ return DiagnosticRule::kDerivativeUniformity;
+ }
+ return DiagnosticRule::kUndefined;
+}
+
+std::ostream& operator<<(std::ostream& out, DiagnosticRule value) {
+ switch (value) {
+ case DiagnosticRule::kUndefined:
+ return out << "undefined";
+ case DiagnosticRule::kChromiumUnreachableCode:
+ return out << "chromium_unreachable_code";
+ case DiagnosticRule::kDerivativeUniformity:
+ return out << "derivative_uniformity";
+ }
+ return out << "<unknown>";
+}
+
} // namespace tint::ast
diff --git a/src/tint/ast/diagnostic_control.cc.tmpl b/src/tint/ast/diagnostic_control.cc.tmpl
index c5d1a73..c6dc463 100644
--- a/src/tint/ast/diagnostic_control.cc.tmpl
+++ b/src/tint/ast/diagnostic_control.cc.tmpl
@@ -28,8 +28,25 @@
return ctx->dst->create<DiagnosticControl>(src, severity, rule);
}
+diag::Severity ToSeverity(DiagnosticSeverity sc) {
+ switch (sc) {
+ case DiagnosticSeverity::kError:
+ return diag::Severity::Error;
+ case DiagnosticSeverity::kWarning:
+ return diag::Severity::Warning;
+ case DiagnosticSeverity::kInfo:
+ return diag::Severity::Note;
+ default:
+ return diag::Severity::InternalCompilerError;
+ }
+}
+
{{ Eval "ParseEnum" (Sem.Enum "diagnostic_severity")}}
{{ Eval "EnumOStream" (Sem.Enum "diagnostic_severity")}}
+{{ Eval "ParseEnum" (Sem.Enum "diagnostic_rule")}}
+
+{{ Eval "EnumOStream" (Sem.Enum "diagnostic_rule")}}
+
} // namespace tint::ast
diff --git a/src/tint/ast/diagnostic_control.h b/src/tint/ast/diagnostic_control.h
index f127c1f..7b6e28c 100644
--- a/src/tint/ast/diagnostic_control.h
+++ b/src/tint/ast/diagnostic_control.h
@@ -25,6 +25,7 @@
#include <ostream>
#include <string>
+#include <unordered_map>
#include "src/tint/ast/node.h"
@@ -61,6 +62,34 @@
"warning",
};
+/// The diagnostic rule.
+enum class DiagnosticRule {
+ kUndefined,
+ kChromiumUnreachableCode,
+ kDerivativeUniformity,
+};
+
+/// @param out the std::ostream to write to
+/// @param value the DiagnosticRule
+/// @returns `out` so calls can be chained
+std::ostream& operator<<(std::ostream& out, DiagnosticRule value);
+
+/// ParseDiagnosticRule parses a DiagnosticRule from a string.
+/// @param str the string to parse
+/// @returns the parsed enum, or DiagnosticRule::kUndefined if the string could not be parsed.
+DiagnosticRule ParseDiagnosticRule(std::string_view str);
+
+constexpr const char* kDiagnosticRuleStrings[] = {
+ "chromium_unreachable_code",
+ "derivative_uniformity",
+};
+
+/// Convert a DiagnosticSeverity to the corresponding diag::Severity.
+diag::Severity ToSeverity(DiagnosticSeverity sc);
+
+/// DiagnosticRuleSeverities is a map from diagnostic rule to diagnostic severity.
+using DiagnosticRuleSeverities = std::unordered_map<DiagnosticRule, DiagnosticSeverity>;
+
/// A diagnostic control used for diagnostic directives and attributes.
class DiagnosticControl : public Castable<DiagnosticControl, Node> {
public:
diff --git a/src/tint/ast/diagnostic_control.h.tmpl b/src/tint/ast/diagnostic_control.h.tmpl
index 5ddbc6d..49946c2 100644
--- a/src/tint/ast/diagnostic_control.h.tmpl
+++ b/src/tint/ast/diagnostic_control.h.tmpl
@@ -15,6 +15,7 @@
#include <ostream>
#include <string>
+#include <unordered_map>
#include "src/tint/ast/node.h"
@@ -28,6 +29,15 @@
/// The diagnostic severity control.
{{ Eval "DeclareEnum" (Sem.Enum "diagnostic_severity") }}
+/// The diagnostic rule.
+{{ Eval "DeclareEnum" (Sem.Enum "diagnostic_rule") }}
+
+/// Convert a DiagnosticSeverity to the corresponding diag::Severity.
+diag::Severity ToSeverity(DiagnosticSeverity sc);
+
+/// DiagnosticRuleSeverities is a map from diagnostic rule to diagnostic severity.
+using DiagnosticRuleSeverities = std::unordered_map<DiagnosticRule, DiagnosticSeverity>;
+
/// A diagnostic control used for diagnostic directives and attributes.
class DiagnosticControl : public Castable<DiagnosticControl, Node> {
public:
diff --git a/src/tint/ast/diagnostic_control_bench.cc b/src/tint/ast/diagnostic_control_bench.cc
index d96f93b..faf354e 100644
--- a/src/tint/ast/diagnostic_control_bench.cc
+++ b/src/tint/ast/diagnostic_control_bench.cc
@@ -46,5 +46,23 @@
BENCHMARK(DiagnosticSeverityParser);
+void DiagnosticRuleParser(::benchmark::State& state) {
+ std::array kStrings{
+ "hromium_unyeachable_code", "chrorrillmGunnreachable_c77de", "chromium_unreachable4cod00",
+ "chromium_unreachable_code", "chromium_unracaboo_code", "chromium_unrzzchabl_code",
+ "ciipp11ium_unreachable_cod", "derivXXtive_uniformity", "55erivativeIIunifonn99ity",
+ "derirratHHaae_YniforSSity", "derivative_uniformity", "erivtive_unHkkormit",
+ "jerivaive_uniforRgty", "derivatbve_unformiy",
+ };
+ for (auto _ : state) {
+ for (auto& str : kStrings) {
+ auto result = ParseDiagnosticRule(str);
+ benchmark::DoNotOptimize(result);
+ }
+ }
+}
+
+BENCHMARK(DiagnosticRuleParser);
+
} // namespace
} // namespace tint::ast
diff --git a/src/tint/ast/diagnostic_control_bench.cc.tmpl b/src/tint/ast/diagnostic_control_bench.cc.tmpl
index 48058ca..55d3cce 100644
--- a/src/tint/ast/diagnostic_control_bench.cc.tmpl
+++ b/src/tint/ast/diagnostic_control_bench.cc.tmpl
@@ -21,5 +21,7 @@
{{ Eval "BenchmarkParseEnum" (Sem.Enum "diagnostic_severity")}}
+{{ Eval "BenchmarkParseEnum" (Sem.Enum "diagnostic_rule")}}
+
} // namespace
} // namespace tint::ast
diff --git a/src/tint/ast/diagnostic_control_test.cc b/src/tint/ast/diagnostic_control_test.cc
index 2dbd139..11a544a 100644
--- a/src/tint/ast/diagnostic_control_test.cc
+++ b/src/tint/ast/diagnostic_control_test.cc
@@ -100,5 +100,57 @@
} // namespace diagnostic_severity_tests
+namespace diagnostic_rule_tests {
+
+namespace parse_print_tests {
+
+struct Case {
+ const char* string;
+ DiagnosticRule value;
+};
+
+inline std::ostream& operator<<(std::ostream& out, Case c) {
+ return out << "'" << std::string(c.string) << "'";
+}
+
+static constexpr Case kValidCases[] = {
+ {"chromium_unreachable_code", DiagnosticRule::kChromiumUnreachableCode},
+ {"derivative_uniformity", DiagnosticRule::kDerivativeUniformity},
+};
+
+static constexpr Case kInvalidCases[] = {
+ {"cXromggum_unreachable_cde", DiagnosticRule::kUndefined},
+ {"chroVium_unruchble_codX", DiagnosticRule::kUndefined},
+ {"chromium_3nreachable_code", DiagnosticRule::kUndefined},
+ {"derivatEve_uniformity", DiagnosticRule::kUndefined},
+ {"deTTPivative_uniformit", DiagnosticRule::kUndefined},
+ {"derivtive_uddxxformity", DiagnosticRule::kUndefined},
+};
+
+using DiagnosticRuleParseTest = testing::TestWithParam<Case>;
+
+TEST_P(DiagnosticRuleParseTest, Parse) {
+ const char* string = GetParam().string;
+ DiagnosticRule expect = GetParam().value;
+ EXPECT_EQ(expect, ParseDiagnosticRule(string));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidCases, DiagnosticRuleParseTest, testing::ValuesIn(kValidCases));
+INSTANTIATE_TEST_SUITE_P(InvalidCases, DiagnosticRuleParseTest, testing::ValuesIn(kInvalidCases));
+
+using DiagnosticRulePrintTest = testing::TestWithParam<Case>;
+
+TEST_P(DiagnosticRulePrintTest, Print) {
+ DiagnosticRule value = GetParam().value;
+ const char* expect = GetParam().string;
+ EXPECT_EQ(expect, utils::ToString(value));
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidCases, DiagnosticRulePrintTest, testing::ValuesIn(kValidCases));
+
+} // namespace parse_print_tests
+
+} // namespace diagnostic_rule_tests
+
} // namespace
} // namespace tint::ast
diff --git a/src/tint/ast/diagnostic_control_test.cc.tmpl b/src/tint/ast/diagnostic_control_test.cc.tmpl
index a078cce..2d1a2cc 100644
--- a/src/tint/ast/diagnostic_control_test.cc.tmpl
+++ b/src/tint/ast/diagnostic_control_test.cc.tmpl
@@ -41,5 +41,11 @@
} // namespace diagnostic_severity_tests
+namespace diagnostic_rule_tests {
+
+{{ Eval "TestParsePrintEnum" (Sem.Enum "diagnostic_rule")}}
+
+} // namespace diagnostic_rule_tests
+
} // namespace
} // namespace tint::ast
diff --git a/src/tint/ast/module.cc b/src/tint/ast/module.cc
index 5003996..843f1fc 100644
--- a/src/tint/ast/module.cc
+++ b/src/tint/ast/module.cc
@@ -71,17 +71,28 @@
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, var, program_id);
global_variables_.Push(var);
},
+ [&](const DiagnosticControl* diag_control) {
+ TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, diag_control, program_id);
+ diagnostic_controls_.Push(diag_control);
+ },
[&](const Enable* enable) {
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, enable, program_id);
enables_.Push(enable);
},
- [&](const StaticAssert* assertion) {
+ [&](const ConstAssert* assertion) {
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, assertion, program_id);
- static_asserts_.Push(assertion);
+ const_asserts_.Push(assertion);
},
[&](Default) { TINT_ICE(AST, diags) << "Unknown global declaration type"; });
}
+void Module::AddDiagnosticControl(const ast::DiagnosticControl* control) {
+ TINT_ASSERT(AST, control);
+ TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, control, program_id);
+ global_declarations_.Push(control);
+ diagnostic_controls_.Push(control);
+}
+
void Module::AddEnable(const ast::Enable* enable) {
TINT_ASSERT(AST, enable);
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, enable, program_id);
@@ -96,10 +107,10 @@
global_declarations_.Push(var);
}
-void Module::AddStaticAssert(const StaticAssert* assertion) {
+void Module::AddConstAssert(const ConstAssert* assertion) {
TINT_ASSERT(AST, assertion);
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, assertion, program_id);
- static_asserts_.Push(assertion);
+ const_asserts_.Push(assertion);
global_declarations_.Push(assertion);
}
diff --git a/src/tint/ast/module.h b/src/tint/ast/module.h
index 2818cb8..7d0cdfd 100644
--- a/src/tint/ast/module.h
+++ b/src/tint/ast/module.h
@@ -17,9 +17,10 @@
#include <string>
+#include "src/tint/ast/const_assert.h"
+#include "src/tint/ast/diagnostic_control.h"
#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"
@@ -92,19 +93,26 @@
return out;
}
+ /// Add a global diagnostic control to the module
+ /// @param control the diagnostic control to add
+ void AddDiagnosticControl(const DiagnosticControl* control);
+
/// Add a enable directive to the module
/// @param ext the enable directive to add
void AddEnable(const Enable* ext);
+ /// @returns the global diagnostic controls for the module
+ const auto& DiagnosticControls() const { return diagnostic_controls_; }
+
/// @returns the extension set for the module
const auto& Enables() const { return enables_; }
- /// Add a global static assertion to the module
- /// @param assertion the static assert to add
- void AddStaticAssert(const StaticAssert* assertion);
+ /// Add a global const assertion to the module
+ /// @param assertion the const assert to add
+ void AddConstAssert(const ConstAssert* assertion);
- /// @returns the list of global static assertions
- const auto& StaticAsserts() const { return static_asserts_; }
+ /// @returns the list of global const assertions
+ const auto& ConstAsserts() const { return const_asserts_; }
/// Adds a type declaration to the module
/// @param decl the type declaration to add
@@ -146,8 +154,9 @@
utils::Vector<const TypeDecl*, 16> type_decls_;
FunctionList functions_;
utils::Vector<const Variable*, 32> global_variables_;
+ utils::Vector<const DiagnosticControl*, 8> diagnostic_controls_;
utils::Vector<const Enable*, 8> enables_;
- utils::Vector<const StaticAssert*, 8> static_asserts_;
+ utils::Vector<const ConstAssert*, 8> const_asserts_;
};
} // namespace tint::ast
diff --git a/src/tint/ast/module_clone_test.cc b/src/tint/ast/module_clone_test.cc
index 09dced5..7bda881 100644
--- a/src/tint/ast/module_clone_test.cc
+++ b/src/tint/ast/module_clone_test.cc
@@ -22,10 +22,12 @@
namespace {
TEST(ModuleCloneTest, Clone) {
-#if TINT_BUILD_WGSL_READER && TINT_BUILD_WGSL_WRITER
// Shader that exercises the bulk of the AST nodes and types.
// See also fuzzers/tint_ast_clone_fuzzer.cc for further coverage of cloning.
- Source::File file("test.wgsl", R"(struct S0 {
+ Source::File file("test.wgsl", R"(enable f16;
+diagnostic(off, chromium_unreachable_code);
+
+struct S0 {
@size(4)
m0 : u32,
m1 : array<u32>,
@@ -63,6 +65,7 @@
return 0.0;
}
+@diagnostic(warning, chromium_unreachable_code)
fn f1(p0 : f32, p1 : i32) -> f32 {
var l0 : i32 = 3;
var l1 : f32 = 8.0;
@@ -170,11 +173,6 @@
ASSERT_TRUE(result.success);
auto dst_wgsl = result.wgsl;
ASSERT_EQ(src_wgsl, dst_wgsl);
-
-#else // #if TINT_BUILD_WGSL_READER && TINT_BUILD_WGSL_WRITER
- GTEST_SKIP() << "ModuleCloneTest requires TINT_BUILD_WGSL_READER and "
- "TINT_BUILD_WGSL_WRITER to be enabled";
-#endif
}
} // namespace
diff --git a/src/tint/ast/module_test.cc b/src/tint/ast/module_test.cc
index c7b77c9..980645a 100644
--- a/src/tint/ast/module_test.cc
+++ b/src/tint/ast/module_test.cc
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "gmock/gmock.h"
#include "gtest/gtest-spi.h"
#include "src/tint/ast/test_helper.h"
#include "src/tint/clone_context.h"
@@ -130,5 +131,29 @@
ASSERT_EQ(cloned.Symbols().NameFor(decls[4]->As<ast::Alias>()->name), "inserted_before_V");
}
+TEST_F(ModuleTest, Directives) {
+ auto* enable_1 = Enable(ast::Extension::kF16);
+ auto* diagnostic_1 = DiagnosticDirective(DiagnosticSeverity::kWarning, Expr("foo"));
+ auto* enable_2 = Enable(ast::Extension::kChromiumExperimentalFullPtrParameters);
+ auto* diagnostic_2 = DiagnosticDirective(DiagnosticSeverity::kOff, Expr("bar"));
+
+ this->SetResolveOnBuild(false);
+ Program program(std::move(*this));
+ EXPECT_THAT(program.AST().GlobalDeclarations(), ::testing::ContainerEq(utils::Vector{
+ enable_1,
+ diagnostic_1,
+ enable_2,
+ diagnostic_2,
+ }));
+ EXPECT_THAT(program.AST().Enables(), ::testing::ContainerEq(utils::Vector{
+ enable_1,
+ enable_2,
+ }));
+ EXPECT_THAT(program.AST().DiagnosticControls(), ::testing::ContainerEq(utils::Vector{
+ diagnostic_1,
+ diagnostic_2,
+ }));
+}
+
} // namespace
} // namespace tint::ast
diff --git a/src/tint/cmd/main.cc b/src/tint/cmd/main.cc
index a30a133..fa8dbc0 100644
--- a/src/tint/cmd/main.cc
+++ b/src/tint/cmd/main.cc
@@ -103,6 +103,10 @@
bool rename_all = false;
+#if TINT_BUILD_SPV_READER
+ tint::reader::spirv::Options spirv_reader_options;
+#endif
+
std::vector<std::string> transforms;
std::string fxc_path;
@@ -135,6 +139,9 @@
--transform <name list> -- Runs transforms, name list is comma separated
Available transforms:
${transforms} --parse-only -- Stop after parsing the input
+ --allow-non-uniform-derivatives -- When using SPIR-V input, allow non-uniform derivatives by
+ inserting a module-scope directive to suppress any uniformity
+ violations that may be produced.
--disable-workgroup-init -- Disable workgroup memory zero initialization.
--demangle -- Preserve original source names. Demangle them.
Affects AST dumping, and text-based output languages.
@@ -443,6 +450,13 @@
opts->transforms = split_on_comma(args[i]);
} else if (arg == "--parse-only") {
opts->parse_only = true;
+ } else if (arg == "--allow-non-uniform-derivatives") {
+#if TINT_BUILD_SPV_READER
+ opts->spirv_reader_options.allow_non_uniform_derivatives = true;
+#else
+ std::cerr << "Tint not built with the SPIR-V reader enabled" << std::endl;
+ return false;
+#endif
} else if (arg == "--disable-workgroup-init") {
opts->disable_workgroup_init = true;
} else if (arg == "--demangle") {
@@ -1285,7 +1299,8 @@
if (!ReadFile<uint32_t>(options.input_filename, &data)) {
return 1;
}
- program = std::make_unique<tint::Program>(tint::reader::spirv::Parse(data));
+ program = std::make_unique<tint::Program>(
+ tint::reader::spirv::Parse(data, options.spirv_reader_options));
break;
#else
std::cerr << "Tint not built with the SPIR-V reader enabled" << std::endl;
@@ -1309,7 +1324,8 @@
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS)) {
return 1;
}
- program = std::make_unique<tint::Program>(tint::reader::spirv::Parse(data));
+ program = std::make_unique<tint::Program>(
+ tint::reader::spirv::Parse(data, options.spirv_reader_options));
break;
#else
std::cerr << "Tint not built with the SPIR-V reader enabled" << std::endl;
diff --git a/src/tint/fuzzers/data_builder.h b/src/tint/fuzzers/data_builder.h
index 2aaecf7..d9e8e18 100644
--- a/src/tint/fuzzers/data_builder.h
+++ b/src/tint/fuzzers/data_builder.h
@@ -23,8 +23,6 @@
#include <vector>
#include "src/tint/fuzzers/random_generator.h"
-#include "src/tint/writer/hlsl/generator.h"
-#include "src/tint/writer/msl/generator.h"
namespace tint::fuzzers {
diff --git a/src/tint/intrinsics.def b/src/tint/intrinsics.def
index 05fe8c9..f3eb2ac 100644
--- a/src/tint/intrinsics.def
+++ b/src/tint/intrinsics.def
@@ -40,6 +40,14 @@
@internal point_size
}
+// https://gpuweb.github.io/gpuweb/wgsl/#filterable-triggering-rules
+enum diagnostic_rule {
+ // Rules defined in the spec.
+ derivative_uniformity
+ // Chromium specific rules not defined in the spec.
+ chromium_unreachable_code
+}
+
// https://gpuweb.github.io/gpuweb/wgsl/#syntax-severity_control_name
enum diagnostic_severity {
error
diff --git a/src/tint/ir/binary_test.cc b/src/tint/ir/binary_test.cc
index 68341ad..b5fba93 100644
--- a/src/tint/ir/binary_test.cc
+++ b/src/tint/ir/binary_test.cc
@@ -521,15 +521,15 @@
EXPECT_EQ(instr->GetKind(), Binary::Kind::kAnd);
ASSERT_NE(instr->Result(), nullptr);
- ASSERT_EQ(instr->Result()->Usage().Length(), 1);
+ ASSERT_EQ(instr->Result()->Usage().Length(), 1u);
EXPECT_EQ(instr->Result()->Usage()[0], instr);
ASSERT_NE(instr->LHS(), nullptr);
- ASSERT_EQ(instr->LHS()->Usage().Length(), 1);
+ ASSERT_EQ(instr->LHS()->Usage().Length(), 1u);
EXPECT_EQ(instr->LHS()->Usage()[0], instr);
ASSERT_NE(instr->RHS(), nullptr);
- ASSERT_EQ(instr->RHS()->Usage().Length(), 1);
+ ASSERT_EQ(instr->RHS()->Usage().Length(), 1u);
EXPECT_EQ(instr->RHS()->Usage()[0], instr);
}
@@ -544,13 +544,13 @@
EXPECT_EQ(instr->GetKind(), Binary::Kind::kAnd);
ASSERT_NE(instr->Result(), nullptr);
- ASSERT_EQ(instr->Result()->Usage().Length(), 1);
+ ASSERT_EQ(instr->Result()->Usage().Length(), 1u);
EXPECT_EQ(instr->Result()->Usage()[0], instr);
ASSERT_EQ(instr->LHS(), instr->RHS());
ASSERT_NE(instr->LHS(), nullptr);
- ASSERT_EQ(instr->LHS()->Usage().Length(), 1);
+ ASSERT_EQ(instr->LHS()->Usage().Length(), 1u);
EXPECT_EQ(instr->LHS()->Usage()[0], instr);
}
diff --git a/src/tint/ir/bitcast_test.cc b/src/tint/ir/bitcast_test.cc
index a6924df..ee7bd3d 100644
--- a/src/tint/ir/bitcast_test.cc
+++ b/src/tint/ir/bitcast_test.cc
@@ -53,11 +53,11 @@
b.builder.Bitcast(b.builder.ir.types.Get<type::I32>(), b.builder.Constant(4_i));
ASSERT_NE(instr->Result(), nullptr);
- ASSERT_EQ(instr->Result()->Usage().Length(), 1);
+ ASSERT_EQ(instr->Result()->Usage().Length(), 1u);
EXPECT_EQ(instr->Result()->Usage()[0], instr);
ASSERT_NE(instr->Val(), nullptr);
- ASSERT_EQ(instr->Val()->Usage().Length(), 1);
+ ASSERT_EQ(instr->Val()->Usage().Length(), 1u);
EXPECT_EQ(instr->Val()->Usage()[0], instr);
}
diff --git a/src/tint/ir/builder_impl.cc b/src/tint/ir/builder_impl.cc
index 0bcaf24..2a8ed06 100644
--- a/src/tint/ir/builder_impl.cc
+++ b/src/tint/ir/builder_impl.cc
@@ -21,6 +21,7 @@
#include "src/tint/ast/bool_literal_expression.h"
#include "src/tint/ast/break_if_statement.h"
#include "src/tint/ast/break_statement.h"
+#include "src/tint/ast/const_assert.h"
#include "src/tint/ast/continue_statement.h"
#include "src/tint/ast/float_literal_expression.h"
#include "src/tint/ast/for_loop_statement.h"
@@ -33,7 +34,6 @@
#include "src/tint/ast/override.h"
#include "src/tint/ast/return_statement.h"
#include "src/tint/ast/statement.h"
-#include "src/tint/ast/static_assert.h"
#include "src/tint/ast/struct.h"
#include "src/tint/ast/struct_member_align_attribute.h"
#include "src/tint/ast/struct_member_size_attribute.h"
@@ -155,7 +155,7 @@
// TODO(dsinclair): Implement? I think these need to be passed along so further stages
// know what is enabled.
// },
- [&](const ast::StaticAssert*) {
+ [&](const ast::ConstAssert*) {
// Evaluated by the resolver, drop from the IR.
return true;
},
@@ -253,7 +253,7 @@
[&](const ast::ReturnStatement* r) { return EmitReturn(r); },
[&](const ast::SwitchStatement* s) { return EmitSwitch(s); },
[&](const ast::VariableDeclStatement* v) { return EmitVariable(v->variable); },
- [&](const ast::StaticAssert*) {
+ [&](const ast::ConstAssert*) {
return true; // Not emitted
},
[&](Default) {
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index 4e760de..c05abf9 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -37,9 +37,11 @@
#include "src/tint/ast/case_statement.h"
#include "src/tint/ast/compound_assignment_statement.h"
#include "src/tint/ast/const.h"
+#include "src/tint/ast/const_assert.h"
#include "src/tint/ast/continue_statement.h"
#include "src/tint/ast/depth_multisampled_texture.h"
#include "src/tint/ast/depth_texture.h"
+#include "src/tint/ast/diagnostic_attribute.h"
#include "src/tint/ast/diagnostic_control.h"
#include "src/tint/ast/disable_validation_attribute.h"
#include "src/tint/ast/discard_statement.h"
@@ -72,7 +74,6 @@
#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"
@@ -1926,38 +1927,38 @@
/// @param source the source information
/// @param condition the assertion condition
- /// @returns a new `ast::StaticAssert`, which is automatically registered as a global statement
+ /// @returns a new `ast::ConstAssert`, which is automatically registered as a global statement
/// with the ast::Module.
template <typename EXPR>
- const ast::StaticAssert* GlobalStaticAssert(const Source& source, EXPR&& condition) {
- auto* sa = StaticAssert(source, std::forward<EXPR>(condition));
- AST().AddStaticAssert(sa);
+ const ast::ConstAssert* GlobalConstAssert(const Source& source, EXPR&& condition) {
+ auto* sa = ConstAssert(source, std::forward<EXPR>(condition));
+ AST().AddConstAssert(sa);
return sa;
}
/// @param condition the assertion condition
- /// @returns a new `ast::StaticAssert`, which is automatically registered as a global statement
+ /// @returns a new `ast::ConstAssert`, which is automatically registered as a global statement
/// with the ast::Module.
template <typename EXPR, typename = DisableIfSource<EXPR>>
- const ast::StaticAssert* GlobalStaticAssert(EXPR&& condition) {
- auto* sa = StaticAssert(std::forward<EXPR>(condition));
- AST().AddStaticAssert(sa);
+ const ast::ConstAssert* GlobalConstAssert(EXPR&& condition) {
+ auto* sa = ConstAssert(std::forward<EXPR>(condition));
+ AST().AddConstAssert(sa);
return sa;
}
/// @param source the source information
/// @param condition the assertion condition
- /// @returns a new `ast::StaticAssert` with the given assertion condition
+ /// @returns a new `ast::ConstAssert` with the given assertion condition
template <typename EXPR>
- const ast::StaticAssert* StaticAssert(const Source& source, EXPR&& condition) {
- return create<ast::StaticAssert>(source, Expr(std::forward<EXPR>(condition)));
+ const ast::ConstAssert* ConstAssert(const Source& source, EXPR&& condition) {
+ return create<ast::ConstAssert>(source, Expr(std::forward<EXPR>(condition)));
}
/// @param condition the assertion condition
- /// @returns a new `ast::StaticAssert` with the given assertion condition
+ /// @returns a new `ast::ConstAssert` with the given assertion condition
template <typename EXPR, typename = DisableIfSource<EXPR>>
- const ast::StaticAssert* StaticAssert(EXPR&& condition) {
- return create<ast::StaticAssert>(Expr(std::forward<EXPR>(condition)));
+ const ast::ConstAssert* ConstAssert(EXPR&& condition) {
+ return create<ast::ConstAssert>(Expr(std::forward<EXPR>(condition)));
}
/// @param source the source information
@@ -3221,6 +3222,30 @@
validation);
}
+ /// Creates an ast::DiagnosticAttribute
+ /// @param source the source information
+ /// @param severity the diagnostic severity control
+ /// @param rule_name the diagnostic rule name
+ /// @returns the diagnostic attribute pointer
+ const ast::DiagnosticAttribute* DiagnosticAttribute(
+ const Source& source,
+ ast::DiagnosticSeverity severity,
+ const ast::IdentifierExpression* rule_name) {
+ return create<ast::DiagnosticAttribute>(source,
+ DiagnosticControl(source, severity, rule_name));
+ }
+
+ /// Creates an ast::DiagnosticAttribute
+ /// @param severity the diagnostic severity control
+ /// @param rule_name the diagnostic rule name
+ /// @returns the diagnostic attribute pointer
+ const ast::DiagnosticAttribute* DiagnosticAttribute(
+ ast::DiagnosticSeverity severity,
+ const ast::IdentifierExpression* rule_name) {
+ return create<ast::DiagnosticAttribute>(source_,
+ DiagnosticControl(source_, severity, rule_name));
+ }
+
/// Creates an ast::DiagnosticControl
/// @param source the source information
/// @param severity the diagnostic severity control
@@ -3241,6 +3266,30 @@
return create<ast::DiagnosticControl>(source_, severity, rule_name);
}
+ /// Add a global diagnostic control to the module.
+ /// @param source the source information
+ /// @param severity the diagnostic severity control
+ /// @param rule_name the diagnostic rule name
+ /// @returns the diagnostic control pointer
+ const ast::DiagnosticControl* DiagnosticDirective(const Source& source,
+ ast::DiagnosticSeverity severity,
+ const ast::IdentifierExpression* rule_name) {
+ auto* control = DiagnosticControl(source, severity, rule_name);
+ AST().AddDiagnosticControl(control);
+ return control;
+ }
+
+ /// Add a global diagnostic control to the module.
+ /// @param severity the diagnostic severity control
+ /// @param rule_name the diagnostic rule name
+ /// @returns the diagnostic control pointer
+ const ast::DiagnosticControl* DiagnosticDirective(ast::DiagnosticSeverity severity,
+ const ast::IdentifierExpression* rule_name) {
+ auto* control = DiagnosticControl(source_, severity, rule_name);
+ AST().AddDiagnosticControl(control);
+ return control;
+ }
+
/// Sets the current builder source to `src`
/// @param src the Source used for future create() calls
void SetSource(const Source& src) {
diff --git a/src/tint/reader/spirv/function_memory_test.cc b/src/tint/reader/spirv/function_memory_test.cc
index 0ff9ba5..1877725 100644
--- a/src/tint/reader/spirv/function_memory_test.cc
+++ b/src/tint/reader/spirv/function_memory_test.cc
@@ -950,7 +950,7 @@
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly << p->error();
const auto module_str = test::ToString(p->program());
- EXPECT_THAT(module_str, HasSubstr(R"(type RTArr = @stride(4) array<u32>;
+ EXPECT_THAT(module_str, HasSubstr(R"(alias RTArr = @stride(4) array<u32>;
struct S {
/* @offset(0) */
diff --git a/src/tint/reader/spirv/parser.cc b/src/tint/reader/spirv/parser.cc
index ac43b9e..41e6df3 100644
--- a/src/tint/reader/spirv/parser.cc
+++ b/src/tint/reader/spirv/parser.cc
@@ -27,7 +27,7 @@
namespace tint::reader::spirv {
-Program Parse(const std::vector<uint32_t>& input) {
+Program Parse(const std::vector<uint32_t>& input, const Options& options) {
ParserImpl parser(input);
bool parsed = parser.Parse();
@@ -38,6 +38,13 @@
return Program(std::move(builder));
}
+ if (options.allow_non_uniform_derivatives) {
+ // Suppress errors regarding non-uniform derivative operations if requested, by adding a
+ // diagnostic directive to the module.
+ builder.DiagnosticDirective(ast::DiagnosticSeverity::kOff,
+ builder.Expr("derivative_uniformity"));
+ }
+
// The SPIR-V parser can construct disjoint AST nodes, which is invalid for
// the Resolver. Clone the Program to clean these up.
builder.SetResolveOnBuild(false);
diff --git a/src/tint/reader/spirv/parser.h b/src/tint/reader/spirv/parser.h
index 3641e08..78c80c0 100644
--- a/src/tint/reader/spirv/parser.h
+++ b/src/tint/reader/spirv/parser.h
@@ -21,13 +21,20 @@
namespace tint::reader::spirv {
+/// Options that control how the SPIR-V parser should behave.
+struct Options {
+ /// Set to `true` to allow calls to derivative builtins in non-uniform control flow.
+ bool allow_non_uniform_derivatives = false;
+};
+
/// Parses the SPIR-V source data, returning the parsed program.
/// If the source data fails to parse then the returned
/// `program.Diagnostics.contains_errors()` will be true, and the
/// `program.Diagnostics()` will describe the error.
/// @param input the source data
+/// @param options the parser options
/// @returns the parsed program
-Program Parse(const std::vector<uint32_t>& input);
+Program Parse(const std::vector<uint32_t>& input, const Options& options = {});
} // namespace tint::reader::spirv
diff --git a/src/tint/reader/spirv/parser_impl_module_var_test.cc b/src/tint/reader/spirv/parser_impl_module_var_test.cc
index 7543792..5077a51 100644
--- a/src/tint/reader/spirv/parser_impl_module_var_test.cc
+++ b/src/tint/reader/spirv/parser_impl_module_var_test.cc
@@ -1251,7 +1251,7 @@
ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
EXPECT_TRUE(p->error().empty());
const auto module_str = test::ToString(p->program());
- EXPECT_THAT(module_str, HasSubstr(R"(type Arr = @stride(4) array<u32, 2u>;
+ EXPECT_THAT(module_str, HasSubstr(R"(alias Arr = @stride(4) array<u32, 2u>;
struct S {
/* @offset(0) */
@@ -2424,13 +2424,13 @@
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = test::ToString(p->program());
- const std::string expected = R"(type Arr = @stride(4) array<u32, 1u>;
+ const std::string expected = R"(alias Arr = @stride(4) array<u32, 1u>;
-type Arr_1 = @stride(4) array<u32, 2u>;
+alias Arr_1 = @stride(4) array<u32, 2u>;
-type Arr_2 = @stride(4) array<i32, 1u>;
+alias Arr_2 = @stride(4) array<i32, 1u>;
-type Arr_3 = @stride(4) array<i32, 2u>;
+alias Arr_3 = @stride(4) array<i32, 2u>;
var<private> x_1 : Arr;
@@ -2463,13 +2463,13 @@
ASSERT_TRUE(p->BuildAndParseInternalModule()) << p->error() << assembly;
EXPECT_TRUE(p->error().empty());
const auto module_str = test::ToString(p->program());
- const std::string expected = R"(type Arr = @stride(4) array<u32, 1u>;
+ const std::string expected = R"(alias Arr = @stride(4) array<u32, 1u>;
-type Arr_1 = @stride(4) array<u32, 2u>;
+alias Arr_1 = @stride(4) array<u32, 2u>;
-type Arr_2 = @stride(4) array<i32, 1u>;
+alias Arr_2 = @stride(4) array<i32, 1u>;
-type Arr_3 = @stride(4) array<i32, 2u>;
+alias Arr_3 = @stride(4) array<i32, 2u>;
var<private> x_1 : Arr;
diff --git a/src/tint/reader/spirv/parser_impl_named_types_test.cc b/src/tint/reader/spirv/parser_impl_named_types_test.cc
index 3fb07a4..2b29fbe 100644
--- a/src/tint/reader/spirv/parser_impl_named_types_test.cc
+++ b/src/tint/reader/spirv/parser_impl_named_types_test.cc
@@ -89,9 +89,9 @@
%arr2 = OpTypeRuntimeArray %uint
)"));
EXPECT_TRUE(p->BuildAndParseInternalModule());
- EXPECT_THAT(test::ToString(p->program()), HasSubstr(R"(type RTArr = @stride(8) array<u32>;
+ EXPECT_THAT(test::ToString(p->program()), HasSubstr(R"(alias RTArr = @stride(8) array<u32>;
-type RTArr_1 = @stride(8) array<u32>;
+alias RTArr_1 = @stride(8) array<u32>;
)"));
p->DeliberatelyInvalidSpirv();
@@ -135,9 +135,9 @@
%arr2 = OpTypeArray %uint %uint_5
)"));
EXPECT_TRUE(p->BuildAndParseInternalModule());
- EXPECT_THAT(test::ToString(p->program()), HasSubstr(R"(type Arr = @stride(8) array<u32, 5u>;
+ EXPECT_THAT(test::ToString(p->program()), HasSubstr(R"(alias Arr = @stride(8) array<u32, 5u>;
-type Arr_1 = @stride(8) array<u32, 5u>;
+alias Arr_1 = @stride(8) array<u32, 5u>;
)"));
p->DeliberatelyInvalidSpirv();
diff --git a/src/tint/reader/spirv/parser_test.cc b/src/tint/reader/spirv/parser_test.cc
index 35cb5da..9cbb461 100644
--- a/src/tint/reader/spirv/parser_test.cc
+++ b/src/tint/reader/spirv/parser_test.cc
@@ -14,7 +14,9 @@
#include "src/tint/reader/spirv/parser.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "src/tint/reader/spirv/spirv_tools_helpers_test.h"
namespace tint::reader::spirv {
namespace {
@@ -29,6 +31,53 @@
EXPECT_EQ(errs, "error: line:0: Invalid SPIR-V magic number.\n");
}
+constexpr auto kShaderWithNonUniformDerivative = R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %foo "foo" %x
+ OpExecutionMode %foo OriginUpperLeft
+ OpDecorate %x Location 0
+ %float = OpTypeFloat 32
+%_ptr_Input_float = OpTypePointer Input %float
+ %x = OpVariable %_ptr_Input_float Input
+ %void = OpTypeVoid
+ %float_0 = OpConstantNull %float
+ %bool = OpTypeBool
+ %func_type = OpTypeFunction %void
+ %foo = OpFunction %void None %func_type
+ %foo_start = OpLabel
+ %x_value = OpLoad %float %x
+ %condition = OpFOrdGreaterThan %bool %x_value %float_0
+ OpSelectionMerge %merge None
+ OpBranchConditional %condition %true_branch %merge
+%true_branch = OpLabel
+ %result = OpDPdx %float %x_value
+ OpBranch %merge
+ %merge = OpLabel
+ OpReturn
+ OpFunctionEnd
+)";
+
+TEST_F(ParserTest, AllowNonUniformDerivatives_False) {
+ auto spv = test::Assemble(kShaderWithNonUniformDerivative);
+ Options options;
+ options.allow_non_uniform_derivatives = false;
+ auto program = Parse(spv, options);
+ auto errs = diag::Formatter().format(program.Diagnostics());
+ EXPECT_FALSE(program.IsValid()) << errs;
+ EXPECT_THAT(errs, ::testing::HasSubstr("'dpdx' must only be called from uniform control flow"));
+}
+
+TEST_F(ParserTest, AllowNonUniformDerivatives_True) {
+ auto spv = test::Assemble(kShaderWithNonUniformDerivative);
+ Options options;
+ options.allow_non_uniform_derivatives = true;
+ auto program = Parse(spv, options);
+ auto errs = diag::Formatter().format(program.Diagnostics());
+ EXPECT_TRUE(program.IsValid()) << errs;
+ EXPECT_EQ(program.Diagnostics().count(), 0u) << errs;
+}
+
// TODO(dneto): uint32 vec, valid SPIR-V
// TODO(dneto): uint32 vec, invalid SPIR-V
diff --git a/src/tint/reader/wgsl/classify_template_args.cc b/src/tint/reader/wgsl/classify_template_args.cc
new file mode 100644
index 0000000..136ff18
--- /dev/null
+++ b/src/tint/reader/wgsl/classify_template_args.cc
@@ -0,0 +1,168 @@
+// Copyright 2023 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/reader/wgsl/classify_template_args.h"
+
+#include <vector>
+
+#include "src/tint/debug.h"
+#include "src/tint/utils/vector.h"
+
+namespace tint::reader::wgsl {
+
+namespace {
+
+/// If the token at index @p idx is a '>>', '>=' or '>>=', then the token is split into two, with
+/// the first being '>', otherwise MaybeSplit() will be a no-op.
+/// @param tokens the vector of tokens
+/// @param idx the index of the token to (maybe) split
+void MaybeSplit(std::vector<Token>& tokens, size_t idx) {
+ Token* token = &tokens[idx];
+ switch (token->type()) {
+ case Token::Type::kShiftRight: // '>>'
+ TINT_ASSERT(Reader, token[1].type() == Token::Type::kPlaceholder);
+ token[0].SetType(Token::Type::kGreaterThan);
+ token[1].SetType(Token::Type::kGreaterThan);
+ break;
+ case Token::Type::kGreaterThanEqual: // '>='
+ TINT_ASSERT(Reader, token[1].type() == Token::Type::kPlaceholder);
+ token[0].SetType(Token::Type::kGreaterThan);
+ token[1].SetType(Token::Type::kEqual);
+ break;
+ case Token::Type::kShiftRightEqual: // '>>='
+ TINT_ASSERT(Reader, token[1].type() == Token::Type::kPlaceholder);
+ token[0].SetType(Token::Type::kGreaterThan);
+ token[1].SetType(Token::Type::kGreaterThanEqual);
+ break;
+ default:
+ break;
+ }
+}
+
+} // namespace
+
+void ClassifyTemplateArguments(std::vector<Token>& tokens) {
+ const size_t count = tokens.size();
+
+ // The current expression nesting depth.
+ // Each '(', '[' increments the depth.
+ // Each ')', ']' decrements the depth.
+ uint64_t expr_depth = 0;
+
+ // A stack of '<' tokens.
+ // Used to pair '<' and '>' tokens at the same expression depth.
+ struct StackEntry {
+ Token* token; // A pointer to the opening '<' token
+ uint64_t expr_depth; // The value of 'expr_depth' for the opening '<'
+ };
+ utils::Vector<StackEntry, 16> stack;
+
+ for (size_t i = 0; i < count - 1; i++) {
+ switch (tokens[i].type()) {
+ // <identifier> + all type / builtin keywords that will become identifiers.
+ case Token::Type::kIdentifier:
+ case Token::Type::kArray:
+ case Token::Type::kAtomic:
+ case Token::Type::kBitcast:
+ case Token::Type::kMat2x2:
+ case Token::Type::kMat2x3:
+ case Token::Type::kMat2x4:
+ case Token::Type::kMat3x2:
+ case Token::Type::kMat3x3:
+ case Token::Type::kMat3x4:
+ case Token::Type::kMat4x2:
+ case Token::Type::kMat4x3:
+ case Token::Type::kMat4x4:
+ case Token::Type::kPtr:
+ case Token::Type::kTextureMultisampled2d:
+ case Token::Type::kTextureSampled1d:
+ case Token::Type::kTextureSampled2d:
+ case Token::Type::kTextureSampled2dArray:
+ case Token::Type::kTextureSampled3d:
+ case Token::Type::kTextureSampledCube:
+ case Token::Type::kTextureSampledCubeArray:
+ case Token::Type::kTextureStorage1d:
+ case Token::Type::kTextureStorage2d:
+ case Token::Type::kTextureStorage2dArray:
+ case Token::Type::kVec2:
+ case Token::Type::kVec3:
+ case Token::Type::kVec4:
+ case Token::Type::kTextureStorage3d: {
+ auto& next = tokens[i + 1];
+ if (next.type() == Token::Type::kLessThan) {
+ // ident '<'
+ // Push this '<' to the stack, along with the current nesting expr_depth.
+ stack.Push(StackEntry{&tokens[i + 1], expr_depth});
+ i++; // Skip the '<'
+ }
+ break;
+ }
+ case Token::Type::kGreaterThan: // '>'
+ case Token::Type::kShiftRight: // '>>'
+ case Token::Type::kGreaterThanEqual: // '>='
+ case Token::Type::kShiftRightEqual: // '>>='
+ if (!stack.IsEmpty() && stack.Back().expr_depth == expr_depth) {
+ // '<' and '>' at same expr_depth, and no terminating tokens in-between.
+ // Consider both as a template argument list.
+ MaybeSplit(tokens, i);
+ stack.Pop().token->SetType(Token::Type::kTemplateArgsLeft);
+ tokens[i].SetType(Token::Type::kTemplateArgsRight);
+ }
+ break;
+
+ case Token::Type::kParenLeft: // '('
+ case Token::Type::kBracketLeft: // '['
+ // Entering a nested expression
+ expr_depth++;
+ break;
+
+ case Token::Type::kParenRight: // ')'
+ case Token::Type::kBracketRight: // ']'
+ // Exiting a nested expression
+ // Pop the stack until we return to the current expression expr_depth
+ while (!stack.IsEmpty() && stack.Back().expr_depth == expr_depth) {
+ stack.Pop();
+ }
+ if (expr_depth > 0) {
+ expr_depth--;
+ }
+ break;
+
+ case Token::Type::kSemicolon: // ';'
+ case Token::Type::kBraceLeft: // '{'
+ case Token::Type::kEqual: // '='
+ case Token::Type::kColon: // ':'
+ // Expression terminating tokens. No opening template list can hold these tokens, so
+ // clear the stack and expression depth.
+ expr_depth = 0;
+ stack.Clear();
+ break;
+
+ case Token::Type::kOrOr: // '||'
+ case Token::Type::kAndAnd: // '&&'
+ // Treat 'a < b || c > d' as a logical binary operator of two comparison operators
+ // instead of a single template argument 'b||c'.
+ // Use parentheses around 'b||c' to parse as a template argument list.
+ while (!stack.IsEmpty() && stack.Back().expr_depth == expr_depth) {
+ stack.Pop();
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+} // namespace tint::reader::wgsl
diff --git a/src/tint/reader/wgsl/classify_template_args.h b/src/tint/reader/wgsl/classify_template_args.h
new file mode 100644
index 0000000..92d3eb7
--- /dev/null
+++ b/src/tint/reader/wgsl/classify_template_args.h
@@ -0,0 +1,28 @@
+// Copyright 2023 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_READER_WGSL_CLASSIFY_TEMPLATE_ARGS_H_
+#define SRC_TINT_READER_WGSL_CLASSIFY_TEMPLATE_ARGS_H_
+
+#include <vector>
+
+#include "src/tint/reader/wgsl/token.h"
+
+namespace tint::reader::wgsl {
+
+void ClassifyTemplateArguments(std::vector<Token>& tokens);
+
+} // namespace tint::reader::wgsl
+
+#endif // SRC_TINT_READER_WGSL_CLASSIFY_TEMPLATE_ARGS_H_
diff --git a/src/tint/reader/wgsl/classify_template_args_test.cc b/src/tint/reader/wgsl/classify_template_args_test.cc
new file mode 100644
index 0000000..fb9bcf9
--- /dev/null
+++ b/src/tint/reader/wgsl/classify_template_args_test.cc
@@ -0,0 +1,483 @@
+// Copyright 2023 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 "gmock/gmock.h"
+
+#include "src/tint/reader/wgsl/classify_template_args.h"
+#include "src/tint/reader/wgsl/lexer.h"
+#include "src/tint/utils/transform.h"
+
+namespace tint::reader::wgsl {
+namespace {
+
+using T = Token::Type;
+
+struct Case {
+ const char* wgsl;
+ std::vector<T> tokens;
+};
+
+static std::ostream& operator<<(std::ostream& out, const Case& c) {
+ return out << "'" << c.wgsl << "'";
+}
+
+using ClassifyTemplateArgsTest = testing::TestWithParam<Case>;
+
+TEST_P(ClassifyTemplateArgsTest, Classify) {
+ auto& params = GetParam();
+ Source::File file("", params.wgsl);
+ Lexer l(&file);
+ auto tokens = l.Lex();
+ ClassifyTemplateArguments(tokens);
+ auto types = utils::Transform(tokens, [&](const Token& t) { return t.type(); });
+ EXPECT_THAT(types, testing::ContainerEq(params.tokens));
+}
+
+INSTANTIATE_TEST_SUITE_P(NonTemplate,
+ ClassifyTemplateArgsTest,
+ testing::ValuesIn(std::vector<Case>{
+ {
+ "",
+ {T::kEOF},
+ },
+ {
+ "abc",
+ {T::kIdentifier, T::kEOF},
+ },
+ {
+ "a<b",
+ {T::kIdentifier, T::kLessThan, T::kIdentifier, T::kEOF},
+ },
+ {
+ "a>b",
+ {T::kIdentifier, T::kGreaterThan, T::kIdentifier, T::kEOF},
+ },
+ {
+ "(a<b)>c",
+ {
+ T::kParenLeft, // (
+ T::kIdentifier, // a
+ T::kLessThan, // <
+ T::kIdentifier, // b
+ T::kParenRight, // )
+ T::kGreaterThan, // >
+ T::kIdentifier, // c
+ T::kEOF,
+ },
+ },
+ {
+ "a<(b>c)",
+ {
+ T::kIdentifier, // a
+ T::kLessThan, // <
+ T::kParenLeft, // (
+ T::kIdentifier, // b
+ T::kGreaterThan, // >
+ T::kIdentifier, // c
+ T::kParenRight, // )
+ T::kEOF,
+ },
+ },
+ {
+ "a((b<c), d>(e))",
+ {
+ T::kIdentifier, // a
+ T::kParenLeft, // (
+ T::kParenLeft, // (
+ T::kIdentifier, // b
+ T::kLessThan, // <
+ T::kIdentifier, // c
+ T::kParenRight, // )
+ T::kComma, // ,
+ T::kIdentifier, // d
+ T::kGreaterThan, // >
+ T::kParenLeft, // (
+ T::kIdentifier, // e
+ T::kParenRight, // )
+ T::kParenRight, // )
+ T::kEOF,
+ },
+ },
+ {
+ "a<b[c>(d)]",
+ {
+ T::kIdentifier, // a
+ T::kLessThan, // <
+ T::kIdentifier, // b
+ T::kBracketLeft, // [
+ T::kIdentifier, // c
+ T::kGreaterThan, // >
+ T::kParenLeft, // (
+ T::kIdentifier, // d
+ T::kParenRight, // )
+ T::kBracketRight, // ]
+ T::kEOF,
+ },
+ },
+ {
+ "a<b;c>d()",
+ {
+ T::kIdentifier, // a
+ T::kLessThan, // <
+ T::kIdentifier, // b
+ T::kSemicolon, // ;
+ T::kIdentifier, // c
+ T::kGreaterThan, // >
+ T::kIdentifier, // d
+ T::kParenLeft, // (
+ T::kParenRight, // )
+ T::kEOF,
+ },
+ },
+ {
+ "if a < b {} else if c > d {}",
+ {
+ T::kIf, // a
+ T::kIdentifier, // a
+ T::kLessThan, // <
+ T::kIdentifier, // b
+ T::kBraceLeft, // {
+ T::kBraceRight, // }
+ T::kElse, // else
+ T::kIf, // if
+ T::kIdentifier, // c
+ T::kGreaterThan, // >
+ T::kIdentifier, // d
+ T::kBraceLeft, // {
+ T::kBraceRight, // }
+ T::kEOF,
+ },
+ },
+ {
+ "a<b&&c>d",
+ {
+ T::kIdentifier, // a
+ T::kLessThan, // <
+ T::kIdentifier, // b
+ T::kAndAnd, // &&
+ T::kPlaceholder, // <placeholder>
+ T::kIdentifier, // c
+ T::kGreaterThan, // >
+ T::kIdentifier, // d
+ T::kEOF,
+ },
+ },
+ {
+ "a<b||c>d",
+ {
+ T::kIdentifier, // a
+ T::kLessThan, // <
+ T::kIdentifier, // b
+ T::kOrOr, // ||
+ T::kIdentifier, // c
+ T::kGreaterThan, // >
+ T::kIdentifier, // d
+ T::kEOF,
+ },
+ },
+ {
+ "a<b<c||d>>",
+ {
+ T::kIdentifier, // a
+ T::kLessThan, // <
+ T::kIdentifier, // b
+ T::kLessThan, // <
+ T::kIdentifier, // c
+ T::kOrOr, // ||
+ T::kIdentifier, // d
+ T::kShiftRight, // >>
+ T::kPlaceholder, // <placeholder>
+ T::kEOF,
+ },
+ },
+ }));
+
+INSTANTIATE_TEST_SUITE_P(Template,
+ ClassifyTemplateArgsTest,
+ testing::ValuesIn(std::vector<Case>{
+ {
+ "a<b>()",
+ {
+ T::kIdentifier, // a
+ T::kTemplateArgsLeft, // <
+ T::kIdentifier, // b
+ T::kTemplateArgsRight, // >
+ T::kParenLeft, // (
+ T::kParenRight, // )
+ T::kEOF,
+ },
+ },
+ {
+ "a<b>c",
+ {
+ T::kIdentifier, // a
+ T::kTemplateArgsLeft, // <
+ T::kIdentifier, // b
+ T::kTemplateArgsRight, // >
+ T::kIdentifier, // c
+ T::kEOF,
+ },
+ },
+ {
+ "vec3<i32>",
+ {
+ T::kVec3, // vec3
+ T::kTemplateArgsLeft, // <
+ T::kI32, // i32
+ T::kTemplateArgsRight, // >
+ T::kEOF,
+ },
+ },
+ {
+ "vec3<i32>()",
+ {
+ T::kVec3, // vec3
+ T::kTemplateArgsLeft, // <
+ T::kI32, // i32
+ T::kTemplateArgsRight, // >
+ T::kParenLeft, // (
+ T::kParenRight, // )
+ T::kEOF,
+ },
+ },
+ {
+ "array<vec3<i32>,5>",
+ {
+ T::kArray, // array
+ T::kTemplateArgsLeft, // <
+ T::kVec3, // vec3
+ T::kTemplateArgsLeft, // <
+ T::kI32, // i32
+ T::kTemplateArgsRight, // >
+ T::kComma, // ,
+ T::kIntLiteral, // 5
+ T::kTemplateArgsRight, // >
+ T::kEOF,
+ },
+ },
+ {
+ "a(b<c, d>(e))",
+ {
+ T::kIdentifier, // a
+ T::kParenLeft, // (
+ T::kIdentifier, // b
+ T::kTemplateArgsLeft, // <
+ T::kIdentifier, // c
+ T::kComma, // ,
+ T::kIdentifier, // d
+ T::kTemplateArgsRight, // >
+ T::kParenLeft, // (
+ T::kIdentifier, // e
+ T::kParenRight, // )
+ T::kParenRight, // )
+ T::kEOF,
+ },
+ },
+ {
+ "a<1+2>()",
+ {
+ T::kIdentifier, // a
+ T::kTemplateArgsLeft, // <
+ T::kIntLiteral, // 1
+ T::kPlus, // +
+ T::kIntLiteral, // 2
+ T::kTemplateArgsRight, // >
+ T::kParenLeft, // (
+ T::kParenRight, // )
+ T::kEOF,
+ },
+ },
+ {
+ "a<1,b>()",
+ {
+ T::kIdentifier, // a
+ T::kTemplateArgsLeft, // <
+ T::kIntLiteral, // 1
+ T::kComma, // ,
+ T::kIdentifier, // b
+ T::kTemplateArgsRight, // >
+ T::kParenLeft, // (
+ T::kParenRight, // )
+ T::kEOF,
+ },
+ },
+ {
+ "a<b,c>=d",
+ {
+ T::kIdentifier, // a
+ T::kTemplateArgsLeft, // <
+ T::kIdentifier, // b
+ T::kComma, // ,
+ T::kIdentifier, // c
+ T::kTemplateArgsRight, // >
+ T::kEqual, // =
+ T::kIdentifier, // d
+ T::kEOF,
+ },
+ },
+ {
+ "a<b,c>=d>()",
+ {
+ T::kIdentifier, // a
+ T::kTemplateArgsLeft, // <
+ T::kIdentifier, // b
+ T::kComma, // ,
+ T::kIdentifier, // c
+ T::kTemplateArgsRight, // >
+ T::kEqual, // =
+ T::kIdentifier, // d
+ T::kGreaterThan, // >
+ T::kParenLeft, // (
+ T::kParenRight, // )
+ T::kEOF,
+ },
+ },
+ {
+ "a<b<c>>=",
+ {
+ T::kIdentifier, // a
+ T::kTemplateArgsLeft, // <
+ T::kIdentifier, // b
+ T::kTemplateArgsLeft, // <
+ T::kIdentifier, // c
+ T::kTemplateArgsRight, // >
+ T::kTemplateArgsRight, // >
+ T::kEqual, // =
+ T::kEOF,
+ },
+ },
+ {
+ "a<b>c>()",
+ {
+ T::kIdentifier, // a
+ T::kTemplateArgsLeft, // <
+ T::kIdentifier, // b
+ T::kTemplateArgsRight, // >
+ T::kIdentifier, // c
+ T::kGreaterThan, // >
+ T::kParenLeft, // (
+ T::kParenRight, // )
+ T::kEOF,
+ },
+ },
+ {
+ "a<b<c>()",
+ {
+ T::kIdentifier, // a
+ T::kLessThan, // <
+ T::kIdentifier, // b
+ T::kTemplateArgsLeft, // <
+ T::kIdentifier, // c
+ T::kTemplateArgsRight, // >
+ T::kParenLeft, // (
+ T::kParenRight, // )
+ T::kEOF,
+ },
+ },
+ {
+ "a<b<c>>()",
+ {
+ T::kIdentifier, // a
+ T::kTemplateArgsLeft, // <
+ T::kIdentifier, // b
+ T::kTemplateArgsLeft, // <
+ T::kIdentifier, // c
+ T::kTemplateArgsRight, // >
+ T::kTemplateArgsRight, // >
+ T::kParenLeft, // (
+ T::kParenRight, // )
+ T::kEOF,
+ },
+ },
+ {
+ "a<b<c>()>()",
+ {
+ T::kIdentifier, // a
+ T::kTemplateArgsLeft, // <
+ T::kIdentifier, // b
+ T::kTemplateArgsLeft, // <
+ T::kIdentifier, // c
+ T::kTemplateArgsRight, // >
+ T::kParenLeft, // (
+ T::kParenRight, // )
+ T::kTemplateArgsRight, // >
+ T::kParenLeft, // (
+ T::kParenRight, // )
+ T::kEOF,
+ },
+ },
+ {
+ "a<b>.c",
+ {
+ T::kIdentifier, // a
+ T::kTemplateArgsLeft, // <
+ T::kIdentifier, // b
+ T::kTemplateArgsRight, // >
+ T::kPeriod, // .
+ T::kIdentifier, // c
+ T::kEOF,
+ },
+ },
+ {
+ "a<(b&&c)>d",
+ {
+ T::kIdentifier, // a
+ T::kTemplateArgsLeft, // <
+ T::kParenLeft, // (
+ T::kIdentifier, // b
+ T::kAndAnd, // &&
+ T::kPlaceholder, // <placeholder>
+ T::kIdentifier, // c
+ T::kParenRight, // )
+ T::kTemplateArgsRight, // >
+ T::kIdentifier, // d
+ T::kEOF,
+ },
+ },
+ {
+ "a<(b||c)>d",
+ {
+ T::kIdentifier, // a
+ T::kTemplateArgsLeft, // <
+ T::kParenLeft, // (
+ T::kIdentifier, // b
+ T::kOrOr, // ||
+ T::kIdentifier, // c
+ T::kParenRight, // )
+ T::kTemplateArgsRight, // >
+ T::kIdentifier, // d
+ T::kEOF,
+ },
+ },
+ {
+ "a<b<(c||d)>>",
+ {
+ T::kIdentifier, // a
+ T::kTemplateArgsLeft, // <
+ T::kIdentifier, // b
+ T::kTemplateArgsLeft, // <
+ T::kParenLeft, // (
+ T::kIdentifier, // c
+ T::kOrOr, // ||
+ T::kIdentifier, // d
+ T::kParenRight, // )
+ T::kTemplateArgsRight, // >
+ T::kTemplateArgsRight, // >
+ T::kEOF,
+ },
+ },
+ }));
+
+} // namespace
+} // namespace tint::reader::wgsl
diff --git a/src/tint/reader/wgsl/lexer.cc b/src/tint/reader/wgsl/lexer.cc
index 6371db7..722058c 100644
--- a/src/tint/reader/wgsl/lexer.cc
+++ b/src/tint/reader/wgsl/lexer.cc
@@ -1115,6 +1115,9 @@
}
Token Lexer::check_keyword(const Source& source, std::string_view str) {
+ if (str == "alias") {
+ return {Token::Type::kAlias, source, "alias"};
+ }
if (str == "array") {
return {Token::Type::kArray, source, "array"};
}
@@ -1136,6 +1139,9 @@
if (str == "const") {
return {Token::Type::kConst, source, "const"};
}
+ if (str == "const_assert") {
+ return {Token::Type::kConstAssert, source, "const_assert"};
+ }
if (str == "continue") {
return {Token::Type::kContinue, source, "continue"};
}
diff --git a/src/tint/reader/wgsl/lexer_test.cc b/src/tint/reader/wgsl/lexer_test.cc
index a1a4764..a376888 100644
--- a/src/tint/reader/wgsl/lexer_test.cc
+++ b/src/tint/reader/wgsl/lexer_test.cc
@@ -1057,12 +1057,14 @@
INSTANTIATE_TEST_SUITE_P(
LexerTest,
KeywordTest,
- testing::Values(TokenData{"array", Token::Type::kArray},
+ testing::Values(TokenData{"alias", Token::Type::kAlias},
+ TokenData{"array", Token::Type::kArray},
TokenData{"bitcast", Token::Type::kBitcast},
TokenData{"bool", Token::Type::kBool},
TokenData{"break", Token::Type::kBreak},
TokenData{"case", Token::Type::kCase},
TokenData{"const", Token::Type::kConst},
+ TokenData{"const_assert", Token::Type::kConstAssert},
TokenData{"continue", Token::Type::kContinue},
TokenData{"continuing", Token::Type::kContinuing},
TokenData{"default", Token::Type::kDefault},
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index 620dc1e..37c3ac3 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -38,6 +38,7 @@
#include "src/tint/ast/variable_decl_statement.h"
#include "src/tint/ast/vector.h"
#include "src/tint/ast/workgroup_attribute.h"
+#include "src/tint/reader/wgsl/classify_template_args.h"
#include "src/tint/reader/wgsl/lexer.h"
#include "src/tint/type/depth_texture.h"
#include "src/tint/type/external_texture.h"
@@ -319,6 +320,7 @@
void ParserImpl::InitializeLex() {
Lexer l{file_};
tokens_ = l.Lex();
+ ClassifyTemplateArguments(tokens_);
}
bool ParserImpl::Parse() {
@@ -358,14 +360,47 @@
}
// global_directive
-// : enable_directive
+// : diagnostic_directive
+// | enable_directive
Maybe<Void> ParserImpl::global_directive(bool have_parsed_decl) {
auto& p = peek();
- auto ed = enable_directive();
- if (ed.matched && have_parsed_decl) {
- return add_error(p, "enable directives must come before all global declarations");
+ Maybe<Void> result = diagnostic_directive();
+ if (!result.errored && !result.matched) {
+ result = enable_directive();
}
- return ed;
+
+ if (result.matched && have_parsed_decl) {
+ return add_error(p, "directives must come before all global declarations");
+ }
+ return result;
+}
+
+// diagnostic_directive
+// : diagnostic diagnostic_control SEMICOLON
+Maybe<Void> ParserImpl::diagnostic_directive() {
+ auto decl = sync(Token::Type::kSemicolon, [&]() -> Maybe<Void> {
+ if (!match(Token::Type::kDiagnostic)) {
+ return Failure::kNoMatch;
+ }
+
+ auto control = expect_diagnostic_control();
+ if (control.errored) {
+ return Failure::kErrored;
+ }
+
+ if (!expect("diagnostic directive", Token::Type::kSemicolon)) {
+ return Failure::kErrored;
+ }
+
+ builder_.AST().AddDiagnosticControl(std::move(control.value));
+
+ return kSuccess;
+ });
+
+ if (decl.errored) {
+ return Failure::kErrored;
+ }
+ return decl;
}
// enable_directive
@@ -427,7 +462,7 @@
// | type_alias_decl SEMICOLON
// | struct_decl
// | function_decl
-// | static_assert_statement SEMICOLON
+// | const_assert_statement SEMICOLON
Maybe<Void> ParserImpl::global_decl() {
if (match(Token::Type::kSemicolon) || match(Token::Type::kEOF)) {
return kSuccess;
@@ -486,13 +521,13 @@
return kSuccess;
}
- auto assertion = static_assert_statement();
+ auto assertion = const_assert_statement();
if (assertion.errored) {
return Failure::kErrored;
}
if (assertion.matched) {
- builder_.AST().AddStaticAssert(assertion.value);
- if (!expect("static assertion declaration", Token::Type::kSemicolon)) {
+ builder_.AST().AddConstAssert(assertion.value);
+ if (!expect("const assertion declaration", Token::Type::kSemicolon)) {
return Failure::kErrored;
}
return kSuccess;
@@ -731,7 +766,7 @@
if (dim.matched) {
const char* use = "sampled texture type";
- auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
+ auto subtype = expect_template_arg_block(use, [&] { return expect_type(use); });
if (subtype.errored) {
return Failure::kErrored;
}
@@ -743,7 +778,7 @@
if (ms_dim.matched) {
const char* use = "multisampled texture type";
- auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
+ auto subtype = expect_template_arg_block(use, [&] { return expect_type(use); });
if (subtype.errored) {
return Failure::kErrored;
}
@@ -755,7 +790,7 @@
if (storage.matched) {
const char* use = "storage texture type";
using StorageTextureInfo = std::pair<tint::type::TexelFormat, tint::type::Access>;
- auto params = expect_lt_gt_block(use, [&]() -> Expect<StorageTextureInfo> {
+ auto params = expect_template_arg_block(use, [&]() -> Expect<StorageTextureInfo> {
auto format = expect_texel_format(use);
if (format.errored) {
return Failure::kErrored;
@@ -1008,13 +1043,18 @@
}
// type_alias_decl
-// : TYPE IDENT EQUAL type_specifier
+// : ALIAS IDENT EQUAL type_specifier
Maybe<const ast::Alias*> ParserImpl::type_alias_decl() {
- if (!peek_is(Token::Type::kType)) {
+ Source source;
+ if (match(Token::Type::kAlias, &source)) {
+ // matched.
+ } else if (match(Token::Type::kType, &source)) {
+ // TODO(crbug.com/tint/1812): DEPRECATED
+ deprecated(source, "'type' has been renamed to 'alias'");
+ } else {
return Failure::kNoMatch;
}
- auto& t = next();
const char* use = "type alias";
auto name = expect_ident(use);
@@ -1034,7 +1074,7 @@
return add_error(peek(), "invalid type alias");
}
- return builder_.ty.alias(make_source_range_from(t.source()), name.value, type.value);
+ return builder_.ty.alias(make_source_range_from(source), name.value, type.value);
}
// vec_prefix
@@ -1124,7 +1164,8 @@
return builder_.ty.u32(t.source());
}
- if (t.Is(Token::Type::kArray) && peek_is(Token::Type::kLessThan, 1)) {
+ if (t.Is(Token::Type::kArray) &&
+ (peek_is(Token::Type::kTemplateArgsLeft, 1) || peek_is(Token::Type::kLessThan, 1))) {
if (match(Token::Type::kArray)) {
return expect_type_specifier_array(t.source());
}
@@ -1138,14 +1179,16 @@
return expect_type_specifier_pointer(t.source());
}
- if (t.IsMatrix() && peek_is(Token::Type::kLessThan, 1)) {
+ if (t.IsMatrix() &&
+ (peek_is(Token::Type::kTemplateArgsLeft, 1) || peek_is(Token::Type::kLessThan, 1))) {
auto mat = mat_prefix();
if (mat.matched) {
return expect_type_specifier_matrix(t.source(), mat.value);
}
}
- if (t.IsVector() && peek_is(Token::Type::kLessThan, 1)) {
+ if (t.IsVector() &&
+ (peek_is(Token::Type::kTemplateArgsLeft, 1) || peek_is(Token::Type::kLessThan, 1))) {
auto vec = vec_prefix();
if (vec.matched) {
return expect_type_specifier_vector(t.source(), vec.value);
@@ -1254,7 +1297,7 @@
auto address_space = type::AddressSpace::kNone;
auto access = type::Access::kUndefined;
- auto subtype = expect_lt_gt_block(use, [&]() -> Expect<const ast::Type*> {
+ auto subtype = expect_template_arg_block(use, [&]() -> Expect<const ast::Type*> {
auto sc = expect_address_space(use);
if (sc.errored) {
return Failure::kErrored;
@@ -1292,7 +1335,7 @@
Expect<const ast::Type*> ParserImpl::expect_type_specifier_atomic(const Source& s) {
const char* use = "atomic declaration";
- auto subtype = expect_lt_gt_block(use, [&] { return expect_type(use); });
+ auto subtype = expect_template_arg_block(use, [&] { return expect_type(use); });
if (subtype.errored) {
return Failure::kErrored;
}
@@ -1303,7 +1346,7 @@
// LESS_THAN type_specifier GREATER_THAN
Expect<const ast::Type*> ParserImpl::expect_type_specifier_vector(const Source& s, uint32_t count) {
const char* use = "vector";
- auto ty = expect_lt_gt_block(use, [&] { return expect_type(use); });
+ auto ty = expect_template_arg_block(use, [&] { return expect_type(use); });
if (ty.errored) {
return Failure::kErrored;
}
@@ -1320,11 +1363,7 @@
const ast::Expression* size = nullptr;
};
- if (!peek_is(Token::Type::kLessThan)) {
- return add_error(peek(), "expected < for array");
- }
-
- auto type_size = expect_lt_gt_block(use, [&]() -> Expect<TypeAndSize> {
+ auto type_size = expect_template_arg_block(use, [&]() -> Expect<TypeAndSize> {
auto type = expect_type(use);
if (type.errored) {
return Failure::kErrored;
@@ -1356,7 +1395,7 @@
Expect<const ast::Type*> ParserImpl::expect_type_specifier_matrix(const Source& s,
const MatrixDimensions& dims) {
const char* use = "matrix";
- auto ty = expect_lt_gt_block(use, [&] { return expect_type(use); });
+ auto ty = expect_template_arg_block(use, [&] { return expect_type(use); });
if (ty.errored) {
return Failure::kErrored;
}
@@ -1450,11 +1489,15 @@
decl->type, std::move(attrs.value));
}
-// static_assert_statement
+// const_assert_statement
// : STATIC_ASSERT expression
-Maybe<const ast::StaticAssert*> ParserImpl::static_assert_statement() {
+Maybe<const ast::ConstAssert*> ParserImpl::const_assert_statement() {
Source start;
- if (!match(Token::Type::kStaticAssert, &start)) {
+ if (match(Token::Type::kConstAssert, &start)) {
+ // matched
+ } else if (match(Token::Type::kStaticAssert, &start)) {
+ deprecated(start, "'static_assert' has been renamed to 'const_assert'");
+ } else {
return Failure::kNoMatch;
}
@@ -1467,7 +1510,7 @@
}
Source source = make_source_range_from(start);
- return create<ast::StaticAssert>(source, condition.value);
+ return create<ast::ConstAssert>(source, condition.value);
}
// function_decl
@@ -1781,7 +1824,7 @@
// | continue_statement SEMICOLON
// | DISCARD SEMICOLON
// | variable_updating_statement SEMICOLON
-// | static_assert_statement SEMICOLON
+// | const_assert_statement SEMICOLON
Maybe<const ast::Statement*> ParserImpl::non_block_statement() {
auto stmt = [&]() -> Maybe<const ast::Statement*> {
auto ret_stmt = return_statement();
@@ -1838,7 +1881,7 @@
return assign.value;
}
- auto stmt_static_assert = static_assert_statement();
+ auto stmt_static_assert = const_assert_statement();
if (stmt_static_assert.errored) {
return Failure::kErrored;
}
@@ -2546,7 +2589,7 @@
if (match(Token::Type::kBitcast)) {
const char* use = "bitcast expression";
- auto type = expect_lt_gt_block(use, [&] { return expect_type(use); });
+ auto type = expect_template_arg_block(use, [&] { return expect_type(use); });
if (type.errored) {
return Failure::kErrored;
}
@@ -2583,6 +2626,14 @@
if (t.IsIdentifier()) {
next();
+ if (Source source; match(Token::Type::kTemplateArgsLeft, &source)) {
+ return add_error(
+ source,
+ "'<' treated as the start of a template argument list, which is not supported for "
+ "user-declared types or functions. If you intended less-than, wrap the expression "
+ "in parentheses");
+ }
+
auto* ident =
create<ast::IdentifierExpression>(t.source(), builder_.Symbols().Register(t.to_str()));
@@ -3452,6 +3503,7 @@
// | ATTR 'binding' PAREN_LEFT expression COMMA? PAREN_RIGHT
// | ATTR 'builtin' PAREN_LEFT builtin_value_name COMMA? PAREN_RIGHT
// | ATTR 'const'
+// | ATTR 'diagnostic' diagnostic_control
// | ATTR 'group' PAREN_LEFT expression COMMA? PAREN_RIGHT
// | ATTR 'id' PAREN_LEFT expression COMMA? PAREN_RIGHT
// | ATTR 'interpolate' PAREN_LEFT interpolation_type_name COMMA? PAREN_RIGHT
@@ -3471,7 +3523,7 @@
using Result = Maybe<const ast::Attribute*>;
auto& t = next();
- if (!t.IsIdentifier()) {
+ if (!t.IsIdentifier() && !(t.Is(Token::Type::kDiagnostic))) {
return Failure::kNoMatch;
}
@@ -3525,6 +3577,14 @@
// Note, `const` is not valid in a WGSL source file, it's internal only
+ if (t.Is(Token::Type::kDiagnostic)) {
+ auto control = expect_diagnostic_control();
+ if (control.errored) {
+ return Failure::kErrored;
+ }
+ return create<ast::DiagnosticAttribute>(t.source(), control.value);
+ }
+
if (t == "fragment") {
return create<ast::StageAttribute>(t.source(), ast::PipelineStage::kFragment);
}
@@ -3679,6 +3739,43 @@
return false;
}
+// severity_control_name
+// : 'error'
+// | 'warning'
+// | 'info'
+// | 'off'
+Expect<ast::DiagnosticSeverity> ParserImpl::expect_severity_control_name() {
+ return expect_enum("severity control", ast::ParseDiagnosticSeverity,
+ ast::kDiagnosticSeverityStrings);
+}
+
+// diagnostic_control
+// : PAREN_LEFT severity_control_name COMMA ident_pattern_token COMMA ? PAREN_RIGHT
+Expect<const ast::DiagnosticControl*> ParserImpl::expect_diagnostic_control() {
+ auto source = last_source();
+ return expect_paren_block("diagnostic control", [&]() -> Expect<const ast::DiagnosticControl*> {
+ auto severity_control = expect_severity_control_name();
+ if (severity_control.errored) {
+ return Failure::kErrored;
+ }
+
+ if (!expect("diagnostic control", Token::Type::kComma)) {
+ return Failure::kErrored;
+ }
+
+ auto rule_name = expect_ident("diagnostic control");
+ if (rule_name.errored) {
+ return Failure::kErrored;
+ }
+ match(Token::Type::kComma);
+
+ return create<ast::DiagnosticControl>(
+ source, severity_control.value,
+ create<ast::IdentifierExpression>(rule_name.source,
+ builder_.Symbols().Register(rule_name.value)));
+ });
+}
+
bool ParserImpl::match(Token::Type tok, Source* source /*= nullptr*/) {
auto& t = peek();
@@ -3724,7 +3821,11 @@
}
std::stringstream err;
- err << "expected '" << Token::TypeToName(tok) << "'";
+ if (tok == Token::Type::kTemplateArgsLeft && t.type() == Token::Type::kLessThan) {
+ err << "missing closing '>'";
+ } else {
+ err << "expected '" << Token::TypeToName(tok) << "'";
+ }
if (!use.empty()) {
err << " for " << use;
}
@@ -3834,6 +3935,12 @@
}
template <typename F, typename T>
+T ParserImpl::expect_template_arg_block(std::string_view use, F&& body) {
+ return expect_block(Token::Type::kTemplateArgsLeft, Token::Type::kTemplateArgsRight, use,
+ std::forward<F>(body));
+}
+
+template <typename F, typename T>
T ParserImpl::sync(Token::Type tok, F&& body) {
if (parse_depth_ >= kMaxParseDepth) {
// We've hit a maximum parser recursive depth.
diff --git a/src/tint/reader/wgsl/parser_impl.h b/src/tint/reader/wgsl/parser_impl.h
index 4ef19bb..8101261 100644
--- a/src/tint/reader/wgsl/parser_impl.h
+++ b/src/tint/reader/wgsl/parser_impl.h
@@ -402,6 +402,9 @@
/// @param has_parsed_decl flag indicating if the parser has consumed a global declaration.
/// @return true on parse success, otherwise an error or no-match.
Maybe<Void> global_directive(bool has_parsed_decl);
+ /// Parses the `diagnostic_directive` grammar element, erroring on parse failure.
+ /// @return true on parse success, otherwise an error or no-match.
+ Maybe<Void> diagnostic_directive();
/// Parses the `enable_directive` grammar element, erroring on parse failure.
/// @return true on parse success, otherwise an error or no-match.
Maybe<Void> enable_directive();
@@ -505,9 +508,9 @@
/// @param use a description of what was being parsed if an error was raised
/// @returns returns the texel format or kNone if none matched.
Expect<type::TexelFormat> expect_texel_format(std::string_view use);
- /// Parses a `static_assert_statement` grammar element
- /// @returns returns the static assert, if it matched.
- Maybe<const ast::StaticAssert*> static_assert_statement();
+ /// Parses a `const_assert_statement` grammar element
+ /// @returns returns the const assert, if it matched.
+ Maybe<const ast::ConstAssert*> const_assert_statement();
/// Parses a `function_header` grammar element
/// @returns the parsed function header
Maybe<FunctionHeader> function_header();
@@ -702,6 +705,12 @@
/// @see #attribute for the full list of attributes this method parses.
/// @return the parsed attribute, or nullptr on error.
Expect<const ast::Attribute*> expect_attribute();
+ /// Parses a severity_control_name grammar element.
+ /// @return the parsed severity control name, or nullptr on error.
+ Expect<ast::DiagnosticSeverity> expect_severity_control_name();
+ /// Parses a diagnostic_control grammar element.
+ /// @return the parsed diagnostic control, or nullptr on error.
+ Expect<const ast::DiagnosticControl*> expect_diagnostic_control();
/// Splits a peekable token into to parts filling in the peekable fields.
/// @param lhs the token to set in the current position
@@ -799,6 +808,16 @@
/// an Expect with error state.
template <typename F, typename T = ReturnType<F>>
T expect_lt_gt_block(std::string_view use, F&& body);
+ /// A convenience function that calls `expect_block` passing
+ /// `Token::Type::kTemplateArgsLeft` and `Token::Type::kTemplateArgsRight` for the `start` and
+ /// `end` arguments, respectively.
+ /// @param use a description of what was being parsed if an error was raised
+ /// @param body a function or lambda that is called to parse the lexical block body, with the
+ /// signature: `Expect<Result>()` or `Maybe<Result>()`.
+ /// @return the value returned by `body` if no errors are raised, otherwise an Expect with error
+ /// state.
+ template <typename F, typename T = ReturnType<F>>
+ T expect_template_arg_block(std::string_view use, F&& body);
/// sync() calls the function `func`, and attempts to resynchronize the
/// parser to the next found resynchronization token if `func` fails. If the
diff --git a/src/tint/reader/wgsl/parser_impl_diagnostic_attribute_test.cc b/src/tint/reader/wgsl/parser_impl_diagnostic_attribute_test.cc
new file mode 100644
index 0000000..aebd1a0
--- /dev/null
+++ b/src/tint/reader/wgsl/parser_impl_diagnostic_attribute_test.cc
@@ -0,0 +1,36 @@
+// Copyright 2023 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/reader/wgsl/parser_impl_test_helper.h"
+
+#include "src/tint/ast/diagnostic_control.h"
+
+namespace tint::reader::wgsl {
+namespace {
+
+TEST_F(ParserImplTest, DiagnosticAttribute_Valid) {
+ auto p = parser("diagnostic(off, foo)");
+ auto a = p->attribute();
+ EXPECT_FALSE(p->has_error()) << p->error();
+ EXPECT_TRUE(a.matched);
+ auto* d = a.value->As<ast::DiagnosticAttribute>();
+ ASSERT_NE(d, nullptr);
+ EXPECT_EQ(d->control->severity, ast::DiagnosticSeverity::kOff);
+ auto* r = As<ast::IdentifierExpression>(d->control->rule_name);
+ ASSERT_NE(r, nullptr);
+ EXPECT_EQ(p->builder().Symbols().NameFor(r->symbol), "foo");
+}
+
+} // namespace
+} // namespace tint::reader::wgsl
diff --git a/src/tint/reader/wgsl/parser_impl_diagnostic_control_test.cc b/src/tint/reader/wgsl/parser_impl_diagnostic_control_test.cc
new file mode 100644
index 0000000..36de427
--- /dev/null
+++ b/src/tint/reader/wgsl/parser_impl_diagnostic_control_test.cc
@@ -0,0 +1,119 @@
+// Copyright 2023 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/reader/wgsl/parser_impl_test_helper.h"
+
+#include "src/tint/ast/diagnostic_control.h"
+
+namespace tint::reader::wgsl {
+namespace {
+
+using SeverityPair = std::pair<std::string, ast::DiagnosticSeverity>;
+class DiagnosticControlParserTest : public ParserImplTestWithParam<SeverityPair> {};
+
+TEST_P(DiagnosticControlParserTest, DiagnosticControl_Valid) {
+ auto& params = GetParam();
+ auto p = parser("(" + params.first + ", foo)");
+ auto e = p->expect_diagnostic_control();
+ EXPECT_FALSE(e.errored);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(e.value, nullptr);
+ ASSERT_TRUE(e->Is<ast::DiagnosticControl>());
+ EXPECT_EQ(e->severity, params.second);
+
+ auto* r = As<ast::IdentifierExpression>(e->rule_name);
+ ASSERT_NE(r, nullptr);
+ EXPECT_EQ(p->builder().Symbols().NameFor(r->symbol), "foo");
+}
+INSTANTIATE_TEST_SUITE_P(DiagnosticControlParserTest,
+ DiagnosticControlParserTest,
+ testing::Values(SeverityPair{"error", ast::DiagnosticSeverity::kError},
+ SeverityPair{"warning", ast::DiagnosticSeverity::kWarning},
+ SeverityPair{"info", ast::DiagnosticSeverity::kInfo},
+ SeverityPair{"off", ast::DiagnosticSeverity::kOff}));
+
+TEST_F(ParserImplTest, DiagnosticControl_Valid_TrailingComma) {
+ auto p = parser("(error, foo,)");
+ auto e = p->expect_diagnostic_control();
+ EXPECT_FALSE(e.errored);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(e.value, nullptr);
+ ASSERT_TRUE(e->Is<ast::DiagnosticControl>());
+ EXPECT_EQ(e->severity, ast::DiagnosticSeverity::kError);
+
+ auto* r = As<ast::IdentifierExpression>(e->rule_name);
+ ASSERT_NE(r, nullptr);
+ EXPECT_EQ(p->builder().Symbols().NameFor(r->symbol), "foo");
+}
+
+TEST_F(ParserImplTest, DiagnosticControl_MissingOpenParen) {
+ auto p = parser("off, foo)");
+ auto e = p->expect_diagnostic_control();
+ EXPECT_TRUE(e.errored);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), R"(1:1: expected '(' for diagnostic control)");
+}
+
+TEST_F(ParserImplTest, DiagnosticControl_MissingCloseParen) {
+ auto p = parser("(off, foo");
+ auto e = p->expect_diagnostic_control();
+ EXPECT_TRUE(e.errored);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), R"(1:10: expected ')' for diagnostic control)");
+}
+
+TEST_F(ParserImplTest, DiagnosticControl_MissingDiagnosticSeverity) {
+ auto p = parser("(, foo");
+ auto e = p->expect_diagnostic_control();
+ EXPECT_TRUE(e.errored);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), R"(1:2: expected severity control
+Possible values: 'error', 'info', 'off', 'warning')");
+}
+
+TEST_F(ParserImplTest, DiagnosticControl_InvalidDiagnosticSeverity) {
+ auto p = parser("(fatal, foo)");
+ auto e = p->expect_diagnostic_control();
+ EXPECT_TRUE(e.errored);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), R"(1:2: expected severity control
+Possible values: 'error', 'info', 'off', 'warning')");
+}
+
+TEST_F(ParserImplTest, DiagnosticControl_MissingComma) {
+ auto p = parser("(off foo");
+ auto e = p->expect_diagnostic_control();
+ EXPECT_TRUE(e.errored);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), R"(1:6: expected ',' for diagnostic control)");
+}
+
+TEST_F(ParserImplTest, DiagnosticControl_MissingRuleName) {
+ auto p = parser("(off,)");
+ auto e = p->expect_diagnostic_control();
+ EXPECT_TRUE(e.errored);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), R"(1:6: expected identifier for diagnostic control)");
+}
+
+TEST_F(ParserImplTest, DiagnosticControl_InvalidRuleName) {
+ auto p = parser("(off, foo$bar)");
+ auto e = p->expect_diagnostic_control();
+ EXPECT_TRUE(e.errored);
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), R"(1:10: invalid character found)");
+}
+
+} // namespace
+} // namespace tint::reader::wgsl
diff --git a/src/tint/reader/wgsl/parser_impl_diagnostic_directive_test.cc b/src/tint/reader/wgsl/parser_impl_diagnostic_directive_test.cc
new file mode 100644
index 0000000..c91b7d1
--- /dev/null
+++ b/src/tint/reader/wgsl/parser_impl_diagnostic_directive_test.cc
@@ -0,0 +1,71 @@
+// Copyright 2023 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/reader/wgsl/parser_impl_test_helper.h"
+
+#include "src/tint/ast/diagnostic_control.h"
+
+namespace tint::reader::wgsl {
+namespace {
+
+TEST_F(ParserImplTest, DiagnosticDirective_Valid) {
+ auto p = parser("diagnostic(off, foo);");
+ p->diagnostic_directive();
+ EXPECT_FALSE(p->has_error()) << p->error();
+ auto& ast = p->builder().AST();
+ ASSERT_EQ(ast.DiagnosticControls().Length(), 1u);
+ auto* control = ast.DiagnosticControls()[0];
+ EXPECT_EQ(control->severity, ast::DiagnosticSeverity::kOff);
+ ASSERT_EQ(ast.GlobalDeclarations().Length(), 1u);
+ EXPECT_EQ(ast.GlobalDeclarations()[0], control);
+
+ auto* r = As<ast::IdentifierExpression>(control->rule_name);
+ ASSERT_NE(r, nullptr);
+ EXPECT_EQ(p->builder().Symbols().NameFor(r->symbol), "foo");
+}
+
+TEST_F(ParserImplTest, DiagnosticDirective_MissingSemicolon) {
+ auto p = parser("diagnostic(off, foo)");
+ p->translation_unit();
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), "1:21: expected ';' for diagnostic directive");
+ auto program = p->program();
+ auto& ast = program.AST();
+ EXPECT_EQ(ast.DiagnosticControls().Length(), 0u);
+ EXPECT_EQ(ast.GlobalDeclarations().Length(), 0u);
+}
+
+TEST_F(ParserImplTest, DiagnosticDirective_FollowingOtherGlobalDecl) {
+ auto p = parser(R"(
+var<private> t: f32 = 0f;
+diagnostic(off, foo);
+)");
+ p->translation_unit();
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), "3:1: directives must come before all global declarations");
+}
+
+TEST_F(ParserImplTest, DiagnosticDirective_FollowingEmptySemicolon) {
+ auto p = parser(R"(
+;
+diagnostic(off, foo);
+)");
+ p->translation_unit();
+ // An empty semicolon is treated as a global declaration.
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), "3:1: directives must come before all global declarations");
+}
+
+} // namespace
+} // namespace tint::reader::wgsl
diff --git a/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc b/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc
index 392f0dd..23a8cb2 100644
--- a/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc
@@ -161,7 +161,7 @@
)");
p->translation_unit();
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "3:1: enable directives must come before all global declarations");
+ EXPECT_EQ(p->error(), "3:1: directives must come before all global declarations");
auto program = p->program();
auto& ast = program.AST();
// Accept the enable directive although it caused an error
@@ -181,7 +181,7 @@
p->translation_unit();
// An empty semicolon is treated as a global declaration
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "3:1: enable directives must come before all global declarations");
+ EXPECT_EQ(p->error(), "3:1: directives must come before all global declarations");
auto program = p->program();
auto& ast = program.AST();
// Accept the enable directive although it cause an error
diff --git a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
index ff077ab..f458834 100644
--- a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
@@ -52,8 +52,13 @@
}
TEST_F(ParserImplErrorTest, AliasDeclInvalidAttribute) {
- EXPECT("@invariant type e=u32;",
- R"(test.wgsl:1:2 error: unexpected attributes
+ EXPECT(
+ "@invariant type e=u32;",
+ R"(test.wgsl:1:12 warning: use of deprecated language feature: 'type' has been renamed to 'alias'
+@invariant type e=u32;
+ ^^^^
+
+test.wgsl:1:2 error: unexpected attributes
@invariant type e=u32;
^^^^^^^^^
)");
@@ -124,9 +129,9 @@
TEST_F(ParserImplErrorTest, BitcastExprMissingGreaterThan) {
EXPECT("fn f() { x = bitcast<u32(y); }",
- R"(test.wgsl:1:25 error: expected '>' for bitcast expression
+ R"(test.wgsl:1:21 error: missing closing '>' for bitcast expression
fn f() { x = bitcast<u32(y); }
- ^
+ ^
)");
}
@@ -306,46 +311,130 @@
)");
}
-TEST_F(ParserImplErrorTest, FunctionDeclStaticAssertMissingCondThenEOF) {
- EXPECT("fn f() { static_assert }", R"(test.wgsl:1:24 error: unable to parse condition expression
-fn f() { static_assert }
- ^
-)");
-}
-
-TEST_F(ParserImplErrorTest, FunctionDeclStaticAssertMissingCondThenSemicolon) {
- EXPECT("fn f() { static_assert; }",
- R"(test.wgsl:1:23 error: unable to parse condition expression
-fn f() { static_assert; }
+TEST_F(ParserImplErrorTest, FunctionDeclConstAssertMissingCondThenEOF) {
+ EXPECT("fn f() { const_assert }", R"(test.wgsl:1:23 error: unable to parse condition expression
+fn f() { const_assert }
^
)");
}
-TEST_F(ParserImplErrorTest, FunctionDeclStaticAssertMissingCondThenLet) {
- EXPECT("fn f() { static_assert\nlet x = 0; }",
+TEST_F(ParserImplErrorTest, FunctionDeclConstAssertMissingCondThenSemicolon) {
+ EXPECT("fn f() { const_assert; }",
+ R"(test.wgsl:1:22 error: unable to parse condition expression
+fn f() { const_assert; }
+ ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, FunctionDeclConstAssertMissingCondThenLet) {
+ EXPECT("fn f() { const_assert\nlet x = 0; }",
R"(test.wgsl:2:1 error: unable to parse condition expression
let x = 0; }
^^^
)");
}
-TEST_F(ParserImplErrorTest, FunctionDeclStaticAssertMissingLParen) {
- EXPECT("fn f() { static_assert true);", R"(test.wgsl:1:28 error: expected ';' for statement
+TEST_F(ParserImplErrorTest, FunctionDeclConstAssertMissingLParen) {
+ EXPECT("fn f() { const_assert true);", R"(test.wgsl:1:27 error: expected ';' for statement
+fn f() { const_assert true);
+ ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, FunctionDeclConstAssertMissingRParen) {
+ EXPECT("fn f() { const_assert (true;", R"(test.wgsl:1:28 error: expected ')'
+fn f() { const_assert (true;
+ ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, FunctionDeclConstAssertMissingSemicolon) {
+ EXPECT("fn f() { const_assert true }",
+ R"(test.wgsl:1:28 error: expected ';' for statement
+fn f() { const_assert true }
+ ^
+)");
+}
+
+// TODO(crbug.com/tint/1807)
+TEST_F(ParserImplErrorTest, DEPRECATED_FunctionDeclStaticAssertMissingCondThenEOF) {
+ EXPECT(
+ "fn f() { static_assert }",
+ R"(test.wgsl:1:10 warning: use of deprecated language feature: 'static_assert' has been renamed to 'const_assert'
+fn f() { static_assert }
+ ^^^^^^^^^^^^^
+
+test.wgsl:1:24 error: unable to parse condition expression
+fn f() { static_assert }
+ ^
+)");
+}
+
+// TODO(crbug.com/tint/1807)
+TEST_F(ParserImplErrorTest, DEPRECATED_FunctionDeclStaticAssertMissingCondThenSemicolon) {
+ EXPECT(
+ "fn f() { static_assert; }",
+ R"(test.wgsl:1:10 warning: use of deprecated language feature: 'static_assert' has been renamed to 'const_assert'
+fn f() { static_assert; }
+ ^^^^^^^^^^^^^
+
+test.wgsl:1:23 error: unable to parse condition expression
+fn f() { static_assert; }
+ ^
+)");
+}
+
+// TODO(crbug.com/tint/1807)
+TEST_F(ParserImplErrorTest, DEPRECATED_FunctionDeclStaticAssertMissingCondThenLet) {
+ EXPECT(
+ "fn f() { static_assert\nlet x = 0; }",
+ R"(test.wgsl:1:10 warning: use of deprecated language feature: 'static_assert' has been renamed to 'const_assert'
+fn f() { static_assert
+ ^^^^^^^^^^^^^
+
+test.wgsl:2:1 error: unable to parse condition expression
+let x = 0; }
+^^^
+)");
+}
+
+// TODO(crbug.com/tint/1807)
+TEST_F(ParserImplErrorTest, DEPRECATED_FunctionDeclStaticAssertMissingLParen) {
+ EXPECT(
+ "fn f() { static_assert true);",
+ R"(test.wgsl:1:10 warning: use of deprecated language feature: 'static_assert' has been renamed to 'const_assert'
+fn f() { static_assert true);
+ ^^^^^^^^^^^^^
+
+test.wgsl:1:28 error: expected ';' for statement
fn f() { static_assert true);
^
)");
}
-TEST_F(ParserImplErrorTest, FunctionDeclStaticAssertMissingRParen) {
- EXPECT("fn f() { static_assert (true;", R"(test.wgsl:1:29 error: expected ')'
+// TODO(crbug.com/tint/1807)
+TEST_F(ParserImplErrorTest, DEPRECATED_FunctionDeclStaticAssertMissingRParen) {
+ EXPECT(
+ "fn f() { static_assert (true;",
+ R"(test.wgsl:1:10 warning: use of deprecated language feature: 'static_assert' has been renamed to 'const_assert'
+fn f() { static_assert (true;
+ ^^^^^^^^^^^^^
+
+test.wgsl:1:29 error: expected ')'
fn f() { static_assert (true;
^
)");
}
-TEST_F(ParserImplErrorTest, FunctionDeclStaticAssertMissingSemicolon) {
- EXPECT("fn f() { static_assert true }",
- R"(test.wgsl:1:29 error: expected ';' for statement
+// TODO(crbug.com/tint/1807)
+TEST_F(ParserImplErrorTest, DEPRECATED_FunctionDeclStaticAssertMissingSemicolon) {
+ EXPECT(
+ "fn f() { static_assert true }",
+ R"(test.wgsl:1:10 warning: use of deprecated language feature: 'static_assert' has been renamed to 'const_assert'
+fn f() { static_assert true }
+ ^^^^^^^^^^^^^
+
+test.wgsl:1:29 error: expected ';' for statement
fn f() { static_assert true }
^
)");
@@ -579,9 +668,9 @@
TEST_F(ParserImplErrorTest, GlobalDeclSampledTextureMissingGreaterThan) {
EXPECT("var x : texture_1d<f32;",
- R"(test.wgsl:1:23 error: expected '>' for sampled texture type
+ R"(test.wgsl:1:19 error: missing closing '>' for sampled texture type
var x : texture_1d<f32;
- ^
+ ^
)");
}
@@ -603,9 +692,9 @@
TEST_F(ParserImplErrorTest, GlobalDeclMultisampledTextureMissingGreaterThan) {
EXPECT("var x : texture_multisampled_2d<f32;",
- R"(test.wgsl:1:36 error: expected '>' for multisampled texture type
+ R"(test.wgsl:1:32 error: missing closing '>' for multisampled texture type
var x : texture_multisampled_2d<f32;
- ^
+ ^
)");
}
@@ -617,46 +706,124 @@
)");
}
-TEST_F(ParserImplErrorTest, GlobalDeclStaticAssertMissingCondThenEOF) {
- EXPECT("static_assert", R"(test.wgsl:1:14 error: unable to parse condition expression
-static_assert
- ^
+TEST_F(ParserImplErrorTest, GlobalDeclConstAssertMissingCondThenEOF) {
+ EXPECT("const_assert", R"(test.wgsl:1:13 error: unable to parse condition expression
+const_assert
+ ^
)");
}
-TEST_F(ParserImplErrorTest, GlobalDeclStaticAssertMissingCondThenSemicolon) {
- EXPECT("static_assert;", R"(test.wgsl:1:14 error: unable to parse condition expression
-static_assert;
- ^
+TEST_F(ParserImplErrorTest, GlobalDeclConstAssertMissingCondThenSemicolon) {
+ EXPECT("const_assert;", R"(test.wgsl:1:13 error: unable to parse condition expression
+const_assert;
+ ^
)");
}
-TEST_F(ParserImplErrorTest, GlobalDeclStaticAssertMissingCondThenAlias) {
- EXPECT("static_assert\ntype T = i32;",
+TEST_F(ParserImplErrorTest, GlobalDeclConstAssertMissingCondThenAlias) {
+ EXPECT("const_assert\ntype T = i32;",
R"(test.wgsl:2:1 error: unable to parse condition expression
type T = i32;
^^^^
)");
}
-TEST_F(ParserImplErrorTest, GlobalDeclStaticAssertMissingLParen) {
- EXPECT("static_assert true);",
- R"(test.wgsl:1:19 error: expected ';' for static assertion declaration
+TEST_F(ParserImplErrorTest, GlobalDeclConstAssertMissingLParen) {
+ EXPECT("const_assert true);",
+ R"(test.wgsl:1:18 error: expected ';' for const assertion declaration
+const_assert true);
+ ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclConstAssertMissingRParen) {
+ EXPECT("const_assert (true;", R"(test.wgsl:1:19 error: expected ')'
+const_assert (true;
+ ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclConstAssertMissingSemicolon) {
+ EXPECT("const_assert true const_assert true;",
+ R"(test.wgsl:1:19 error: expected ';' for const assertion declaration
+const_assert true const_assert true;
+ ^^^^^^^^^^^^
+)");
+}
+
+// TODO(crbug.com/tint/1807): DEPRECATED
+TEST_F(ParserImplErrorTest, DEPRECATED_GlobalDeclStaticAssertMissingCondThenEOF) {
+ EXPECT("const_assert", R"(test.wgsl:1:13 error: unable to parse condition expression
+const_assert
+ ^
+)");
+}
+
+// TODO(crbug.com/tint/1807): DEPRECATED
+TEST_F(ParserImplErrorTest, DEPRECATED_GlobalDeclStaticAssertMissingCondThenSemicolon) {
+ EXPECT(
+ "static_assert;",
+ R"(test.wgsl:1:1 warning: use of deprecated language feature: 'static_assert' has been renamed to 'const_assert'
+static_assert;
+^^^^^^^^^^^^^
+
+test.wgsl:1:14 error: unable to parse condition expression
+static_assert;
+ ^
+)");
+}
+
+// TODO(crbug.com/tint/1807): DEPRECATED
+TEST_F(ParserImplErrorTest, DEPRECATED_GlobalDeclStaticAssertMissingCondThenAlias) {
+ EXPECT(
+ "static_assert\ntype T = i32;",
+ R"(test.wgsl:1:1 warning: use of deprecated language feature: 'static_assert' has been renamed to 'const_assert'
+static_assert
+^^^^^^^^^^^^^
+
+test.wgsl:2:1 error: unable to parse condition expression
+type T = i32;
+^^^^
+)");
+}
+
+// TODO(crbug.com/tint/1807): DEPRECATED
+TEST_F(ParserImplErrorTest, DEPRECATED_GlobalDeclStaticAssertMissingLParen) {
+ EXPECT(
+ "static_assert true);",
+ R"(test.wgsl:1:1 warning: use of deprecated language feature: 'static_assert' has been renamed to 'const_assert'
+static_assert true);
+^^^^^^^^^^^^^
+
+test.wgsl:1:19 error: expected ';' for const assertion declaration
static_assert true);
^
)");
}
-TEST_F(ParserImplErrorTest, GlobalDeclStaticAssertMissingRParen) {
- EXPECT("static_assert (true;", R"(test.wgsl:1:20 error: expected ')'
+// TODO(crbug.com/tint/1807): DEPRECATED
+TEST_F(ParserImplErrorTest, DEPRECATED_GlobalDeclStaticAssertMissingRParen) {
+ EXPECT(
+ "static_assert (true;",
+ R"(test.wgsl:1:1 warning: use of deprecated language feature: 'static_assert' has been renamed to 'const_assert'
+static_assert (true;
+^^^^^^^^^^^^^
+
+test.wgsl:1:20 error: expected ')'
static_assert (true;
^
)");
}
-TEST_F(ParserImplErrorTest, GlobalDeclStaticAssertMissingSemicolon) {
- EXPECT("static_assert true static_assert true;",
- R"(test.wgsl:1:20 error: expected ';' for static assertion declaration
+// TODO(crbug.com/tint/1807): DEPRECATED
+TEST_F(ParserImplErrorTest, DEPRECATED_GlobalDeclStaticAssertMissingSemicolon) {
+ EXPECT(
+ "static_assert true static_assert true;",
+ R"(test.wgsl:1:1 warning: use of deprecated language feature: 'static_assert' has been renamed to 'const_assert'
+static_assert true static_assert true;
+^^^^^^^^^^^^^
+
+test.wgsl:1:20 error: expected ';' for const assertion declaration
static_assert true static_assert true;
^^^^^^^^^^^^^
)");
@@ -672,9 +839,9 @@
TEST_F(ParserImplErrorTest, GlobalDeclStorageTextureMissingGreaterThan) {
EXPECT("var x : texture_storage_2d<r32uint, read;",
- R"(test.wgsl:1:41 error: expected '>' for storage texture type
+ R"(test.wgsl:1:27 error: missing closing '>' for storage texture type
var x : texture_storage_2d<r32uint, read;
- ^
+ ^
)");
}
@@ -745,29 +912,80 @@
}
TEST_F(ParserImplErrorTest, GlobalDeclTypeAliasMissingIdentifier) {
- EXPECT("type 1 = f32;",
- R"(test.wgsl:1:6 error: expected identifier for type alias
-type 1 = f32;
- ^
+ EXPECT("alias 1 = f32;",
+ R"(test.wgsl:1:7 error: expected identifier for type alias
+alias 1 = f32;
+ ^
)");
}
TEST_F(ParserImplErrorTest, GlobalDeclTypeAliasInvalidType) {
- EXPECT("type meow = 1;", R"(test.wgsl:1:13 error: invalid type alias
+ EXPECT("alias meow = 1;", R"(test.wgsl:1:14 error: invalid type alias
+alias meow = 1;
+ ^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclTypeAliasMissingAssignment) {
+ EXPECT("alias meow f32", R"(test.wgsl:1:12 error: expected '=' for type alias
+alias meow f32
+ ^^^
+)");
+}
+
+TEST_F(ParserImplErrorTest, GlobalDeclTypeAliasMissingSemicolon) {
+ EXPECT("alias meow = f32", R"(test.wgsl:1:17 error: expected ';' for type alias
+alias meow = f32
+ ^
+)");
+}
+
+// TODO(crbug.com/tint/1812): DEPRECATED
+TEST_F(ParserImplErrorTest, DEPRECATED_GlobalDeclTypeAliasMissingIdentifier) {
+ EXPECT("alias 1 = f32;",
+ R"(test.wgsl:1:7 error: expected identifier for type alias
+alias 1 = f32;
+ ^
+)");
+}
+
+// TODO(crbug.com/tint/1812): DEPRECATED
+TEST_F(ParserImplErrorTest, DEPRECATED_GlobalDeclTypeAliasInvalidType) {
+ EXPECT(
+ "type meow = 1;",
+ R"(test.wgsl:1:1 warning: use of deprecated language feature: 'type' has been renamed to 'alias'
+type meow = 1;
+^^^^
+
+test.wgsl:1:13 error: invalid type alias
type meow = 1;
^
)");
}
-TEST_F(ParserImplErrorTest, GlobalDeclTypeAliasMissingAssignment) {
- EXPECT("type meow f32", R"(test.wgsl:1:11 error: expected '=' for type alias
+// TODO(crbug.com/tint/1812): DEPRECATED
+TEST_F(ParserImplErrorTest, DEPRECATED_GlobalDeclTypeAliasMissingAssignment) {
+ EXPECT(
+ "type meow f32",
+ R"(test.wgsl:1:1 warning: use of deprecated language feature: 'type' has been renamed to 'alias'
+type meow f32
+^^^^
+
+test.wgsl:1:11 error: expected '=' for type alias
type meow f32
^^^
)");
}
-TEST_F(ParserImplErrorTest, GlobalDeclTypeAliasMissingSemicolon) {
- EXPECT("type meow = f32", R"(test.wgsl:1:16 error: expected ';' for type alias
+// TODO(crbug.com/tint/1812): DEPRECATED
+TEST_F(ParserImplErrorTest, DEPRECATED_GlobalDeclTypeAliasMissingSemicolon) {
+ EXPECT(
+ "type meow = f32",
+ R"(test.wgsl:1:1 warning: use of deprecated language feature: 'type' has been renamed to 'alias'
+type meow = f32
+^^^^
+
+test.wgsl:1:16 error: expected ';' for type alias
type meow = f32
^
)");
@@ -775,9 +993,9 @@
TEST_F(ParserImplErrorTest, GlobalDeclVarArrayMissingGreaterThan) {
EXPECT("var i : array<u32, 3;",
- R"(test.wgsl:1:21 error: expected '>' for array declaration
+ R"(test.wgsl:1:14 error: missing closing '>' for array declaration
var i : array<u32, 3;
- ^
+ ^
)");
}
@@ -956,9 +1174,9 @@
}
TEST_F(ParserImplErrorTest, GlobalDeclVarMatrixMissingGreaterThan) {
- EXPECT("var i : mat4x4<u32;", R"(test.wgsl:1:19 error: expected '>' for matrix
+ EXPECT("var i : mat4x4<u32;", R"(test.wgsl:1:15 error: missing closing '>' for matrix
var i : mat4x4<u32;
- ^
+ ^
)");
}
@@ -987,9 +1205,9 @@
TEST_F(ParserImplErrorTest, GlobalDeclVarPtrMissingGreaterThan) {
EXPECT("var i : ptr<private, u32;",
- R"(test.wgsl:1:25 error: expected '>' for ptr declaration
+ R"(test.wgsl:1:12 error: missing closing '>' for ptr declaration
var i : ptr<private, u32;
- ^
+ ^
)");
}
@@ -1028,9 +1246,9 @@
TEST_F(ParserImplErrorTest, GlobalDeclVarAtomicMissingGreaterThan) {
EXPECT("var i : atomic<u32 x;",
- R"(test.wgsl:1:20 error: expected '>' for atomic declaration
+ R"(test.wgsl:1:15 error: missing closing '>' for atomic declaration
var i : atomic<u32 x;
- ^
+ ^
)");
}
@@ -1052,9 +1270,9 @@
}
TEST_F(ParserImplErrorTest, GlobalDeclVarVectorMissingGreaterThan) {
- EXPECT("var i : vec3<u32;", R"(test.wgsl:1:17 error: expected '>' for vector
+ EXPECT("var i : vec3<u32;", R"(test.wgsl:1:13 error: missing closing '>' for vector
var i : vec3<u32;
- ^
+ ^
)");
}
diff --git a/src/tint/reader/wgsl/parser_impl_expression_test.cc b/src/tint/reader/wgsl/parser_impl_expression_test.cc
index 71d1d99..9ae4591 100644
--- a/src/tint/reader/wgsl/parser_impl_expression_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_expression_test.cc
@@ -463,6 +463,9 @@
bool should_parse;
};
+static bool ParsedAsTemplateArgumentList(BinaryOperatorInfo lhs_op, BinaryOperatorInfo rhs_op) {
+ return lhs_op.bit == kOpLt && rhs_op.bit & (kOpGt | kOpGe | kOpShr);
+}
static std::ostream& operator<<(std::ostream& o, const Case& c) {
return o << "a " << c.lhs_op.symbol << " b " << c.rhs_op.symbol << " c ";
}
@@ -471,7 +474,8 @@
std::vector<Case> out;
for (auto& lhs_op : kBinaryOperators) {
for (auto& rhs_op : kBinaryOperators) {
- bool should_parse = lhs_op.can_follow_without_paren & rhs_op.bit;
+ bool should_parse = (lhs_op.can_follow_without_paren & rhs_op.bit) &&
+ !ParsedAsTemplateArgumentList(lhs_op, rhs_op);
out.push_back({lhs_op, rhs_op, should_parse});
}
}
@@ -494,8 +498,14 @@
EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error());
std::stringstream expected;
- expected << "1:3: mixing '" << GetParam().lhs_op.symbol << "' and '"
- << GetParam().rhs_op.symbol << "' requires parenthesis";
+ if (ParsedAsTemplateArgumentList(GetParam().lhs_op, GetParam().rhs_op)) {
+ expected << "1:3: '<' treated as the start of a template argument list, which is not "
+ "supported for user-declared types or functions. If you intended "
+ "less-than, wrap the expression in parentheses";
+ } else {
+ expected << "1:3: mixing '" << GetParam().lhs_op.symbol << "' and '"
+ << GetParam().rhs_op.symbol << "' requires parenthesis";
+ }
EXPECT_EQ(p->error(), expected.str());
}
}
diff --git a/src/tint/reader/wgsl/parser_impl_global_decl_test.cc b/src/tint/reader/wgsl/parser_impl_global_decl_test.cc
index 2f124d5..c79af0d 100644
--- a/src/tint/reader/wgsl/parser_impl_global_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_global_decl_test.cc
@@ -99,7 +99,7 @@
}
TEST_F(ParserImplTest, GlobalDecl_TypeAlias) {
- auto p = parser("type A = i32;");
+ auto p = parser("alias A = i32;");
p->global_decl();
ASSERT_FALSE(p->has_error()) << p->error();
@@ -113,6 +113,42 @@
auto p = parser(R"(struct A {
a : f32,
}
+alias B = A;)");
+ p->global_decl();
+ p->global_decl();
+ ASSERT_FALSE(p->has_error()) << p->error();
+
+ auto program = p->program();
+ ASSERT_EQ(program.AST().TypeDecls().Length(), 2u);
+ ASSERT_TRUE(program.AST().TypeDecls()[0]->Is<ast::Struct>());
+ auto* str = program.AST().TypeDecls()[0]->As<ast::Struct>();
+ EXPECT_EQ(str->name, program.Symbols().Get("A"));
+
+ ASSERT_TRUE(program.AST().TypeDecls()[1]->Is<ast::Alias>());
+ auto* alias = program.AST().TypeDecls()[1]->As<ast::Alias>();
+ EXPECT_EQ(alias->name, program.Symbols().Get("B"));
+ auto* tn = alias->type->As<ast::TypeName>();
+ EXPECT_NE(tn, nullptr);
+ EXPECT_EQ(tn->name, str->name);
+}
+
+// TODO(crbug.com/tint/1812): DEPRECATED
+TEST_F(ParserImplTest, DEPRECATED_GlobalDecl_TypeAlias) {
+ auto p = parser("type A = i32;");
+ p->global_decl();
+ ASSERT_FALSE(p->has_error()) << p->error();
+
+ auto program = p->program();
+ ASSERT_EQ(program.AST().TypeDecls().Length(), 1u);
+ ASSERT_TRUE(program.AST().TypeDecls()[0]->Is<ast::Alias>());
+ EXPECT_EQ(program.Symbols().NameFor(program.AST().TypeDecls()[0]->As<ast::Alias>()->name), "A");
+}
+
+// TODO(crbug.com/tint/1812): DEPRECATED
+TEST_F(ParserImplTest, DEPRECATED_GlobalDecl_TypeAlias_StructIdent) {
+ auto p = parser(R"(struct A {
+ a : f32,
+}
type B = A;)");
p->global_decl();
p->global_decl();
@@ -133,10 +169,20 @@
}
TEST_F(ParserImplTest, GlobalDecl_TypeAlias_MissingSemicolon) {
+ auto p = parser("alias A = i32");
+ p->global_decl();
+ ASSERT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), "1:14: expected ';' for type alias");
+}
+
+// TODO(crbug.com/tint/1812): DEPRECATED
+TEST_F(ParserImplTest, DEPRECATED_GlobalDecl_TypeAlias_MissingSemicolon) {
auto p = parser("type A = i32");
p->global_decl();
ASSERT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:13: expected ';' for type alias");
+ EXPECT_EQ(p->error(),
+ R"(1:1: use of deprecated language feature: 'type' has been renamed to 'alias'
+1:13: expected ';' for type alias)");
}
TEST_F(ParserImplTest, GlobalDecl_Function) {
@@ -211,14 +257,57 @@
EXPECT_EQ(p->error(), "1:2: unexpected attributes");
}
-TEST_F(ParserImplTest, GlobalDecl_StaticAssert_WithParen) {
+TEST_F(ParserImplTest, GlobalDecl_ConstAssert_WithParen) {
+ auto p = parser("const_assert(true);");
+ p->global_decl();
+ ASSERT_FALSE(p->has_error()) << p->error();
+
+ auto program = p->program();
+ ASSERT_EQ(program.AST().ConstAsserts().Length(), 1u);
+ auto* sa = program.AST().ConstAsserts()[0];
+ EXPECT_EQ(sa->source.range.begin.line, 1u);
+ EXPECT_EQ(sa->source.range.begin.column, 1u);
+ EXPECT_EQ(sa->source.range.end.line, 1u);
+ EXPECT_EQ(sa->source.range.end.column, 19u);
+
+ EXPECT_TRUE(sa->condition->Is<ast::BoolLiteralExpression>());
+ EXPECT_EQ(sa->condition->source.range.begin.line, 1u);
+ EXPECT_EQ(sa->condition->source.range.begin.column, 14u);
+ EXPECT_EQ(sa->condition->source.range.end.line, 1u);
+ EXPECT_EQ(sa->condition->source.range.end.column, 18u);
+}
+
+TEST_F(ParserImplTest, GlobalDecl_ConstAssert_WithoutParen) {
+ auto p = parser("const_assert true;");
+ p->global_decl();
+ ASSERT_FALSE(p->has_error()) << p->error();
+
+ auto program = p->program();
+ ASSERT_EQ(program.AST().ConstAsserts().Length(), 1u);
+ auto* sa = program.AST().ConstAsserts()[0];
+ EXPECT_TRUE(sa->condition->Is<ast::BoolLiteralExpression>());
+
+ EXPECT_EQ(sa->source.range.begin.line, 1u);
+ EXPECT_EQ(sa->source.range.begin.column, 1u);
+ EXPECT_EQ(sa->source.range.end.line, 1u);
+ EXPECT_EQ(sa->source.range.end.column, 19u);
+
+ EXPECT_TRUE(sa->condition->Is<ast::BoolLiteralExpression>());
+ EXPECT_EQ(sa->condition->source.range.begin.line, 1u);
+ EXPECT_EQ(sa->condition->source.range.begin.column, 15u);
+ EXPECT_EQ(sa->condition->source.range.end.line, 1u);
+ EXPECT_EQ(sa->condition->source.range.end.column, 19u);
+}
+
+// TODO(crbug.com/tint/1807)
+TEST_F(ParserImplTest, DEPRECATED_GlobalDecl_StaticAssert_WithParen) {
auto p = parser("static_assert(true);");
p->global_decl();
ASSERT_FALSE(p->has_error()) << p->error();
auto program = p->program();
- ASSERT_EQ(program.AST().StaticAsserts().Length(), 1u);
- auto* sa = program.AST().StaticAsserts()[0];
+ ASSERT_EQ(program.AST().ConstAsserts().Length(), 1u);
+ auto* sa = program.AST().ConstAsserts()[0];
EXPECT_EQ(sa->source.range.begin.line, 1u);
EXPECT_EQ(sa->source.range.begin.column, 1u);
EXPECT_EQ(sa->source.range.end.line, 1u);
@@ -231,14 +320,15 @@
EXPECT_EQ(sa->condition->source.range.end.column, 19u);
}
-TEST_F(ParserImplTest, GlobalDecl_StaticAssert_WithoutParen) {
+// TODO(crbug.com/tint/1807)
+TEST_F(ParserImplTest, DEPRECATED_GlobalDecl_StaticAssert_WithoutParen) {
auto p = parser("static_assert true;");
p->global_decl();
ASSERT_FALSE(p->has_error()) << p->error();
auto program = p->program();
- ASSERT_EQ(program.AST().StaticAsserts().Length(), 1u);
- auto* sa = program.AST().StaticAsserts()[0];
+ ASSERT_EQ(program.AST().ConstAsserts().Length(), 1u);
+ auto* sa = program.AST().ConstAsserts()[0];
EXPECT_TRUE(sa->condition->Is<ast::BoolLiteralExpression>());
EXPECT_EQ(sa->source.range.begin.line, 1u);
diff --git a/src/tint/reader/wgsl/parser_impl_primary_expression_test.cc b/src/tint/reader/wgsl/parser_impl_primary_expression_test.cc
index f7f9a87..ec6561f 100644
--- a/src/tint/reader/wgsl/parser_impl_primary_expression_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_primary_expression_test.cc
@@ -266,7 +266,7 @@
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
ASSERT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:12: expected '>' for bitcast expression");
+ EXPECT_EQ(p->error(), "1:8: missing closing '>' for bitcast expression");
}
TEST_F(ParserImplTest, PrimaryExpression_Bitcast_MissingType) {
@@ -319,5 +319,18 @@
EXPECT_EQ(p->error(), "1:14: unable to parse expression");
}
+TEST_F(ParserImplTest, PrimaryExpression_Template) {
+ auto p = parser("a<b>()");
+ auto e = p->primary_expression();
+ EXPECT_FALSE(e.matched);
+ EXPECT_TRUE(e.errored);
+ EXPECT_EQ(e.value, nullptr);
+ ASSERT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(),
+ "1:2: '<' treated as the start of a template argument list, which is not supported "
+ "for user-declared types or functions. If you intended less-than, wrap the "
+ "expression in parentheses");
+}
+
} // namespace
} // namespace tint::reader::wgsl
diff --git a/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc b/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc
index f2adc06..c20302d 100644
--- a/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_reserved_keyword_test.cc
@@ -78,10 +78,10 @@
}
TEST_P(ParserImplReservedKeywordTest, Alias) {
auto name = GetParam();
- auto p = parser("type " + name + " = i32;");
+ auto p = parser("alias " + name + " = i32;");
EXPECT_FALSE(p->Parse());
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:6: '" + name + "' is a reserved keyword");
+ EXPECT_EQ(p->error(), "1:7: '" + name + "' is a reserved keyword");
}
INSTANTIATE_TEST_SUITE_P(ParserImplReservedKeywordTest,
ParserImplReservedKeywordTest,
diff --git a/src/tint/reader/wgsl/parser_impl_statement_test.cc b/src/tint/reader/wgsl/parser_impl_statement_test.cc
index f78e944..f21a4e7 100644
--- a/src/tint/reader/wgsl/parser_impl_statement_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_statement_test.cc
@@ -272,14 +272,57 @@
EXPECT_EQ(p->error(), "1:3: expected '}'");
}
-TEST_F(ParserImplTest, Statement_StaticAssert_WithParen) {
+TEST_F(ParserImplTest, Statement_ConstAssert_WithParen) {
+ auto p = parser("const_assert(true);");
+ auto e = p->statement();
+ ASSERT_FALSE(p->has_error()) << p->error();
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+
+ auto* sa = As<ast::ConstAssert>(e.value);
+ ASSERT_NE(sa, nullptr);
+ EXPECT_EQ(sa->source.range.begin.line, 1u);
+ EXPECT_EQ(sa->source.range.begin.column, 1u);
+ EXPECT_EQ(sa->source.range.end.line, 1u);
+ EXPECT_EQ(sa->source.range.end.column, 19u);
+
+ EXPECT_TRUE(sa->condition->Is<ast::BoolLiteralExpression>());
+ EXPECT_EQ(sa->condition->source.range.begin.line, 1u);
+ EXPECT_EQ(sa->condition->source.range.begin.column, 14u);
+ EXPECT_EQ(sa->condition->source.range.end.line, 1u);
+ EXPECT_EQ(sa->condition->source.range.end.column, 18u);
+}
+
+TEST_F(ParserImplTest, Statement_ConstAssert_WithoutParen) {
+ auto p = parser("const_assert true;");
+ auto e = p->statement();
+ ASSERT_FALSE(p->has_error()) << p->error();
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+
+ auto* sa = As<ast::ConstAssert>(e.value);
+ ASSERT_NE(sa, nullptr);
+ EXPECT_EQ(sa->source.range.begin.line, 1u);
+ EXPECT_EQ(sa->source.range.begin.column, 1u);
+ EXPECT_EQ(sa->source.range.end.line, 1u);
+ EXPECT_EQ(sa->source.range.end.column, 19u);
+
+ EXPECT_TRUE(sa->condition->Is<ast::BoolLiteralExpression>());
+ EXPECT_EQ(sa->condition->source.range.begin.line, 1u);
+ EXPECT_EQ(sa->condition->source.range.begin.column, 15u);
+ EXPECT_EQ(sa->condition->source.range.end.line, 1u);
+ EXPECT_EQ(sa->condition->source.range.end.column, 19u);
+}
+
+// TODO(crbug.com/tint/1807)
+TEST_F(ParserImplTest, DEPRECATED_Statement_StaticAssert_WithParen) {
auto p = parser("static_assert(true);");
auto e = p->statement();
ASSERT_FALSE(p->has_error()) << p->error();
EXPECT_TRUE(e.matched);
EXPECT_FALSE(e.errored);
- auto* sa = As<ast::StaticAssert>(e.value);
+ auto* sa = As<ast::ConstAssert>(e.value);
ASSERT_NE(sa, nullptr);
EXPECT_EQ(sa->source.range.begin.line, 1u);
EXPECT_EQ(sa->source.range.begin.column, 1u);
@@ -293,14 +336,15 @@
EXPECT_EQ(sa->condition->source.range.end.column, 19u);
}
-TEST_F(ParserImplTest, Statement_StaticAssert_WithoutParen) {
+// TODO(crbug.com/tint/1807)
+TEST_F(ParserImplTest, DEPRECATED_Statement_StaticAssert_WithoutParen) {
auto p = parser("static_assert true;");
auto e = p->statement();
ASSERT_FALSE(p->has_error()) << p->error();
EXPECT_TRUE(e.matched);
EXPECT_FALSE(e.errored);
- auto* sa = As<ast::StaticAssert>(e.value);
+ auto* sa = As<ast::ConstAssert>(e.value);
ASSERT_NE(sa, nullptr);
EXPECT_EQ(sa->source.range.begin.line, 1u);
EXPECT_EQ(sa->source.range.begin.column, 1u);
diff --git a/src/tint/reader/wgsl/parser_impl_test.cc b/src/tint/reader/wgsl/parser_impl_test.cc
index 21efd2f..6df73c4 100644
--- a/src/tint/reader/wgsl/parser_impl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_test.cc
@@ -154,7 +154,7 @@
auto& n = p->next();
ASSERT_TRUE(n.Is(Token::Type::kGreaterThanEqual));
EXPECT_TRUE(p->peek_is(Token::Type::kVec2)) << "expected: vec2 got: " << p->peek().to_name();
- EXPECT_TRUE(p->peek_is(Token::Type::kLessThan, 1))
+ EXPECT_TRUE(p->peek_is(Token::Type::kTemplateArgsLeft, 1))
<< "expected: < got: " << p->peek(1).to_name();
}
@@ -163,10 +163,10 @@
auto& n = p->next();
ASSERT_TRUE(n.Is(Token::Type::kGreaterThanEqual));
EXPECT_TRUE(p->peek_is(Token::Type::kGreaterThanEqual))
- << "expected: <= got: " << p->peek().to_name();
+ << "expected: >= got: " << p->peek().to_name();
EXPECT_TRUE(p->peek_is(Token::Type::kVec2, 1))
<< "expected: vec2 got: " << p->peek(1).to_name();
- EXPECT_TRUE(p->peek_is(Token::Type::kLessThan, 2))
+ EXPECT_TRUE(p->peek_is(Token::Type::kTemplateArgsLeft, 2))
<< "expected: < got: " << p->peek(2).to_name();
}
diff --git a/src/tint/reader/wgsl/parser_impl_texture_sampler_test.cc b/src/tint/reader/wgsl/parser_impl_texture_sampler_test.cc
index 43d4ada..299bd33 100644
--- a/src/tint/reader/wgsl/parser_impl_texture_sampler_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_texture_sampler_test.cc
@@ -136,7 +136,7 @@
EXPECT_EQ(t.value, nullptr);
EXPECT_FALSE(t.matched);
EXPECT_TRUE(t.errored);
- EXPECT_EQ(p->error(), "1:15: expected '>' for sampled texture type");
+ EXPECT_EQ(p->error(), "1:11: missing closing '>' for sampled texture type");
}
TEST_F(ParserImplTest, TextureSamplerTypes_MultisampledTexture_I32) {
@@ -178,7 +178,7 @@
EXPECT_EQ(t.value, nullptr);
EXPECT_FALSE(t.matched);
EXPECT_TRUE(t.errored);
- EXPECT_EQ(p->error(), "1:28: expected '>' for multisampled texture type");
+ EXPECT_EQ(p->error(), "1:24: missing closing '>' for multisampled texture type");
}
TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_Readonly1dRg32Float) {
@@ -270,7 +270,7 @@
EXPECT_EQ(t.value, nullptr);
EXPECT_FALSE(t.matched);
EXPECT_TRUE(t.errored);
- EXPECT_EQ(p->error(), "1:33: expected '>' for storage texture type");
+ EXPECT_EQ(p->error(), "1:19: missing closing '>' for storage texture type");
}
} // namespace
diff --git a/src/tint/reader/wgsl/parser_impl_type_alias_test.cc b/src/tint/reader/wgsl/parser_impl_type_alias_test.cc
index c9bb72c..f4c20de 100644
--- a/src/tint/reader/wgsl/parser_impl_type_alias_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_type_alias_test.cc
@@ -73,7 +73,9 @@
EXPECT_FALSE(t.matched);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(t.value, nullptr);
- EXPECT_EQ(p->error(), "1:6: expected identifier for type alias");
+ EXPECT_EQ(p->error(),
+ R"(1:1: use of deprecated language feature: 'type' has been renamed to 'alias'
+1:6: expected identifier for type alias)");
}
TEST_F(ParserImplTest, TypeDecl_InvalidIdent) {
@@ -83,7 +85,9 @@
EXPECT_FALSE(t.matched);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(t.value, nullptr);
- EXPECT_EQ(p->error(), "1:6: expected identifier for type alias");
+ EXPECT_EQ(p->error(),
+ R"(1:1: use of deprecated language feature: 'type' has been renamed to 'alias'
+1:6: expected identifier for type alias)");
}
TEST_F(ParserImplTest, TypeDecl_MissingEqual) {
@@ -93,7 +97,9 @@
EXPECT_FALSE(t.matched);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(t.value, nullptr);
- EXPECT_EQ(p->error(), "1:8: expected '=' for type alias");
+ EXPECT_EQ(p->error(),
+ R"(1:1: use of deprecated language feature: 'type' has been renamed to 'alias'
+1:8: expected '=' for type alias)");
}
} // namespace
diff --git a/src/tint/reader/wgsl/parser_impl_type_decl_test.cc b/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
index 1d54255..5e18b00 100644
--- a/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
@@ -139,7 +139,7 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:9: expected '>' for vector");
+ ASSERT_EQ(p->error(), "1:5: missing closing '>' for vector");
}
INSTANTIATE_TEST_SUITE_P(ParserImplTest,
VecMissingGreaterThanTest,
@@ -232,7 +232,7 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:18: expected '>' for ptr declaration");
+ ASSERT_EQ(p->error(), "1:4: missing closing '>' for ptr declaration");
}
TEST_F(ParserImplTest, TypeDecl_Ptr_MissingGreaterThanAfterAccess) {
@@ -242,7 +242,7 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:24: expected '>' for ptr declaration");
+ ASSERT_EQ(p->error(), "1:4: missing closing '>' for ptr declaration");
}
TEST_F(ParserImplTest, TypeDecl_Ptr_MissingCommaAfterAddressSpace) {
@@ -380,7 +380,7 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:11: expected '>' for atomic declaration");
+ ASSERT_EQ(p->error(), "1:7: missing closing '>' for atomic declaration");
}
TEST_F(ParserImplTest, TypeDecl_Atomic_MissingType) {
@@ -561,7 +561,7 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:10: expected '>' for array declaration");
+ ASSERT_EQ(p->error(), "1:6: missing closing '>' for array declaration");
}
TEST_F(ParserImplTest, TypeDecl_Array_MissingComma) {
@@ -623,7 +623,7 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:11: expected '>' for matrix");
+ ASSERT_EQ(p->error(), "1:7: missing closing '>' for matrix");
}
INSTANTIATE_TEST_SUITE_P(ParserImplTest,
MatrixMissingGreaterThanTest,
diff --git a/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc b/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc
index f2ab172..ea146c9 100644
--- a/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc
@@ -130,7 +130,7 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:9: expected '>' for vector");
+ ASSERT_EQ(p->error(), "1:5: missing closing '>' for vector");
}
INSTANTIATE_TEST_SUITE_P(ParserImplTest,
TypeDeclWithoutIdent_VecMissingGreaterThanTest,
@@ -223,7 +223,7 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:18: expected '>' for ptr declaration");
+ ASSERT_EQ(p->error(), "1:4: missing closing '>' for ptr declaration");
}
TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingGreaterThanAfterAccess) {
@@ -233,7 +233,7 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:24: expected '>' for ptr declaration");
+ ASSERT_EQ(p->error(), "1:4: missing closing '>' for ptr declaration");
}
TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingCommaAfterAddressSpace) {
@@ -371,7 +371,7 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:11: expected '>' for atomic declaration");
+ ASSERT_EQ(p->error(), "1:7: missing closing '>' for atomic declaration");
}
TEST_F(ParserImplTest, TypeDeclWithoutIdent_Atomic_MissingType) {
@@ -552,7 +552,7 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:10: expected '>' for array declaration");
+ ASSERT_EQ(p->error(), "1:6: missing closing '>' for array declaration");
}
TEST_F(ParserImplTest, TypeDeclWithoutIdent_Array_MissingComma) {
@@ -615,7 +615,7 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:11: expected '>' for matrix");
+ ASSERT_EQ(p->error(), "1:7: missing closing '>' for matrix");
}
INSTANTIATE_TEST_SUITE_P(ParserImplTest,
TypeDeclWithoutIdent_MatrixMissingGreaterThanTest,
diff --git a/src/tint/reader/wgsl/token.cc b/src/tint/reader/wgsl/token.cc
index 8e3fbdb..f13b317 100644
--- a/src/tint/reader/wgsl/token.cc
+++ b/src/tint/reader/wgsl/token.cc
@@ -70,12 +70,14 @@
return "=";
case Token::Type::kEqualEqual:
return "==";
+ case Token::Type::kTemplateArgsRight:
case Token::Type::kGreaterThan:
return ">";
case Token::Type::kGreaterThanEqual:
return ">=";
case Token::Type::kShiftRight:
return ">>";
+ case Token::Type::kTemplateArgsLeft:
case Token::Type::kLessThan:
return "<";
case Token::Type::kLessThanEqual:
@@ -135,6 +137,8 @@
case Token::Type::kShiftRightEqual:
return ">>=";
+ case Token::Type::kAlias:
+ return "alias";
case Token::Type::kArray:
return "array";
case Token::Type::kAtomic:
@@ -149,6 +153,8 @@
return "case";
case Token::Type::kConst:
return "const";
+ case Token::Type::kConstAssert:
+ return "const_assert";
case Token::Type::kContinue:
return "continue";
case Token::Type::kContinuing:
diff --git a/src/tint/reader/wgsl/token.h b/src/tint/reader/wgsl/token.h
index 1c8531b..7935cc3 100644
--- a/src/tint/reader/wgsl/token.h
+++ b/src/tint/reader/wgsl/token.h
@@ -32,7 +32,7 @@
kError = -2,
/// Uninitialized token
kUninitialized = 0,
- /// Placeholder token which maybe fillled in later
+ /// Placeholder token which maybe filled in later
kPlaceholder = 1,
/// End of input string reached
kEOF,
@@ -80,12 +80,16 @@
kEqual,
/// A '=='
kEqualEqual,
+ /// A '>' (post template-args classification)
+ kTemplateArgsRight,
/// A '>'
kGreaterThan,
/// A '>='
kGreaterThanEqual,
/// A '>>'
kShiftRight,
+ /// A '<' (post template-args classification)
+ kTemplateArgsLeft,
/// A '<'
kLessThan,
/// A '<='
@@ -145,6 +149,8 @@
/// A '<<='
kShiftLeftEqual,
+ /// A 'alias'
+ kAlias,
/// A 'array'
kArray,
/// A 'atomic'
@@ -159,6 +165,8 @@
kCase,
/// A 'const'
kConst,
+ /// A 'const_assert'
+ kConstAssert,
/// A 'continue'
kContinue,
/// A 'continuing'
@@ -452,12 +460,10 @@
std::variant<int64_t, double, std::string, std::string_view> value_;
};
-#ifndef NDEBUG
inline std::ostream& operator<<(std::ostream& out, Token::Type type) {
out << Token::TypeToName(type);
return out;
}
-#endif // NDEBUG
} // namespace tint::reader::wgsl
diff --git a/src/tint/resolver/alias_analysis_test.cc b/src/tint/resolver/alias_analysis_test.cc
index db78d64..66d5033 100644
--- a/src/tint/resolver/alias_analysis_test.cc
+++ b/src/tint/resolver/alias_analysis_test.cc
@@ -36,7 +36,7 @@
// }
struct TwoPointerConfig {
type::AddressSpace address_space; // The address space for the pointers.
- bool aliased; // Whether the pointers alias or not.
+ bool aliased; // Whether the pointers alias or not.
};
class TwoPointers : public ResolverTestWithParam<TwoPointerConfig> {
protected:
diff --git a/src/tint/resolver/const_assert_test.cc b/src/tint/resolver/const_assert_test.cc
new file mode 100644
index 0000000..fb08b8b
--- /dev/null
+++ b/src/tint/resolver/const_assert_test.cc
@@ -0,0 +1,107 @@
+// 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/resolver/resolver.h"
+
+#include "gmock/gmock.h"
+#include "src/tint/resolver/resolver_test_helper.h"
+
+using namespace tint::number_suffixes; // NOLINT
+
+namespace tint::resolver {
+namespace {
+
+using ResolverConstAssertTest = ResolverTest;
+
+TEST_F(ResolverConstAssertTest, Global_True_Pass) {
+ GlobalConstAssert(true);
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverConstAssertTest, Global_False_Fail) {
+ GlobalConstAssert(Source{{12, 34}}, false);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: const assertion failed");
+}
+
+TEST_F(ResolverConstAssertTest, Global_Const_Pass) {
+ GlobalConst("C", ty.bool_(), Expr(true));
+ GlobalConstAssert("C");
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverConstAssertTest, Global_Const_Fail) {
+ GlobalConst("C", ty.bool_(), Expr(false));
+ GlobalConstAssert(Source{{12, 34}}, "C");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: const assertion failed");
+}
+
+TEST_F(ResolverConstAssertTest, Global_LessThan_Pass) {
+ GlobalConstAssert(LessThan(2_i, 3_i));
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverConstAssertTest, Global_LessThan_Fail) {
+ GlobalConstAssert(Source{{12, 34}}, LessThan(4_i, 3_i));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: const assertion failed");
+}
+
+TEST_F(ResolverConstAssertTest, Local_True_Pass) {
+ WrapInFunction(ConstAssert(true));
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverConstAssertTest, Local_False_Fail) {
+ WrapInFunction(ConstAssert(Source{{12, 34}}, false));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: const assertion failed");
+}
+
+TEST_F(ResolverConstAssertTest, Local_Const_Pass) {
+ GlobalConst("C", ty.bool_(), Expr(true));
+ WrapInFunction(ConstAssert("C"));
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverConstAssertTest, Local_Const_Fail) {
+ GlobalConst("C", ty.bool_(), Expr(false));
+ WrapInFunction(ConstAssert(Source{{12, 34}}, "C"));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: const assertion failed");
+}
+
+TEST_F(ResolverConstAssertTest, Local_NonConst) {
+ GlobalVar("V", ty.bool_(), Expr(true), type::AddressSpace::kPrivate);
+ WrapInFunction(ConstAssert(Expr(Source{{12, 34}}, "V")));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: const assertion requires a const-expression, but expression is a "
+ "runtime-expression");
+}
+
+TEST_F(ResolverConstAssertTest, Local_LessThan_Pass) {
+ WrapInFunction(ConstAssert(LessThan(2_i, 3_i)));
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverConstAssertTest, Local_LessThan_Fail) {
+ WrapInFunction(ConstAssert(Source{{12, 34}}, LessThan(4_i, 3_i)));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: const assertion failed");
+}
+
+} // namespace
+} // namespace tint::resolver
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index 4047ce5..5ece264 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -31,6 +31,7 @@
#include "src/tint/ast/continue_statement.h"
#include "src/tint/ast/depth_multisampled_texture.h"
#include "src/tint/ast/depth_texture.h"
+#include "src/tint/ast/diagnostic_attribute.h"
#include "src/tint/ast/discard_statement.h"
#include "src/tint/ast/external_texture.h"
#include "src/tint/ast/f16.h"
@@ -204,10 +205,13 @@
TraverseExpression(var->initializer);
}
},
- [&](const ast::Enable*) {
- // Enable directives do not effect the dependency graph.
+ [&](const ast::DiagnosticControl*) {
+ // Diagnostic control directives do not affect the dependency graph.
},
- [&](const ast::StaticAssert* assertion) { TraverseExpression(assertion->condition); },
+ [&](const ast::Enable*) {
+ // Enable directives do not affect the dependency graph.
+ },
+ [&](const ast::ConstAssert* assertion) { TraverseExpression(assertion->condition); },
[&](Default) { UnhandledNode(diagnostics_, global->node); });
}
@@ -319,7 +323,7 @@
TraverseExpression(w->condition);
TraverseStatement(w->body);
},
- [&](const ast::StaticAssert* assertion) { TraverseExpression(assertion->condition); },
+ [&](const ast::ConstAssert* assertion) { TraverseExpression(assertion->condition); },
[&](Default) {
if (TINT_UNLIKELY((!stmt->IsAnyOf<ast::BreakStatement, ast::ContinueStatement,
ast::DiscardStatement>()))) {
@@ -454,9 +458,9 @@
return;
}
- if (attr->IsAnyOf<ast::BuiltinAttribute, ast::InternalAttribute, ast::InterpolateAttribute,
- ast::InvariantAttribute, ast::StageAttribute, ast::StrideAttribute,
- ast::StructMemberOffsetAttribute>()) {
+ if (attr->IsAnyOf<ast::BuiltinAttribute, ast::DiagnosticAttribute, ast::InternalAttribute,
+ ast::InterpolateAttribute, ast::InvariantAttribute, ast::StageAttribute,
+ ast::StrideAttribute, ast::StructMemberOffsetAttribute>()) {
return;
}
@@ -555,8 +559,9 @@
[&](const ast::TypeDecl* td) { return td->name; },
[&](const ast::Function* func) { return func->symbol; },
[&](const ast::Variable* var) { return var->symbol; },
+ [&](const ast::DiagnosticControl*) { return Symbol(); },
[&](const ast::Enable*) { return Symbol(); },
- [&](const ast::StaticAssert*) { return Symbol(); },
+ [&](const ast::ConstAssert*) { return Symbol(); },
[&](Default) {
UnhandledNode(diagnostics_, node);
return Symbol{};
@@ -575,12 +580,12 @@
/// declaration
std::string KindOf(const ast::Node* node) {
return Switch(
- node, //
- [&](const ast::Struct*) { return "struct"; }, //
- [&](const ast::Alias*) { return "alias"; }, //
- [&](const ast::Function*) { return "function"; }, //
- [&](const ast::Variable* v) { return v->Kind(); }, //
- [&](const ast::StaticAssert*) { return "static_assert"; }, //
+ node, //
+ [&](const ast::Struct*) { return "struct"; }, //
+ [&](const ast::Alias*) { return "alias"; }, //
+ [&](const ast::Function*) { return "function"; }, //
+ [&](const ast::Variable* v) { return v->Kind(); }, //
+ [&](const ast::ConstAssert*) { return "const_assert"; }, //
[&](Default) {
UnhandledNode(diagnostics_, node);
return "<error>";
@@ -663,16 +668,16 @@
return; // This code assumes there are no undeclared identifiers.
}
- // Make sure all 'enable' directives go before any other global declarations.
+ // Make sure all directives go before any other global declarations.
for (auto* global : declaration_order_) {
- if (auto* enable = global->node->As<ast::Enable>()) {
- sorted_.Add(enable);
+ if (global->node->IsAnyOf<ast::DiagnosticControl, ast::Enable>()) {
+ sorted_.Add(global->node);
}
}
for (auto* global : declaration_order_) {
- if (global->node->Is<ast::Enable>()) {
- // Skip 'enable' directives here, as they are already added.
+ if (global->node->IsAnyOf<ast::DiagnosticControl, ast::Enable>()) {
+ // Skip directives here, as they are already added.
continue;
}
utils::UniqueVector<const Global*, 8> stack;
diff --git a/src/tint/resolver/dependency_graph_test.cc b/src/tint/resolver/dependency_graph_test.cc
index 75faf34..f1f0a0c 100644
--- a/src/tint/resolver/dependency_graph_test.cc
+++ b/src/tint/resolver/dependency_graph_test.cc
@@ -1088,18 +1088,19 @@
testing::Combine(testing::ValuesIn(kFuncDeclKinds),
testing::ValuesIn(kFuncUseKinds)));
-TEST_F(ResolverDependencyGraphOrderedGlobalsTest, EnableFirst) {
- // Test that enable nodes always go before any other global declaration.
- // Although all enable directives in a valid WGSL program must go before any other global
- // declaration, a transform may produce such a AST tree that has some declarations before enable
- // nodes. DependencyGraph should deal with these cases.
+TEST_F(ResolverDependencyGraphOrderedGlobalsTest, DirectiveFirst) {
+ // Test that directive nodes always go before any other global declaration.
+ // Although all directives in a valid WGSL program must go before any other global declaration,
+ // a transform may produce such a AST tree that has some declarations before directive nodes.
+ // DependencyGraph should deal with these cases.
auto* var_1 = GlobalVar("SYMBOL1", ty.i32());
- auto* enable_1 = Enable(ast::Extension::kF16);
+ auto* enable = Enable(ast::Extension::kF16);
auto* var_2 = GlobalVar("SYMBOL2", ty.f32());
- auto* enable_2 = Enable(ast::Extension::kF16);
+ auto* diagnostic = DiagnosticControl(ast::DiagnosticSeverity::kWarning, Expr("foo"));
+ AST().AddDiagnosticControl(diagnostic);
- EXPECT_THAT(AST().GlobalDeclarations(), ElementsAre(var_1, enable_1, var_2, enable_2));
- EXPECT_THAT(Build().ordered_globals, ElementsAre(enable_1, enable_2, var_1, var_2));
+ EXPECT_THAT(AST().GlobalDeclarations(), ElementsAre(var_1, enable, var_2, diagnostic));
+ EXPECT_THAT(Build().ordered_globals, ElementsAre(enable, diagnostic, var_1, var_2));
}
} // namespace ordered_globals
diff --git a/src/tint/resolver/diagnostic_control_test.cc b/src/tint/resolver/diagnostic_control_test.cc
new file mode 100644
index 0000000..6c217e6
--- /dev/null
+++ b/src/tint/resolver/diagnostic_control_test.cc
@@ -0,0 +1,185 @@
+// Copyright 2023 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/resolver/resolver.h"
+
+#include "src/tint/resolver/resolver_test_helper.h"
+
+using namespace tint::number_suffixes; // NOLINT
+
+namespace tint::resolver {
+namespace {
+
+using ResolverDiagnosticControlTest = ResolverTest;
+
+TEST_F(ResolverDiagnosticControlTest, UnreachableCode_DefaultSeverity) {
+ auto stmts = utils::Vector{Return(), Return()};
+ Func("foo", {}, ty.void_(), stmts);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), R"(warning: code is unreachable)");
+}
+
+TEST_F(ResolverDiagnosticControlTest, UnreachableCode_ErrorViaDirective) {
+ DiagnosticDirective(ast::DiagnosticSeverity::kError, Expr("chromium_unreachable_code"));
+
+ auto stmts = utils::Vector{Return(), Return()};
+ Func("foo", {}, ty.void_(), stmts);
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(error: code is unreachable)");
+}
+
+TEST_F(ResolverDiagnosticControlTest, UnreachableCode_WarningViaDirective) {
+ DiagnosticDirective(ast::DiagnosticSeverity::kWarning, Expr("chromium_unreachable_code"));
+
+ auto stmts = utils::Vector{Return(), Return()};
+ Func("foo", {}, ty.void_(), stmts);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), R"(warning: code is unreachable)");
+}
+
+TEST_F(ResolverDiagnosticControlTest, UnreachableCode_InfoViaDirective) {
+ DiagnosticDirective(ast::DiagnosticSeverity::kInfo, Expr("chromium_unreachable_code"));
+
+ auto stmts = utils::Vector{Return(), Return()};
+ Func("foo", {}, ty.void_(), stmts);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), R"(note: code is unreachable)");
+}
+
+TEST_F(ResolverDiagnosticControlTest, UnreachableCode_OffViaDirective) {
+ DiagnosticDirective(ast::DiagnosticSeverity::kOff, Expr("chromium_unreachable_code"));
+
+ auto stmts = utils::Vector{Return(), Return()};
+ Func("foo", {}, ty.void_(), stmts);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->error().empty());
+}
+
+TEST_F(ResolverDiagnosticControlTest, UnreachableCode_ErrorViaAttribute) {
+ auto* attr =
+ DiagnosticAttribute(ast::DiagnosticSeverity::kError, Expr("chromium_unreachable_code"));
+
+ auto stmts = utils::Vector{Return(), Return()};
+ Func("foo", {}, ty.void_(), stmts, utils::Vector{attr});
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(error: code is unreachable)");
+}
+
+TEST_F(ResolverDiagnosticControlTest, UnreachableCode_WarningViaAttribute) {
+ auto* attr =
+ DiagnosticAttribute(ast::DiagnosticSeverity::kWarning, Expr("chromium_unreachable_code"));
+
+ auto stmts = utils::Vector{Return(), Return()};
+ Func("foo", {}, ty.void_(), stmts, utils::Vector{attr});
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), R"(warning: code is unreachable)");
+}
+
+TEST_F(ResolverDiagnosticControlTest, UnreachableCode_InfoViaAttribute) {
+ auto* attr =
+ DiagnosticAttribute(ast::DiagnosticSeverity::kInfo, Expr("chromium_unreachable_code"));
+
+ auto stmts = utils::Vector{Return(), Return()};
+ Func("foo", {}, ty.void_(), stmts, utils::Vector{attr});
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), R"(note: code is unreachable)");
+}
+
+TEST_F(ResolverDiagnosticControlTest, UnreachableCode_OffViaAttribute) {
+ auto* attr =
+ DiagnosticAttribute(ast::DiagnosticSeverity::kOff, Expr("chromium_unreachable_code"));
+
+ auto stmts = utils::Vector{Return(), Return()};
+ Func("foo", {}, ty.void_(), stmts, utils::Vector{attr});
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->error().empty());
+}
+
+TEST_F(ResolverDiagnosticControlTest, UnreachableCode_ErrorViaDirective_OverriddenViaAttribute) {
+ // diagnostic(error, chromium_unreachable_code);
+ //
+ // @diagnostic(off, chromium_unreachable_code) fn foo() {
+ // return;
+ // return; // Should produce a warning
+ // }
+ DiagnosticDirective(ast::DiagnosticSeverity::kError, Expr("chromium_unreachable_code"));
+ auto* attr =
+ DiagnosticAttribute(ast::DiagnosticSeverity::kWarning, Expr("chromium_unreachable_code"));
+
+ auto stmts = utils::Vector{Return(), Return()};
+ Func("foo", {}, ty.void_(), stmts, utils::Vector{attr});
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), R"(warning: code is unreachable)");
+}
+
+TEST_F(ResolverDiagnosticControlTest, FunctionAttributeScope) {
+ // @diagnostic(off, chromium_unreachable_code) fn foo() {
+ // return;
+ // return; // Should not produce a diagnostic
+ // }
+ //
+ // fn zoo() {
+ // return;
+ // return; // Should produce a warning (default severity)
+ // }
+ //
+ // @diagnostic(info, chromium_unreachable_code) fn bar() {
+ // return;
+ // return; // Should produce an info
+ // }
+ {
+ auto* attr =
+ DiagnosticAttribute(ast::DiagnosticSeverity::kOff, Expr("chromium_unreachable_code"));
+ Func("foo", {}, ty.void_(),
+ utils::Vector{
+ Return(),
+ Return(Source{{12, 34}}),
+ },
+ utils::Vector{attr});
+ }
+ {
+ Func("bar", {}, ty.void_(),
+ utils::Vector{
+ Return(),
+ Return(Source{{45, 67}}),
+ });
+ }
+ {
+ auto* attr =
+ DiagnosticAttribute(ast::DiagnosticSeverity::kInfo, Expr("chromium_unreachable_code"));
+ Func("zoo", {}, ty.void_(),
+ utils::Vector{
+ Return(),
+ Return(Source{{89, 10}}),
+ },
+ utils::Vector{attr});
+ }
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), R"(45:67 warning: code is unreachable
+89:10 note: code is unreachable)");
+}
+
+} // namespace
+} // namespace tint::resolver
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 3f306b5..7db9a2f 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -136,9 +136,20 @@
return false;
}
- // Create the semantic module
- builder_->Sem().SetModule(builder_->create<sem::Module>(
- std::move(dependencies_.ordered_globals), std::move(enabled_extensions_)));
+ // Create the semantic module.
+ auto* mod = builder_->create<sem::Module>(std::move(dependencies_.ordered_globals),
+ std::move(enabled_extensions_));
+ ApplyDiagnosticSeverities(mod);
+ builder_->Sem().SetModule(mod);
+
+ if (result) {
+ // Run the uniformity analysis, which requires a complete semantic module.
+ if (!enabled_extensions_.Contains(ast::Extension::kChromiumDisableUniformityAnalysis)) {
+ if (!AnalyzeUniformity(builder_, dependencies_)) {
+ return false;
+ }
+ }
+ }
return result;
}
@@ -151,11 +162,12 @@
Mark(decl);
if (!Switch<bool>(
decl, //
+ [&](const ast::DiagnosticControl* dc) { return DiagnosticControl(dc); },
[&](const ast::Enable* e) { return Enable(e); },
[&](const ast::TypeDecl* td) { return TypeDecl(td); },
[&](const ast::Function* func) { return Function(func); },
[&](const ast::Variable* var) { return GlobalVariable(var); },
- [&](const ast::StaticAssert* sa) { return StaticAssert(sa); },
+ [&](const ast::ConstAssert* ca) { return ConstAssert(ca); },
[&](Default) {
TINT_UNREACHABLE(Resolver, diagnostics_)
<< "unhandled global declaration: " << decl->TypeInfo().name;
@@ -179,14 +191,6 @@
return false;
}
- if (!enabled_extensions_.Contains(ast::Extension::kChromiumDisableUniformityAnalysis)) {
- if (!AnalyzeUniformity(builder_, dependencies_)) {
- if (kUniformityFailuresAsError) {
- return false;
- }
- }
- }
-
bool result = true;
for (auto* node : builder_->ASTNodes().Objects()) {
if (TINT_UNLIKELY(!marked_[node->node_id.value])) {
@@ -936,8 +940,8 @@
return sem;
}
-sem::Statement* Resolver::StaticAssert(const ast::StaticAssert* assertion) {
- ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "static assertion"};
+sem::Statement* Resolver::ConstAssert(const ast::ConstAssert* assertion) {
+ ExprEvalStageConstraint constraint{sem::EvaluationStage::kConstant, "const assertion"};
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
auto* expr = Expression(assertion->condition);
if (!expr) {
@@ -946,12 +950,12 @@
auto* cond = expr->ConstantValue();
if (auto* ty = cond->Type(); !ty->Is<type::Bool>()) {
AddError(
- "static assertion condition must be a bool, got '" + builder_->FriendlyName(ty) + "'",
+ "const assertion condition must be a bool, got '" + builder_->FriendlyName(ty) + "'",
assertion->condition->source);
return nullptr;
}
if (!cond->ValueAs<bool>()) {
- AddError("static assertion failed", assertion->source);
+ AddError("const assertion failed", assertion->source);
return nullptr;
}
auto* sem =
@@ -965,6 +969,21 @@
utils::Hashmap<Symbol, Source, 8> parameter_names;
utils::Vector<sem::Parameter*, 8> parameters;
+ validator_.DiagnosticFilters().Push();
+ TINT_DEFER(validator_.DiagnosticFilters().Pop());
+ for (auto* attr : decl->attributes) {
+ Mark(attr);
+ if (auto* dc = attr->As<ast::DiagnosticAttribute>()) {
+ Mark(dc->control);
+ if (!DiagnosticControl(dc->control)) {
+ return nullptr;
+ }
+ }
+ }
+ if (!validator_.NoDuplicateAttributes(decl->attributes)) {
+ return nullptr;
+ }
+
// Resolve all the parameters
for (auto* param : decl->params) {
Mark(param);
@@ -1031,9 +1050,6 @@
return_location = value.Get();
}
}
- if (!validator_.NoDuplicateAttributes(decl->attributes)) {
- return nullptr;
- }
if (auto* str = return_type->As<sem::Struct>()) {
if (!ApplyAddressSpaceUsageToType(type::AddressSpace::kNone, str, decl->source)) {
@@ -1060,6 +1076,7 @@
auto* func =
builder_->create<sem::Function>(decl, return_type, return_location, std::move(parameters));
+ ApplyDiagnosticSeverities(func);
builder_->Sem().Add(decl, func);
TINT_SCOPED_ASSIGNMENT(current_function_, func);
@@ -1095,10 +1112,6 @@
}
}
- for (auto* attr : decl->attributes) {
- Mark(attr);
- }
-
if (!validator_.NoDuplicateAttributes(decl->return_type_attributes)) {
return nullptr;
}
@@ -1258,7 +1271,7 @@
[&](const ast::IncrementDecrementStatement* i) { return IncrementDecrementStatement(i); },
[&](const ast::ReturnStatement* r) { return ReturnStatement(r); },
[&](const ast::VariableDeclStatement* v) { return VariableDeclStatement(v); },
- [&](const ast::StaticAssert* sa) { return StaticAssert(sa); },
+ [&](const ast::ConstAssert* sa) { return ConstAssert(sa); },
// Error cases
[&](const ast::CaseStatement*) {
@@ -3050,6 +3063,16 @@
return sem;
}
+bool Resolver::DiagnosticControl(const ast::DiagnosticControl* control) {
+ Mark(control->rule_name);
+ auto rule_name = builder_->Symbols().NameFor(control->rule_name->symbol);
+ auto rule = ast::ParseDiagnosticRule(rule_name);
+ if (rule != ast::DiagnosticRule::kUndefined) {
+ validator_.DiagnosticFilters().Set(rule, control->severity);
+ }
+ return true;
+}
+
bool Resolver::Enable(const ast::Enable* enable) {
enabled_extensions_.Add(enable->extension);
return true;
@@ -3859,6 +3882,13 @@
return false;
}
+template <typename NODE>
+void Resolver::ApplyDiagnosticSeverities(NODE* node) {
+ for (auto itr : validator_.DiagnosticFilters().Top()) {
+ node->SetDiagnosticSeverity(itr.key, itr.value);
+ }
+}
+
void Resolver::AddError(const std::string& msg, const Source& source) const {
diagnostics_.add_error(diag::System::Resolver, msg, source);
}
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index 38d08c0..519cea8 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -30,7 +30,6 @@
#include "src/tint/resolver/intrinsic_table.h"
#include "src/tint/resolver/sem_helper.h"
#include "src/tint/resolver/validator.h"
-#include "src/tint/scope_stack.h"
#include "src/tint/sem/binding_point.h"
#include "src/tint/sem/block_statement.h"
#include "src/tint/sem/function.h"
@@ -230,6 +229,7 @@
sem::CaseStatement* CaseStatement(const ast::CaseStatement*, const type::Type*);
sem::Statement* CompoundAssignmentStatement(const ast::CompoundAssignmentStatement*);
sem::Statement* ContinueStatement(const ast::ContinueStatement*);
+ sem::Statement* ConstAssert(const ast::ConstAssert*);
sem::Statement* DiscardStatement(const ast::DiscardStatement*);
sem::ForLoopStatement* ForLoopStatement(const ast::ForLoopStatement*);
sem::WhileStatement* WhileStatement(const ast::WhileStatement*);
@@ -240,7 +240,6 @@
sem::LoopStatement* LoopStatement(const ast::LoopStatement*);
sem::Statement* ReturnStatement(const ast::ReturnStatement*);
sem::Statement* Statement(const ast::Statement*);
- sem::Statement* StaticAssert(const ast::StaticAssert*);
sem::SwitchStatement* SwitchStatement(const ast::SwitchStatement* s);
sem::Statement* VariableDeclStatement(const ast::VariableDeclStatement*);
bool Statements(utils::VectorRef<const ast::Statement*>);
@@ -262,6 +261,10 @@
/// @param ty the ast::Type
type::Type* Type(const ast::Type* ty);
+ /// @param control the diagnostic control
+ /// @returns true on success, false on failure
+ bool DiagnosticControl(const ast::DiagnosticControl* control);
+
/// @param enable the enable declaration
/// @returns the resolved extension
bool Enable(const ast::Enable* enable);
@@ -411,6 +414,11 @@
/// @returns true on success, false on error
bool Mark(const ast::Node* node);
+ /// Applies the diagnostic severities from the current scope to a semantic node.
+ /// @param node the semantic node to apply the diagnostic severities to
+ template <typename NODE>
+ void ApplyDiagnosticSeverities(NODE* node);
+
/// Adds the given error message to the diagnostics
void AddError(const std::string& msg, const Source& source) const;
diff --git a/src/tint/resolver/static_assert_test.cc b/src/tint/resolver/static_assert_test.cc
deleted file mode 100644
index 3915dcd..0000000
--- a/src/tint/resolver/static_assert_test.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-// 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/resolver/resolver.h"
-
-#include "gmock/gmock.h"
-#include "src/tint/resolver/resolver_test_helper.h"
-
-using namespace tint::number_suffixes; // NOLINT
-
-namespace tint::resolver {
-namespace {
-
-using ResolverStaticAssertTest = ResolverTest;
-
-TEST_F(ResolverStaticAssertTest, Global_True_Pass) {
- GlobalStaticAssert(true);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverStaticAssertTest, Global_False_Fail) {
- GlobalStaticAssert(Source{{12, 34}}, false);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: static assertion failed");
-}
-
-TEST_F(ResolverStaticAssertTest, Global_Const_Pass) {
- GlobalConst("C", ty.bool_(), Expr(true));
- GlobalStaticAssert("C");
- ASSERT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverStaticAssertTest, Global_Const_Fail) {
- GlobalConst("C", ty.bool_(), Expr(false));
- GlobalStaticAssert(Source{{12, 34}}, "C");
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: static assertion failed");
-}
-
-TEST_F(ResolverStaticAssertTest, Global_LessThan_Pass) {
- GlobalStaticAssert(LessThan(2_i, 3_i));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverStaticAssertTest, Global_LessThan_Fail) {
- GlobalStaticAssert(Source{{12, 34}}, LessThan(4_i, 3_i));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: static assertion failed");
-}
-
-TEST_F(ResolverStaticAssertTest, Local_True_Pass) {
- WrapInFunction(StaticAssert(true));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverStaticAssertTest, Local_False_Fail) {
- WrapInFunction(StaticAssert(Source{{12, 34}}, false));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: static assertion failed");
-}
-
-TEST_F(ResolverStaticAssertTest, Local_Const_Pass) {
- GlobalConst("C", ty.bool_(), Expr(true));
- WrapInFunction(StaticAssert("C"));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverStaticAssertTest, Local_Const_Fail) {
- GlobalConst("C", ty.bool_(), Expr(false));
- WrapInFunction(StaticAssert(Source{{12, 34}}, "C"));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: static assertion failed");
-}
-
-TEST_F(ResolverStaticAssertTest, Local_NonConst) {
- GlobalVar("V", ty.bool_(), Expr(true), type::AddressSpace::kPrivate);
- WrapInFunction(StaticAssert(Expr(Source{{12, 34}}, "V")));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: static assertion requires a const-expression, but expression is a "
- "runtime-expression");
-}
-
-TEST_F(ResolverStaticAssertTest, Local_LessThan_Pass) {
- WrapInFunction(StaticAssert(LessThan(2_i, 3_i)));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverStaticAssertTest, Local_LessThan_Fail) {
- WrapInFunction(StaticAssert(Source{{12, 34}}, LessThan(4_i, 3_i)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: static assertion failed");
-}
-
-} // namespace
-} // namespace tint::resolver
diff --git a/src/tint/resolver/uniformity.cc b/src/tint/resolver/uniformity.cc
index 7b297e1..ec10d92 100644
--- a/src/tint/resolver/uniformity.cc
+++ b/src/tint/resolver/uniformity.cc
@@ -66,9 +66,12 @@
}
/// CallSiteTag describes the uniformity requirements on the call sites of a function.
-enum CallSiteTag {
- CallSiteRequiredToBeUniform,
- CallSiteNoRestriction,
+struct CallSiteTag {
+ enum {
+ CallSiteRequiredToBeUniform,
+ CallSiteNoRestriction,
+ } tag;
+ ast::DiagnosticSeverity severity = ast::DiagnosticSeverity::kUndefined;
};
/// FunctionTag describes a functions effects on uniformity.
@@ -78,10 +81,13 @@
};
/// ParameterTag describes the uniformity requirements of values passed to a function parameter.
-enum ParameterTag {
- ParameterValueRequiredToBeUniform,
- ParameterContentsRequiredToBeUniform,
- ParameterNoRestriction,
+struct ParameterTag {
+ enum {
+ ParameterValueRequiredToBeUniform,
+ ParameterContentsRequiredToBeUniform,
+ ParameterNoRestriction,
+ } tag;
+ ast::DiagnosticSeverity severity = ast::DiagnosticSeverity::kUndefined;
};
/// Node represents a node in the graph of control flow and value nodes within the analysis of a
@@ -138,9 +144,9 @@
/// The semantic node in corresponds to this parameter.
const sem::Parameter* sem;
/// The parameter's direct uniformity requirements.
- ParameterTag tag_direct = ParameterNoRestriction;
+ ParameterTag tag_direct = {ParameterTag::ParameterNoRestriction};
/// The parameter's uniformity requirements that affect the function return value.
- ParameterTag tag_retval = ParameterNoRestriction;
+ ParameterTag tag_retval = {ParameterTag::ParameterNoRestriction};
/// Will be `true` if this function may cause the contents of this pointer parameter to become
/// non-uniform.
bool pointer_may_become_non_uniform = false;
@@ -166,11 +172,13 @@
/// @param builder the program builder
FunctionInfo(const ast::Function* func, const ProgramBuilder* builder) {
name = builder->Symbols().NameFor(func->symbol);
- callsite_tag = CallSiteNoRestriction;
+ callsite_tag = {CallSiteTag::CallSiteNoRestriction};
function_tag = NoRestriction;
// Create special nodes.
- required_to_be_uniform = CreateNode({"RequiredToBeUniform"});
+ required_to_be_uniform_error = CreateNode({"RequiredToBeUniform_Error"});
+ required_to_be_uniform_warning = CreateNode({"RequiredToBeUniform_Warning"});
+ required_to_be_uniform_info = CreateNode({"RequiredToBeUniform_Info"});
may_be_non_uniform = CreateNode({"MayBeNonUniform"});
cf_start = CreateNode({"CF_start"});
if (func->return_type) {
@@ -214,14 +222,16 @@
/// The control flow graph.
utils::BlockAllocator<Node> nodes;
- /// Special `RequiredToBeUniform` node.
- Node* required_to_be_uniform;
+ /// Special `RequiredToBeUniform` nodes.
+ Node* required_to_be_uniform_error = nullptr;
+ Node* required_to_be_uniform_warning = nullptr;
+ Node* required_to_be_uniform_info = nullptr;
/// Special `MayBeNonUniform` node.
- Node* may_be_non_uniform;
+ Node* may_be_non_uniform = nullptr;
/// Special `CF_start` node.
- Node* cf_start;
+ Node* cf_start = nullptr;
/// Special `Value_return` node.
- Node* value_return;
+ Node* value_return = nullptr;
/// Map from variables to their value nodes in the graph, scoped with respect to control flow.
ScopeStack<const sem::Variable*, Node*> variables;
@@ -246,6 +256,21 @@
utils::Hashmap<const sem::Variable*, Node*, 4> var_exit_nodes;
};
+ /// @returns the RequiredToBeUniform node that corresponds to `severity`
+ Node* RequiredToBeUniform(ast::DiagnosticSeverity severity) {
+ switch (severity) {
+ case ast::DiagnosticSeverity::kError:
+ return required_to_be_uniform_error;
+ case ast::DiagnosticSeverity::kWarning:
+ return required_to_be_uniform_warning;
+ case ast::DiagnosticSeverity::kInfo:
+ return required_to_be_uniform_info;
+ default:
+ TINT_ASSERT(Resolver, false && "unhandled severity");
+ return nullptr;
+ }
+ }
+
/// @returns a LoopSwitchInfo for the given statement, allocating the LoopSwitchInfo if this is
/// the first call with the given statement.
LoopSwitchInfo& LoopSwitchInfoFor(const sem::Statement* stmt) {
@@ -401,32 +426,49 @@
// For pointers, we distinguish between requiring uniformity of the contents versus
// the pointer itself.
if (reachable.Contains(param_info.ptr_input_contents)) {
- return ParameterContentsRequiredToBeUniform;
+ return ParameterTag::ParameterContentsRequiredToBeUniform;
} else if (reachable.Contains(param_info.value)) {
- return ParameterValueRequiredToBeUniform;
+ return ParameterTag::ParameterValueRequiredToBeUniform;
}
} else if (reachable.Contains(current_function_->variables.Get(param))) {
// For non-pointers, the requirement is always on the value.
- return ParameterValueRequiredToBeUniform;
+ return ParameterTag::ParameterValueRequiredToBeUniform;
}
- return ParameterNoRestriction;
+ return ParameterTag::ParameterNoRestriction;
};
// Look at which nodes are reachable from "RequiredToBeUniform".
{
utils::UniqueVector<Node*, 4> reachable;
- Traverse(current_function_->required_to_be_uniform, &reachable);
- if (reachable.Contains(current_function_->may_be_non_uniform)) {
- MakeError(*current_function_, current_function_->may_be_non_uniform);
- return false;
- }
- if (reachable.Contains(current_function_->cf_start)) {
- current_function_->callsite_tag = CallSiteRequiredToBeUniform;
- }
+ auto traverse = [&](ast::DiagnosticSeverity severity) {
+ Traverse(current_function_->RequiredToBeUniform(severity), &reachable);
+ if (reachable.Contains(current_function_->may_be_non_uniform)) {
+ MakeError(*current_function_, current_function_->may_be_non_uniform, severity);
+ return false;
+ }
+ if (reachable.Contains(current_function_->cf_start)) {
+ if (current_function_->callsite_tag.tag == CallSiteTag::CallSiteNoRestriction) {
+ current_function_->callsite_tag = {CallSiteTag::CallSiteRequiredToBeUniform,
+ severity};
+ }
+ }
- // Set the tags to capture the direct uniformity requirements of each parameter.
- for (size_t i = 0; i < func->params.Length(); i++) {
- current_function_->parameters[i].tag_direct = get_param_tag(reachable, i);
+ // Set the tags to capture the direct uniformity requirements of each parameter.
+ for (size_t i = 0; i < func->params.Length(); i++) {
+ if (current_function_->parameters[i].tag_direct.tag ==
+ ParameterTag::ParameterNoRestriction) {
+ current_function_->parameters[i].tag_direct = {get_param_tag(reachable, i),
+ severity};
+ }
+ }
+ return true;
+ };
+ if (!traverse(ast::DiagnosticSeverity::kError)) {
+ return false;
+ } else {
+ if (traverse(ast::DiagnosticSeverity::kWarning)) {
+ traverse(ast::DiagnosticSeverity::kInfo);
+ }
}
}
@@ -441,7 +483,7 @@
// Set the tags to capture the uniformity requirements of each parameter with respect to
// the function return value.
for (size_t i = 0; i < func->params.Length(); i++) {
- current_function_->parameters[i].tag_retval = get_param_tag(reachable, i);
+ current_function_->parameters[i].tag_retval = {get_param_tag(reachable, i)};
}
}
@@ -467,9 +509,9 @@
for (size_t j = 0; j < func->params.Length(); j++) {
auto tag = get_param_tag(reachable, j);
auto* source_param = sem_.Get<sem::Parameter>(func->params[j]);
- if (tag == ParameterContentsRequiredToBeUniform) {
+ if (tag == ParameterTag::ParameterContentsRequiredToBeUniform) {
param_info.ptr_output_source_param_contents.Push(source_param);
- } else if (tag == ParameterValueRequiredToBeUniform) {
+ } else if (tag == ParameterTag::ParameterValueRequiredToBeUniform) {
param_info.ptr_output_source_param_values.Push(source_param);
}
}
@@ -1067,7 +1109,7 @@
return cf;
},
- [&](const ast::StaticAssert*) {
+ [&](const ast::ConstAssert*) {
return cf; // No impact on uniformity
},
@@ -1444,8 +1486,11 @@
result->type = Node::kFunctionCallReturnValue;
Node* cf_after = CreateNode({"CF_after_", name}, call);
+ auto default_severity = kUniformityFailuresAsError ? ast::DiagnosticSeverity::kError
+ : ast::DiagnosticSeverity::kWarning;
+
// Get tags for the callee.
- CallSiteTag callsite_tag = CallSiteNoRestriction;
+ CallSiteTag callsite_tag = {CallSiteTag::CallSiteNoRestriction};
FunctionTag function_tag = NoRestriction;
auto* sem = SemCall(call);
const FunctionInfo* func_info = nullptr;
@@ -1455,21 +1500,23 @@
// Most builtins have no restrictions. The exceptions are barriers, derivatives,
// some texture sampling builtins, and atomics.
if (builtin->IsBarrier()) {
- callsite_tag = CallSiteRequiredToBeUniform;
+ callsite_tag = {CallSiteTag::CallSiteRequiredToBeUniform, default_severity};
} else if (builtin->Type() == sem::BuiltinType::kWorkgroupUniformLoad) {
- callsite_tag = CallSiteRequiredToBeUniform;
+ callsite_tag = {CallSiteTag::CallSiteRequiredToBeUniform, default_severity};
} else if (builtin->IsDerivative() ||
builtin->Type() == sem::BuiltinType::kTextureSample ||
builtin->Type() == sem::BuiltinType::kTextureSampleBias ||
builtin->Type() == sem::BuiltinType::kTextureSampleCompare) {
- callsite_tag = CallSiteRequiredToBeUniform;
- function_tag = ReturnValueMayBeNonUniform;
+ // Get the severity of derivative uniformity violations in this context.
+ auto severity =
+ sem_.DiagnosticSeverity(call, ast::DiagnosticRule::kDerivativeUniformity);
+ if (severity != ast::DiagnosticSeverity::kOff) {
+ callsite_tag = {CallSiteTag::CallSiteRequiredToBeUniform, severity};
+ function_tag = ReturnValueMayBeNonUniform;
+ }
} else if (builtin->IsAtomic()) {
- callsite_tag = CallSiteNoRestriction;
+ callsite_tag = {CallSiteTag::CallSiteNoRestriction};
function_tag = ReturnValueMayBeNonUniform;
- } else {
- callsite_tag = CallSiteNoRestriction;
- function_tag = NoRestriction;
}
},
[&](const sem::Function* func) {
@@ -1482,11 +1529,11 @@
func_info = info;
},
[&](const sem::TypeInitializer*) {
- callsite_tag = CallSiteNoRestriction;
+ callsite_tag = {CallSiteTag::CallSiteNoRestriction};
function_tag = NoRestriction;
},
[&](const sem::TypeConversion*) {
- callsite_tag = CallSiteNoRestriction;
+ callsite_tag = {CallSiteTag::CallSiteNoRestriction};
function_tag = NoRestriction;
},
[&](Default) {
@@ -1507,27 +1554,29 @@
auto& param_info = func_info->parameters[i];
// Capture the direct uniformity requirements.
- switch (param_info.tag_direct) {
- case ParameterValueRequiredToBeUniform:
- current_function_->required_to_be_uniform->AddEdge(args[i]);
+ switch (param_info.tag_direct.tag) {
+ case ParameterTag::ParameterValueRequiredToBeUniform:
+ current_function_->RequiredToBeUniform(param_info.tag_direct.severity)
+ ->AddEdge(args[i]);
break;
- case ParameterContentsRequiredToBeUniform: {
- current_function_->required_to_be_uniform->AddEdge(ptrarg_contents[i]);
+ case ParameterTag::ParameterContentsRequiredToBeUniform: {
+ current_function_->RequiredToBeUniform(param_info.tag_direct.severity)
+ ->AddEdge(ptrarg_contents[i]);
break;
}
- case ParameterNoRestriction:
+ case ParameterTag::ParameterNoRestriction:
break;
}
// Capture the effects of this parameter on the return value.
- switch (param_info.tag_retval) {
- case ParameterValueRequiredToBeUniform:
+ switch (param_info.tag_retval.tag) {
+ case ParameterTag::ParameterValueRequiredToBeUniform:
result->AddEdge(args[i]);
break;
- case ParameterContentsRequiredToBeUniform: {
+ case ParameterTag::ParameterContentsRequiredToBeUniform: {
result->AddEdge(ptrarg_contents[i]);
break;
}
- case ParameterNoRestriction:
+ case ParameterTag::ParameterNoRestriction:
break;
}
@@ -1566,7 +1615,7 @@
auto* builtin = sem->Target()->As<sem::Builtin>();
if (builtin && builtin->Type() == sem::BuiltinType::kWorkgroupUniformLoad) {
// The workgroupUniformLoad builtin requires its parameter to be uniform.
- current_function_->required_to_be_uniform->AddEdge(args[i]);
+ current_function_->RequiredToBeUniform(default_severity)->AddEdge(args[i]);
} else {
// All other builtin function parameters are RequiredToBeUniformForReturnValue,
// as are parameters for type initializers and type conversions.
@@ -1578,8 +1627,8 @@
// Add the callsite requirement last.
// We traverse edges in reverse order, so this makes the callsite requirement take highest
// priority when reporting violations.
- if (callsite_tag == CallSiteRequiredToBeUniform) {
- current_function_->required_to_be_uniform->AddEdge(call_node);
+ if (callsite_tag.tag == CallSiteTag::CallSiteRequiredToBeUniform) {
+ current_function_->RequiredToBeUniform(callsite_tag.severity)->AddEdge(call_node);
}
return {cf_after, result};
@@ -1625,8 +1674,9 @@
}
/// Recursively descend through the function called by `call` and the functions that it calls in
- /// order to find a call to a builtin function that requires uniformity.
- const ast::CallExpression* FindBuiltinThatRequiresUniformity(const ast::CallExpression* call) {
+ /// order to find a call to a builtin function that requires uniformity with the given severity.
+ const ast::CallExpression* FindBuiltinThatRequiresUniformity(const ast::CallExpression* call,
+ ast::DiagnosticSeverity severity) {
auto* target = SemCall(call)->Target();
if (target->Is<sem::Builtin>()) {
// This is a call to a builtin, so we must be done.
@@ -1635,10 +1685,10 @@
// This is a call to a user-defined function, so inspect the functions called by that
// function and look for one whose node has an edge from the RequiredToBeUniform node.
auto target_info = functions_.Find(user->Declaration());
- for (auto* call_node : target_info->required_to_be_uniform->edges) {
+ for (auto* call_node : target_info->RequiredToBeUniform(severity)->edges) {
if (call_node->type == Node::kRegular) {
auto* child_call = call_node->ast->As<ast::CallExpression>();
- return FindBuiltinThatRequiresUniformity(child_call);
+ return FindBuiltinThatRequiresUniformity(child_call, severity);
}
}
TINT_ASSERT(Resolver, false && "unable to find child call with uniformity requirement");
@@ -1783,16 +1833,15 @@
});
}
- /// Generate an error message for a uniformity issue.
+ /// Generate a diagnostic message for a uniformity issue.
/// @param function the function that the diagnostic is being produced for
/// @param source_node the node that has caused a uniformity issue in `function`
- void MakeError(FunctionInfo& function, Node* source_node) {
+ /// @param severity the severity of the diagnostic
+ void MakeError(FunctionInfo& function, Node* source_node, ast::DiagnosticSeverity severity) {
// Helper to produce a diagnostic message, as a note or with the global failure severity.
auto report = [&](Source source, std::string msg, bool note) {
diag::Diagnostic error{};
- auto failureSeverity =
- kUniformityFailuresAsError ? diag::Severity::Error : diag::Severity::Warning;
- error.severity = note ? diag::Severity::Note : failureSeverity;
+ error.severity = note ? diag::Severity::Note : ast::ToSeverity(severity);
error.system = diag::System::Resolver;
error.source = source;
error.message = msg;
@@ -1801,12 +1850,12 @@
// Traverse the graph to generate a path from RequiredToBeUniform to the source node.
function.ResetVisited();
- Traverse(function.required_to_be_uniform);
+ Traverse(function.RequiredToBeUniform(severity));
TINT_ASSERT(Resolver, source_node->visited_from);
// Find a node that is required to be uniform that has a path to the source node.
auto* cause = TraceBackAlongPathUntil(source_node, [&](Node* node) {
- return node->visited_from == function.required_to_be_uniform;
+ return node->visited_from == function.RequiredToBeUniform(severity);
});
// The node will always have a corresponding call expression.
@@ -1825,7 +1874,7 @@
auto next_function = functions_.Find(user_func->Declaration());
auto& param_info = next_function->parameters[cause->arg_index];
MakeError(*next_function,
- is_value ? param_info.value : param_info.ptr_input_contents);
+ is_value ? param_info.value : param_info.ptr_input_contents, severity);
}
// Show the place where the non-uniform argument was passed.
@@ -1838,7 +1887,7 @@
// Show the origin of non-uniformity for the value or data that is being passed.
ShowSourceOfNonUniformity(source_node->visited_from);
} else {
- auto* builtin_call = FindBuiltinThatRequiresUniformity(call);
+ auto* builtin_call = FindBuiltinThatRequiresUniformity(call, severity);
{
// Show a builtin was reachable from this call (which may be the call itself).
// This will be the trigger location for the failure.
diff --git a/src/tint/resolver/uniformity.h b/src/tint/resolver/uniformity.h
index 1139980..fd17449 100644
--- a/src/tint/resolver/uniformity.h
+++ b/src/tint/resolver/uniformity.h
@@ -26,7 +26,7 @@
namespace tint::resolver {
/// If true, uniformity analysis failures will be treated as an error, else as a warning.
-constexpr bool kUniformityFailuresAsError = false;
+constexpr bool kUniformityFailuresAsError = true;
/// Analyze the uniformity of a program.
/// @param builder the program to analyze
diff --git a/src/tint/resolver/uniformity_test.cc b/src/tint/resolver/uniformity_test.cc
index 2631976..274287a 100644
--- a/src/tint/resolver/uniformity_test.cc
+++ b/src/tint/resolver/uniformity_test.cc
@@ -13,6 +13,7 @@
// limitations under the License.
#include <memory>
+#include <sstream>
#include <string>
#include <tuple>
#include <utility>
@@ -42,11 +43,7 @@
bool valid = program.IsValid();
if (should_pass) {
EXPECT_TRUE(valid) << error_;
- if (program.Diagnostics().count() == 1u) {
- EXPECT_THAT(program.Diagnostics().str(), ::testing::HasSubstr("unreachable"));
- } else {
- EXPECT_EQ(program.Diagnostics().count(), 0u) << error_;
- }
+ EXPECT_FALSE(program.Diagnostics().contains_errors());
} else {
if (kUniformityFailuresAsError) {
EXPECT_FALSE(valid);
@@ -407,7 +404,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -468,7 +465,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -511,7 +508,7 @@
if (!should_pass) {
EXPECT_EQ(
error_,
- R"(test:5:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:5:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -546,7 +543,7 @@
if (!should_pass) {
EXPECT_EQ(
error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -591,7 +588,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -622,7 +619,7 @@
RunTest(src, should_pass);
if (!should_pass) {
EXPECT_EQ(error_,
- R"(test:5:5 warning: 'dpdx' must only be called from uniform control flow
+ R"(test:5:5 error: 'dpdx' must only be called from uniform control flow
dpdx(0.5);
^^^^
@@ -656,7 +653,7 @@
RunTest(src, should_pass);
if (!should_pass) {
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'dpdx' must only be called from uniform control flow
+ R"(test:9:5 error: 'dpdx' must only be called from uniform control flow
dpdx(0.5);
^^^^
@@ -693,7 +690,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:5:5 warning: 'dpdx' must only be called from uniform control flow
+ R"(test:5:5 error: 'dpdx' must only be called from uniform control flow
dpdx(0.5);
^^^^
@@ -723,7 +720,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'dpdx' must only be called from uniform control flow
+ R"(test:9:5 error: 'dpdx' must only be called from uniform control flow
dpdx(0.5);
^^^^
@@ -836,7 +833,7 @@
EXPECT_THAT(
error_,
::testing::StartsWith(
- R"(test:13:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:13:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();)"));
EXPECT_THAT(error_,
::testing::HasSubstr("test:14:9 note: reading from read_write storage buffer "
@@ -876,7 +873,7 @@
EXPECT_THAT(
error_,
::testing::StartsWith(
- R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:14:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();)"));
EXPECT_THAT(error_,
::testing::HasSubstr("test:13:9 note: reading from read_write storage buffer "
@@ -917,7 +914,7 @@
EXPECT_THAT(
error_,
::testing::StartsWith(
- R"(test:15:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:15:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();)"));
EXPECT_THAT(error_,
::testing::HasSubstr("test:13:9 note: reading from read_write storage buffer "
@@ -964,7 +961,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:7:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:7:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -1015,7 +1012,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -1083,7 +1080,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -1167,7 +1164,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:14:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:14:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -1205,7 +1202,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:15:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -1248,7 +1245,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:20:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:20:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -1322,7 +1319,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:20:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:20:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -1363,7 +1360,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -1407,7 +1404,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:16:9 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:16:9 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -1448,7 +1445,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -1489,7 +1486,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -1607,7 +1604,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:16:9 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:16:9 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -1669,7 +1666,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -1702,7 +1699,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -1757,7 +1754,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -1812,7 +1809,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -1873,7 +1870,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:15:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -1918,7 +1915,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:21:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:21:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -1960,7 +1957,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2001,7 +1998,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2038,7 +2035,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:7:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:7:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2134,7 +2131,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:7:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:7:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2171,7 +2168,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2236,7 +2233,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:17:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:17:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2283,7 +2280,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:23:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:23:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2326,7 +2323,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2369,7 +2366,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2464,7 +2461,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2492,7 +2489,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:7:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:7:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2521,7 +2518,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2550,7 +2547,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2579,7 +2576,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2608,7 +2605,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2636,7 +2633,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:7:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:7:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2667,7 +2664,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2744,7 +2741,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:15:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2827,7 +2824,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:12:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -2864,7 +2861,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:13:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:13:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3021,7 +3018,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:3 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3058,7 +3055,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:7:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:7:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3088,7 +3085,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:7:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:7:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3123,7 +3120,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:11:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:11:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3212,7 +3209,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:14:9 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:14:9 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3252,7 +3249,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:19:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:19:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3318,7 +3315,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:18:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:18:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3387,7 +3384,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:21:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:21:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3429,7 +3426,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3534,7 +3531,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3564,7 +3561,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3629,7 +3626,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:11:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:11:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3657,7 +3654,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:7:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:7:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3686,7 +3683,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3718,7 +3715,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3754,7 +3751,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:7:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:7:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3787,7 +3784,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3828,7 +3825,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:12:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3861,7 +3858,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3902,7 +3899,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -3985,7 +3982,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4023,7 +4020,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4064,7 +4061,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4112,7 +4109,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:11:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:11:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4154,7 +4151,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4200,7 +4197,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4248,7 +4245,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4278,7 +4275,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4310,7 +4307,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:11:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:11:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4361,7 +4358,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:11:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:11:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4411,7 +4408,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:12:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4464,7 +4461,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:12:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4498,7 +4495,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:13:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:13:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4581,7 +4578,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:21:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:21:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4675,7 +4672,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:18:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:18:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4712,7 +4709,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:16:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:16:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4749,7 +4746,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:16:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:16:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4794,7 +4791,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:24:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:24:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4833,7 +4830,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:18:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:18:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4869,7 +4866,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:7:7 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:7:7 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4905,7 +4902,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:15:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:15:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4942,7 +4939,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:16:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:16:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -4979,7 +4976,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:16:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:16:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5020,7 +5017,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:20:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:20:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5079,7 +5076,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:14:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5136,7 +5133,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:14:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5191,7 +5188,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:12:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5229,7 +5226,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:14:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5269,7 +5266,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:16:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:16:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5339,7 +5336,7 @@
RunTest(std::move(b), false);
EXPECT_EQ(error_,
- R"(warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(error: 'workgroupBarrier' must only be called from uniform control flow
note: control flow depends on possibly non-uniform value
note: reading from module-scope private variable 'non_uniform_global' may result in a non-uniform value)");
}
@@ -5375,7 +5372,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5404,7 +5401,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5449,7 +5446,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5482,7 +5479,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5600,7 +5597,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5630,7 +5627,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5663,7 +5660,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5692,7 +5689,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5725,7 +5722,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5829,7 +5826,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5862,7 +5859,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5895,7 +5892,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5929,7 +5926,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5964,7 +5961,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -5998,7 +5995,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6028,7 +6025,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6059,7 +6056,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6091,7 +6088,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6123,7 +6120,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6153,7 +6150,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6202,7 +6199,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6235,7 +6232,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:12:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6288,7 +6285,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:12:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:12:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6325,7 +6322,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:13:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:13:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6363,7 +6360,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:14:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6401,7 +6398,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:14:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6456,7 +6453,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:13:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:13:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6557,7 +6554,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:13:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:13:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6593,7 +6590,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:14:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6629,7 +6626,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:14:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:14:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6666,7 +6663,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:13:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:13:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6707,7 +6704,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6736,7 +6733,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6781,7 +6778,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6812,7 +6809,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6845,7 +6842,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6892,7 +6889,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6925,7 +6922,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6958,7 +6955,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -6991,7 +6988,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:9:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:9:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -7025,7 +7022,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -7060,7 +7057,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -7094,7 +7091,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:10:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:10:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -7240,7 +7237,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -7267,7 +7264,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -7294,7 +7291,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -7324,7 +7321,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -7354,7 +7351,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:8:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -7391,7 +7388,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:5:41 warning: 'dpdx' must only be called from uniform control flow
+ R"(test:5:41 error: 'dpdx' must only be called from uniform control flow
let b = (non_uniform_global == 0) && (dpdx(1.0) == 0.0);
^^^^
@@ -7538,7 +7535,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:16:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:16:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -7600,7 +7597,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:17:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:17:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -7644,7 +7641,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:21:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:21:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -7714,7 +7711,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:28 warning: possibly non-uniform value passed here
+ R"(test:8:28 error: possibly non-uniform value passed here
if (workgroupUniformLoad(&data[idx]) > 0) {
^
@@ -7746,7 +7743,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:8:31 warning: possibly non-uniform value passed here
+ R"(test:8:31 error: possibly non-uniform value passed here
return workgroupUniformLoad(p);
^
@@ -7777,7 +7774,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -7804,7 +7801,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:6:5 warning: 'storageBarrier' must only be called from uniform control flow
+ R"(test:6:5 error: 'storageBarrier' must only be called from uniform control flow
storageBarrier();
^^^^^^^^^^^^^^
@@ -7863,12 +7860,290 @@
RunTest(std::move(b), false);
EXPECT_EQ(error_,
- R"(warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(error: 'workgroupBarrier' must only be called from uniform control flow
note: control flow depends on possibly non-uniform value
note: reading from module-scope private variable 'v0' may result in a non-uniform value)");
}
////////////////////////////////////////////////////////////////////////////////
+/// Tests for the derivative_uniformity diagnostic filter.
+////////////////////////////////////////////////////////////////////////////////
+
+class UniformityAnalysisDiagnosticFilterTest
+ : public UniformityAnalysisTestBase,
+ public ::testing::TestWithParam<ast::DiagnosticSeverity> {
+ protected:
+ // TODO(jrprice): Remove this in favour of utils::ToString() when we change "note" to "info".
+ const char* ToStr(ast::DiagnosticSeverity severity) {
+ switch (severity) {
+ case ast::DiagnosticSeverity::kError:
+ return "error";
+ case ast::DiagnosticSeverity::kWarning:
+ return "warning";
+ case ast::DiagnosticSeverity::kInfo:
+ return "note";
+ default:
+ return "<undefined>";
+ }
+ }
+};
+
+TEST_P(UniformityAnalysisDiagnosticFilterTest, Directive) {
+ auto& param = GetParam();
+ std::ostringstream ss;
+ ss << "diagnostic(" << param << ", derivative_uniformity);"
+ << R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+@group(0) @binding(1) var t : texture_2d<f32>;
+@group(0) @binding(2) var s : sampler;
+
+fn foo() {
+ if (non_uniform == 42) {
+ let color = textureSample(t, s, vec2(0, 0));
+ }
+}
+)";
+
+ RunTest(ss.str(), param != ast::DiagnosticSeverity::kError);
+
+ if (param == ast::DiagnosticSeverity::kOff) {
+ EXPECT_TRUE(error_.empty());
+ } else {
+ std::ostringstream err;
+ err << ToStr(param) << ": 'textureSample' must only be called";
+ EXPECT_THAT(error_, ::testing::HasSubstr(err.str()));
+ }
+}
+
+TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnFunction) {
+ auto& param = GetParam();
+ std::ostringstream ss;
+ ss << R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+@group(0) @binding(1) var t : texture_2d<f32>;
+@group(0) @binding(2) var s : sampler;
+)"
+ << "@diagnostic(" << param << ", derivative_uniformity)"
+ <<
+ R"(fn foo() {
+ if (non_uniform == 42) {
+ let color = textureSample(t, s, vec2(0, 0));
+ }
+}
+)";
+
+ RunTest(ss.str(), param != ast::DiagnosticSeverity::kError);
+ if (param == ast::DiagnosticSeverity::kOff) {
+ EXPECT_TRUE(error_.empty());
+ } else {
+ std::ostringstream err;
+ err << ToStr(param) << ": 'textureSample' must only be called";
+ EXPECT_THAT(error_, ::testing::HasSubstr(err.str()));
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(UniformityAnalysisTest,
+ UniformityAnalysisDiagnosticFilterTest,
+ ::testing::Values(ast::DiagnosticSeverity::kError,
+ ast::DiagnosticSeverity::kWarning,
+ ast::DiagnosticSeverity::kInfo,
+ ast::DiagnosticSeverity::kOff));
+
+TEST_F(UniformityAnalysisDiagnosticFilterTest, AttributeOnFunction_CalledByAnotherFunction) {
+ std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+@diagnostic(info, derivative_uniformity)
+fn bar() {
+ dpdx(1.0);
+}
+
+fn foo() {
+ if (non_uniform == 42) {
+ bar();
+ }
+}
+)";
+
+ RunTest(src, true);
+ EXPECT_THAT(error_, ::testing::HasSubstr("note: 'dpdx' must only be called"));
+}
+
+TEST_F(UniformityAnalysisDiagnosticFilterTest, AttributeOnFunction_RequirementOnParameter) {
+ std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+@diagnostic(info, derivative_uniformity)
+fn bar(x : i32) {
+ if (x == 0) {
+ dpdx(1.0);
+ }
+}
+
+fn foo() {
+ bar(non_uniform);
+}
+)";
+
+ RunTest(src, true);
+ EXPECT_THAT(error_, ::testing::HasSubstr("note: 'dpdx' must only be called"));
+}
+
+TEST_F(UniformityAnalysisDiagnosticFilterTest, AttributeOnFunction_BuiltinInChildCall) {
+ // Make sure that the diagnostic filter does not descend into functions called by the function
+ // with the attribute.
+ std::string src = R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn bar() {
+ dpdx(1.0);
+}
+
+@diagnostic(off, derivative_uniformity)
+fn foo() {
+ if (non_uniform == 42) {
+ bar();
+ }
+}
+)";
+
+ RunTest(src, false);
+ EXPECT_THAT(error_, ::testing::HasSubstr(": 'dpdx' must only be called"));
+}
+
+TEST_F(UniformityAnalysisDiagnosticFilterTest, MixOfGlobalAndLocalFilters) {
+ // Test that a global filter is overridden by a local attribute, and that we find multiple
+ // violations until an error is found.
+ std::string src = R"(
+diagnostic(info, derivative_uniformity);
+
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn a() {
+ if (non_uniform == 42) {
+ dpdx(1.0);
+ }
+}
+
+@diagnostic(off, derivative_uniformity)
+fn b() {
+ if (non_uniform == 42) {
+ dpdx(1.0);
+ }
+}
+
+@diagnostic(info, derivative_uniformity)
+fn c() {
+ if (non_uniform == 42) {
+ dpdx(1.0);
+ }
+}
+
+@diagnostic(warning, derivative_uniformity)
+fn d() {
+ if (non_uniform == 42) {
+ dpdx(1.0);
+ }
+}
+
+@diagnostic(error, derivative_uniformity)
+fn e() {
+ if (non_uniform == 42) {
+ dpdx(1.0);
+ }
+}
+)";
+
+ RunTest(src, false);
+ EXPECT_EQ(error_,
+ R"(test:8:5 note: 'dpdx' must only be called from uniform control flow
+ dpdx(1.0);
+ ^^^^
+
+test:7:3 note: control flow depends on possibly non-uniform value
+ if (non_uniform == 42) {
+ ^^
+
+test:7:7 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+ if (non_uniform == 42) {
+ ^^^^^^^^^^^
+
+test:22:5 note: 'dpdx' must only be called from uniform control flow
+ dpdx(1.0);
+ ^^^^
+
+test:21:3 note: control flow depends on possibly non-uniform value
+ if (non_uniform == 42) {
+ ^^
+
+test:21:7 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+ if (non_uniform == 42) {
+ ^^^^^^^^^^^
+
+test:29:5 warning: 'dpdx' must only be called from uniform control flow
+ dpdx(1.0);
+ ^^^^
+
+test:28:3 note: control flow depends on possibly non-uniform value
+ if (non_uniform == 42) {
+ ^^
+
+test:28:7 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+ if (non_uniform == 42) {
+ ^^^^^^^^^^^
+
+test:36:5 error: 'dpdx' must only be called from uniform control flow
+ dpdx(1.0);
+ ^^^^
+
+test:35:3 note: control flow depends on possibly non-uniform value
+ if (non_uniform == 42) {
+ ^^
+
+test:35:7 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+ if (non_uniform == 42) {
+ ^^^^^^^^^^^
+)");
+}
+
+TEST_F(UniformityAnalysisDiagnosticFilterTest, BarriersNotAffected) {
+ // Make sure that the diagnostic filter does not affect barriers.
+ std::string src = R"(
+diagnostic(off, derivative_uniformity);
+
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+
+fn foo() {
+ if (non_uniform == 42) {
+ dpdx(1.0);
+ }
+}
+
+fn bar() {
+ if (non_uniform == 42) {
+ workgroupBarrier();
+ }
+}
+
+)";
+
+ RunTest(src, false);
+ EXPECT_EQ(error_,
+ R"(test:14:5 error: 'workgroupBarrier' must only be called from uniform control flow
+ workgroupBarrier();
+ ^^^^^^^^^^^^^^^^
+
+test:13:3 note: control flow depends on possibly non-uniform value
+ if (non_uniform == 42) {
+ ^^
+
+test:13:7 note: reading from read_write storage buffer 'non_uniform' may result in a non-uniform value
+ if (non_uniform == 42) {
+ ^^^^^^^^^^^
+)");
+}
+
+////////////////////////////////////////////////////////////////////////////////
/// Tests for the quality of the error messages produced by the analysis.
////////////////////////////////////////////////////////////////////////////////
@@ -7889,7 +8164,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:5:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:5:3 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -7932,7 +8207,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:5:3 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:5:3 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -7975,7 +8250,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -8038,7 +8313,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:18:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:18:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
@@ -8073,7 +8348,7 @@
RunTest(src, false);
EXPECT_EQ(error_,
- R"(test:6:5 warning: 'workgroupBarrier' must only be called from uniform control flow
+ R"(test:6:5 error: 'workgroupBarrier' must only be called from uniform control flow
workgroupBarrier();
^^^^^^^^^^^^^^^^
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 18f53fb..57a2e3a 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -166,7 +166,13 @@
sem_(sem),
enabled_extensions_(enabled_extensions),
atomic_composite_info_(atomic_composite_info),
- valid_type_storage_layouts_(valid_type_storage_layouts) {}
+ valid_type_storage_layouts_(valid_type_storage_layouts) {
+ // Set default severities for filterable diagnostic rules.
+ diagnostic_filters_.Set(ast::DiagnosticRule::kDerivativeUniformity,
+ ast::DiagnosticSeverity::kError);
+ diagnostic_filters_.Set(ast::DiagnosticRule::kChromiumUnreachableCode,
+ ast::DiagnosticSeverity::kWarning);
+}
Validator::~Validator() = default;
@@ -182,6 +188,24 @@
diagnostics_.add_note(diag::System::Resolver, msg, source);
}
+bool Validator::AddDiagnostic(ast::DiagnosticRule rule,
+ const std::string& msg,
+ const Source& source) const {
+ auto severity = diagnostic_filters_.Get(rule);
+ if (severity != ast::DiagnosticSeverity::kOff) {
+ diag::Diagnostic d{};
+ d.severity = ToSeverity(severity);
+ d.system = diag::System::Resolver;
+ d.source = source;
+ d.message = msg;
+ diagnostics_.add(std::move(d));
+ if (severity == ast::DiagnosticSeverity::kError) {
+ return false;
+ }
+ }
+ return true;
+}
+
// https://gpuweb.github.io/gpuweb/wgsl/#plain-types-section
bool Validator::IsPlain(const type::Type* type) const {
return type->is_scalar() ||
@@ -982,7 +1006,8 @@
attr->source);
return false;
}
- } else if (!attr->IsAnyOf<ast::StageAttribute, ast::InternalAttribute>()) {
+ } else if (!attr->IsAnyOf<ast::DiagnosticAttribute, ast::StageAttribute,
+ ast::InternalAttribute>()) {
AddError("attribute is not valid for functions", attr->source);
return false;
}
@@ -1358,9 +1383,10 @@
bool Validator::Statements(utils::VectorRef<const ast::Statement*> stmts) const {
for (auto* stmt : stmts) {
if (!sem_.Get(stmt)->IsReachable()) {
- /// TODO(https://github.com/gpuweb/gpuweb/issues/2378): This may need to
- /// become an error.
- AddWarning("code is unreachable", stmt->source);
+ if (!AddDiagnostic(ast::DiagnosticRule::kChromiumUnreachableCode, "code is unreachable",
+ stmt->source)) {
+ return false;
+ }
break;
}
}
diff --git a/src/tint/resolver/validator.h b/src/tint/resolver/validator.h
index 891f02d..635a6cc 100644
--- a/src/tint/resolver/validator.h
+++ b/src/tint/resolver/validator.h
@@ -22,6 +22,7 @@
#include "src/tint/ast/pipeline_stage.h"
#include "src/tint/program_builder.h"
#include "src/tint/resolver/sem_helper.h"
+#include "src/tint/scope_stack.h"
#include "src/tint/sem/evaluation_stage.h"
#include "src/tint/source.h"
#include "src/tint/utils/hash.h"
@@ -84,6 +85,9 @@
}
};
+/// DiagnosticFilterStack is a scoped stack of diagnostic filters.
+using DiagnosticFilterStack = ScopeStack<ast::DiagnosticRule, ast::DiagnosticSeverity>;
+
/// Validation logic for various ast nodes. The validations in general should
/// be shallow and depend on the resolver to call on children. The validations
/// also assume that sem changes have already been made. The validation checks
@@ -118,6 +122,18 @@
/// @param source the note source
void AddNote(const std::string& msg, const Source& source) const;
+ /// Adds the given message to the diagnostics with current severity for the given rule.
+ /// @param rule the diagnostic trigger rule
+ /// @param msg the diagnostic message
+ /// @param source the diagnostic source
+ /// @returns false if the diagnostic is an error for the given trigger rule
+ bool AddDiagnostic(ast::DiagnosticRule rule,
+ const std::string& msg,
+ const Source& source) const;
+
+ /// @returns the diagnostic filter stack
+ DiagnosticFilterStack& DiagnosticFilters() { return diagnostic_filters_; }
+
/// @param type the given type
/// @returns true if the given type is a plain type
bool IsPlain(const type::Type* type) const;
@@ -525,6 +541,7 @@
SymbolTable& symbols_;
diag::List& diagnostics_;
SemHelper& sem_;
+ DiagnosticFilterStack diagnostic_filters_;
const ast::Extensions& enabled_extensions_;
const utils::Hashmap<const type::Type*, const Source*, 8>& atomic_composite_info_;
utils::Hashset<TypeAndAddressSpace, 8>& valid_type_storage_layouts_;
diff --git a/src/tint/sem/diagnostic_severity_test.cc b/src/tint/sem/diagnostic_severity_test.cc
new file mode 100644
index 0000000..7ede2bc
--- /dev/null
+++ b/src/tint/sem/diagnostic_severity_test.cc
@@ -0,0 +1,68 @@
+// Copyright 2023 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/sem/test_helper.h"
+
+#include "src/tint/sem/module.h"
+
+using namespace tint::number_suffixes; // NOLINT
+
+namespace tint::sem {
+namespace {
+
+class DiagnosticSeverityTest : public TestHelper {
+ protected:
+ /// Create a program with two functions, setting the severity for "chromium_unreachable_code"
+ /// using an attribute. Test that we correctly track the severity of the filter for the
+ /// functions and the statements with them.
+ /// @param global_severity the global severity of the "chromium_unreachable_code" filter
+ void Run(ast::DiagnosticSeverity global_severity) {
+ // @diagnostic(off, chromium_unreachable_code)
+ // fn foo() {
+ // return;
+ // }
+ //
+ // fn bar() {
+ // return;
+ // }
+ auto rule = ast::DiagnosticRule::kChromiumUnreachableCode;
+ auto func_severity = ast::DiagnosticSeverity::kOff;
+
+ auto* return_1 = Return();
+ auto* return_2 = Return();
+ auto* func_attr = DiagnosticAttribute(func_severity, Expr("chromium_unreachable_code"));
+ auto* foo = Func("foo", {}, ty.void_(), utils::Vector{return_1}, utils::Vector{func_attr});
+ auto* bar = Func("bar", {}, ty.void_(), utils::Vector{return_2});
+
+ auto p = Build();
+ EXPECT_TRUE(p.IsValid()) << p.Diagnostics().str();
+
+ EXPECT_EQ(p.Sem().DiagnosticSeverity(foo, rule), func_severity);
+ EXPECT_EQ(p.Sem().DiagnosticSeverity(return_1, rule), func_severity);
+ EXPECT_EQ(p.Sem().DiagnosticSeverity(bar, rule), global_severity);
+ EXPECT_EQ(p.Sem().DiagnosticSeverity(return_2, rule), global_severity);
+ }
+};
+
+TEST_F(DiagnosticSeverityTest, WithDirective) {
+ DiagnosticDirective(ast::DiagnosticSeverity::kError, Expr("chromium_unreachable_code"));
+ Run(ast::DiagnosticSeverity::kError);
+}
+
+TEST_F(DiagnosticSeverityTest, WithoutDirective) {
+ Run(ast::DiagnosticSeverity::kWarning);
+}
+
+} // namespace
+} // namespace tint::sem
diff --git a/src/tint/sem/function.h b/src/tint/sem/function.h
index 32468aa..25bacda 100644
--- a/src/tint/sem/function.h
+++ b/src/tint/sem/function.h
@@ -20,6 +20,7 @@
#include <utility>
#include <vector>
+#include "src/tint/ast/diagnostic_control.h"
#include "src/tint/ast/variable.h"
#include "src/tint/sem/call.h"
#include "src/tint/utils/unique_vector.h"
@@ -256,6 +257,18 @@
/// @return the location for the return, if provided
std::optional<uint32_t> ReturnLocation() const { return return_location_; }
+ /// Modifies the severity of a specific diagnostic rule for this function.
+ /// @param rule the diagnostic rule
+ /// @param severity the new diagnostic severity
+ void SetDiagnosticSeverity(ast::DiagnosticRule rule, ast::DiagnosticSeverity severity) {
+ diagnostic_severities_[rule] = severity;
+ }
+
+ /// @returns the diagnostic severity modifications applied to this function
+ const ast::DiagnosticRuleSeverities& DiagnosticSeverities() const {
+ return diagnostic_severities_;
+ }
+
private:
Function(const Function&) = delete;
Function(Function&&) = delete;
@@ -276,6 +289,7 @@
std::vector<const Function*> ancestor_entry_points_;
const Statement* discard_stmt_ = nullptr;
sem::Behaviors behaviors_{sem::Behavior::kNext};
+ ast::DiagnosticRuleSeverities diagnostic_severities_;
std::optional<uint32_t> return_location_;
};
diff --git a/src/tint/sem/info.cc b/src/tint/sem/info.cc
index edeab7e..a3f5b48 100644
--- a/src/tint/sem/info.cc
+++ b/src/tint/sem/info.cc
@@ -14,6 +14,11 @@
#include "src/tint/sem/info.h"
+#include "src/tint/sem/expression.h"
+#include "src/tint/sem/function.h"
+#include "src/tint/sem/module.h"
+#include "src/tint/sem/statement.h"
+
namespace tint::sem {
Info::Info() = default;
@@ -24,4 +29,61 @@
Info& Info::operator=(Info&&) = default;
+ast::DiagnosticSeverity Info::DiagnosticSeverity(const ast::Node* ast_node,
+ ast::DiagnosticRule rule) const {
+ // Get the diagnostic severity modification for a node.
+ auto check = [&](auto* node) {
+ auto& severities = node->DiagnosticSeverities();
+ auto itr = severities.find(rule);
+ if (itr != severities.end()) {
+ return itr->second;
+ }
+ return ast::DiagnosticSeverity::kUndefined;
+ };
+
+ // Get the diagnostic severity modification for a function.
+ auto check_func = [&](const sem::Function* func) {
+ auto severity = check(func);
+ if (severity != ast::DiagnosticSeverity::kUndefined) {
+ return severity;
+ }
+
+ // No severity set on the function, so check the module instead.
+ return check(module_);
+ };
+
+ // Get the diagnostic severity modification for a statement.
+ auto check_stmt = [&](const sem::Statement* stmt) {
+ // Walk up the statement hierarchy, checking for diagnostic severity modifications.
+ while (true) {
+ auto severity = check(stmt);
+ if (severity != ast::DiagnosticSeverity::kUndefined) {
+ return severity;
+ }
+ if (!stmt->Parent()) {
+ break;
+ }
+ stmt = stmt->Parent();
+ }
+
+ // No severity set on the statement, so check the function instead.
+ return check_func(stmt->Function());
+ };
+
+ // Query the diagnostic severity from the semantic node that corresponds to the AST node.
+ auto* sem = Get(ast_node);
+ TINT_ASSERT(Resolver, sem != nullptr);
+ auto severity = Switch(
+ sem, //
+ [&](const sem::Expression* expr) { return check_stmt(expr->Stmt()); },
+ [&](const sem::Statement* stmt) { return check_stmt(stmt); },
+ [&](const sem::Function* func) { return check_func(func); },
+ [&](Default) {
+ // Use the global severity set on the module.
+ return check(module_);
+ });
+ TINT_ASSERT(Resolver, severity != ast::DiagnosticSeverity::kUndefined);
+ return severity;
+}
+
} // namespace tint::sem
diff --git a/src/tint/sem/info.h b/src/tint/sem/info.h
index 246510c..c97b014 100644
--- a/src/tint/sem/info.h
+++ b/src/tint/sem/info.h
@@ -20,6 +20,7 @@
#include <unordered_map>
#include <vector>
+#include "src/tint/ast/diagnostic_control.h"
#include "src/tint/ast/node.h"
#include "src/tint/debug.h"
#include "src/tint/sem/node.h"
@@ -144,6 +145,13 @@
return &referenced_overrides_.at(from);
}
+ /// Determines the severity of a filterable diagnostic rule for the AST node `ast_node`.
+ /// @param ast_node the AST node
+ /// @param rule the diagnostic rule
+ /// @returns the severity of the rule for that AST node
+ ast::DiagnosticSeverity DiagnosticSeverity(const ast::Node* ast_node,
+ ast::DiagnosticRule rule) const;
+
private:
// AST node index to semantic node
std::vector<const CastableBase*> nodes_;
diff --git a/src/tint/sem/module.h b/src/tint/sem/module.h
index 216a2c4..21b08f2 100644
--- a/src/tint/sem/module.h
+++ b/src/tint/sem/module.h
@@ -15,6 +15,7 @@
#ifndef SRC_TINT_SEM_MODULE_H_
#define SRC_TINT_SEM_MODULE_H_
+#include "src/tint/ast/diagnostic_control.h"
#include "src/tint/ast/extension.h"
#include "src/tint/sem/node.h"
#include "src/tint/utils/vector.h"
@@ -46,9 +47,22 @@
/// @returns the list of enabled extensions in the module
const ast::Extensions& Extensions() const { return extensions_; }
+ /// Modifies the severity of a specific diagnostic rule for this module.
+ /// @param rule the diagnostic rule
+ /// @param severity the new diagnostic severity
+ void SetDiagnosticSeverity(ast::DiagnosticRule rule, ast::DiagnosticSeverity severity) {
+ diagnostic_severities_[rule] = severity;
+ }
+
+ /// @returns the diagnostic severity modifications applied to this module
+ const ast::DiagnosticRuleSeverities& DiagnosticSeverities() const {
+ return diagnostic_severities_;
+ }
+
private:
const utils::Vector<const ast::Node*, 64> dep_ordered_decls_;
ast::Extensions extensions_;
+ ast::DiagnosticRuleSeverities diagnostic_severities_;
};
} // namespace tint::sem
diff --git a/src/tint/sem/statement.h b/src/tint/sem/statement.h
index 09112d5..2d17295 100644
--- a/src/tint/sem/statement.h
+++ b/src/tint/sem/statement.h
@@ -15,6 +15,7 @@
#ifndef SRC_TINT_SEM_STATEMENT_H_
#define SRC_TINT_SEM_STATEMENT_H_
+#include "src/tint/ast/diagnostic_control.h"
#include "src/tint/sem/behavior.h"
#include "src/tint/sem/node.h"
#include "src/tint/symbol.h"
@@ -109,12 +110,25 @@
/// according to the behavior analysis
void SetIsReachable(bool is_reachable) { is_reachable_ = is_reachable; }
+ /// Modifies the severity of a specific diagnostic rule for this statement.
+ /// @param rule the diagnostic rule
+ /// @param severity the new diagnostic severity
+ void SetDiagnosticSeverity(ast::DiagnosticRule rule, ast::DiagnosticSeverity severity) {
+ diagnostic_severities_[rule] = severity;
+ }
+
+ /// @returns the diagnostic severity modifications applied to this statement
+ const ast::DiagnosticRuleSeverities& DiagnosticSeverities() const {
+ return diagnostic_severities_;
+ }
+
private:
const ast::Statement* const declaration_;
const CompoundStatement* const parent_;
const sem::Function* const function_;
sem::Behaviors behaviors_{sem::Behavior::kNext};
bool is_reachable_ = true;
+ ast::DiagnosticRuleSeverities diagnostic_severities_;
};
/// CompoundStatement is the base class of statements that can hold other
diff --git a/src/tint/transform/add_block_attribute_test.cc b/src/tint/transform/add_block_attribute_test.cc
index e4519dd..74dd5a9 100644
--- a/src/tint/transform/add_block_attribute_test.cc
+++ b/src/tint/transform/add_block_attribute_test.cc
@@ -143,7 +143,7 @@
}
)";
auto* expect = R"(
-type Numbers = array<vec4<f32>, 4u>;
+alias Numbers = array<vec4<f32>, 4u>;
@internal(block)
struct u_block {
@@ -718,13 +718,13 @@
f : f32,
}
-type MyInner = Inner;
+alias MyInner = Inner;
struct Outer {
i : MyInner,
}
-type MyOuter = Outer;
+alias MyOuter = Outer;
@internal(block)
struct u0_block {
@@ -792,7 +792,7 @@
@group(0) @binding(1) var<uniform> u1 : u1_block;
-type MyInner = Inner;
+alias MyInner = Inner;
@internal(block)
struct u0_block {
@@ -801,7 +801,7 @@
@group(0) @binding(0) var<uniform> u0 : u0_block;
-type MyOuter = Outer;
+alias MyOuter = Outer;
struct Outer {
i : MyInner,
diff --git a/src/tint/transform/canonicalize_entry_point_io_test.cc b/src/tint/transform/canonicalize_entry_point_io_test.cc
index 3fc0033..5af8a14 100644
--- a/src/tint/transform/canonicalize_entry_point_io_test.cc
+++ b/src/tint/transform/canonicalize_entry_point_io_test.cc
@@ -173,7 +173,7 @@
)";
auto* expect = R"(
-type myf32 = f32;
+alias myf32 = f32;
struct tint_symbol_1 {
@location(1)
@@ -222,7 +222,7 @@
frag_main_inner(tint_symbol.loc1);
}
-type myf32 = f32;
+alias myf32 = f32;
)";
DataMap data;
@@ -1589,7 +1589,7 @@
)";
auto* expect = R"(
-type myf32 = f32;
+alias myf32 = f32;
struct FragmentInput {
col1 : myf32,
@@ -1601,9 +1601,9 @@
col2 : myf32,
}
-type MyFragmentInput = FragmentInput;
+alias MyFragmentInput = FragmentInput;
-type MyFragmentOutput = FragmentOutput;
+alias MyFragmentOutput = FragmentOutput;
fn foo(x : MyFragmentInput) -> myf32 {
return x.col1;
@@ -1703,9 +1703,9 @@
return wrapper_result;
}
-type MyFragmentInput = FragmentInput;
+alias MyFragmentInput = FragmentInput;
-type MyFragmentOutput = FragmentOutput;
+alias MyFragmentOutput = FragmentOutput;
fn foo(x : MyFragmentInput) -> myf32 {
return x.col1;
@@ -1721,7 +1721,7 @@
col2 : myf32,
}
-type myf32 = f32;
+alias myf32 = f32;
)";
DataMap data;
diff --git a/src/tint/transform/combine_samplers_test.cc b/src/tint/transform/combine_samplers_test.cc
index cad3109..e400ed5 100644
--- a/src/tint/transform/combine_samplers_test.cc
+++ b/src/tint/transform/combine_samplers_test.cc
@@ -247,7 +247,7 @@
}
)";
auto* expect = R"(
-type Tex2d = texture_2d<f32>;
+alias Tex2d = texture_2d<f32>;
@group(0) @binding(0) @internal(disable_validation__binding_point_collision) var placeholder_sampler : sampler;
@@ -297,7 +297,7 @@
return textureSample(t_s_1, placeholder_sampler, coords);
}
-type Tex2d = texture_2d<f32>;
+alias Tex2d = texture_2d<f32>;
)";
DataMap data;
diff --git a/src/tint/transform/decompose_memory_access.cc b/src/tint/transform/decompose_memory_access.cc
index cbef564..abb78b2 100644
--- a/src/tint/transform/decompose_memory_access.cc
+++ b/src/tint/transform/decompose_memory_access.cc
@@ -111,8 +111,8 @@
struct LoadStoreKey {
type::AddressSpace const address_space; // buffer address space
type::Access const access; // buffer access
- type::Type const* buf_ty = nullptr; // buffer type
- type::Type const* el_ty = nullptr; // element type
+ type::Type const* buf_ty = nullptr; // buffer type
+ type::Type const* el_ty = nullptr; // element type
bool operator==(const LoadStoreKey& rhs) const {
return address_space == rhs.address_space && access == rhs.access && buf_ty == rhs.buf_ty &&
el_ty == rhs.el_ty;
diff --git a/src/tint/transform/decompose_memory_access_test.cc b/src/tint/transform/decompose_memory_access_test.cc
index 4c2cd11..b58d340 100644
--- a/src/tint/transform/decompose_memory_access_test.cc
+++ b/src/tint/transform/decompose_memory_access_test.cc
@@ -3439,9 +3439,9 @@
c : i32,
}
-type A1 = S1;
+alias A1 = S1;
-type A1_Array = array<S1, 3>;
+alias A1_Array = array<S1, 3>;
struct S2 {
a : i32,
@@ -3449,9 +3449,9 @@
c : i32,
}
-type A2 = S2;
+alias A2 = S2;
-type A2_Array = array<S2>;
+alias A2_Array = array<S2>;
struct SB {
@size(128)
@@ -3537,9 +3537,9 @@
b : A2_Array,
}
-type A2_Array = array<S2>;
+alias A2_Array = array<S2>;
-type A2 = S2;
+alias A2 = S2;
struct S2 {
a : i32,
@@ -3547,9 +3547,9 @@
c : i32,
}
-type A1 = S1;
+alias A1 = S1;
-type A1_Array = array<S1, 3>;
+alias A1_Array = array<S1, 3>;
struct S1 {
a : i32,
diff --git a/src/tint/transform/decompose_strided_array_test.cc b/src/tint/transform/decompose_strided_array_test.cc
index 3befa65..abc01f2 100644
--- a/src/tint/transform/decompose_strided_array_test.cc
+++ b/src/tint/transform/decompose_strided_array_test.cc
@@ -533,7 +533,7 @@
el : f32,
}
-type ARR = array<strided_arr, 4u>;
+alias ARR = array<strided_arr, 4u>;
struct S {
a : ARR,
@@ -626,14 +626,14 @@
el : f32,
}
-type ARR_A = array<strided_arr, 2u>;
+alias ARR_A = array<strided_arr, 2u>;
struct strided_arr_1 {
@size(128)
el : array<ARR_A, 3u>,
}
-type ARR_B = array<strided_arr_1, 4u>;
+alias ARR_B = array<strided_arr_1, 4u>;
struct S {
a : ARR_B,
diff --git a/src/tint/transform/demote_to_helper_test.cc b/src/tint/transform/demote_to_helper_test.cc
index 943da0d..3753360 100644
--- a/src/tint/transform/demote_to_helper_test.cc
+++ b/src/tint/transform/demote_to_helper_test.cc
@@ -542,10 +542,10 @@
@fragment
fn foo_no_discard(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+ let ret = bar_no_discard(in, coord);
if (in == 0.0) {
return;
}
- let ret = bar_no_discard(in, coord);
v2 = ret;
}
)";
@@ -591,10 +591,10 @@
@fragment
fn foo_no_discard(@location(0) in : f32, @location(1) coord : vec2<f32>) {
+ let ret = bar_no_discard(in, coord);
if ((in == 0.0)) {
return;
}
- let ret = bar_no_discard(in, coord);
v2 = ret;
}
)";
@@ -992,8 +992,9 @@
}
var result = 0;
for (var i = 0; i < 10; i = atomicAdd(&a, 1)) {
- result += i32(textureSample(t, s, coord).x);
+ result += i;
}
+ result += i32(textureSample(t, s, coord).x);
return result;
}
)";
@@ -1020,7 +1021,7 @@
break;
}
{
- result += i32(textureSample(t, s, coord).x);
+ result += i;
}
continuing {
@@ -1032,6 +1033,7 @@
}
}
}
+ result += i32(textureSample(t, s, coord).x);
if (tint_discarded) {
discard;
}
@@ -1060,8 +1062,9 @@
var result = 0;
if (!atomicCompareExchangeWeak(&a, i32(in), 42).exchanged) {
let xchg = atomicCompareExchangeWeak(&a, i32(in), 42);
- result = i32(textureSample(t, s, coord).x) * xchg.old_value;
+ result = xchg.old_value;
}
+ result += i32(textureSample(t, s, coord).x);
return result;
}
)";
@@ -1100,8 +1103,9 @@
tint_symbol_3.exchanged = tint_symbol_4.exchanged;
}
let xchg = tint_symbol_3;
- result = (i32(textureSample(t, s, coord).x) * xchg.old_value);
+ result = xchg.old_value;
}
+ result += i32(textureSample(t, s, coord).x);
if (tint_discarded) {
discard;
}
diff --git a/src/tint/transform/direct_variable_access_test.cc b/src/tint/transform/direct_variable_access_test.cc
index 4c112d4..71e7c94 100644
--- a/src/tint/transform/direct_variable_access_test.cc
+++ b/src/tint/transform/direct_variable_access_test.cc
@@ -242,7 +242,7 @@
@group(0) @binding(0) var<uniform> U : array<array<array<vec4<i32>, 8>, 8>, 8>;
-type U_X_X_X = array<u32, 3u>;
+alias U_X_X_X = array<u32, 3u>;
fn a_U_X_X_X(pre : i32, p : U_X_X_X, post : i32) -> vec4<i32> {
return U[p[0]][p[1]][p[2]];
@@ -339,7 +339,7 @@
return i;
}
-type U_X_X_X = array<u32, 3u>;
+alias U_X_X_X = array<u32, 3u>;
fn a_U_X_X_X(pre : i32, p : U_X_X_X, post : i32) -> vec4<i32> {
return U[p[0]][p[1]][p[2]];
@@ -429,7 +429,7 @@
return i;
}
-type U_X_X = array<u32, 2u>;
+alias U_X_X = array<u32, 2u>;
fn a_U_X_X(pre : i32, p : U_X_X, post : i32) -> vec4<i32> {
return U[p[0]][p[1]];
@@ -515,7 +515,7 @@
return i;
}
-type U_X_X = array<u32, 2u>;
+alias U_X_X = array<u32, 2u>;
fn a_U_X_X(pre : i32, p : U_X_X, post : i32) -> vec4<i32> {
return U[p[0]][p[1]];
@@ -605,7 +605,7 @@
return i;
}
-type U_X_X = array<u32, 2u>;
+alias U_X_X = array<u32, 2u>;
fn a_U_X_X(pre : i32, p : U_X_X, post : i32) -> vec4<i32> {
return U[p[0]][p[1]];
@@ -695,7 +695,7 @@
return i;
}
-type U_X_X = array<u32, 2u>;
+alias U_X_X = array<u32, 2u>;
fn a_U_X_X(pre : i32, p : U_X_X, post : i32) -> vec4<i32> {
return U[p[0]][p[1]];
@@ -793,7 +793,7 @@
@group(0) @binding(0) var<uniform> U : array<vec4<i32>, 8>;
-type U_X = array<u32, 1u>;
+alias U_X = array<u32, 1u>;
fn a_U_X(pre : i32, p : U_X, post : i32) -> vec4<i32> {
return U[p[0]];
@@ -880,7 +880,7 @@
mat : mat3x4<f32>,
}
-type InnerArr = array<Inner, 4>;
+alias InnerArr = array<Inner, 4>;
struct Outer {
arr : InnerArr,
@@ -889,19 +889,19 @@
@group(0) @binding(0) var<uniform> U : Outer;
-type U_mat_X = array<u32, 1u>;
+alias U_mat_X = array<u32, 1u>;
fn f0_U_mat_X(p : U_mat_X) -> f32 {
return U.mat[p[0]].x;
}
-type U_arr_X_mat_X = array<u32, 2u>;
+alias U_arr_X_mat_X = array<u32, 2u>;
fn f0_U_arr_X_mat_X(p : U_arr_X_mat_X) -> f32 {
return U.arr[p[0]].mat[p[0]].x;
}
-type U_arr_X_mat_X_1 = array<u32, 2u>;
+alias U_arr_X_mat_X_1 = array<u32, 2u>;
fn f0_U_arr_X_mat_X_1(p : U_arr_X_mat_X_1) -> f32 {
return U.arr[p[0]].mat[p[1]].x;
@@ -926,7 +926,7 @@
return res;
}
-type U_arr_X_mat = array<u32, 1u>;
+alias U_arr_X_mat = array<u32, 1u>;
fn f1_U_arr_X_mat(p : U_arr_X_mat) -> f32 {
var res : f32;
@@ -947,7 +947,7 @@
return res;
}
-type U_arr_X = array<u32, 1u>;
+alias U_arr_X = array<u32, 1u>;
fn f2_U_arr_X(p : U_arr_X) -> f32 {
let p_mat = &(U.arr[p[0]].mat);
@@ -1087,7 +1087,7 @@
@group(0) @binding(0) var<storage, read_write> S : array<vec4<i32>, 8>;
-type S_X = array<u32, 1u>;
+alias S_X = array<u32, 1u>;
fn a_S_X(pre : i32, p : S_X, post : i32) {
S[p[0]] = vec4<i32>();
@@ -1174,7 +1174,7 @@
mat : mat3x4<f32>,
}
-type InnerArr = array<Inner, 4>;
+alias InnerArr = array<Inner, 4>;
struct Outer {
arr : InnerArr,
@@ -1183,19 +1183,19 @@
@group(0) @binding(0) var<storage> S : Outer;
-type S_mat_X = array<u32, 1u>;
+alias S_mat_X = array<u32, 1u>;
fn f0_S_mat_X(p : S_mat_X) -> f32 {
return S.mat[p[0]].x;
}
-type S_arr_X_mat_X = array<u32, 2u>;
+alias S_arr_X_mat_X = array<u32, 2u>;
fn f0_S_arr_X_mat_X(p : S_arr_X_mat_X) -> f32 {
return S.arr[p[0]].mat[p[0]].x;
}
-type S_arr_X_mat_X_1 = array<u32, 2u>;
+alias S_arr_X_mat_X_1 = array<u32, 2u>;
fn f0_S_arr_X_mat_X_1(p : S_arr_X_mat_X_1) -> f32 {
return S.arr[p[0]].mat[p[1]].x;
@@ -1220,7 +1220,7 @@
return res;
}
-type S_arr_X_mat = array<u32, 1u>;
+alias S_arr_X_mat = array<u32, 1u>;
fn f1_S_arr_X_mat(p : S_arr_X_mat) -> f32 {
var res : f32;
@@ -1241,7 +1241,7 @@
return res;
}
-type S_arr_X = array<u32, 1u>;
+alias S_arr_X = array<u32, 1u>;
fn f2_S_arr_X(p : S_arr_X) -> f32 {
let p_mat = &(S.arr[p[0]].mat);
@@ -1296,7 +1296,7 @@
var<workgroup> W : array<vec4<i32>, 8>;
-type W_X = array<u32, 1u>;
+alias W_X = array<u32, 1u>;
fn a_W_X(pre : i32, p : W_X, post : i32) -> vec4<i32> {
return W[p[0]];
@@ -1332,7 +1332,7 @@
var<workgroup> W : array<vec4<i32>, 8>;
-type W_X = array<u32, 1u>;
+alias W_X = array<u32, 1u>;
fn a_W_X(pre : i32, p : W_X, post : i32) {
W[p[0]] = vec4<i32>();
@@ -1418,7 +1418,7 @@
mat : mat3x4<f32>,
}
-type InnerArr = array<Inner, 4>;
+alias InnerArr = array<Inner, 4>;
struct Outer {
arr : InnerArr,
@@ -1427,19 +1427,19 @@
var<workgroup> W : Outer;
-type W_mat_X = array<u32, 1u>;
+alias W_mat_X = array<u32, 1u>;
fn f0_W_mat_X(p : W_mat_X) -> f32 {
return W.mat[p[0]].x;
}
-type W_arr_X_mat_X = array<u32, 2u>;
+alias W_arr_X_mat_X = array<u32, 2u>;
fn f0_W_arr_X_mat_X(p : W_arr_X_mat_X) -> f32 {
return W.arr[p[0]].mat[p[0]].x;
}
-type W_arr_X_mat_X_1 = array<u32, 2u>;
+alias W_arr_X_mat_X_1 = array<u32, 2u>;
fn f0_W_arr_X_mat_X_1(p : W_arr_X_mat_X_1) -> f32 {
return W.arr[p[0]].mat[p[1]].x;
@@ -1464,7 +1464,7 @@
return res;
}
-type W_arr_X_mat = array<u32, 1u>;
+alias W_arr_X_mat = array<u32, 1u>;
fn f1_W_arr_X_mat(p : W_arr_X_mat) -> f32 {
var res : f32;
@@ -1485,7 +1485,7 @@
return res;
}
-type W_arr_X = array<u32, 1u>;
+alias W_arr_X = array<u32, 1u>;
fn f2_W_arr_X(p : W_arr_X) -> f32 {
let p_mat = &(W.arr[p[0]].mat);
@@ -1762,7 +1762,7 @@
return (*(p)).i;
}
-type F_X = array<u32, 1u>;
+alias F_X = array<u32, 1u>;
fn a_F_X(pre : i32, p_base : ptr<private, array<i32, 4u>>, p_indices : F_X, post : i32) -> i32 {
return (*(p_base))[p_indices[0]];
@@ -1774,7 +1774,7 @@
var<private> Pa : array<i32, 4>;
-type F_X_1 = array<u32, 1u>;
+alias F_X_1 = array<u32, 1u>;
fn b() {
a_F(10, &(Pi), 20);
@@ -1890,7 +1890,7 @@
mat : mat3x4<f32>,
}
-type InnerArr = array<Inner, 4>;
+alias InnerArr = array<Inner, 4>;
struct Outer {
arr : InnerArr,
@@ -1899,27 +1899,27 @@
var<private> P : Outer;
-type F_mat_X = array<u32, 1u>;
+alias F_mat_X = array<u32, 1u>;
fn f0_F_mat_X(p_base : ptr<private, Outer>, p_indices : F_mat_X) -> f32 {
return (*(p_base)).mat[p_indices[0]].x;
}
-type F_arr_X_mat_X = array<u32, 2u>;
+alias F_arr_X_mat_X = array<u32, 2u>;
fn f0_F_arr_X_mat_X(p_base : ptr<private, Outer>, p_indices : F_arr_X_mat_X) -> f32 {
return (*(p_base)).arr[p_indices[0]].mat[p_indices[0]].x;
}
-type F_arr_X_mat_X_1 = array<u32, 2u>;
+alias F_arr_X_mat_X_1 = array<u32, 2u>;
fn f0_F_arr_X_mat_X_1(p_base : ptr<private, Outer>, p_indices : F_arr_X_mat_X_1) -> f32 {
return (*(p_base)).arr[p_indices[0]].mat[p_indices[1]].x;
}
-type F_mat_X_1 = array<u32, 1u>;
+alias F_mat_X_1 = array<u32, 1u>;
-type F_arr_X_mat_X_2 = array<u32, 2u>;
+alias F_arr_X_mat_X_2 = array<u32, 2u>;
fn f1_F_mat(p : ptr<private, Outer>) -> f32 {
var res : f32;
@@ -1940,9 +1940,9 @@
return res;
}
-type F_arr_X_mat = array<u32, 1u>;
+alias F_arr_X_mat = array<u32, 1u>;
-type F_arr_X_mat_X_3 = array<u32, 2u>;
+alias F_arr_X_mat_X_3 = array<u32, 2u>;
fn f1_F_arr_X_mat(p_base : ptr<private, Outer>, p_indices : F_arr_X_mat) -> f32 {
var res : f32;
@@ -1963,16 +1963,16 @@
return res;
}
-type F_arr_X = array<u32, 1u>;
+alias F_arr_X = array<u32, 1u>;
-type F_arr_X_mat_1 = array<u32, 1u>;
+alias F_arr_X_mat_1 = array<u32, 1u>;
fn f2_F_arr_X(p_base : ptr<private, Outer>, p_indices : F_arr_X) -> f32 {
let p_mat = &((*(p_base)).arr[p_indices[0]].mat);
return f1_F_arr_X_mat(p_base, F_arr_X_mat_1(p_indices[0u]));
}
-type F_arr_X_1 = array<u32, 1u>;
+alias F_arr_X_1 = array<u32, 1u>;
fn f3_F_arr_F_mat(p0 : ptr<private, Outer>, p1 : ptr<private, Outer>) -> f32 {
let p0_inner = &((*(p0)).arr[3]);
@@ -2001,7 +2001,7 @@
mat : mat3x4<f32>,
}
-type InnerArr = array<Inner, 4>;
+alias InnerArr = array<Inner, 4>;
struct Outer {
arr : InnerArr,
@@ -2268,13 +2268,13 @@
return (*(p)).i;
}
-type F_X = array<u32, 1u>;
+alias F_X = array<u32, 1u>;
fn a_F_X(pre : i32, p_base : ptr<function, array<i32, 4u>>, p_indices : F_X, post : i32) -> i32 {
return (*(p_base))[p_indices[0]];
}
-type F_X_1 = array<u32, 1u>;
+alias F_X_1 = array<u32, 1u>;
fn b() {
var Fi : i32;
@@ -2460,13 +2460,13 @@
return U_str.i;
}
-type U_arr_X = array<u32, 1u>;
+alias U_arr_X = array<u32, 1u>;
fn fn_u_U_arr_X(p : U_arr_X) -> vec4<i32> {
return U_arr[p[0]];
}
-type U_arr_arr_X_X = array<u32, 2u>;
+alias U_arr_arr_X_X = array<u32, 2u>;
fn fn_u_U_arr_arr_X_X(p : U_arr_arr_X_X) -> vec4<i32> {
return U_arr_arr[p[0]][p[1]];
@@ -2480,13 +2480,13 @@
return S_str.i;
}
-type S_arr_X = array<u32, 1u>;
+alias S_arr_X = array<u32, 1u>;
fn fn_s_S_arr_X(p : S_arr_X) -> vec4<i32> {
return S_arr[p[0]];
}
-type S_arr_arr_X_X = array<u32, 2u>;
+alias S_arr_arr_X_X = array<u32, 2u>;
fn fn_s_S_arr_arr_X_X(p : S_arr_arr_X_X) -> vec4<i32> {
return S_arr_arr[p[0]][p[1]];
@@ -2500,13 +2500,13 @@
return W_str.i;
}
-type W_arr_X = array<u32, 1u>;
+alias W_arr_X = array<u32, 1u>;
fn fn_w_W_arr_X(p : W_arr_X) -> vec4<i32> {
return W_arr[p[0]];
}
-type W_arr_arr_X_X = array<u32, 2u>;
+alias W_arr_arr_X_X = array<u32, 2u>;
fn fn_w_W_arr_arr_X_X(p : W_arr_arr_X_X) -> vec4<i32> {
return W_arr_arr[p[0]][p[1]];
@@ -2578,7 +2578,7 @@
return i;
}
-type S_X = array<u32, 1u>;
+alias S_X = array<u32, 1u>;
fn b_S_X(p : S_X) -> i32 {
return S[p[0]][a(S[p[0]][0][1][2])][a(S[p[0]][a(3)][4][5])][a(S[p[0]][6][a(7)][8])];
@@ -2620,13 +2620,13 @@
@group(0) @binding(0) var<storage> S : array<array<array<array<i32, 9>, 9>, 9>, 50>;
-type S_X_X_X_X = array<u32, 4u>;
+alias S_X_X_X_X = array<u32, 4u>;
fn a_S_X_X_X_X(pre : i32, i : S_X_X_X_X, post : i32) -> i32 {
return S[i[0]][i[0]][i[1]][i[2]];
}
-type S_X = array<u32, 1u>;
+alias S_X = array<u32, 1u>;
fn b_S_X(p : S_X) -> i32 {
return a_S_X_X_X_X(10, S_X_X_X_X(p[0u], u32(a_S_X_X_X_X(20, S_X_X_X_X(p[0u], 0, 1, 2), 30)), u32(a_S_X_X_X_X(40, S_X_X_X_X(p[0u], 3, 4, 5), 50)), u32(a_S_X_X_X_X(60, S_X_X_X_X(p[0u], 6, 7, 8), 70))), 80);
@@ -2673,9 +2673,9 @@
return i;
}
-type S_X = array<u32, 1u>;
+alias S_X = array<u32, 1u>;
-type U_X = array<u32, 1u>;
+alias U_X = array<u32, 1u>;
fn b_S_X_U_X(s : S_X, u : U_X) -> i32 {
return S[s[0]][a(U[u[0]][0][1].x)][a(U[u[0]][a(3)][4].y)];
diff --git a/src/tint/transform/module_scope_var_to_entry_point_param_test.cc b/src/tint/transform/module_scope_var_to_entry_point_param_test.cc
index 7ae15be..999761d 100644
--- a/src/tint/transform/module_scope_var_to_entry_point_param_test.cc
+++ b/src/tint/transform/module_scope_var_to_entry_point_param_test.cc
@@ -621,7 +621,7 @@
arr : array<f32>,
}
-type myarray = array<f32>;
+alias myarray = array<f32>;
@compute @workgroup_size(1)
fn main(@group(0) @binding(0) @internal(disable_validation__entry_point_parameter) @internal(disable_validation__ignore_address_space) tint_symbol : ptr<storage, tint_symbol_1>) {
@@ -656,7 +656,7 @@
_ = (*(tint_symbol)).arr[0];
}
-type myarray = array<f32>;
+alias myarray = array<f32>;
)";
auto got = Run<ModuleScopeVarToEntryPointParam>(src);
diff --git a/src/tint/transform/multiplanar_external_texture_test.cc b/src/tint/transform/multiplanar_external_texture_test.cc
index c76e3e3..af1d0b4 100644
--- a/src/tint/transform/multiplanar_external_texture_test.cc
+++ b/src/tint/transform/multiplanar_external_texture_test.cc
@@ -1657,7 +1657,7 @@
@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
-type ET = texture_external;
+alias ET = texture_external;
fn gammaCorrection(v : vec3<f32>, params : GammaTransferParams) -> vec3<f32> {
let cond = (abs(v) < vec3<f32>(params.D));
@@ -1795,7 +1795,7 @@
@group(0) @binding(1) var smp : sampler;
-type ET = texture_external;
+alias ET = texture_external;
)";
DataMap data;
data.Add<MultiplanarExternalTexture::NewBindingPoints>(MultiplanarExternalTexture::BindingsMap{
diff --git a/src/tint/transform/preserve_padding_test.cc b/src/tint/transform/preserve_padding_test.cc
index f47cb3e..854b4a5 100644
--- a/src/tint/transform/preserve_padding_test.cc
+++ b/src/tint/transform/preserve_padding_test.cc
@@ -386,7 +386,7 @@
auto* expect = R"(
enable chromium_experimental_full_ptr_parameters;
-type Array = array<array<vec3<u32>, 4>, 3>;
+alias Array = array<array<vec3<u32>, 4>, 3>;
@group(0) @binding(0) var<storage, read_write> v : Array;
diff --git a/src/tint/transform/promote_initializers_to_let_test.cc b/src/tint/transform/promote_initializers_to_let_test.cc
index dd72671..32e2ec9 100644
--- a/src/tint/transform/promote_initializers_to_let_test.cc
+++ b/src/tint/transform/promote_initializers_to_let_test.cc
@@ -1194,7 +1194,7 @@
TEST_F(PromoteInitializersToLetTest, NoChangeOnVarDecl) {
auto* src = R"(
-type F = f32;
+alias F = f32;
fn f() {
var local_arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
@@ -1222,7 +1222,7 @@
const module_str : F = F(2.0);
-type F = f32;
+alias F = f32;
const module_arr : array<f32, 4u> = array<f32, 4u>(0.0, 1.0, 2.0, 3.0);
)";
diff --git a/src/tint/transform/renamer_test.cc b/src/tint/transform/renamer_test.cc
index 0628063..152489a 100644
--- a/src/tint/transform/renamer_test.cc
+++ b/src/tint/transform/renamer_test.cc
@@ -1673,7 +1673,7 @@
)");
auto expect = expand(R"(
-type tint_symbol = i32;
+alias tint_symbol = i32;
@fragment
fn tint_symbol_1() {
diff --git a/src/tint/transform/single_entry_point_test.cc b/src/tint/transform/single_entry_point_test.cc
index b225008..b8f79f6 100644
--- a/src/tint/transform/single_entry_point_test.cc
+++ b/src/tint/transform/single_entry_point_test.cc
@@ -389,7 +389,7 @@
@id(5) override c5 : u32 = (2 * c4);
-type arr_ty = array<i32, (2 * c5)>;
+alias arr_ty = array<i32, (2 * c5)>;
var<workgroup> arr : arr_ty;
@@ -593,7 +593,7 @@
auto* src = R"(
const MY_SIZE = 5u;
-type Arr = array<i32, MY_SIZE>;
+alias Arr = array<i32, MY_SIZE>;
@fragment
fn main() {
diff --git a/src/tint/transform/spirv_atomic_test.cc b/src/tint/transform/spirv_atomic_test.cc
index 36312f7..bef061c 100644
--- a/src/tint/transform/spirv_atomic_test.cc
+++ b/src/tint/transform/spirv_atomic_test.cc
@@ -226,13 +226,13 @@
)";
auto* expect = R"(
-type A0 = u32;
+alias A0 = u32;
-type A1 = array<A0, 1>;
+alias A1 = array<A0, 1>;
-type A2 = array<A1, 2>;
+alias A2 = array<A1, 2>;
-type A3 = array<A2, 3>;
+alias A3 = array<A2, 3>;
var<workgroup> wg : array<array<array<atomic<u32>, 1u>, 2u>, 3u>;
diff --git a/src/tint/transform/unshadow_test.cc b/src/tint/transform/unshadow_test.cc
index d2ee8a5..ec17ff5 100644
--- a/src/tint/transform/unshadow_test.cc
+++ b/src/tint/transform/unshadow_test.cc
@@ -66,7 +66,7 @@
)";
auto* expect = R"(
-type a = i32;
+alias a = i32;
fn X() {
var a_1 = false;
@@ -116,7 +116,7 @@
const a_3 = true;
}
-type a = i32;
+alias a = i32;
)";
auto got = Run<Unshadow>(src);
@@ -704,7 +704,7 @@
)";
auto* expect = R"(
-type a = i32;
+alias a = i32;
fn F(a_1 : a) {
{
@@ -745,7 +745,7 @@
}
}
-type a = i32;
+alias a = i32;
)";
auto got = Run<Unshadow>(src);
diff --git a/src/tint/utils/math.h b/src/tint/utils/math.h
index 27dd232..8bbbcc8 100644
--- a/src/tint/utils/math.h
+++ b/src/tint/utils/math.h
@@ -66,10 +66,10 @@
#endif
// Non intrinsic (slow) path. Supports constexpr evaluation.
- for (size_t clz = 0; clz < 64; clz++) {
- size_t bit = 63 - clz;
- if (value & (static_cast<size_t>(1u) << bit)) {
- return bit;
+ for (uint64_t clz = 0; clz < 64; clz++) {
+ uint64_t bit = 63 - clz;
+ if (value & (static_cast<uint64_t>(1u) << bit)) {
+ return static_cast<uint32_t>(bit);
}
}
return 64;
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index 8bd4033..df49694 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -250,7 +250,7 @@
auto* mod = builder_.Sem().Module();
for (auto* decl : mod->DependencyOrderedDeclarations()) {
- if (decl->IsAnyOf<ast::Alias, ast::StaticAssert>()) {
+ if (decl->IsAnyOf<ast::Alias, ast::ConstAssert, ast::DiagnosticControl>()) {
continue; // These are not emitted.
}
@@ -2796,7 +2796,7 @@
return false;
});
},
- [&](const ast::StaticAssert*) {
+ [&](const ast::ConstAssert*) {
return true; // Not emitted
},
[&](Default) {
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index 82237db..bde9d70 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -310,7 +310,7 @@
auto* mod = builder_.Sem().Module();
for (auto* decl : mod->DependencyOrderedDeclarations()) {
- if (decl->IsAnyOf<ast::Alias, ast::Enable, ast::StaticAssert>()) {
+ if (decl->IsAnyOf<ast::Alias, ast::DiagnosticControl, ast::Enable, ast::ConstAssert>()) {
continue; // These are not emitted.
}
@@ -3842,7 +3842,7 @@
return false;
});
},
- [&](const ast::StaticAssert*) {
+ [&](const ast::ConstAssert*) {
return true; // Not emitted
},
[&](Default) { //
diff --git a/src/tint/writer/hlsl/generator_impl_static_assert_test.cc b/src/tint/writer/hlsl/generator_impl_const_assert_test.cc
similarity index 78%
rename from src/tint/writer/hlsl/generator_impl_static_assert_test.cc
rename to src/tint/writer/hlsl/generator_impl_const_assert_test.cc
index 8ae5dd8..eac90c6 100644
--- a/src/tint/writer/hlsl/generator_impl_static_assert_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_const_assert_test.cc
@@ -21,23 +21,23 @@
using HlslGeneratorImplTest = TestHelper;
-TEST_F(HlslGeneratorImplTest, Emit_GlobalStaticAssert) {
- GlobalStaticAssert(true);
+TEST_F(HlslGeneratorImplTest, Emit_GlobalConstAssert) {
+ GlobalConstAssert(true);
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.Generate()) << gen.error();
- // static asserts are not emitted
+ // const asserts are not emitted
EXPECT_EQ(gen.result(), "");
}
-TEST_F(HlslGeneratorImplTest, Emit_FunctionStaticAssert) {
- Func("f", utils::Empty, ty.void_(), utils::Vector{StaticAssert(true)});
+TEST_F(HlslGeneratorImplTest, Emit_FunctionConstAssert) {
+ Func("f", utils::Empty, ty.void_(), utils::Vector{ConstAssert(true)});
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.Generate()) << gen.error();
- // static asserts are not emitted
+ // const asserts are not emitted
EXPECT_EQ(gen.result(), R"(void f() {
}
)");
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index 251483e..29ce69f 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -313,11 +313,15 @@
}
return EmitFunction(func);
},
+ [&](const ast::DiagnosticControl*) {
+ // Do nothing for diagnostic directives in MSL
+ return true;
+ },
[&](const ast::Enable*) {
// Do nothing for enabling extension in MSL
return true;
},
- [&](const ast::StaticAssert*) {
+ [&](const ast::ConstAssert*) {
return true; // Not emitted
},
[&](Default) {
@@ -2469,7 +2473,7 @@
return false;
});
},
- [&](const ast::StaticAssert*) {
+ [&](const ast::ConstAssert*) {
return true; // Not emitted
},
[&](Default) {
diff --git a/src/tint/writer/msl/generator_impl_static_assert_test.cc b/src/tint/writer/msl/generator_impl_const_assert_test.cc
similarity index 79%
rename from src/tint/writer/msl/generator_impl_static_assert_test.cc
rename to src/tint/writer/msl/generator_impl_const_assert_test.cc
index 5b8ca3f..a9850eb 100644
--- a/src/tint/writer/msl/generator_impl_static_assert_test.cc
+++ b/src/tint/writer/msl/generator_impl_const_assert_test.cc
@@ -21,26 +21,26 @@
using MslGeneratorImplTest = TestHelper;
-TEST_F(MslGeneratorImplTest, Emit_GlobalStaticAssert) {
- GlobalStaticAssert(true);
+TEST_F(MslGeneratorImplTest, Emit_GlobalConstAssert) {
+ GlobalConstAssert(true);
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.Generate()) << gen.error();
- // static asserts are not emitted
+ // const asserts are not emitted
EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
using namespace metal;
)");
}
-TEST_F(MslGeneratorImplTest, Emit_FunctionStaticAssert) {
- Func("f", utils::Empty, ty.void_(), utils::Vector{StaticAssert(true)});
+TEST_F(MslGeneratorImplTest, Emit_FunctionConstAssert) {
+ Func("f", utils::Empty, ty.void_(), utils::Vector{ConstAssert(true)});
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.Generate()) << gen.error();
- // static asserts are not emitted
+ // const asserts are not emitted
EXPECT_EQ(gen.result(), R"(#include <metal_stdlib>
using namespace metal;
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index 9df433b..39297ae 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -3614,7 +3614,7 @@
[&](const ast::ReturnStatement* r) { return GenerateReturnStatement(r); },
[&](const ast::SwitchStatement* s) { return GenerateSwitchStatement(s); },
[&](const ast::VariableDeclStatement* v) { return GenerateVariableDeclStatement(v); },
- [&](const ast::StaticAssert*) {
+ [&](const ast::ConstAssert*) {
return true; // Not emitted
},
[&](Default) {
diff --git a/src/tint/writer/spirv/builder_static_assert_test.cc b/src/tint/writer/spirv/builder_const_assert_test.cc
similarity index 82%
rename from src/tint/writer/spirv/builder_static_assert_test.cc
rename to src/tint/writer/spirv/builder_const_assert_test.cc
index 6e5092d..bca6deb 100644
--- a/src/tint/writer/spirv/builder_static_assert_test.cc
+++ b/src/tint/writer/spirv/builder_const_assert_test.cc
@@ -22,26 +22,26 @@
using BuilderTest = TestHelper;
-TEST_F(BuilderTest, GlobalStaticAssert) {
- GlobalStaticAssert(true);
+TEST_F(BuilderTest, GlobalConstAssert) {
+ GlobalConstAssert(true);
spirv::Builder& b = Build();
ASSERT_TRUE(b.Build()) << b.error();
- // static asserts are not emitted
+ // const asserts are not emitted
EXPECT_EQ(DumpInstructions(b.types()), "");
EXPECT_EQ(b.functions().size(), 0u);
}
-TEST_F(BuilderTest, FunctionStaticAssert) {
- Func("f", utils::Empty, ty.void_(), utils::Vector{StaticAssert(true)});
+TEST_F(BuilderTest, FunctionConstAssert) {
+ Func("f", utils::Empty, ty.void_(), utils::Vector{ConstAssert(true)});
spirv::Builder& b = Build();
ASSERT_TRUE(b.Build()) << b.error();
- // static asserts are not emitted
+ // const asserts are not emitted
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
)");
diff --git a/src/tint/writer/wgsl/generator_impl.cc b/src/tint/writer/wgsl/generator_impl.cc
index 0c20af7..bb91d93 100644
--- a/src/tint/writer/wgsl/generator_impl.cc
+++ b/src/tint/writer/wgsl/generator_impl.cc
@@ -63,18 +63,28 @@
GeneratorImpl::~GeneratorImpl() = default;
bool GeneratorImpl::Generate() {
- // Generate enable directives before any other global declarations.
+ // Generate directives before any other global declarations.
+ bool has_directives = false;
for (auto enable : program_->AST().Enables()) {
if (!EmitEnable(enable)) {
return false;
}
+ has_directives = true;
}
- if (!program_->AST().Enables().IsEmpty()) {
+ for (auto diagnostic : program_->AST().DiagnosticControls()) {
+ auto out = line();
+ if (!EmitDiagnosticControl(out, diagnostic)) {
+ return false;
+ }
+ out << ";";
+ has_directives = true;
+ }
+ if (has_directives) {
line();
}
// Generate global declarations in the order they appear in the module.
for (auto* decl : program_->AST().GlobalDeclarations()) {
- if (decl->Is<ast::Enable>()) {
+ if (decl->IsAnyOf<ast::DiagnosticControl, ast::Enable>()) {
continue;
}
if (!Switch(
@@ -82,7 +92,7 @@
[&](const ast::TypeDecl* td) { return EmitTypeDecl(td); },
[&](const ast::Function* func) { return EmitFunction(func); },
[&](const ast::Variable* var) { return EmitVariable(line(), var); },
- [&](const ast::StaticAssert* sa) { return EmitStaticAssert(sa); },
+ [&](const ast::ConstAssert* ca) { return EmitConstAssert(ca); },
[&](Default) {
TINT_UNREACHABLE(Writer, diagnostics_);
return false;
@@ -97,6 +107,13 @@
return true;
}
+bool GeneratorImpl::EmitDiagnosticControl(std::ostream& out,
+ const ast::DiagnosticControl* diagnostic) {
+ out << "diagnostic(" << diagnostic->severity << ", "
+ << program_->Symbols().NameFor(diagnostic->rule_name->symbol) << ")";
+ return true;
+}
+
bool GeneratorImpl::EmitEnable(const ast::Enable* enable) {
auto out = line();
out << "enable " << enable->extension << ";";
@@ -108,7 +125,7 @@
ty,
[&](const ast::Alias* alias) { //
auto out = line();
- out << "type " << program_->Symbols().NameFor(alias->name) << " = ";
+ out << "alias " << program_->Symbols().NameFor(alias->name) << " = ";
if (!EmitType(out, alias->type)) {
return false;
}
@@ -780,6 +797,9 @@
out << "builtin(" << builtin->builtin << ")";
return true;
},
+ [&](const ast::DiagnosticAttribute* diagnostic) {
+ return EmitDiagnosticControl(out, diagnostic->control);
+ },
[&](const ast::InterpolateAttribute* interpolate) {
out << "interpolate(" << interpolate->type;
if (interpolate->sampling != ast::InterpolationSampling::kUndefined) {
@@ -992,7 +1012,7 @@
[&](const ast::ForLoopStatement* l) { return EmitForLoop(l); },
[&](const ast::WhileStatement* l) { return EmitWhile(l); },
[&](const ast::ReturnStatement* r) { return EmitReturn(r); },
- [&](const ast::StaticAssert* s) { return EmitStaticAssert(s); },
+ [&](const ast::ConstAssert* c) { return EmitConstAssert(c); },
[&](const ast::SwitchStatement* s) { return EmitSwitch(s); },
[&](const ast::VariableDeclStatement* v) { return EmitVariable(line(), v->variable); },
[&](Default) {
@@ -1299,7 +1319,7 @@
return true;
}
-bool GeneratorImpl::EmitStaticAssert(const ast::StaticAssert* stmt) {
+bool GeneratorImpl::EmitConstAssert(const ast::ConstAssert* stmt) {
auto out = line();
out << "static_assert ";
if (!EmitExpression(out, stmt->condition)) {
diff --git a/src/tint/writer/wgsl/generator_impl.h b/src/tint/writer/wgsl/generator_impl.h
index f314efb..87a1b54 100644
--- a/src/tint/writer/wgsl/generator_impl.h
+++ b/src/tint/writer/wgsl/generator_impl.h
@@ -52,7 +52,12 @@
/// @returns true on successful generation; false otherwise
bool Generate();
- /// Handles generating a enable directive
+ /// Handles generating a diagnostic control
+ /// @param out the output of the expression stream
+ /// @param diagnostic the diagnostic control node
+ /// @returns true if the diagnostic control was emitted
+ bool EmitDiagnosticControl(std::ostream& out, const ast::DiagnosticControl* diagnostic);
+ /// Handles generating an enable directive
/// @param enable the enable node
/// @returns true if the enable directive was emitted
bool EmitEnable(const ast::Enable* enable);
@@ -165,10 +170,10 @@
/// @param stmt the statement to emit
/// @returns true if the statement was successfully emitted
bool EmitReturn(const ast::ReturnStatement* stmt);
- /// Handles static assertion statements
+ /// Handles const assertion statements
/// @param stmt the statement to emit
/// @returns true if the statement was successfully emitted
- bool EmitStaticAssert(const ast::StaticAssert* stmt);
+ bool EmitConstAssert(const ast::ConstAssert* stmt);
/// Handles statement
/// @param stmt the statement to emit
/// @returns true if the statement was emitted
diff --git a/src/tint/writer/wgsl/generator_impl_alias_type_test.cc b/src/tint/writer/wgsl/generator_impl_alias_type_test.cc
index dd0584d..10a56b1 100644
--- a/src/tint/writer/wgsl/generator_impl_alias_type_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_alias_type_test.cc
@@ -25,7 +25,7 @@
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.EmitTypeDecl(alias)) << gen.error();
- EXPECT_EQ(gen.result(), R"(type a = f32;
+ EXPECT_EQ(gen.result(), R"(alias a = f32;
)");
}
@@ -45,7 +45,7 @@
a : f32,
b : i32,
}
-type B = A;
+alias B = A;
)");
}
@@ -60,7 +60,7 @@
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.EmitTypeDecl(alias)) << gen.error();
- EXPECT_EQ(gen.result(), R"(type B = A;
+ EXPECT_EQ(gen.result(), R"(alias B = A;
)");
}
diff --git a/src/tint/writer/wgsl/generator_impl_static_assert_test.cc b/src/tint/writer/wgsl/generator_impl_const_assert_test.cc
similarity index 83%
rename from src/tint/writer/wgsl/generator_impl_static_assert_test.cc
rename to src/tint/writer/wgsl/generator_impl_const_assert_test.cc
index 9e7a1c0..9f76d65 100644
--- a/src/tint/writer/wgsl/generator_impl_static_assert_test.cc
+++ b/src/tint/writer/wgsl/generator_impl_const_assert_test.cc
@@ -21,8 +21,8 @@
using WgslGeneratorImplTest = TestHelper;
-TEST_F(WgslGeneratorImplTest, Emit_GlobalStaticAssert) {
- GlobalStaticAssert(true);
+TEST_F(WgslGeneratorImplTest, Emit_GlobalConstAssert) {
+ GlobalConstAssert(true);
GeneratorImpl& gen = Build();
@@ -31,8 +31,8 @@
)");
}
-TEST_F(WgslGeneratorImplTest, Emit_FunctionStaticAssert) {
- Func("f", utils::Empty, ty.void_(), utils::Vector{StaticAssert(true)});
+TEST_F(WgslGeneratorImplTest, Emit_FunctionConstAssert) {
+ Func("f", utils::Empty, ty.void_(), utils::Vector{ConstAssert(true)});
GeneratorImpl& gen = Build();
diff --git a/src/tint/writer/wgsl/generator_impl_diagnostic_test.cc b/src/tint/writer/wgsl/generator_impl_diagnostic_test.cc
new file mode 100644
index 0000000..d9ce01c
--- /dev/null
+++ b/src/tint/writer/wgsl/generator_impl_diagnostic_test.cc
@@ -0,0 +1,48 @@
+// 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/writer/wgsl/test_helper.h"
+
+namespace tint::writer::wgsl {
+namespace {
+
+using WgslGeneratorImplTest = TestHelper;
+
+TEST_F(WgslGeneratorImplTest, Emit_DiagnosticDirective) {
+ DiagnosticDirective(ast::DiagnosticSeverity::kError, Expr("chromium_unreachable_code"));
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate());
+ EXPECT_EQ(gen.result(), R"(diagnostic(error, chromium_unreachable_code);
+
+)");
+}
+
+TEST_F(WgslGeneratorImplTest, Emit_DiagnosticAttribute) {
+ auto* attr =
+ DiagnosticAttribute(ast::DiagnosticSeverity::kError, Expr("chromium_unreachable_code"));
+ Func("foo", {}, ty.void_(), {}, utils::Vector{attr});
+
+ GeneratorImpl& gen = Build();
+
+ ASSERT_TRUE(gen.Generate());
+ EXPECT_EQ(gen.result(), R"(@diagnostic(error, chromium_unreachable_code)
+fn foo() {
+}
+)");
+}
+
+} // namespace
+} // namespace tint::writer::wgsl