Import Tint changes from Dawn
Changes:
- 52cd8caa07ced53d9908c4d8b11f861b27895038 tint/hlsl: fix frexp always returning positive values for... by Antonio Maiorano <amaiorano@google.com>
- 8cf01eef04141772cd4de3fcf9d32a0572cd1518 tint: Support @diagnostic on switch body by James Price <jrprice@google.com>
- 498e91826e94b4b5fcdd8082cbdde7e77aec3b13 tint: Support @diagnostic on continuing statements by James Price <jrprice@google.com>
- d7d8b80c8131bb0707f00c76f1d5efdbb7a7ea92 tint: Support @diagnostic on loop and loop body by James Price <jrprice@google.com>
GitOrigin-RevId: 52cd8caa07ced53d9908c4d8b11f861b27895038
Change-Id: I9d1ec36e15744d2ff3d33ed0a697a7dca62682dd
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/124423
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/ast/loop_statement.cc b/src/tint/ast/loop_statement.cc
index 731dee7..cd946cb 100644
--- a/src/tint/ast/loop_statement.cc
+++ b/src/tint/ast/loop_statement.cc
@@ -14,6 +14,8 @@
#include "src/tint/ast/loop_statement.h"
+#include <utility>
+
#include "src/tint/program_builder.h"
TINT_INSTANTIATE_TYPEINFO(tint::ast::LoopStatement);
@@ -24,11 +26,16 @@
NodeID nid,
const Source& src,
const BlockStatement* b,
- const BlockStatement* cont)
- : Base(pid, nid, src), body(b), continuing(cont) {
+ const BlockStatement* cont,
+ utils::VectorRef<const ast::Attribute*> attrs)
+ : Base(pid, nid, src), body(b), continuing(cont), attributes(std::move(attrs)) {
TINT_ASSERT(AST, body);
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, body, program_id);
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, continuing, program_id);
+ for (auto* attr : attributes) {
+ TINT_ASSERT(AST, attr);
+ TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, attr, program_id);
+ }
}
LoopStatement::~LoopStatement() = default;
@@ -38,7 +45,8 @@
auto src = ctx->Clone(source);
auto* b = ctx->Clone(body);
auto* cont = ctx->Clone(continuing);
- return ctx->dst->create<LoopStatement>(src, b, cont);
+ auto attrs = ctx->Clone(attributes);
+ return ctx->dst->create<LoopStatement>(src, b, cont, std::move(attrs));
}
} // namespace tint::ast
diff --git a/src/tint/ast/loop_statement.h b/src/tint/ast/loop_statement.h
index c1e5cc0..fc764e2 100644
--- a/src/tint/ast/loop_statement.h
+++ b/src/tint/ast/loop_statement.h
@@ -28,12 +28,13 @@
/// @param source the loop statement source
/// @param body the body statements
/// @param continuing the continuing statements
+ /// @param attributes the while statement attributes
LoopStatement(ProgramID pid,
NodeID nid,
const Source& source,
const BlockStatement* body,
- const BlockStatement* continuing);
-
+ const BlockStatement* continuing,
+ utils::VectorRef<const ast::Attribute*> attributes);
/// Destructor
~LoopStatement() override;
@@ -48,6 +49,9 @@
/// The continuing statements
const BlockStatement* const continuing;
+
+ /// The attribute list
+ const utils::Vector<const Attribute*, 1> attributes;
};
} // namespace tint::ast
diff --git a/src/tint/ast/loop_statement_test.cc b/src/tint/ast/loop_statement_test.cc
index caa995d..e0fc0f1 100644
--- a/src/tint/ast/loop_statement_test.cc
+++ b/src/tint/ast/loop_statement_test.cc
@@ -14,6 +14,7 @@
#include "src/tint/ast/loop_statement.h"
+#include "gmock/gmock.h"
#include "gtest/gtest-spi.h"
#include "src/tint/ast/discard_statement.h"
#include "src/tint/ast/if_statement.h"
@@ -30,7 +31,7 @@
auto* continuing = Block(create<DiscardStatement>());
- auto* l = create<LoopStatement>(body, continuing);
+ auto* l = create<LoopStatement>(body, continuing, utils::Empty);
ASSERT_EQ(l->body->statements.Length(), 1u);
EXPECT_EQ(l->body->statements[0], b);
ASSERT_EQ(l->continuing->statements.Length(), 1u);
@@ -42,21 +43,32 @@
auto* continuing = Block(create<DiscardStatement>());
- auto* l = create<LoopStatement>(Source{Source::Location{20, 2}}, body, continuing);
+ auto* l =
+ create<LoopStatement>(Source{Source::Location{20, 2}}, body, continuing, utils::Empty);
auto src = l->source;
EXPECT_EQ(src.range.begin.line, 20u);
EXPECT_EQ(src.range.begin.column, 2u);
}
+TEST_F(LoopStatementTest, Creation_WithAttributes) {
+ auto* attr1 = DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "foo");
+ auto* attr2 = DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "bar");
+
+ auto* body = Block(Return());
+ auto* l = create<LoopStatement>(body, nullptr, utils::Vector{attr1, attr2});
+
+ EXPECT_THAT(l->attributes, testing::ElementsAre(attr1, attr2));
+}
+
TEST_F(LoopStatementTest, IsLoop) {
- auto* l = create<LoopStatement>(Block(), Block());
+ auto* l = create<LoopStatement>(Block(), Block(), utils::Empty);
EXPECT_TRUE(l->Is<LoopStatement>());
}
TEST_F(LoopStatementTest, HasContinuing_WithoutContinuing) {
auto* body = Block(create<DiscardStatement>());
- auto* l = create<LoopStatement>(body, nullptr);
+ auto* l = create<LoopStatement>(body, nullptr, utils::Empty);
EXPECT_FALSE(l->continuing);
}
@@ -65,7 +77,7 @@
auto* continuing = Block(create<DiscardStatement>());
- auto* l = create<LoopStatement>(body, continuing);
+ auto* l = create<LoopStatement>(body, continuing, utils::Empty);
EXPECT_TRUE(l->continuing);
}
@@ -73,7 +85,7 @@
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
- b.create<LoopStatement>(nullptr, nullptr);
+ b.create<LoopStatement>(nullptr, nullptr, utils::Empty);
},
"internal compiler error");
}
@@ -83,7 +95,7 @@
{
ProgramBuilder b1;
ProgramBuilder b2;
- b1.create<LoopStatement>(b2.Block(), b1.Block());
+ b1.create<LoopStatement>(b2.Block(), b1.Block(), utils::Empty);
},
"internal compiler error");
}
@@ -93,7 +105,7 @@
{
ProgramBuilder b1;
ProgramBuilder b2;
- b1.create<LoopStatement>(b1.Block(), b2.Block());
+ b1.create<LoopStatement>(b1.Block(), b2.Block(), utils::Empty);
},
"internal compiler error");
}
diff --git a/src/tint/ast/switch_statement.cc b/src/tint/ast/switch_statement.cc
index 4e5d95d..fa6fe07 100644
--- a/src/tint/ast/switch_statement.cc
+++ b/src/tint/ast/switch_statement.cc
@@ -27,8 +27,13 @@
const Source& src,
const Expression* cond,
utils::VectorRef<const CaseStatement*> b,
- utils::VectorRef<const Attribute*> attrs)
- : Base(pid, nid, src), condition(cond), body(std::move(b)), attributes(std::move(attrs)) {
+ utils::VectorRef<const Attribute*> stmt_attrs,
+ utils::VectorRef<const Attribute*> body_attrs)
+ : Base(pid, nid, src),
+ condition(cond),
+ body(std::move(b)),
+ attributes(std::move(stmt_attrs)),
+ body_attributes(std::move(body_attrs)) {
TINT_ASSERT(AST, condition);
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, condition, program_id);
for (auto* stmt : body) {
@@ -39,6 +44,10 @@
TINT_ASSERT(AST, attr);
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, attr, program_id);
}
+ for (auto* attr : body_attributes) {
+ TINT_ASSERT(AST, attr);
+ TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, attr, program_id);
+ }
}
SwitchStatement::~SwitchStatement() = default;
@@ -49,7 +58,9 @@
auto* cond = ctx->Clone(condition);
auto b = ctx->Clone(body);
auto attrs = ctx->Clone(attributes);
- return ctx->dst->create<SwitchStatement>(src, cond, std::move(b), std::move(attrs));
+ auto body_attrs = ctx->Clone(body_attributes);
+ return ctx->dst->create<SwitchStatement>(src, cond, std::move(b), std::move(attrs),
+ std::move(body_attrs));
}
} // namespace tint::ast
diff --git a/src/tint/ast/switch_statement.h b/src/tint/ast/switch_statement.h
index 354b6ad..f2b8201 100644
--- a/src/tint/ast/switch_statement.h
+++ b/src/tint/ast/switch_statement.h
@@ -29,13 +29,15 @@
/// @param src the source of this node
/// @param condition the switch condition
/// @param body the switch body
- /// @param attributes the switch statement attributes
+ /// @param stmt_attributes the switch statement attributes
+ /// @param body_attributes the switch body attributes
SwitchStatement(ProgramID pid,
NodeID nid,
const Source& src,
const Expression* condition,
utils::VectorRef<const CaseStatement*> body,
- utils::VectorRef<const Attribute*> attributes);
+ utils::VectorRef<const Attribute*> stmt_attributes,
+ utils::VectorRef<const Attribute*> body_attributes);
/// Destructor
~SwitchStatement() override;
@@ -53,8 +55,11 @@
const utils::Vector<const CaseStatement*, 4> body;
SwitchStatement(const SwitchStatement&) = delete;
- /// The attribute list
+ /// The attribute list for the statement
const utils::Vector<const Attribute*, 1> attributes;
+
+ /// The attribute list for the body
+ const utils::Vector<const Attribute*, 1> body_attributes;
};
} // namespace tint::ast
diff --git a/src/tint/ast/switch_statement_test.cc b/src/tint/ast/switch_statement_test.cc
index aab2604..795220d 100644
--- a/src/tint/ast/switch_statement_test.cc
+++ b/src/tint/ast/switch_statement_test.cc
@@ -30,7 +30,7 @@
auto* ident = Expr("ident");
utils::Vector body{case_stmt};
- auto* stmt = create<SwitchStatement>(ident, body, utils::Empty);
+ auto* stmt = create<SwitchStatement>(ident, body, utils::Empty, utils::Empty);
EXPECT_EQ(stmt->condition, ident);
ASSERT_EQ(stmt->body.Length(), 1u);
EXPECT_EQ(stmt->body[0], case_stmt);
@@ -38,8 +38,8 @@
TEST_F(SwitchStatementTest, Creation_WithSource) {
auto* ident = Expr("ident");
- auto* stmt =
- create<SwitchStatement>(Source{Source::Location{20, 2}}, ident, utils::Empty, utils::Empty);
+ auto* stmt = create<SwitchStatement>(Source{Source::Location{20, 2}}, ident, utils::Empty,
+ utils::Empty, utils::Empty);
auto src = stmt->source;
EXPECT_EQ(src.range.begin.line, 20u);
EXPECT_EQ(src.range.begin.column, 2u);
@@ -49,17 +49,28 @@
auto* attr1 = DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "foo");
auto* attr2 = DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "bar");
auto* ident = Expr("ident");
- auto* stmt = create<SwitchStatement>(ident, utils::Empty, utils::Vector{attr1, attr2});
+ auto* stmt =
+ create<SwitchStatement>(ident, utils::Empty, utils::Vector{attr1, attr2}, utils::Empty);
EXPECT_THAT(stmt->attributes, testing::ElementsAre(attr1, attr2));
}
+TEST_F(SwitchStatementTest, Creation_WithBodyAttributes) {
+ auto* attr1 = DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "foo");
+ auto* attr2 = DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "bar");
+ auto* ident = Expr("ident");
+ auto* stmt =
+ create<SwitchStatement>(ident, utils::Empty, utils::Empty, utils::Vector{attr1, attr2});
+
+ EXPECT_THAT(stmt->body_attributes, testing::ElementsAre(attr1, attr2));
+}
+
TEST_F(SwitchStatementTest, IsSwitch) {
utils::Vector lit{CaseSelector(2_i)};
auto* ident = Expr("ident");
utils::Vector body{create<CaseStatement>(lit, Block())};
- auto* stmt = create<SwitchStatement>(ident, body, utils::Empty);
+ auto* stmt = create<SwitchStatement>(ident, body, utils::Empty, utils::Empty);
EXPECT_TRUE(stmt->Is<SwitchStatement>());
}
@@ -71,7 +82,7 @@
CaseStatementList cases;
cases.Push(
b.create<CaseStatement>(utils::Vector{b.CaseSelector(b.Expr(1_i))}, b.Block()));
- b.create<SwitchStatement>(nullptr, cases, utils::Empty);
+ b.create<SwitchStatement>(nullptr, cases, utils::Empty, utils::Empty);
},
"internal compiler error");
}
@@ -81,7 +92,8 @@
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
- b.create<SwitchStatement>(b.Expr(true), CaseStatementList{nullptr}, utils::Empty);
+ b.create<SwitchStatement>(b.Expr(true), CaseStatementList{nullptr}, utils::Empty,
+ utils::Empty);
},
"internal compiler error");
}
@@ -99,7 +111,7 @@
},
b1.Block()),
},
- utils::Empty);
+ utils::Empty, utils::Empty);
},
"internal compiler error");
}
@@ -117,7 +129,7 @@
},
b2.Block()),
},
- utils::Empty);
+ utils::Empty, utils::Empty);
},
"internal compiler error");
}
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index 31f566b..0c00452 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -3264,20 +3264,26 @@
/// @param source the source information
/// @param body the loop body
/// @param continuing the optional continuing block
+ /// @param attributes optional attributes
/// @returns the loop statement pointer
- const ast::LoopStatement* Loop(const Source& source,
- const ast::BlockStatement* body,
- const ast::BlockStatement* continuing = nullptr) {
- return create<ast::LoopStatement>(source, body, continuing);
+ const ast::LoopStatement* Loop(
+ const Source& source,
+ const ast::BlockStatement* body,
+ const ast::BlockStatement* continuing = nullptr,
+ utils::VectorRef<const ast::Attribute*> attributes = utils::Empty) {
+ return create<ast::LoopStatement>(source, body, continuing, std::move(attributes));
}
/// Creates a ast::LoopStatement with input body and optional continuing
/// @param body the loop body
/// @param continuing the optional continuing block
+ /// @param attributes optional attributes
/// @returns the loop statement pointer
- const ast::LoopStatement* Loop(const ast::BlockStatement* body,
- const ast::BlockStatement* continuing = nullptr) {
- return create<ast::LoopStatement>(body, continuing);
+ const ast::LoopStatement* Loop(
+ const ast::BlockStatement* body,
+ const ast::BlockStatement* continuing = nullptr,
+ utils::VectorRef<const ast::Attribute*> attributes = utils::Empty) {
+ return create<ast::LoopStatement>(body, continuing, std::move(attributes));
}
/// Creates a ast::ForLoopStatement with input body and optional initializer, condition,
@@ -3378,7 +3384,7 @@
source, Expr(std::forward<ExpressionInit>(condition)),
utils::Vector<const ast::CaseStatement*, sizeof...(cases)>{
std::forward<Cases>(cases)...},
- utils::Empty);
+ utils::Empty, utils::Empty);
}
/// Creates a ast::SwitchStatement with input expression and cases
@@ -3394,37 +3400,42 @@
Expr(std::forward<ExpressionInit>(condition)),
utils::Vector<const ast::CaseStatement*, sizeof...(cases)>{
std::forward<Cases>(cases)...},
- utils::Empty);
+ utils::Empty, utils::Empty);
}
/// Creates a ast::SwitchStatement with input expression, cases, and optional attributes
/// @param source the source information
/// @param condition the condition expression initializer
/// @param cases case statements
- /// @param attributes optional attributes
+ /// @param stmt_attributes optional statement attributes
+ /// @param body_attributes optional body attributes
/// @returns the switch statement pointer
template <typename ExpressionInit>
const ast::SwitchStatement* Switch(
const Source& source,
ExpressionInit&& condition,
utils::VectorRef<const ast::CaseStatement*> cases,
- utils::VectorRef<const ast::Attribute*> attributes = utils::Empty) {
+ utils::VectorRef<const ast::Attribute*> stmt_attributes = utils::Empty,
+ utils::VectorRef<const ast::Attribute*> body_attributes = utils::Empty) {
return create<ast::SwitchStatement>(source, Expr(std::forward<ExpressionInit>(condition)),
- cases, std::move(attributes));
+ cases, std::move(stmt_attributes),
+ std::move(body_attributes));
}
/// Creates a ast::SwitchStatement with input expression, cases, and optional attributes
/// @param condition the condition expression initializer
/// @param cases case statements
- /// @param attributes optional attributes
+ /// @param stmt_attributes optional statement attributes
+ /// @param body_attributes optional body attributes
/// @returns the switch statement pointer
template <typename ExpressionInit, typename = DisableIfSource<ExpressionInit>>
const ast::SwitchStatement* Switch(
ExpressionInit&& condition,
utils::VectorRef<const ast::CaseStatement*> cases,
- utils::VectorRef<const ast::Attribute*> attributes = utils::Empty) {
+ utils::VectorRef<const ast::Attribute*> stmt_attributes = utils::Empty,
+ utils::VectorRef<const ast::Attribute*> body_attributes = utils::Empty) {
return create<ast::SwitchStatement>(Expr(std::forward<ExpressionInit>(condition)), cases,
- std::move(attributes));
+ std::move(stmt_attributes), std::move(body_attributes));
}
/// Creates a ast::CaseStatement with input list of selectors, and body
diff --git a/src/tint/reader/spirv/function.cc b/src/tint/reader/spirv/function.cc
index ff0d721..e729a17 100644
--- a/src/tint/reader/spirv/function.cc
+++ b/src/tint/reader/spirv/function.cc
@@ -706,8 +706,7 @@
auto reversed_cases = cases;
std::reverse(reversed_cases.begin(), reversed_cases.end());
- return builder->create<ast::SwitchStatement>(Source{}, condition, std::move(reversed_cases),
- utils::Empty);
+ return builder->Switch(Source{}, condition, std::move(reversed_cases));
}
/// Switch statement condition
@@ -743,7 +742,7 @@
/// @param builder the program builder
/// @returns the built ast::LoopStatement
ast::LoopStatement* Build(ProgramBuilder* builder) const override {
- return builder->create<ast::LoopStatement>(Source{}, body, continuing);
+ return builder->create<ast::LoopStatement>(Source{}, body, continuing, utils::Empty);
}
/// Loop-statement block body
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index 38d9aa2..753f60b 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -1281,7 +1281,7 @@
return sw.value;
}
- auto loop = loop_statement();
+ auto loop = loop_statement(attrs.value);
if (loop.errored) {
return Failure::kErrored;
}
@@ -1620,6 +1620,11 @@
return add_error(peek(), "unable to parse selector expression");
}
+ auto body_attrs = attribute_list();
+ if (body_attrs.errored) {
+ return Failure::kErrored;
+ }
+
auto body = expect_brace_block("switch statement", [&]() -> Expect<CaseStatementList> {
bool errored = false;
CaseStatementList list;
@@ -1645,7 +1650,8 @@
}
TINT_DEFER(attrs.Clear());
- return create<ast::SwitchStatement>(source, condition.value, body.value, std::move(attrs));
+ return create<ast::SwitchStatement>(source, condition.value, body.value, std::move(attrs),
+ std::move(body_attrs.value));
}
// switch_body
@@ -1731,13 +1737,18 @@
}
// loop_statement
-// : LOOP BRACKET_LEFT statements continuing_statement? BRACKET_RIGHT
-Maybe<const ast::LoopStatement*> ParserImpl::loop_statement() {
+// : attribute* LOOP attribute* BRACKET_LEFT statements continuing_statement? BRACKET_RIGHT
+Maybe<const ast::LoopStatement*> ParserImpl::loop_statement(AttributeList& attrs) {
Source source;
if (!match(Token::Type::kLoop, &source)) {
return Failure::kNoMatch;
}
+ auto body_attrs = attribute_list();
+ if (body_attrs.errored) {
+ return Failure::kErrored;
+ }
+
Maybe<const ast::BlockStatement*> continuing(Failure::kErrored);
auto body_start = peek().source();
auto body = expect_brace_block("loop", [&]() -> Maybe<StatementList> {
@@ -1757,11 +1768,12 @@
}
auto body_end = last_source();
+ TINT_DEFER(attrs.Clear());
return create<ast::LoopStatement>(
source,
create<ast::BlockStatement>(Source::Combine(body_start, body_end), body.value,
- utils::Empty),
- continuing.value);
+ std::move(body_attrs.value)),
+ continuing.value, std::move(attrs));
}
ForHeader::ForHeader(const ast::Statement* init,
@@ -1969,8 +1981,13 @@
}
// continuing_compound_statement:
-// brace_left statement* break_if_statement? brace_right
+// attribute* BRACE_LEFT statement* break_if_statement? BRACE_RIGHT
Maybe<const ast::BlockStatement*> ParserImpl::continuing_compound_statement() {
+ auto attrs = attribute_list();
+ if (attrs.errored) {
+ return Failure::kErrored;
+ }
+
auto source_start = peek().source();
auto body = expect_brace_block("", [&]() -> Expect<StatementList> {
StatementList stmts;
@@ -2004,7 +2021,7 @@
auto source_end = last_source();
return create<ast::BlockStatement>(Source::Combine(source_start, source_end), body.value,
- utils::Empty);
+ std::move(attrs.value));
}
// continuing_statement
diff --git a/src/tint/reader/wgsl/parser_impl.h b/src/tint/reader/wgsl/parser_impl.h
index 7ad2e6f..9789b4d 100644
--- a/src/tint/reader/wgsl/parser_impl.h
+++ b/src/tint/reader/wgsl/parser_impl.h
@@ -512,9 +512,10 @@
/// Parses a `func_call_statement` grammar element
/// @returns the parsed function call or nullptr
Maybe<const ast::CallStatement*> func_call_statement();
- /// Parses a `loop_statement` grammar element
+ /// Parses a `loop_statement` grammar element, with the attribute list provided as `attrs`.
+ /// @param attrs the list of attributes for the statement
/// @returns the parsed loop or nullptr
- Maybe<const ast::LoopStatement*> loop_statement();
+ Maybe<const ast::LoopStatement*> loop_statement(AttributeList& attrs);
/// Parses a `for_header` grammar element, erroring on parse failure.
/// @returns the parsed for header or nullptr
Expect<std::unique_ptr<ForHeader>> expect_for_header();
diff --git a/src/tint/reader/wgsl/parser_impl_continuing_stmt_test.cc b/src/tint/reader/wgsl/parser_impl_continuing_stmt_test.cc
index d2113af..3354399 100644
--- a/src/tint/reader/wgsl/parser_impl_continuing_stmt_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_continuing_stmt_test.cc
@@ -28,6 +28,16 @@
ASSERT_TRUE(e->statements[0]->Is<ast::DiscardStatement>());
}
+TEST_F(ParserImplTest, ContinuingStmt_WithAttributes) {
+ auto p = parser("continuing @diagnostic(off, derivative_uniformity) { discard; }");
+ auto e = p->continuing_statement();
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ ASSERT_EQ(e->attributes.Length(), 1u);
+ EXPECT_TRUE(e->attributes[0]->Is<ast::DiagnosticAttribute>());
+}
+
TEST_F(ParserImplTest, ContinuingStmt_InvalidBody) {
auto p = parser("continuing { discard }");
auto e = p->continuing_statement();
diff --git a/src/tint/reader/wgsl/parser_impl_loop_stmt_test.cc b/src/tint/reader/wgsl/parser_impl_loop_stmt_test.cc
index 43cf219..c8ad2b4 100644
--- a/src/tint/reader/wgsl/parser_impl_loop_stmt_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_loop_stmt_test.cc
@@ -20,7 +20,8 @@
TEST_F(ParserImplTest, LoopStmt_BodyNoContinuing) {
auto p = parser("loop { discard; }");
- auto e = p->loop_statement();
+ ParserImpl::AttributeList attrs;
+ auto e = p->loop_statement(attrs);
EXPECT_TRUE(e.matched);
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
@@ -39,7 +40,8 @@
TEST_F(ParserImplTest, LoopStmt_BodyWithContinuing) {
auto p = parser("loop { discard; continuing { discard; }}");
- auto e = p->loop_statement();
+ ParserImpl::AttributeList attrs;
+ auto e = p->loop_statement(attrs);
EXPECT_TRUE(e.matched);
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
@@ -64,7 +66,8 @@
TEST_F(ParserImplTest, LoopStmt_NoBodyNoContinuing) {
auto p = parser("loop { }");
- auto e = p->loop_statement();
+ ParserImpl::AttributeList attrs;
+ auto e = p->loop_statement(attrs);
EXPECT_TRUE(e.matched);
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
@@ -75,7 +78,8 @@
TEST_F(ParserImplTest, LoopStmt_NoBodyWithContinuing) {
auto p = parser("loop { continuing { discard; }}");
- auto e = p->loop_statement();
+ ParserImpl::AttributeList attrs;
+ auto e = p->loop_statement(attrs);
EXPECT_TRUE(e.matched);
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
@@ -85,9 +89,35 @@
EXPECT_TRUE(e->continuing->statements[0]->Is<ast::DiscardStatement>());
}
+TEST_F(ParserImplTest, LoopStmt_StmtAttributes) {
+ auto p = parser("@diagnostic(off, derivative_uniformity) loop { }");
+ auto attrs = p->attribute_list();
+ auto l = p->loop_statement(attrs.value);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ EXPECT_FALSE(l.errored);
+ ASSERT_TRUE(l.matched);
+
+ EXPECT_TRUE(attrs->IsEmpty());
+ ASSERT_EQ(l->attributes.Length(), 1u);
+ EXPECT_TRUE(l->attributes[0]->Is<ast::DiagnosticAttribute>());
+}
+
+TEST_F(ParserImplTest, LoopStmt_BodyAttributes) {
+ auto p = parser("loop @diagnostic(off, derivative_uniformity) { }");
+ ParserImpl::AttributeList attrs;
+ auto e = p->loop_statement(attrs);
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(e.value, nullptr);
+ ASSERT_EQ(e->body->attributes.Length(), 1u);
+ EXPECT_TRUE(e->body->attributes[0]->Is<ast::DiagnosticAttribute>());
+}
+
TEST_F(ParserImplTest, LoopStmt_MissingBracketLeft) {
auto p = parser("loop discard; }");
- auto e = p->loop_statement();
+ ParserImpl::AttributeList attrs;
+ auto e = p->loop_statement(attrs);
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
@@ -97,7 +127,8 @@
TEST_F(ParserImplTest, LoopStmt_MissingBracketRight) {
auto p = parser("loop { discard; ");
- auto e = p->loop_statement();
+ ParserImpl::AttributeList attrs;
+ auto e = p->loop_statement(attrs);
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
@@ -107,7 +138,8 @@
TEST_F(ParserImplTest, LoopStmt_InvalidStatements) {
auto p = parser("loop { discard }");
- auto e = p->loop_statement();
+ ParserImpl::AttributeList attrs;
+ auto e = p->loop_statement(attrs);
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
@@ -117,7 +149,8 @@
TEST_F(ParserImplTest, LoopStmt_InvalidContinuing) {
auto p = parser("loop { continuing { discard }}");
- auto e = p->loop_statement();
+ ParserImpl::AttributeList attrs;
+ auto e = p->loop_statement(attrs);
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
@@ -127,7 +160,8 @@
TEST_F(ParserImplTest, LoopStmt_Continuing_BreakIf) {
auto p = parser("loop { continuing { break if 1 + 2 < 5; }}");
- auto e = p->loop_statement();
+ ParserImpl::AttributeList attrs;
+ auto e = p->loop_statement(attrs);
EXPECT_TRUE(e.matched);
EXPECT_FALSE(e.errored);
EXPECT_FALSE(p->has_error()) << p->error();
@@ -139,7 +173,8 @@
TEST_F(ParserImplTest, LoopStmt_Continuing_BreakIf_MissingExpr) {
auto p = parser("loop { continuing { break if; }}");
- auto e = p->loop_statement();
+ ParserImpl::AttributeList attrs;
+ auto e = p->loop_statement(attrs);
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_TRUE(p->has_error());
@@ -149,7 +184,8 @@
TEST_F(ParserImplTest, LoopStmt_Continuing_BreakIf_InvalidExpr) {
auto p = parser("loop { continuing { break if switch; }}");
- auto e = p->loop_statement();
+ ParserImpl::AttributeList attrs;
+ auto e = p->loop_statement(attrs);
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_TRUE(p->has_error());
@@ -159,7 +195,8 @@
TEST_F(ParserImplTest, LoopStmt_NoContinuing_BreakIf) {
auto p = parser("loop { break if true; }");
- auto e = p->loop_statement();
+ ParserImpl::AttributeList attrs;
+ auto e = p->loop_statement(attrs);
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_TRUE(p->has_error());
@@ -169,7 +206,8 @@
TEST_F(ParserImplTest, LoopStmt_Continuing_BreakIf_MissingSemicolon) {
auto p = parser("loop { continuing { break if 1 + 2 < 5 }}");
- auto e = p->loop_statement();
+ ParserImpl::AttributeList attrs;
+ auto e = p->loop_statement(attrs);
EXPECT_FALSE(e.matched);
EXPECT_TRUE(e.errored);
EXPECT_TRUE(p->has_error());
diff --git a/src/tint/reader/wgsl/parser_impl_statement_test.cc b/src/tint/reader/wgsl/parser_impl_statement_test.cc
index 8c920a7..9651fa0 100644
--- a/src/tint/reader/wgsl/parser_impl_statement_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_statement_test.cc
@@ -350,6 +350,18 @@
EXPECT_EQ(s->attributes.Length(), 1u);
}
+TEST_F(ParserImplTest, Statement_ConsumedAttributes_Loop) {
+ auto p = parser("@diagnostic(off, derivative_uniformity) loop {}");
+ auto e = p->statement();
+ ASSERT_FALSE(p->has_error()) << p->error();
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+
+ auto* s = As<ast::LoopStatement>(e.value);
+ ASSERT_NE(s, nullptr);
+ EXPECT_EQ(s->attributes.Length(), 1u);
+}
+
TEST_F(ParserImplTest, Statement_ConsumedAttributes_Switch) {
auto p = parser("@diagnostic(off, derivative_uniformity) switch (0) { default{} }");
auto e = p->statement();
diff --git a/src/tint/reader/wgsl/parser_impl_switch_stmt_test.cc b/src/tint/reader/wgsl/parser_impl_switch_stmt_test.cc
index ef0b1b4..89b4ba8 100644
--- a/src/tint/reader/wgsl/parser_impl_switch_stmt_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_switch_stmt_test.cc
@@ -95,7 +95,7 @@
}
TEST_F(ParserImplTest, SwitchStmt_WithAttributes) {
- auto p = parser(R"(@diagnostic(off, derivative_uniformity) switch a { default{} })");
+ auto p = parser("@diagnostic(off, derivative_uniformity) switch a { default{} }");
auto a = p->attribute_list();
auto e = p->switch_statement(a.value);
EXPECT_TRUE(e.matched);
@@ -109,6 +109,21 @@
EXPECT_TRUE(e->attributes[0]->Is<ast::DiagnosticAttribute>());
}
+TEST_F(ParserImplTest, SwitchStmt_WithBodyAttributes) {
+ auto p = parser("switch a @diagnostic(off, derivative_uniformity) { default{} }");
+ ParserImpl::AttributeList attrs;
+ auto e = p->switch_statement(attrs);
+ EXPECT_TRUE(e.matched);
+ EXPECT_FALSE(e.errored);
+ EXPECT_FALSE(p->has_error()) << p->error();
+ ASSERT_NE(e.value, nullptr);
+ ASSERT_TRUE(e->Is<ast::SwitchStatement>());
+
+ EXPECT_TRUE(e->attributes.IsEmpty());
+ ASSERT_EQ(e->body_attributes.Length(), 1u);
+ EXPECT_TRUE(e->body_attributes[0]->Is<ast::DiagnosticAttribute>());
+}
+
TEST_F(ParserImplTest, SwitchStmt_InvalidExpression) {
auto p = parser("switch a=b {}");
ParserImpl::AttributeList attrs;
diff --git a/src/tint/resolver/attribute_validation_test.cc b/src/tint/resolver/attribute_validation_test.cc
index 23f82a2..45dca0b 100644
--- a/src/tint/resolver/attribute_validation_test.cc
+++ b/src/tint/resolver/attribute_validation_test.cc
@@ -1078,6 +1078,39 @@
TestParams{AttributeKind::kWorkgroup, false},
TestParams{AttributeKind::kBindingAndGroup, false}));
+using SwitchBodyAttributeTest = TestWithParams;
+TEST_P(SwitchBodyAttributeTest, IsValid) {
+ auto& params = GetParam();
+
+ WrapInFunction(Switch(Expr(0_a), utils::Vector{DefaultCase()}, utils::Empty,
+ createAttributes(Source{{12, 34}}, *this, params.kind)));
+
+ if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for switch body");
+ }
+}
+INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
+ SwitchBodyAttributeTest,
+ testing::Values(TestParams{AttributeKind::kAlign, false},
+ TestParams{AttributeKind::kBinding, false},
+ TestParams{AttributeKind::kBuiltin, false},
+ TestParams{AttributeKind::kDiagnostic, true},
+ TestParams{AttributeKind::kGroup, false},
+ TestParams{AttributeKind::kId, false},
+ TestParams{AttributeKind::kInterpolate, false},
+ TestParams{AttributeKind::kInvariant, false},
+ TestParams{AttributeKind::kLocation, false},
+ TestParams{AttributeKind::kMustUse, false},
+ TestParams{AttributeKind::kOffset, false},
+ TestParams{AttributeKind::kSize, false},
+ TestParams{AttributeKind::kStage, false},
+ TestParams{AttributeKind::kStride, false},
+ TestParams{AttributeKind::kWorkgroup, false},
+ TestParams{AttributeKind::kBindingAndGroup, false}));
+
using IfStatementAttributeTest = TestWithParams;
TEST_P(IfStatementAttributeTest, IsValid) {
auto& params = GetParam();
@@ -1144,6 +1177,39 @@
TestParams{AttributeKind::kWorkgroup, false},
TestParams{AttributeKind::kBindingAndGroup, false}));
+using LoopStatementAttributeTest = TestWithParams;
+TEST_P(LoopStatementAttributeTest, IsValid) {
+ auto& params = GetParam();
+
+ WrapInFunction(
+ Loop(Block(Return()), Block(), createAttributes(Source{{12, 34}}, *this, params.kind)));
+
+ if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for loop statements");
+ }
+}
+INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
+ LoopStatementAttributeTest,
+ testing::Values(TestParams{AttributeKind::kAlign, false},
+ TestParams{AttributeKind::kBinding, false},
+ TestParams{AttributeKind::kBuiltin, false},
+ TestParams{AttributeKind::kDiagnostic, true},
+ TestParams{AttributeKind::kGroup, false},
+ TestParams{AttributeKind::kId, false},
+ TestParams{AttributeKind::kInterpolate, false},
+ TestParams{AttributeKind::kInvariant, false},
+ TestParams{AttributeKind::kLocation, false},
+ TestParams{AttributeKind::kMustUse, false},
+ TestParams{AttributeKind::kOffset, false},
+ TestParams{AttributeKind::kSize, false},
+ TestParams{AttributeKind::kStage, false},
+ TestParams{AttributeKind::kStride, false},
+ TestParams{AttributeKind::kWorkgroup, false},
+ TestParams{AttributeKind::kBindingAndGroup, false}));
+
using WhileStatementAttributeTest = TestWithParams;
TEST_P(WhileStatementAttributeTest, IsValid) {
auto& params = GetParam();
@@ -1225,6 +1291,13 @@
});
Check();
}
+TEST_P(BlockStatementTest, LoopStatementBody) {
+ Func("foo", utils::Empty, ty.void_(),
+ utils::Vector{
+ Loop(Block(utils::Vector{Break()}, createAttributes({}, *this, GetParam().kind))),
+ });
+ Check();
+}
TEST_P(BlockStatementTest, WhileStatementBody) {
Func("foo", utils::Empty, ty.void_(),
utils::Vector{
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 2cd40e9..fb8af76 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -3975,6 +3975,22 @@
return false;
}
+ // Handle switch body attributes.
+ for (auto* attr : stmt->body_attributes) {
+ Mark(attr);
+ if (auto* dc = attr->As<ast::DiagnosticAttribute>()) {
+ if (!DiagnosticControl(dc->control)) {
+ return false;
+ }
+ } else {
+ AddError("attribute is not valid for switch body", attr->source);
+ return false;
+ }
+ }
+ if (!validator_.NoDuplicateAttributes(stmt->body_attributes)) {
+ return false;
+ }
+
utils::Vector<sem::CaseStatement*, 4> cases;
cases.Reserve(stmt->body.Length());
for (auto* case_stmt : stmt->body) {
@@ -3986,6 +4002,8 @@
cases.Push(c);
behaviors.Add(c->Behaviors());
sem->Cases().emplace_back(c);
+
+ ApplyDiagnosticSeverities(c);
}
if (behaviors.Contains(sem::Behavior::kBreak)) {
@@ -4281,6 +4299,9 @@
return handle_attributes(f, sem, "for statements");
},
[&](const ast::IfStatement* i) { return handle_attributes(i, sem, "if statements"); },
+ [&](const ast::LoopStatement* l) {
+ return handle_attributes(l, sem, "loop statements");
+ },
[&](const ast::SwitchStatement* s) {
return handle_attributes(s, sem, "switch statements");
},
diff --git a/src/tint/resolver/uniformity_test.cc b/src/tint/resolver/uniformity_test.cc
index e166a06..0bf2436 100644
--- a/src/tint/resolver/uniformity_test.cc
+++ b/src/tint/resolver/uniformity_test.cc
@@ -8547,6 +8547,141 @@
}
}
+TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnLoopStatement_CallInBody) {
+ auto& param = GetParam();
+ utils::StringStream ss;
+ ss << R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+fn foo() {
+ )"
+ << "@diagnostic(" << param << ", derivative_uniformity)"
+ << R"(loop {
+ _ = dpdx(1.0);
+ continuing {
+ break if non_uniform == 0;
+ }
+ }
+}
+)";
+
+ RunTest(ss.str(), param != builtin::DiagnosticSeverity::kError);
+ if (param == builtin::DiagnosticSeverity::kOff) {
+ EXPECT_TRUE(error_.empty());
+ } else {
+ utils::StringStream err;
+ err << ToStr(param) << ": 'dpdx' must only be called";
+ EXPECT_THAT(error_, ::testing::HasSubstr(err.str()));
+ }
+}
+
+TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnLoopStatement_CallInContinuing) {
+ auto& param = GetParam();
+ utils::StringStream ss;
+ ss << R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+fn foo() {
+ )"
+ << "@diagnostic(" << param << ", derivative_uniformity)"
+ << R"(loop {
+ continuing {
+ _ = dpdx(1.0);
+ break if non_uniform == 0;
+ }
+ }
+}
+)";
+
+ RunTest(ss.str(), param != builtin::DiagnosticSeverity::kError);
+ if (param == builtin::DiagnosticSeverity::kOff) {
+ EXPECT_TRUE(error_.empty());
+ } else {
+ utils::StringStream err;
+ err << ToStr(param) << ": 'dpdx' must only be called";
+ EXPECT_THAT(error_, ::testing::HasSubstr(err.str()));
+ }
+}
+
+TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnLoopBody_CallInBody) {
+ auto& param = GetParam();
+ utils::StringStream ss;
+ ss << R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+fn foo() {
+ loop )"
+ << "@diagnostic(" << param << ", derivative_uniformity)"
+ << R"( {
+ _ = dpdx(1.0);
+ continuing {
+ break if non_uniform == 0;
+ }
+ }
+}
+)";
+
+ RunTest(ss.str(), param != builtin::DiagnosticSeverity::kError);
+ if (param == builtin::DiagnosticSeverity::kOff) {
+ EXPECT_TRUE(error_.empty());
+ } else {
+ utils::StringStream err;
+ err << ToStr(param) << ": 'dpdx' must only be called";
+ EXPECT_THAT(error_, ::testing::HasSubstr(err.str()));
+ }
+}
+
+TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnLoopBody_CallInContinuing) {
+ auto& param = GetParam();
+ utils::StringStream ss;
+ ss << R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+fn foo() {
+ loop )"
+ << "@diagnostic(" << param << ", derivative_uniformity)"
+ << R"( {
+ continuing {
+ _ = dpdx(1.0);
+ break if non_uniform == 0;
+ }
+ }
+}
+)";
+
+ RunTest(ss.str(), param != builtin::DiagnosticSeverity::kError);
+ if (param == builtin::DiagnosticSeverity::kOff) {
+ EXPECT_TRUE(error_.empty());
+ } else {
+ utils::StringStream err;
+ err << ToStr(param) << ": 'dpdx' must only be called";
+ EXPECT_THAT(error_, ::testing::HasSubstr(err.str()));
+ }
+}
+
+TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnLoopContinuing_CallInContinuing) {
+ auto& param = GetParam();
+ utils::StringStream ss;
+ ss << R"(
+@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
+fn foo() {
+ loop {
+ continuing )"
+ << "@diagnostic(" << param << ", derivative_uniformity)"
+ << R"( {
+ _ = dpdx(1.0);
+ break if non_uniform == 0;
+ }
+ }
+}
+)";
+
+ RunTest(ss.str(), param != builtin::DiagnosticSeverity::kError);
+ if (param == builtin::DiagnosticSeverity::kOff) {
+ EXPECT_TRUE(error_.empty());
+ } else {
+ utils::StringStream err;
+ err << ToStr(param) << ": 'dpdx' must only be called";
+ EXPECT_THAT(error_, ::testing::HasSubstr(err.str()));
+ }
+}
+
TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnSwitchStatement_CallInCondition) {
auto& param = GetParam();
utils::StringStream ss;
@@ -8599,6 +8734,34 @@
}
}
+TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnSwitchBody_CallInBody) {
+ auto& param = GetParam();
+ utils::StringStream 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;
+fn foo() {
+ switch (non_uniform))"
+ << "@diagnostic(" << param << ", derivative_uniformity)"
+ << R"( {
+ default {
+ let color = textureSample(t, s, vec2(0, 0));
+ }
+ }
+}
+)";
+
+ RunTest(ss.str(), param != builtin::DiagnosticSeverity::kError);
+ if (param == builtin::DiagnosticSeverity::kOff) {
+ EXPECT_TRUE(error_.empty());
+ } else {
+ utils::StringStream err;
+ err << ToStr(param) << ": 'textureSample' must only be called";
+ EXPECT_THAT(error_, ::testing::HasSubstr(err.str()));
+ }
+}
+
TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnWhileStatement_CallInCondition) {
auto& param = GetParam();
utils::StringStream ss;
diff --git a/src/tint/sem/diagnostic_severity_test.cc b/src/tint/sem/diagnostic_severity_test.cc
index a22b43e..9d36ea2 100644
--- a/src/tint/sem/diagnostic_severity_test.cc
+++ b/src/tint/sem/diagnostic_severity_test.cc
@@ -42,7 +42,7 @@
// return;
//
// @diagnostic(error, chromium_unreachable_code)
- // switch (42) {
+ // switch (42) @diagnostic(off, chromium_unreachable_code) {
// case 0 @diagnostic(warning, chromium_unreachable_code) {
// return;
// }
@@ -55,7 +55,14 @@
// for (var i = 0; false; i++) @diagnostic(warning, chromium_unreachable_code) {
// return;
// }
- // }
+ //
+ // @diagnostic(warning, chromium_unreachable_code)
+ // loop @diagnostic(off, chromium_unreachable_code) {
+ // return;
+ // continuing @diagnostic(info, chromium_unreachable_code) {
+ // break if true;
+ // }
+ // }
//
// @diagnostic(error, chromium_unreachable_code)
// while (false) @diagnostic(warning, chromium_unreachable_code) {
@@ -74,9 +81,13 @@
auto if_body_severity = builtin::DiagnosticSeverity::kWarning;
auto else_body_severity = builtin::DiagnosticSeverity::kInfo;
auto switch_severity = builtin::DiagnosticSeverity::kError;
+ auto switch_body_severity = builtin::DiagnosticSeverity::kOff;
auto case_severity = builtin::DiagnosticSeverity::kWarning;
auto for_severity = builtin::DiagnosticSeverity::kError;
auto for_body_severity = builtin::DiagnosticSeverity::kWarning;
+ auto loop_severity = builtin::DiagnosticSeverity::kWarning;
+ auto loop_body_severity = builtin::DiagnosticSeverity::kOff;
+ auto continuing_severity = builtin::DiagnosticSeverity::kInfo;
auto while_severity = builtin::DiagnosticSeverity::kError;
auto while_body_severity = builtin::DiagnosticSeverity::kWarning;
auto attr = [&](auto severity) {
@@ -90,22 +101,28 @@
auto* return_foo_case = Return();
auto* return_foo_default = Return();
auto* return_foo_for = Return();
+ auto* return_foo_loop = Return();
auto* return_foo_while = Return();
+ auto* breakif_foo_continuing = BreakIf(Expr(true));
auto* else_stmt = Block(utils::Vector{return_foo_else}, attr(else_body_severity));
auto* elseif = If(Expr(false), Block(return_foo_elseif), Else(else_stmt));
auto* if_foo = If(Expr(true), Block(utils::Vector{return_foo_if}, attr(if_body_severity)),
Else(elseif), attr(if_severity));
auto* case_stmt =
Case(CaseSelector(0_a), Block(utils::Vector{return_foo_case}, attr(case_severity)));
- auto* swtch = Switch(42_a, utils::Vector{case_stmt, DefaultCase(Block(return_foo_default))},
- attr(switch_severity));
+ auto* default_stmt = DefaultCase(Block(return_foo_default));
+ auto* swtch = Switch(42_a, utils::Vector{case_stmt, default_stmt}, attr(switch_severity),
+ attr(switch_body_severity));
auto* fl =
For(Decl(Var("i", ty.i32())), false, Increment("i"),
Block(utils::Vector{return_foo_for}, attr(for_body_severity)), attr(for_severity));
+ auto* l = Loop(Block(utils::Vector{return_foo_loop}, attr(loop_body_severity)),
+ Block(utils::Vector{breakif_foo_continuing}, attr(continuing_severity)),
+ attr(loop_severity));
auto* wl = While(false, Block(utils::Vector{return_foo_while}, attr(while_body_severity)),
attr(while_severity));
auto* block_1 =
- Block(utils::Vector{if_foo, return_foo_block, swtch, fl, wl}, attr(block_severity));
+ Block(utils::Vector{if_foo, return_foo_block, swtch, fl, l, wl}, attr(block_severity));
auto* func_attr = DiagnosticAttribute(func_severity, "chromium_unreachable_code");
auto* foo = Func("foo", {}, ty.void_(), utils::Vector{block_1}, utils::Vector{func_attr});
@@ -129,16 +146,22 @@
EXPECT_EQ(p.Sem().DiagnosticSeverity(return_foo_else, rule), else_body_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(swtch, rule), switch_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(swtch->condition, rule), switch_severity);
- EXPECT_EQ(p.Sem().DiagnosticSeverity(case_stmt, rule), switch_severity);
+ EXPECT_EQ(p.Sem().DiagnosticSeverity(case_stmt, rule), switch_body_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(case_stmt->body, rule), case_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(return_foo_case, rule), case_severity);
- EXPECT_EQ(p.Sem().DiagnosticSeverity(return_foo_default, rule), switch_severity);
+ EXPECT_EQ(p.Sem().DiagnosticSeverity(default_stmt, rule), switch_body_severity);
+ EXPECT_EQ(p.Sem().DiagnosticSeverity(return_foo_default, rule), switch_body_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(fl, rule), while_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(fl->initializer, rule), for_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(fl->condition, rule), for_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(fl->continuing, rule), for_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(fl->body, rule), for_body_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(return_foo_for, rule), for_body_severity);
+ EXPECT_EQ(p.Sem().DiagnosticSeverity(l, rule), loop_severity);
+ EXPECT_EQ(p.Sem().DiagnosticSeverity(l->body, rule), loop_body_severity);
+ EXPECT_EQ(p.Sem().DiagnosticSeverity(l->continuing, rule), continuing_severity);
+ EXPECT_EQ(p.Sem().DiagnosticSeverity(breakif_foo_continuing, rule), continuing_severity);
+ EXPECT_EQ(p.Sem().DiagnosticSeverity(return_foo_loop, rule), loop_body_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(wl, rule), while_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(wl->condition, rule), while_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(wl->body, rule), while_body_severity);
diff --git a/src/tint/writer/glsl/generator_impl_switch_test.cc b/src/tint/writer/glsl/generator_impl_switch_test.cc
index 8e2f739..ce978dc 100644
--- a/src/tint/writer/glsl/generator_impl_switch_test.cc
+++ b/src/tint/writer/glsl/generator_impl_switch_test.cc
@@ -31,7 +31,7 @@
auto* case_stmt = create<ast::CaseStatement>(utils::Vector{CaseSelector(5_i)}, case_body);
auto* cond = Expr("cond");
- auto* s = create<ast::SwitchStatement>(cond, utils::Vector{case_stmt, def}, utils::Empty);
+ auto* s = Switch(cond, utils::Vector{case_stmt, def});
WrapInFunction(s);
GeneratorImpl& gen = Build();
@@ -58,7 +58,7 @@
def_body);
auto* cond = Expr("cond");
- auto* s = create<ast::SwitchStatement>(cond, utils::Vector{def}, utils::Empty);
+ auto* s = Switch(cond, utils::Vector{def});
WrapInFunction(s);
GeneratorImpl& gen = Build();
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index cc8ad64..4910f81 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -2045,7 +2045,7 @@
}
line(b) << member_type << " exp;";
- line(b) << member_type << " fract = frexp(" << in << ", exp);";
+ line(b) << member_type << " fract = sign(" << in << ") * frexp(" << in << ", exp);";
{
auto l = line(b);
if (!EmitType(l, builtin->ReturnType(), builtin::AddressSpace::kUndefined,
diff --git a/src/tint/writer/hlsl/generator_impl_builtin_test.cc b/src/tint/writer/hlsl/generator_impl_builtin_test.cc
index f473ac8..d552671 100644
--- a/src/tint/writer/hlsl/generator_impl_builtin_test.cc
+++ b/src/tint/writer/hlsl/generator_impl_builtin_test.cc
@@ -606,7 +606,7 @@
};
frexp_result_f32 tint_frexp(float param_0) {
float exp;
- float fract = frexp(param_0, exp);
+ float fract = sign(param_0) * frexp(param_0, exp);
frexp_result_f32 result = {fract, int(exp)};
return result;
}
@@ -635,7 +635,7 @@
};
frexp_result_f16 tint_frexp(float16_t param_0) {
float16_t exp;
- float16_t fract = frexp(param_0, exp);
+ float16_t fract = sign(param_0) * frexp(param_0, exp);
frexp_result_f16 result = {fract, int(exp)};
return result;
}
@@ -662,7 +662,7 @@
};
frexp_result_vec3_f32 tint_frexp(float3 param_0) {
float3 exp;
- float3 fract = frexp(param_0, exp);
+ float3 fract = sign(param_0) * frexp(param_0, exp);
frexp_result_vec3_f32 result = {fract, int3(exp)};
return result;
}
@@ -691,7 +691,7 @@
};
frexp_result_vec3_f16 tint_frexp(vector<float16_t, 3> param_0) {
vector<float16_t, 3> exp;
- vector<float16_t, 3> fract = frexp(param_0, exp);
+ vector<float16_t, 3> fract = sign(param_0) * frexp(param_0, exp);
frexp_result_vec3_f16 result = {fract, int3(exp)};
return result;
}
diff --git a/src/tint/writer/wgsl/generator_impl.cc b/src/tint/writer/wgsl/generator_impl.cc
index 2b5f474..e1bf9fc 100644
--- a/src/tint/writer/wgsl/generator_impl.cc
+++ b/src/tint/writer/wgsl/generator_impl.cc
@@ -1025,7 +1025,21 @@
}
bool GeneratorImpl::EmitLoop(const ast::LoopStatement* stmt) {
- line() << "loop {";
+ {
+ auto out = line();
+
+ if (!stmt->attributes.IsEmpty()) {
+ if (!EmitAttributes(out, stmt->attributes)) {
+ return false;
+ }
+ out << " ";
+ }
+
+ out << "loop ";
+ if (!EmitBlockHeader(out, stmt->body)) {
+ return false;
+ }
+ }
increment_indent();
if (!EmitStatements(stmt->body->statements)) {
@@ -1034,7 +1048,17 @@
if (stmt->continuing && !stmt->continuing->Empty()) {
line();
- line() << "continuing {";
+ {
+ auto out = line();
+ out << "continuing ";
+ if (!stmt->continuing->attributes.IsEmpty()) {
+ if (!EmitAttributes(out, stmt->continuing->attributes)) {
+ return false;
+ }
+ out << " ";
+ }
+ out << "{";
+ }
if (!EmitStatementsWithIndent(stmt->continuing->statements)) {
return false;
}
@@ -1205,7 +1229,16 @@
if (!EmitExpression(out, stmt->condition)) {
return false;
}
- out << ") {";
+ out << ") ";
+
+ if (!stmt->body_attributes.IsEmpty()) {
+ if (!EmitAttributes(out, stmt->body_attributes)) {
+ return false;
+ }
+ out << " ";
+ }
+
+ out << "{";
}
{