tint: Handle @diagnostic on block statements

Use expect_compound_statement() in all the places that use
compound_statement in the WGSL grammar.

Handle attributes on statements inside Resolver::StatementScope, so
that the logic can be reused for the various places where block
statements are used. This will also make it easier to reuse this logic
when we allow these attributes on other types of statement in the
future.

Add an `EmitBlockHeader()` helper to the WGSL writer to reuse the
logic for emitting attributes on block statements for all the places
that use them.

Bug: tint:1809
Change-Id: Iac3bb01f5031e6134c1798ddafdad080412c8bef
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/118000
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: James Price <jrprice@google.com>
This commit is contained in:
James Price 2023-02-01 23:14:10 +00:00 committed by Dawn LUCI CQ
parent e60a579c19
commit d9f659670d
84 changed files with 2506 additions and 112 deletions

View File

@ -23,12 +23,17 @@ namespace tint::ast {
BlockStatement::BlockStatement(ProgramID pid, BlockStatement::BlockStatement(ProgramID pid,
NodeID nid, NodeID nid,
const Source& src, const Source& src,
utils::VectorRef<const Statement*> stmts) utils::VectorRef<const Statement*> stmts,
: Base(pid, nid, src), statements(std::move(stmts)) { utils::VectorRef<const Attribute*> attrs)
: Base(pid, nid, src), statements(std::move(stmts)), attributes(attrs) {
for (auto* stmt : statements) { for (auto* stmt : statements) {
TINT_ASSERT(AST, stmt); TINT_ASSERT(AST, stmt);
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, stmt, program_id); TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, stmt, program_id);
} }
for (auto* attr : attributes) {
TINT_ASSERT(AST, attr);
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, attr, program_id);
}
} }
BlockStatement::BlockStatement(BlockStatement&&) = default; BlockStatement::BlockStatement(BlockStatement&&) = default;
@ -39,7 +44,8 @@ const BlockStatement* BlockStatement::Clone(CloneContext* ctx) const {
// Clone arguments outside of create() call to have deterministic ordering // Clone arguments outside of create() call to have deterministic ordering
auto src = ctx->Clone(source); auto src = ctx->Clone(source);
auto stmts = ctx->Clone(statements); auto stmts = ctx->Clone(statements);
return ctx->dst->create<BlockStatement>(src, std::move(stmts)); auto attrs = ctx->Clone(attributes);
return ctx->dst->create<BlockStatement>(src, std::move(stmts), std::move(attrs));
} }
} // namespace tint::ast } // namespace tint::ast

View File

@ -19,6 +19,11 @@
#include "src/tint/ast/statement.h" #include "src/tint/ast/statement.h"
// Forward declarations
namespace tint::ast {
class Attribute;
} // namespace tint::ast
namespace tint::ast { namespace tint::ast {
/// A block statement /// A block statement
@ -29,10 +34,12 @@ class BlockStatement final : public Castable<BlockStatement, Statement> {
/// @param nid the unique node identifier /// @param nid the unique node identifier
/// @param source the block statement source /// @param source the block statement source
/// @param statements the statements /// @param statements the statements
/// @param attributes the block statement attributes
BlockStatement(ProgramID pid, BlockStatement(ProgramID pid,
NodeID nid, NodeID nid,
const Source& source, const Source& source,
utils::VectorRef<const Statement*> statements); utils::VectorRef<const Statement*> statements,
utils::VectorRef<const Attribute*> attributes);
/// Move constructor /// Move constructor
BlockStatement(BlockStatement&&); BlockStatement(BlockStatement&&);
~BlockStatement() override; ~BlockStatement() override;
@ -51,6 +58,9 @@ class BlockStatement final : public Castable<BlockStatement, Statement> {
/// the statement list /// the statement list
const utils::Vector<const Statement*, 8> statements; const utils::Vector<const Statement*, 8> statements;
/// the attribute list
const utils::Vector<const Attribute*, 4> attributes;
}; };
} // namespace tint::ast } // namespace tint::ast

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "gmock/gmock.h"
#include "gtest/gtest-spi.h" #include "gtest/gtest-spi.h"
#include "src/tint/ast/discard_statement.h" #include "src/tint/ast/discard_statement.h"
#include "src/tint/ast/if_statement.h" #include "src/tint/ast/if_statement.h"
@ -26,21 +27,35 @@ TEST_F(BlockStatementTest, Creation) {
auto* d = create<DiscardStatement>(); auto* d = create<DiscardStatement>();
auto* ptr = d; auto* ptr = d;
auto* b = create<BlockStatement>(utils::Vector{d}); auto* b = create<BlockStatement>(utils::Vector{d}, utils::Empty);
ASSERT_EQ(b->statements.Length(), 1u); ASSERT_EQ(b->statements.Length(), 1u);
EXPECT_EQ(b->statements[0], ptr); EXPECT_EQ(b->statements[0], ptr);
EXPECT_EQ(b->attributes.Length(), 0u);
} }
TEST_F(BlockStatementTest, Creation_WithSource) { TEST_F(BlockStatementTest, Creation_WithSource) {
auto* b = create<BlockStatement>(Source{Source::Location{20, 2}}, utils::Empty); auto* b = create<BlockStatement>(Source{Source::Location{20, 2}}, utils::Empty, utils::Empty);
auto src = b->source; auto src = b->source;
EXPECT_EQ(src.range.begin.line, 20u); EXPECT_EQ(src.range.begin.line, 20u);
EXPECT_EQ(src.range.begin.column, 2u); EXPECT_EQ(src.range.begin.column, 2u);
} }
TEST_F(BlockStatementTest, Creation_WithAttributes) {
auto* d = create<DiscardStatement>();
auto* ptr = d;
auto* attr1 = DiagnosticAttribute(ast::DiagnosticSeverity::kOff, Expr("foo"));
auto* attr2 = DiagnosticAttribute(ast::DiagnosticSeverity::kOff, Expr("bar"));
auto* b = create<BlockStatement>(utils::Vector{d}, utils::Vector{attr1, attr2});
ASSERT_EQ(b->statements.Length(), 1u);
EXPECT_EQ(b->statements[0], ptr);
EXPECT_THAT(b->attributes, testing::ElementsAre(attr1, attr2));
}
TEST_F(BlockStatementTest, IsBlock) { TEST_F(BlockStatementTest, IsBlock) {
auto* b = create<BlockStatement>(utils::Empty); auto* b = create<BlockStatement>(utils::Empty, utils::Empty);
EXPECT_TRUE(b->Is<BlockStatement>()); EXPECT_TRUE(b->Is<BlockStatement>());
} }
@ -48,7 +63,8 @@ TEST_F(BlockStatementTest, Assert_Null_Statement) {
EXPECT_FATAL_FAILURE( EXPECT_FATAL_FAILURE(
{ {
ProgramBuilder b; ProgramBuilder b;
b.create<BlockStatement>(utils::Vector<const ast::Statement*, 1>{nullptr}); b.create<BlockStatement>(utils::Vector<const ast::Statement*, 1>{nullptr},
utils::Empty);
}, },
"internal compiler error"); "internal compiler error");
} }
@ -58,7 +74,7 @@ TEST_F(BlockStatementTest, Assert_DifferentProgramID_Statement) {
{ {
ProgramBuilder b1; ProgramBuilder b1;
ProgramBuilder b2; ProgramBuilder b2;
b1.create<BlockStatement>(utils::Vector{b2.create<DiscardStatement>()}); b1.create<BlockStatement>(utils::Vector{b2.create<DiscardStatement>()}, utils::Empty);
}, },
"internal compiler error"); "internal compiler error");
} }

View File

@ -31,7 +31,7 @@ TEST_F(CaseStatementTest, Creation_i32) {
utils::Vector b{selector}; utils::Vector b{selector};
auto* discard = create<DiscardStatement>(); auto* discard = create<DiscardStatement>();
auto* body = create<BlockStatement>(utils::Vector{discard}); auto* body = create<BlockStatement>(utils::Vector{discard}, utils::Empty);
auto* c = create<CaseStatement>(b, body); auto* c = create<CaseStatement>(b, body);
ASSERT_EQ(c->selectors.Length(), 1u); ASSERT_EQ(c->selectors.Length(), 1u);
@ -45,7 +45,7 @@ TEST_F(CaseStatementTest, Creation_u32) {
utils::Vector b{selector}; utils::Vector b{selector};
auto* discard = create<DiscardStatement>(); auto* discard = create<DiscardStatement>();
auto* body = create<BlockStatement>(utils::Vector{discard}); auto* body = create<BlockStatement>(utils::Vector{discard}, utils::Empty);
auto* c = create<CaseStatement>(b, body); auto* c = create<CaseStatement>(b, body);
ASSERT_EQ(c->selectors.Length(), 1u); ASSERT_EQ(c->selectors.Length(), 1u);
@ -56,22 +56,24 @@ TEST_F(CaseStatementTest, Creation_u32) {
TEST_F(CaseStatementTest, ContainsDefault_WithDefault) { TEST_F(CaseStatementTest, ContainsDefault_WithDefault) {
utils::Vector b{CaseSelector(2_u), DefaultCaseSelector()}; utils::Vector b{CaseSelector(2_u), DefaultCaseSelector()};
auto* c = create<CaseStatement>(b, create<BlockStatement>(utils::Empty)); auto* c = create<CaseStatement>(b, create<BlockStatement>(utils::Empty, utils::Empty));
EXPECT_TRUE(c->ContainsDefault()); EXPECT_TRUE(c->ContainsDefault());
} }
TEST_F(CaseStatementTest, ContainsDefault_WithOutDefault) { TEST_F(CaseStatementTest, ContainsDefault_WithOutDefault) {
utils::Vector b{CaseSelector(2_u), CaseSelector(3_u)}; utils::Vector b{CaseSelector(2_u), CaseSelector(3_u)};
auto* c = create<CaseStatement>(b, create<BlockStatement>(utils::Empty)); auto* c = create<CaseStatement>(b, create<BlockStatement>(utils::Empty, utils::Empty));
EXPECT_FALSE(c->ContainsDefault()); EXPECT_FALSE(c->ContainsDefault());
} }
TEST_F(CaseStatementTest, Creation_WithSource) { TEST_F(CaseStatementTest, Creation_WithSource) {
utils::Vector b{CaseSelector(2_i)}; utils::Vector b{CaseSelector(2_i)};
auto* body = create<BlockStatement>(utils::Vector{ auto* body = create<BlockStatement>(
create<DiscardStatement>(), utils::Vector{
}); create<DiscardStatement>(),
},
utils::Empty);
auto* c = create<CaseStatement>(Source{Source::Location{20, 2}}, b, body); auto* c = create<CaseStatement>(Source{Source::Location{20, 2}}, b, body);
auto src = c->source; auto src = c->source;
EXPECT_EQ(src.range.begin.line, 20u); EXPECT_EQ(src.range.begin.line, 20u);
@ -80,7 +82,7 @@ TEST_F(CaseStatementTest, Creation_WithSource) {
TEST_F(CaseStatementTest, IsCase) { TEST_F(CaseStatementTest, IsCase) {
auto* c = create<CaseStatement>(utils::Vector{DefaultCaseSelector()}, auto* c = create<CaseStatement>(utils::Vector{DefaultCaseSelector()},
create<BlockStatement>(utils::Empty)); create<BlockStatement>(utils::Empty, utils::Empty));
EXPECT_TRUE(c->Is<CaseStatement>()); EXPECT_TRUE(c->Is<CaseStatement>());
} }
@ -98,7 +100,7 @@ TEST_F(CaseStatementTest, Assert_Null_Selector) {
{ {
ProgramBuilder b; ProgramBuilder b;
b.create<CaseStatement>(utils::Vector<const ast::CaseSelector*, 1>{nullptr}, b.create<CaseStatement>(utils::Vector<const ast::CaseSelector*, 1>{nullptr},
b.create<BlockStatement>(utils::Empty)); b.create<BlockStatement>(utils::Empty, utils::Empty));
}, },
"internal compiler error"); "internal compiler error");
} }
@ -109,7 +111,7 @@ TEST_F(CaseStatementTest, Assert_DifferentProgramID_Call) {
ProgramBuilder b1; ProgramBuilder b1;
ProgramBuilder b2; ProgramBuilder b2;
b1.create<CaseStatement>(utils::Vector{b1.DefaultCaseSelector()}, b1.create<CaseStatement>(utils::Vector{b1.DefaultCaseSelector()},
b2.create<BlockStatement>(utils::Empty)); b2.create<BlockStatement>(utils::Empty, utils::Empty));
}, },
"internal compiler error"); "internal compiler error");
} }
@ -120,7 +122,7 @@ TEST_F(CaseStatementTest, Assert_DifferentProgramID_Selector) {
ProgramBuilder b1; ProgramBuilder b1;
ProgramBuilder b2; ProgramBuilder b2;
b1.create<CaseStatement>(utils::Vector{b2.CaseSelector(b2.Expr(2_i))}, b1.create<CaseStatement>(utils::Vector{b2.CaseSelector(b2.Expr(2_i))},
b1.create<BlockStatement>(utils::Empty)); b1.create<BlockStatement>(utils::Empty, utils::Empty));
}, },
"internal compiler error"); "internal compiler error");
} }

View File

@ -2456,7 +2456,8 @@ class ProgramBuilder {
/// @param type the function return type /// @param type the function return type
/// @param body the function body /// @param body the function body
/// @param attributes the optional function attributes /// @param attributes the optional function attributes
/// @param return_type_attributes the optional function return type /// @param return_type_attributes the optional function return type attributes
/// @param body_attributes the optional function body attributes
/// attributes /// attributes
/// @returns the function pointer /// @returns the function pointer
template <typename NAME> template <typename NAME>
@ -2467,11 +2468,12 @@ class ProgramBuilder {
const ast::Type* type, const ast::Type* type,
utils::VectorRef<const ast::Statement*> body, utils::VectorRef<const ast::Statement*> body,
utils::VectorRef<const ast::Attribute*> attributes = utils::Empty, utils::VectorRef<const ast::Attribute*> attributes = utils::Empty,
utils::VectorRef<const ast::Attribute*> return_type_attributes = utils::Empty) { utils::VectorRef<const ast::Attribute*> return_type_attributes = utils::Empty,
auto* func = utils::VectorRef<const ast::Attribute*> body_attributes = utils::Empty) {
create<ast::Function>(source, Sym(std::forward<NAME>(name)), std::move(params), type, auto* func = create<ast::Function>(
create<ast::BlockStatement>(std::move(body)), source, Sym(std::forward<NAME>(name)), std::move(params), type,
std::move(attributes), std::move(return_type_attributes)); create<ast::BlockStatement>(std::move(body), std::move(body_attributes)),
std::move(attributes), std::move(return_type_attributes));
AST().AddFunction(func); AST().AddFunction(func);
return func; return func;
} }
@ -2482,7 +2484,8 @@ class ProgramBuilder {
/// @param type the function return type /// @param type the function return type
/// @param body the function body /// @param body the function body
/// @param attributes the optional function attributes /// @param attributes the optional function attributes
/// @param return_type_attributes the optional function return type /// @param return_type_attributes the optional function return type attributes
/// @param body_attributes the optional function body attributes
/// attributes /// attributes
/// @returns the function pointer /// @returns the function pointer
template <typename NAME> template <typename NAME>
@ -2492,11 +2495,12 @@ class ProgramBuilder {
const ast::Type* type, const ast::Type* type,
utils::VectorRef<const ast::Statement*> body, utils::VectorRef<const ast::Statement*> body,
utils::VectorRef<const ast::Attribute*> attributes = utils::Empty, utils::VectorRef<const ast::Attribute*> attributes = utils::Empty,
utils::VectorRef<const ast::Attribute*> return_type_attributes = utils::Empty) { utils::VectorRef<const ast::Attribute*> return_type_attributes = utils::Empty,
auto* func = utils::VectorRef<const ast::Attribute*> body_attributes = utils::Empty) {
create<ast::Function>(Sym(std::forward<NAME>(name)), std::move(params), type, auto* func = create<ast::Function>(
create<ast::BlockStatement>(std::move(body)), Sym(std::forward<NAME>(name)), std::move(params), type,
std::move(attributes), std::move(return_type_attributes)); create<ast::BlockStatement>(std::move(body), std::move(body_attributes)),
std::move(attributes), std::move(return_type_attributes));
AST().AddFunction(func); AST().AddFunction(func);
return func; return func;
} }
@ -2676,23 +2680,50 @@ class ProgramBuilder {
/// @param source the source information for the block /// @param source the source information for the block
/// @param statements statements of block /// @param statements statements of block
/// @returns the block statement pointer /// @returns the block statement pointer
template <typename... Statements> template <typename... STATEMENTS, typename = DisableIfVectorLike<STATEMENTS...>>
const ast::BlockStatement* Block(const Source& source, Statements&&... statements) { const ast::BlockStatement* Block(const Source& source, STATEMENTS&&... statements) {
return create<ast::BlockStatement>( return create<ast::BlockStatement>(
source, utils::Vector<const ast::Statement*, sizeof...(statements)>{ source,
std::forward<Statements>(statements)..., utils::Vector<const ast::Statement*, sizeof...(statements)>{
}); std::forward<STATEMENTS>(statements)...,
},
utils::Empty);
} }
/// Creates a ast::BlockStatement with input statements /// Creates a ast::BlockStatement with input statements
/// @param statements statements of block /// @param statements statements of block
/// @returns the block statement pointer /// @returns the block statement pointer
template <typename... STATEMENTS, typename = DisableIfSource<STATEMENTS...>> template <typename... STATEMENTS,
typename = DisableIfSource<STATEMENTS...>,
typename = DisableIfVectorLike<STATEMENTS...>>
const ast::BlockStatement* Block(STATEMENTS&&... statements) { const ast::BlockStatement* Block(STATEMENTS&&... statements) {
return create<ast::BlockStatement>( return create<ast::BlockStatement>(
utils::Vector<const ast::Statement*, sizeof...(statements)>{ utils::Vector<const ast::Statement*, sizeof...(statements)>{
std::forward<STATEMENTS>(statements)..., std::forward<STATEMENTS>(statements)...,
}); },
utils::Empty);
}
/// Creates a ast::BlockStatement with input statements and attributes
/// @param source the source information for the block
/// @param statements statements of block
/// @param attributes the attributes
/// @returns the block statement pointer
const ast::BlockStatement* Block(
const Source& source,
utils::VectorRef<const ast::Statement*> statements,
utils::VectorRef<const ast::Attribute*> attributes = utils::Empty) {
return create<ast::BlockStatement>(source, std::move(statements), std::move(attributes));
}
/// Creates a ast::BlockStatement with input statements and attributes
/// @param statements statements of block
/// @param attributes the attributes
/// @returns the block statement pointer
const ast::BlockStatement* Block(
utils::VectorRef<const ast::Statement*> statements,
utils::VectorRef<const ast::Attribute*> attributes = utils::Empty) {
return create<ast::BlockStatement>(std::move(statements), std::move(attributes));
} }
/// A wrapper type for the Else statement used to create If statements. /// A wrapper type for the Else statement used to create If statements.

View File

@ -882,7 +882,7 @@ void FunctionEmitter::PushGuard(const std::string& guard_name, uint32_t end_id)
auto* builder = AddStatementBuilder<IfStatementBuilder>(cond); auto* builder = AddStatementBuilder<IfStatementBuilder>(cond);
PushNewStatementBlock(top.GetConstruct(), end_id, [=](const StatementList& stmts) { PushNewStatementBlock(top.GetConstruct(), end_id, [=](const StatementList& stmts) {
builder->body = create<ast::BlockStatement>(Source{}, stmts); builder->body = create<ast::BlockStatement>(Source{}, stmts, utils::Empty);
}); });
} }
@ -894,7 +894,7 @@ void FunctionEmitter::PushTrueGuard(uint32_t end_id) {
auto* builder = AddStatementBuilder<IfStatementBuilder>(cond); auto* builder = AddStatementBuilder<IfStatementBuilder>(cond);
PushNewStatementBlock(top.GetConstruct(), end_id, [=](const StatementList& stmts) { PushNewStatementBlock(top.GetConstruct(), end_id, [=](const StatementList& stmts) {
builder->body = create<ast::BlockStatement>(Source{}, stmts); builder->body = create<ast::BlockStatement>(Source{}, stmts, utils::Empty);
}); });
} }
@ -985,7 +985,7 @@ const ast::BlockStatement* FunctionEmitter::MakeFunctionBody() {
statements_stack_[0].Finalize(&builder_); statements_stack_[0].Finalize(&builder_);
auto& statements = statements_stack_[0].GetStatements(); auto& statements = statements_stack_[0].GetStatements();
auto* body = create<ast::BlockStatement>(Source{}, statements); auto* body = create<ast::BlockStatement>(Source{}, statements, utils::Empty);
// Maintain the invariant by repopulating the one and only element. // Maintain the invariant by repopulating the one and only element.
statements_stack_.Clear(); statements_stack_.Clear();
@ -1407,7 +1407,7 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
} }
} }
auto* body = create<ast::BlockStatement>(source, stmts); auto* body = create<ast::BlockStatement>(source, stmts, utils::Empty);
AttributeList fn_attrs; AttributeList fn_attrs;
fn_attrs.Push(create<ast::StageAttribute>(source, ep_info_->stage)); fn_attrs.Push(create<ast::StageAttribute>(source, ep_info_->stage));
@ -2938,7 +2938,7 @@ bool FunctionEmitter::EmitIfStart(const BlockInfo& block_info) {
if (!stmts.IsEmpty()) { if (!stmts.IsEmpty()) {
// The "else" consists of the statement list from the top of // The "else" consists of the statement list from the top of
// statements stack, without an "else if" condition. // statements stack, without an "else if" condition.
builder->else_stmt = create<ast::BlockStatement>(Source{}, stmts); builder->else_stmt = create<ast::BlockStatement>(Source{}, stmts, utils::Empty);
} }
}); });
if (false_is_break) { if (false_is_break) {
@ -2985,7 +2985,7 @@ bool FunctionEmitter::EmitIfStart(const BlockInfo& block_info) {
// Push the then clause onto the stack. // Push the then clause onto the stack.
PushNewStatementBlock(construct, then_end, [=](const StatementList& stmts) { PushNewStatementBlock(construct, then_end, [=](const StatementList& stmts) {
builder->body = create<ast::BlockStatement>(Source{}, stmts); builder->body = create<ast::BlockStatement>(Source{}, stmts, utils::Empty);
}); });
if (true_is_break) { if (true_is_break) {
AddStatement(create<ast::BreakStatement>(Source{})); AddStatement(create<ast::BreakStatement>(Source{}));
@ -3098,7 +3098,7 @@ bool FunctionEmitter::EmitSwitchStart(const BlockInfo& block_info) {
auto case_idx = swch->cases.Length(); auto case_idx = swch->cases.Length();
swch->cases.Push(nullptr); swch->cases.Push(nullptr);
PushNewStatementBlock(construct, end_id, [=](const StatementList& stmts) { PushNewStatementBlock(construct, end_id, [=](const StatementList& stmts) {
auto* body = create<ast::BlockStatement>(Source{}, stmts); auto* body = create<ast::BlockStatement>(Source{}, stmts, utils::Empty);
swch->cases[case_idx] = create<ast::CaseStatement>(Source{}, selectors, body); swch->cases[case_idx] = create<ast::CaseStatement>(Source{}, selectors, body);
}); });
@ -3113,7 +3113,7 @@ bool FunctionEmitter::EmitSwitchStart(const BlockInfo& block_info) {
bool FunctionEmitter::EmitLoopStart(const Construct* construct) { bool FunctionEmitter::EmitLoopStart(const Construct* construct) {
auto* builder = AddStatementBuilder<LoopStatementBuilder>(); auto* builder = AddStatementBuilder<LoopStatementBuilder>();
PushNewStatementBlock(construct, construct->end_id, [=](const StatementList& stmts) { PushNewStatementBlock(construct, construct->end_id, [=](const StatementList& stmts) {
builder->body = create<ast::BlockStatement>(Source{}, stmts); builder->body = create<ast::BlockStatement>(Source{}, stmts, utils::Empty);
}); });
return success(); return success();
} }
@ -3128,7 +3128,7 @@ bool FunctionEmitter::EmitContinuingStart(const Construct* construct) {
"expected loop on top of stack"; "expected loop on top of stack";
} }
PushNewStatementBlock(construct, construct->end_id, [=](const StatementList& stmts) { PushNewStatementBlock(construct, construct->end_id, [=](const StatementList& stmts) {
loop->continuing = create<ast::BlockStatement>(Source{}, stmts); loop->continuing = create<ast::BlockStatement>(Source{}, stmts, utils::Empty);
}); });
return success(); return success();
@ -3354,11 +3354,11 @@ const ast::Statement* FunctionEmitter::MakeSimpleIf(const ast::Expression* condi
if (then_stmt != nullptr) { if (then_stmt != nullptr) {
if_stmts.Push(then_stmt); if_stmts.Push(then_stmt);
} }
auto* if_block = create<ast::BlockStatement>(Source{}, if_stmts); auto* if_block = create<ast::BlockStatement>(Source{}, if_stmts, utils::Empty);
const ast::Statement* else_block = nullptr; const ast::Statement* else_block = nullptr;
if (else_stmt) { if (else_stmt) {
else_block = create<ast::BlockStatement>(StatementList{else_stmt}); else_block = create<ast::BlockStatement>(StatementList{else_stmt}, utils::Empty);
} }
auto* if_stmt = create<ast::IfStatement>(Source{}, condition, if_block, else_block); auto* if_stmt = create<ast::IfStatement>(Source{}, condition, if_block, else_block);

View File

@ -1500,7 +1500,7 @@ Maybe<const ast::Function*> ParserImpl::function_decl(AttributeList& attrs) {
// function body. The AST isn't used as we've already errored, but this // function body. The AST isn't used as we've already errored, but this
// catches any errors inside the body, and can help keep the parser in // catches any errors inside the body, and can help keep the parser in
// sync. // sync.
expect_compound_statement(); expect_compound_statement("function body");
} }
return Failure::kErrored; return Failure::kErrored;
} }
@ -1510,7 +1510,7 @@ Maybe<const ast::Function*> ParserImpl::function_decl(AttributeList& attrs) {
bool errored = false; bool errored = false;
auto body = expect_compound_statement(); auto body = expect_compound_statement("function body");
if (body.errored) { if (body.errored) {
errored = true; errored = true;
} }
@ -1666,14 +1666,26 @@ Expect<ast::BuiltinValue> ParserImpl::expect_builtin() {
} }
// compound_statement // compound_statement
// : BRACE_LEFT statement* BRACE_RIGHT // : attribute* BRACE_LEFT statement* BRACE_RIGHT
Expect<ast::BlockStatement*> ParserImpl::expect_compound_statement() { Expect<ast::BlockStatement*> ParserImpl::expect_compound_statement(std::string_view use) {
return expect_brace_block("", [&]() -> Expect<ast::BlockStatement*> { auto attrs = attribute_list();
if (attrs.errored) {
return Failure::kErrored;
}
return expect_compound_statement(attrs.value, use);
}
// compound_statement
// : attribute* BRACE_LEFT statement* BRACE_RIGHT
Expect<ast::BlockStatement*> ParserImpl::expect_compound_statement(AttributeList& attrs,
std::string_view use) {
return expect_brace_block(use, [&]() -> Expect<ast::BlockStatement*> {
auto stmts = expect_statements(); auto stmts = expect_statements();
if (stmts.errored) { if (stmts.errored) {
return Failure::kErrored; return Failure::kErrored;
} }
return create<ast::BlockStatement>(Source{}, stmts.value); TINT_DEFER(attrs.Clear());
return create<ast::BlockStatement>(Source{}, stmts.value, std::move(attrs));
}); });
} }
@ -1731,6 +1743,12 @@ Maybe<const ast::Statement*> ParserImpl::statement() {
// Skip empty statements // Skip empty statements
} }
auto attrs = attribute_list();
if (attrs.errored) {
return Failure::kErrored;
}
TINT_DEFER(expect_attributes_consumed(attrs.value));
// Non-block statements that error can resynchronize on semicolon. // Non-block statements that error can resynchronize on semicolon.
auto stmt = sync(Token::Type::kSemicolon, [&] { return non_block_statement(); }); auto stmt = sync(Token::Type::kSemicolon, [&] { return non_block_statement(); });
if (stmt.errored) { if (stmt.errored) {
@ -1781,7 +1799,7 @@ Maybe<const ast::Statement*> ParserImpl::statement() {
} }
if (peek_is(Token::Type::kBraceLeft)) { if (peek_is(Token::Type::kBraceLeft)) {
auto body = expect_compound_statement(); auto body = expect_compound_statement(attrs.value, "block statement");
if (body.errored) { if (body.errored) {
return Failure::kErrored; return Failure::kErrored;
} }
@ -2023,7 +2041,7 @@ Maybe<const ast::IfStatement*> ParserImpl::if_statement() {
return add_error(peek(), "unable to parse condition expression"); return add_error(peek(), "unable to parse condition expression");
} }
auto body = expect_compound_statement(); auto body = expect_compound_statement("if statement");
if (body.errored) { if (body.errored) {
return Failure::kErrored; return Failure::kErrored;
} }
@ -2059,7 +2077,7 @@ Maybe<const ast::IfStatement*> ParserImpl::if_statement() {
} }
// If it wasn't an "else if", it must just be an "else". // If it wasn't an "else if", it must just be an "else".
auto else_body = expect_compound_statement(); auto else_body = expect_compound_statement("else statement");
if (else_body.errored) { if (else_body.errored) {
return Failure::kErrored; return Failure::kErrored;
} }
@ -2119,8 +2137,8 @@ Maybe<const ast::SwitchStatement*> ParserImpl::switch_statement() {
} }
// switch_body // switch_body
// : CASE case_selectors COLON? BRACKET_LEFT case_body BRACKET_RIGHT // : CASE case_selectors COLON? compound_statement
// | DEFAULT COLON? BRACKET_LEFT case_body BRACKET_RIGHT // | DEFAULT COLON? compound_statement
Maybe<const ast::CaseStatement*> ParserImpl::switch_body() { Maybe<const ast::CaseStatement*> ParserImpl::switch_body() {
if (!peek_is(Token::Type::kCase) && !peek_is(Token::Type::kDefault)) { if (!peek_is(Token::Type::kCase) && !peek_is(Token::Type::kDefault)) {
return Failure::kNoMatch; return Failure::kNoMatch;
@ -2145,14 +2163,10 @@ Maybe<const ast::CaseStatement*> ParserImpl::switch_body() {
match(Token::Type::kColon); match(Token::Type::kColon);
const char* use = "case statement"; const char* use = "case statement";
auto body = expect_brace_block(use, [&] { return case_body(); }); auto body = expect_compound_statement(use);
if (body.errored) { if (body.errored) {
return Failure::kErrored; return Failure::kErrored;
} }
if (!body.matched) {
return add_error(body.source, "expected case body");
}
return create<ast::CaseStatement>(t.source(), selector_list, body.value); return create<ast::CaseStatement>(t.source(), selector_list, body.value);
} }
@ -2231,7 +2245,7 @@ Maybe<const ast::BlockStatement*> ParserImpl::case_body() {
stmts.Push(stmt.value); stmts.Push(stmt.value);
} }
return create<ast::BlockStatement>(Source{}, stmts); return create<ast::BlockStatement>(Source{}, stmts, utils::Empty);
} }
// loop_statement // loop_statement
@ -2253,7 +2267,7 @@ Maybe<const ast::LoopStatement*> ParserImpl::loop_statement() {
return Failure::kErrored; return Failure::kErrored;
} }
auto* body = create<ast::BlockStatement>(source, stmts.value); auto* body = create<ast::BlockStatement>(source, stmts.value, utils::Empty);
return create<ast::LoopStatement>(source, body, continuing.value); return create<ast::LoopStatement>(source, body, continuing.value);
}); });
} }
@ -2345,7 +2359,7 @@ Expect<std::unique_ptr<ForHeader>> ParserImpl::expect_for_header() {
} }
// for_statement // for_statement
// : FOR PAREN_LEFT for_header PAREN_RIGHT BRACE_LEFT statements BRACE_RIGHT // : FOR PAREN_LEFT for_header PAREN_RIGHT compound_statement
Maybe<const ast::ForLoopStatement*> ParserImpl::for_statement() { Maybe<const ast::ForLoopStatement*> ParserImpl::for_statement() {
Source source; Source source;
if (!match(Token::Type::kFor, &source)) { if (!match(Token::Type::kFor, &source)) {
@ -2357,14 +2371,13 @@ Maybe<const ast::ForLoopStatement*> ParserImpl::for_statement() {
return Failure::kErrored; return Failure::kErrored;
} }
auto stmts = expect_brace_block("for loop", [&] { return expect_statements(); }); auto body = expect_compound_statement("for loop");
if (stmts.errored) { if (body.errored) {
return Failure::kErrored; return Failure::kErrored;
} }
return create<ast::ForLoopStatement>(source, header->initializer, header->condition, return create<ast::ForLoopStatement>(source, header->initializer, header->condition,
header->continuing, header->continuing, body.value);
create<ast::BlockStatement>(stmts.value));
} }
// while_statement // while_statement
@ -2383,7 +2396,7 @@ Maybe<const ast::WhileStatement*> ParserImpl::while_statement() {
return add_error(peek(), "unable to parse while condition expression"); return add_error(peek(), "unable to parse while condition expression");
} }
auto body = expect_compound_statement(); auto body = expect_compound_statement("while loop");
if (body.errored) { if (body.errored) {
return Failure::kErrored; return Failure::kErrored;
} }
@ -2491,7 +2504,7 @@ Maybe<const ast::BlockStatement*> ParserImpl::continuing_compound_statement() {
stmts.Push(stmt.value); stmts.Push(stmt.value);
} }
return create<ast::BlockStatement>(Source{}, stmts); return create<ast::BlockStatement>(Source{}, stmts, utils::Empty);
}); });
} }
@ -2499,7 +2512,7 @@ Maybe<const ast::BlockStatement*> ParserImpl::continuing_compound_statement() {
// : CONTINUING continuing_compound_statement // : CONTINUING continuing_compound_statement
Maybe<const ast::BlockStatement*> ParserImpl::continuing_statement() { Maybe<const ast::BlockStatement*> ParserImpl::continuing_statement() {
if (!match(Token::Type::kContinuing)) { if (!match(Token::Type::kContinuing)) {
return create<ast::BlockStatement>(Source{}, utils::Empty); return create<ast::BlockStatement>(Source{}, utils::Empty, utils::Empty);
} }
return continuing_compound_statement(); return continuing_compound_statement();

View File

@ -542,8 +542,15 @@ class ParserImpl {
/// @returns the parsed builtin. /// @returns the parsed builtin.
Expect<ast::BuiltinValue> expect_builtin(); Expect<ast::BuiltinValue> expect_builtin();
/// Parses a `compound_statement` grammar element, erroring on parse failure. /// Parses a `compound_statement` grammar element, erroring on parse failure.
/// @param use a description of what was being parsed if an error was raised
/// @returns the parsed statements /// @returns the parsed statements
Expect<ast::BlockStatement*> expect_compound_statement(); Expect<ast::BlockStatement*> expect_compound_statement(std::string_view use);
/// Parses a `compound_statement` grammar element, with the attribute list provided as `attrs`.
/// @param attrs the list of attributes for the statement
/// @param use a description of what was being parsed if an error was raised
/// @returns the parsed statements
Expect<ast::BlockStatement*> expect_compound_statement(AttributeList& attrs,
std::string_view use);
/// Parses a `paren_expression` grammar element, erroring on parse failure. /// Parses a `paren_expression` grammar element, erroring on parse failure.
/// @returns the parsed element or nullptr /// @returns the parsed element or nullptr
Expect<const ast::Expression*> expect_paren_expression(); Expect<const ast::Expression*> expect_paren_expression();

View File

@ -23,7 +23,7 @@ TEST_F(ParserImplTest, CompoundStmt) {
discard; discard;
return 1 + b / 2; return 1 + b / 2;
})"); })");
auto e = p->expect_compound_statement(); auto e = p->expect_compound_statement("");
ASSERT_FALSE(p->has_error()) << p->error(); ASSERT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(e.errored); ASSERT_FALSE(e.errored);
ASSERT_EQ(e->statements.Length(), 2u); ASSERT_EQ(e->statements.Length(), 2u);
@ -33,7 +33,7 @@ TEST_F(ParserImplTest, CompoundStmt) {
TEST_F(ParserImplTest, CompoundStmt_Empty) { TEST_F(ParserImplTest, CompoundStmt_Empty) {
auto p = parser("{}"); auto p = parser("{}");
auto e = p->expect_compound_statement(); auto e = p->expect_compound_statement("");
ASSERT_FALSE(p->has_error()) << p->error(); ASSERT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(e.errored); ASSERT_FALSE(e.errored);
EXPECT_EQ(e->statements.Length(), 0u); EXPECT_EQ(e->statements.Length(), 0u);
@ -41,7 +41,7 @@ TEST_F(ParserImplTest, CompoundStmt_Empty) {
TEST_F(ParserImplTest, CompoundStmt_InvalidStmt) { TEST_F(ParserImplTest, CompoundStmt_InvalidStmt) {
auto p = parser("{fn main() {}}"); auto p = parser("{fn main() {}}");
auto e = p->expect_compound_statement(); auto e = p->expect_compound_statement("");
ASSERT_TRUE(p->has_error()); ASSERT_TRUE(p->has_error());
ASSERT_TRUE(e.errored); ASSERT_TRUE(e.errored);
EXPECT_EQ(p->error(), "1:2: expected '}'"); EXPECT_EQ(p->error(), "1:2: expected '}'");
@ -49,7 +49,7 @@ TEST_F(ParserImplTest, CompoundStmt_InvalidStmt) {
TEST_F(ParserImplTest, CompoundStmt_MissingRightParen) { TEST_F(ParserImplTest, CompoundStmt_MissingRightParen) {
auto p = parser("{return;"); auto p = parser("{return;");
auto e = p->expect_compound_statement(); auto e = p->expect_compound_statement("");
ASSERT_TRUE(p->has_error()); ASSERT_TRUE(p->has_error());
ASSERT_TRUE(e.errored); ASSERT_TRUE(e.errored);
EXPECT_EQ(p->error(), "1:9: expected '}'"); EXPECT_EQ(p->error(), "1:9: expected '}'");

View File

@ -489,7 +489,7 @@ fn f( {}
} }
TEST_F(ParserImplErrorTest, FunctionDeclMissingArrow) { TEST_F(ParserImplErrorTest, FunctionDeclMissingArrow) {
EXPECT("fn f() f32 {}", R"(test.wgsl:1:8 error: expected '{' EXPECT("fn f() f32 {}", R"(test.wgsl:1:8 error: expected '{' for function body
fn f() f32 {} fn f() f32 {}
^^^ ^^^
)"); )");
@ -526,14 +526,14 @@ fn f(x : i32, ,) {}
} }
TEST_F(ParserImplErrorTest, FunctionDeclMissingLBrace) { TEST_F(ParserImplErrorTest, FunctionDeclMissingLBrace) {
EXPECT("fn f() }", R"(test.wgsl:1:8 error: expected '{' EXPECT("fn f() }", R"(test.wgsl:1:8 error: expected '{' for function body
fn f() } fn f() }
^ ^
)"); )");
} }
TEST_F(ParserImplErrorTest, FunctionDeclMissingRBrace) { TEST_F(ParserImplErrorTest, FunctionDeclMissingRBrace) {
EXPECT("fn f() {", R"(test.wgsl:1:9 error: expected '}' EXPECT("fn f() {", R"(test.wgsl:1:9 error: expected '}' for function body
fn f() { fn f() {
^ ^
)"); )");
@ -541,9 +541,9 @@ fn f() {
TEST_F(ParserImplErrorTest, FunctionScopeUnusedDecl) { TEST_F(ParserImplErrorTest, FunctionScopeUnusedDecl) {
EXPECT("fn f(a:i32)->i32{return a;@size(1)}", EXPECT("fn f(a:i32)->i32{return a;@size(1)}",
R"(test.wgsl:1:27 error: expected '}' R"(test.wgsl:1:28 error: unexpected attributes
fn f(a:i32)->i32{return a;@size(1)} fn f(a:i32)->i32{return a;@size(1)}
^ ^^^^
)"); )");
} }

View File

@ -305,7 +305,7 @@ TEST_F(ParserImplTest, FunctionDecl_MissingLeftBrace) {
EXPECT_FALSE(f.matched); EXPECT_FALSE(f.matched);
EXPECT_TRUE(p->has_error()); EXPECT_TRUE(p->has_error());
EXPECT_EQ(f.value, nullptr); EXPECT_EQ(f.value, nullptr);
EXPECT_EQ(p->error(), "1:11: expected '{'"); EXPECT_EQ(p->error(), "1:11: expected '{' for function body");
} }
} // namespace } // namespace

View File

@ -85,7 +85,7 @@ TEST_F(ParserImplTest, IfStmt_InvalidCondition) {
EXPECT_TRUE(e.errored); EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr); EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error()); EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:6: expected '{'"); EXPECT_EQ(p->error(), "1:6: expected '{' for if statement");
} }
TEST_F(ParserImplTest, IfStmt_MissingCondition) { TEST_F(ParserImplTest, IfStmt_MissingCondition) {
@ -105,7 +105,7 @@ TEST_F(ParserImplTest, IfStmt_InvalidBody) {
EXPECT_TRUE(e.errored); EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr); EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error()); EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:8: expected '}'"); EXPECT_EQ(p->error(), "1:8: expected '}' for if statement");
} }
TEST_F(ParserImplTest, IfStmt_MissingBody) { TEST_F(ParserImplTest, IfStmt_MissingBody) {
@ -115,7 +115,7 @@ TEST_F(ParserImplTest, IfStmt_MissingBody) {
EXPECT_TRUE(e.errored); EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr); EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error()); EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:5: expected '{'"); EXPECT_EQ(p->error(), "1:5: expected '{' for if statement");
} }
TEST_F(ParserImplTest, IfStmt_InvalidElseif) { TEST_F(ParserImplTest, IfStmt_InvalidElseif) {
@ -125,7 +125,7 @@ TEST_F(ParserImplTest, IfStmt_InvalidElseif) {
EXPECT_TRUE(e.errored); EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr); EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error()); EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:21: expected '}'"); EXPECT_EQ(p->error(), "1:21: expected '}' for if statement");
} }
TEST_F(ParserImplTest, IfStmt_InvalidElse) { TEST_F(ParserImplTest, IfStmt_InvalidElse) {
@ -135,7 +135,7 @@ TEST_F(ParserImplTest, IfStmt_InvalidElse) {
EXPECT_TRUE(e.errored); EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr); EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error()); EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:16: expected '}'"); EXPECT_EQ(p->error(), "1:16: expected '}' for else statement");
} }
} // namespace } // namespace

View File

@ -95,7 +95,7 @@ TEST_F(ParserImplTest, Statement_If_Invalid) {
EXPECT_TRUE(e.errored); EXPECT_TRUE(e.errored);
EXPECT_FALSE(e.matched); EXPECT_FALSE(e.matched);
EXPECT_EQ(e.value, nullptr); EXPECT_EQ(e.value, nullptr);
EXPECT_EQ(p->error(), "1:10: expected '}'"); EXPECT_EQ(p->error(), "1:10: expected '}' for if statement");
} }
TEST_F(ParserImplTest, Statement_Variable) { TEST_F(ParserImplTest, Statement_Variable) {
@ -269,7 +269,7 @@ TEST_F(ParserImplTest, Statement_Body_Invalid) {
EXPECT_TRUE(e.errored); EXPECT_TRUE(e.errored);
EXPECT_FALSE(e.matched); EXPECT_FALSE(e.matched);
EXPECT_EQ(e.value, nullptr); EXPECT_EQ(e.value, nullptr);
EXPECT_EQ(p->error(), "1:3: expected '}'"); EXPECT_EQ(p->error(), "1:3: expected '}' for block statement");
} }
TEST_F(ParserImplTest, Statement_ConstAssert_WithParen) { TEST_F(ParserImplTest, Statement_ConstAssert_WithParen) {
@ -358,5 +358,15 @@ TEST_F(ParserImplTest, DEPRECATED_Statement_StaticAssert_WithoutParen) {
EXPECT_EQ(sa->condition->source.range.end.column, 20u); EXPECT_EQ(sa->condition->source.range.end.column, 20u);
} }
TEST_F(ParserImplTest, Statement_UnexpectedAttributes) {
auto p = parser("@diagnostic(off, derivative_uniformity) return;");
auto e = p->statement();
EXPECT_TRUE(p->has_error());
EXPECT_FALSE(e.errored);
EXPECT_TRUE(e.matched);
EXPECT_NE(e.value, nullptr);
EXPECT_EQ(p->error(), "1:2: unexpected attributes");
}
} // namespace } // namespace
} // namespace tint::reader::wgsl } // namespace tint::reader::wgsl

View File

@ -118,7 +118,7 @@ TEST_F(WhileStmtErrorTest, MissingRightParen) {
// Test a while loop with missing left brace is invalid. // Test a while loop with missing left brace is invalid.
TEST_F(WhileStmtErrorTest, MissingLeftBrace) { TEST_F(WhileStmtErrorTest, MissingLeftBrace) {
std::string while_str = "while (true) }"; std::string while_str = "while (true) }";
std::string error_str = "1:14: expected '{'"; std::string error_str = "1:14: expected '{' for while loop";
TestWhileWithError(while_str, error_str); TestWhileWithError(while_str, error_str);
} }
@ -126,7 +126,7 @@ TEST_F(WhileStmtErrorTest, MissingLeftBrace) {
// Test a for loop with missing right brace is invalid. // Test a for loop with missing right brace is invalid.
TEST_F(WhileStmtErrorTest, MissingRightBrace) { TEST_F(WhileStmtErrorTest, MissingRightBrace) {
std::string while_str = "while (true) {"; std::string while_str = "while (true) {";
std::string error_str = "1:15: expected '}'"; std::string error_str = "1:15: expected '}' for while loop";
TestWhileWithError(while_str, error_str); TestWhileWithError(while_str, error_str);
} }
@ -159,7 +159,7 @@ TEST_F(WhileStmtErrorTest, InvalidBody) {
// Test a for loop with a body not matching statements // Test a for loop with a body not matching statements
TEST_F(WhileStmtErrorTest, InvalidBodyMatch) { TEST_F(WhileStmtErrorTest, InvalidBodyMatch) {
std::string while_str = "while (true) { fn main() {} }"; std::string while_str = "while (true) { fn main() {} }";
std::string error_str = "1:16: expected '}'"; std::string error_str = "1:16: expected '}' for while loop";
TestWhileWithError(while_str, error_str); TestWhileWithError(while_str, error_str);
} }

View File

@ -1030,6 +1030,104 @@ TEST_F(OverrideAttributeTest, DuplicateAttribute) {
12:34 note: first attribute declared here)"); 12:34 note: first attribute declared here)");
} }
namespace BlockStatementTests {
class BlockStatementTest : public TestWithParams {
protected:
void Check() {
if (GetParam().should_pass) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
} else {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "error: attribute is not valid for block statements");
}
}
};
TEST_P(BlockStatementTest, CompoundStatement) {
Func("foo", utils::Empty, ty.void_(),
utils::Vector{
Block(utils::Vector{Return()}, createAttributes({}, *this, GetParam().kind)),
});
Check();
}
TEST_P(BlockStatementTest, FunctionBody) {
Func("foo", utils::Empty, ty.void_(),
utils::Vector{
Block(utils::Vector{Return()}),
},
utils::Empty, utils::Empty, createAttributes({}, *this, GetParam().kind));
Check();
}
TEST_P(BlockStatementTest, IfStatementBody) {
Func("foo", utils::Empty, ty.void_(),
utils::Vector{
If(Expr(true),
Block(utils::Vector{Return()}, createAttributes({}, *this, GetParam().kind))),
});
Check();
}
TEST_P(BlockStatementTest, ElseStatementBody) {
Func("foo", utils::Empty, ty.void_(),
utils::Vector{
If(Expr(true), Block(utils::Vector{Return()}),
Else(Block(utils::Vector{Return()}, createAttributes({}, *this, GetParam().kind)))),
});
Check();
}
TEST_P(BlockStatementTest, ForStatementBody) {
Func("foo", utils::Empty, ty.void_(),
utils::Vector{
For(nullptr, Expr(true), nullptr,
Block(utils::Vector{Break()}, createAttributes({}, *this, GetParam().kind))),
});
Check();
}
TEST_P(BlockStatementTest, WhileStatementBody) {
Func("foo", utils::Empty, ty.void_(),
utils::Vector{
While(Expr(true),
Block(utils::Vector{Break()}, createAttributes({}, *this, GetParam().kind))),
});
Check();
}
TEST_P(BlockStatementTest, CaseStatementBody) {
Func("foo", utils::Empty, ty.void_(),
utils::Vector{
Switch(1_a,
Case(CaseSelector(1_a), Block(utils::Vector{Break()},
createAttributes({}, *this, GetParam().kind))),
DefaultCase(Block({}))),
});
Check();
}
TEST_P(BlockStatementTest, DefaultStatementBody) {
Func("foo", utils::Empty, ty.void_(),
utils::Vector{
Switch(1_a, Case(CaseSelector(1_a), Block()),
DefaultCase(Block(utils::Vector{Break()},
createAttributes({}, *this, GetParam().kind)))),
});
Check();
}
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
BlockStatementTest,
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::kOffset, false},
TestParams{AttributeKind::kSize, false},
TestParams{AttributeKind::kStage, false},
TestParams{AttributeKind::kStride, false},
TestParams{AttributeKind::kWorkgroup, false},
TestParams{AttributeKind::kBindingAndGroup, false}));
} // namespace BlockStatementTests
} // namespace } // namespace
} // namespace AttributeTests } // namespace AttributeTests

View File

@ -181,6 +181,66 @@ TEST_F(ResolverDiagnosticControlTest, FunctionAttributeScope) {
89:10 note: code is unreachable)"); 89:10 note: code is unreachable)");
} }
TEST_F(ResolverDiagnosticControlTest, BlockAttributeScope) {
// fn foo() @diagnostic(off, chromium_unreachable_code) {
// {
// return;
// return; // Should not produce a diagnostic
// }
// @diagnostic(warning, chromium_unreachable_code) {
// if (true) @diagnostic(info, chromium_unreachable_code) {
// return;
// return; // Should produce an info
// } else {
// while (true) @diagnostic(off, chromium_unreachable_code) {
// return;
// return; // Should not produce a diagnostic
// }
// return;
// return; // Should produce an warning
// }
// }
// }
auto attr = [&](auto severity) {
return utils::Vector{DiagnosticAttribute(severity, Expr("chromium_unreachable_code"))};
};
Func("foo", {}, ty.void_(),
utils::Vector{
Return(),
Return(Source{{12, 21}}),
Block(utils::Vector{
Block(
utils::Vector{
If(Expr(true),
Block(
utils::Vector{
Return(),
Return(Source{{34, 43}}),
},
attr(ast::DiagnosticSeverity::kInfo)),
Else(Block(utils::Vector{
While(
Expr(true), Block(
utils::Vector{
Return(),
Return(Source{{56, 65}}),
},
attr(ast::DiagnosticSeverity::kOff))),
Return(),
Return(Source{{78, 87}}),
}))),
},
attr(ast::DiagnosticSeverity::kWarning)),
}),
},
attr(ast::DiagnosticSeverity::kOff));
EXPECT_TRUE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), R"(34:43 note: code is unreachable
78:87 warning: code is unreachable)");
}
TEST_F(ResolverDiagnosticControlTest, UnrecognizedRuleName_Directive) { TEST_F(ResolverDiagnosticControlTest, UnrecognizedRuleName_Directive) {
DiagnosticDirective(ast::DiagnosticSeverity::kError, DiagnosticDirective(ast::DiagnosticSeverity::kError,
Expr(Source{{12, 34}}, "chromium_unreachable_cod")); Expr(Source{{12, 34}}, "chromium_unreachable_cod"));

View File

@ -3845,6 +3845,43 @@ SEM* Resolver::StatementScope(const ast::Statement* ast, SEM* sem, F&& callback)
auto* as_compound = As<sem::CompoundStatement, CastFlags::kDontErrorOnImpossibleCast>(sem); auto* as_compound = As<sem::CompoundStatement, CastFlags::kDontErrorOnImpossibleCast>(sem);
// Helper to handle attributes that are supported on certain types of statement.
auto handle_attributes = [&](auto* stmt, sem::Statement* sem_stmt, const char* use) {
for (auto* attr : stmt->attributes) {
Mark(attr);
if (auto* dc = attr->template As<ast::DiagnosticAttribute>()) {
Mark(dc->control);
if (!DiagnosticControl(dc->control)) {
return false;
}
} else {
std::ostringstream ss;
ss << "attribute is not valid for " << use;
AddError(ss.str(), attr->source);
return false;
}
}
if (!validator_.NoDuplicateAttributes(stmt->attributes)) {
return false;
}
ApplyDiagnosticSeverities(sem_stmt);
return true;
};
// Handle attributes, if necessary.
// Some statements can take diagnostic filtering attributes, so push a new diagnostic filter
// scope to capture them.
validator_.DiagnosticFilters().Push();
TINT_DEFER(validator_.DiagnosticFilters().Pop());
if (!Switch(
ast, //
[&](const ast::BlockStatement* block) {
return handle_attributes(block, sem, "block statements");
},
[&](Default) { return true; })) {
return nullptr;
}
TINT_SCOPED_ASSIGNMENT(current_statement_, sem); TINT_SCOPED_ASSIGNMENT(current_statement_, sem);
TINT_SCOPED_ASSIGNMENT(current_compound_statement_, TINT_SCOPED_ASSIGNMENT(current_compound_statement_,
as_compound ? as_compound : current_compound_statement_); as_compound ? as_compound : current_compound_statement_);

View File

@ -7942,6 +7942,32 @@ TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnFunction) {
} }
} }
TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnBlock) {
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;
fn foo() {
if (non_uniform == 42))"
<< "@diagnostic(" << param << ", derivative_uniformity)"
<< R"({
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, INSTANTIATE_TEST_SUITE_P(UniformityAnalysisTest,
UniformityAnalysisDiagnosticFilterTest, UniformityAnalysisDiagnosticFilterTest,
::testing::Values(ast::DiagnosticSeverity::kError, ::testing::Values(ast::DiagnosticSeverity::kError,

View File

@ -30,26 +30,44 @@ class DiagnosticSeverityTest : public TestHelper {
void Run(ast::DiagnosticSeverity global_severity) { void Run(ast::DiagnosticSeverity global_severity) {
// @diagnostic(off, chromium_unreachable_code) // @diagnostic(off, chromium_unreachable_code)
// fn foo() { // fn foo() {
// return; // @diagnostic(info, chromium_unreachable_code) {
// if (true) @diagnostic(warning, chromium_unreachable_code) {
// return;
// }
// }
// } // }
// //
// fn bar() { // fn bar() {
// return; // {
// if (true) {
// return;
// }
// }
// } // }
auto rule = ast::DiagnosticRule::kChromiumUnreachableCode; auto rule = ast::DiagnosticRule::kChromiumUnreachableCode;
auto func_severity = ast::DiagnosticSeverity::kOff; auto func_severity = ast::DiagnosticSeverity::kOff;
auto block_severity = ast::DiagnosticSeverity::kInfo;
auto if_severity = ast::DiagnosticSeverity::kInfo;
auto attr = [&](auto severity) {
return utils::Vector{DiagnosticAttribute(severity, Expr("chromium_unreachable_code"))};
};
auto* return_1 = Return(); auto* return_1 = Return();
auto* return_2 = Return(); auto* if_1 = If(Expr(true), Block(utils::Vector{return_1}, attr(if_severity)));
auto* block_1 = Block(utils::Vector{if_1}, attr(block_severity));
auto* func_attr = DiagnosticAttribute(func_severity, Expr("chromium_unreachable_code")); 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* foo = Func("foo", {}, ty.void_(), utils::Vector{block_1}, utils::Vector{func_attr});
auto* return_2 = Return();
auto* bar = Func("bar", {}, ty.void_(), utils::Vector{return_2}); auto* bar = Func("bar", {}, ty.void_(), utils::Vector{return_2});
auto p = Build(); auto p = Build();
EXPECT_TRUE(p.IsValid()) << p.Diagnostics().str(); EXPECT_TRUE(p.IsValid()) << p.Diagnostics().str();
EXPECT_EQ(p.Sem().DiagnosticSeverity(foo, rule), func_severity); EXPECT_EQ(p.Sem().DiagnosticSeverity(foo, rule), func_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(return_1, rule), func_severity); EXPECT_EQ(p.Sem().DiagnosticSeverity(block_1, rule), block_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(if_1, rule), block_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(return_1, rule), if_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(bar, rule), global_severity); EXPECT_EQ(p.Sem().DiagnosticSeverity(bar, rule), global_severity);
EXPECT_EQ(p.Sem().DiagnosticSeverity(return_2, rule), global_severity); EXPECT_EQ(p.Sem().DiagnosticSeverity(return_2, rule), global_severity);
} }

View File

@ -353,7 +353,10 @@ bool GeneratorImpl::EmitFunction(const ast::Function* func) {
} }
if (func->body) { if (func->body) {
out << " {"; out << " ";
if (!EmitBlockHeader(out, func->body)) {
return false;
}
} }
} }
@ -979,7 +982,12 @@ bool GeneratorImpl::EmitUnaryOp(std::ostream& out, const ast::UnaryOpExpression*
} }
bool GeneratorImpl::EmitBlock(const ast::BlockStatement* stmt) { bool GeneratorImpl::EmitBlock(const ast::BlockStatement* stmt) {
line() << "{"; {
auto out = line();
if (!EmitBlockHeader(out, stmt)) {
return false;
}
}
if (!EmitStatementsWithIndent(stmt->statements)) { if (!EmitStatementsWithIndent(stmt->statements)) {
return false; return false;
} }
@ -988,6 +996,17 @@ bool GeneratorImpl::EmitBlock(const ast::BlockStatement* stmt) {
return true; return true;
} }
bool GeneratorImpl::EmitBlockHeader(std::ostream& out, const ast::BlockStatement* stmt) {
if (!stmt->attributes.IsEmpty()) {
if (!EmitAttributes(out, stmt->attributes)) {
return false;
}
out << " ";
}
out << "{";
return true;
}
bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) { bool GeneratorImpl::EmitStatement(const ast::Statement* stmt) {
return Switch( return Switch(
stmt, // stmt, //
@ -1072,7 +1091,11 @@ bool GeneratorImpl::EmitBreakIf(const ast::BreakIfStatement* b) {
bool GeneratorImpl::EmitCase(const ast::CaseStatement* stmt) { bool GeneratorImpl::EmitCase(const ast::CaseStatement* stmt) {
if (stmt->selectors.Length() == 1 && stmt->ContainsDefault()) { if (stmt->selectors.Length() == 1 && stmt->ContainsDefault()) {
line() << "default: {"; auto out = line();
out << "default: ";
if (!EmitBlockHeader(out, stmt->body)) {
return false;
}
} else { } else {
auto out = line(); auto out = line();
out << "case "; out << "case ";
@ -1091,7 +1114,10 @@ bool GeneratorImpl::EmitCase(const ast::CaseStatement* stmt) {
return false; return false;
} }
} }
out << ": {"; out << ": ";
if (!EmitBlockHeader(out, stmt->body)) {
return false;
}
} }
if (!EmitStatementsWithIndent(stmt->body->statements)) { if (!EmitStatementsWithIndent(stmt->body->statements)) {
return false; return false;
@ -1135,7 +1161,10 @@ bool GeneratorImpl::EmitIf(const ast::IfStatement* stmt) {
if (!EmitExpression(out, stmt->condition)) { if (!EmitExpression(out, stmt->condition)) {
return false; return false;
} }
out << ") {"; out << ") ";
if (!EmitBlockHeader(out, stmt->body)) {
return false;
}
} }
if (!EmitStatementsWithIndent(stmt->body->statements)) { if (!EmitStatementsWithIndent(stmt->body->statements)) {
@ -1151,15 +1180,25 @@ bool GeneratorImpl::EmitIf(const ast::IfStatement* stmt) {
if (!EmitExpression(out, elseif->condition)) { if (!EmitExpression(out, elseif->condition)) {
return false; return false;
} }
out << ") {"; out << ") ";
if (!EmitBlockHeader(out, elseif->body)) {
return false;
}
} }
if (!EmitStatementsWithIndent(elseif->body->statements)) { if (!EmitStatementsWithIndent(elseif->body->statements)) {
return false; return false;
} }
e = elseif->else_statement; e = elseif->else_statement;
} else { } else {
line() << "} else {"; auto* body = e->As<ast::BlockStatement>();
if (!EmitStatementsWithIndent(e->As<ast::BlockStatement>()->statements)) { {
auto out = line();
out << "} else ";
if (!EmitBlockHeader(out, body)) {
return false;
}
}
if (!EmitStatementsWithIndent(body->statements)) {
return false; return false;
} }
break; break;
@ -1270,7 +1309,10 @@ bool GeneratorImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
break; break;
} }
} }
out << " {"; out << " ";
if (!EmitBlockHeader(out, stmt->body)) {
return false;
}
} }
if (!EmitStatementsWithIndent(stmt->body->statements)) { if (!EmitStatementsWithIndent(stmt->body->statements)) {
@ -1294,7 +1336,10 @@ bool GeneratorImpl::EmitWhile(const ast::WhileStatement* stmt) {
return false; return false;
} }
} }
out << " {"; out << " ";
if (!EmitBlockHeader(out, stmt->body)) {
return false;
}
} }
if (!EmitStatementsWithIndent(stmt->body->statements)) { if (!EmitStatementsWithIndent(stmt->body->statements)) {

View File

@ -93,6 +93,11 @@ class GeneratorImpl : public TextGenerator {
/// @param stmt the statement to emit /// @param stmt the statement to emit
/// @returns true if the statement was emitted successfully /// @returns true if the statement was emitted successfully
bool EmitBlock(const ast::BlockStatement* stmt); bool EmitBlock(const ast::BlockStatement* stmt);
/// Handles emitting the start of a block statement (including attributes)
/// @param out the output stream to write the header to
/// @param stmt the block statement to emit the header for
/// @returns true if the statement was emitted successfully
bool EmitBlockHeader(std::ostream& out, const ast::BlockStatement* stmt);
/// Handles a break statement /// Handles a break statement
/// @param stmt the statement to emit /// @param stmt the statement to emit
/// @returns true if the statement was emitted successfully /// @returns true if the statement was emitted successfully

View File

@ -0,0 +1,13 @@
@group(0) @binding(1) var t : texture_2d<f32>;
@group(0) @binding(2) var s : sampler;
@fragment
fn main(@location(0) x : f32) {
switch (i32(x)) {
case 0 @diagnostic(warning, derivative_uniformity) {
_ = textureSample(t, s, vec2(0, 0));
}
default {
}
}
}

View File

@ -0,0 +1,34 @@
diagnostic_filtering/case_body_attribute.wgsl:8:11 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/case_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
switch (i32(x)) {
^^^^^^
diagnostic_filtering/case_body_attribute.wgsl:6:15 note: user-defined input 'x' of 'main' may be non-uniform
switch (i32(x)) {
^
Texture2D<float4> t : register(t1, space0);
SamplerState s : register(s2, space0);
struct tint_symbol_1 {
float x : TEXCOORD0;
};
void main_inner(float x) {
switch(int(x)) {
case 0: {
break;
}
default: {
break;
}
}
}
void main(tint_symbol_1 tint_symbol) {
main_inner(tint_symbol.x);
return;
}

View File

@ -0,0 +1,34 @@
diagnostic_filtering/case_body_attribute.wgsl:8:11 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/case_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
switch (i32(x)) {
^^^^^^
diagnostic_filtering/case_body_attribute.wgsl:6:15 note: user-defined input 'x' of 'main' may be non-uniform
switch (i32(x)) {
^
Texture2D<float4> t : register(t1, space0);
SamplerState s : register(s2, space0);
struct tint_symbol_1 {
float x : TEXCOORD0;
};
void main_inner(float x) {
switch(int(x)) {
case 0: {
break;
}
default: {
break;
}
}
}
void main(tint_symbol_1 tint_symbol) {
main_inner(tint_symbol.x);
return;
}

View File

@ -0,0 +1,31 @@
diagnostic_filtering/case_body_attribute.wgsl:8:11 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/case_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
switch (i32(x)) {
^^^^^^
diagnostic_filtering/case_body_attribute.wgsl:6:15 note: user-defined input 'x' of 'main' may be non-uniform
switch (i32(x)) {
^
#version 310 es
precision mediump float;
layout(location = 0) in float x_1;
void tint_symbol(float x) {
switch(int(x)) {
case 0: {
break;
}
default: {
break;
}
}
}
void main() {
tint_symbol(x_1);
return;
}

View File

@ -0,0 +1,35 @@
diagnostic_filtering/case_body_attribute.wgsl:8:11 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/case_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
switch (i32(x)) {
^^^^^^
diagnostic_filtering/case_body_attribute.wgsl:6:15 note: user-defined input 'x' of 'main' may be non-uniform
switch (i32(x)) {
^
#include <metal_stdlib>
using namespace metal;
struct tint_symbol_2 {
float x [[user(locn0)]];
};
void tint_symbol_inner(float x) {
switch(int(x)) {
case 0: {
break;
}
default: {
break;
}
}
}
fragment void tint_symbol(tint_symbol_2 tint_symbol_1 [[stage_in]]) {
tint_symbol_inner(tint_symbol_1.x);
return;
}

View File

@ -0,0 +1,64 @@
diagnostic_filtering/case_body_attribute.wgsl:8:11 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/case_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
switch (i32(x)) {
^^^^^^
diagnostic_filtering/case_body_attribute.wgsl:6:15 note: user-defined input 'x' of 'main' may be non-uniform
switch (i32(x)) {
^
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 0
; Bound: 25
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %x_1
OpExecutionMode %main OriginUpperLeft
OpName %x_1 "x_1"
OpName %t "t"
OpName %s "s"
OpName %main_inner "main_inner"
OpName %x "x"
OpName %main "main"
OpDecorate %x_1 Location 0
OpDecorate %t DescriptorSet 0
OpDecorate %t Binding 1
OpDecorate %s DescriptorSet 0
OpDecorate %s Binding 2
%float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
%x_1 = OpVariable %_ptr_Input_float Input
%6 = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_6 = OpTypePointer UniformConstant %6
%t = OpVariable %_ptr_UniformConstant_6 UniformConstant
%9 = OpTypeSampler
%_ptr_UniformConstant_9 = OpTypePointer UniformConstant %9
%s = OpVariable %_ptr_UniformConstant_9 UniformConstant
%void = OpTypeVoid
%10 = OpTypeFunction %void %float
%int = OpTypeInt 32 1
%20 = OpTypeFunction %void
%main_inner = OpFunction %void None %10
%x = OpFunctionParameter %float
%14 = OpLabel
%16 = OpConvertFToS %int %x
OpSelectionMerge %15 None
OpSwitch %16 %18 0 %19
%19 = OpLabel
OpBranch %15
%18 = OpLabel
OpBranch %15
%15 = OpLabel
OpReturn
OpFunctionEnd
%main = OpFunction %void None %20
%22 = OpLabel
%24 = OpLoad %float %x_1
%23 = OpFunctionCall %void %main_inner %24
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,26 @@
diagnostic_filtering/case_body_attribute.wgsl:8:11 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/case_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
switch (i32(x)) {
^^^^^^
diagnostic_filtering/case_body_attribute.wgsl:6:15 note: user-defined input 'x' of 'main' may be non-uniform
switch (i32(x)) {
^
@group(0) @binding(1) var t : texture_2d<f32>;
@group(0) @binding(2) var s : sampler;
@fragment
fn main(@location(0) x : f32) {
switch(i32(x)) {
case 0: @diagnostic(warning, derivative_uniformity) {
_ = textureSample(t, s, vec2(0, 0));
}
default: {
}
}
}

View File

@ -0,0 +1,11 @@
@group(0) @binding(1) var t : texture_2d<f32>;
@group(0) @binding(2) var s : sampler;
@fragment
fn main(@location(0) x : f32) {
@diagnostic(warning, derivative_uniformity) {
if (x > 0) {
_ = textureSample(t, s, vec2(0, 0));
}
}
}

View File

@ -0,0 +1,30 @@
diagnostic_filtering/compound_statement_attribute.wgsl:8:11 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/compound_statement_attribute.wgsl:7:5 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/compound_statement_attribute.wgsl:7:9 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
Texture2D<float4> t : register(t1, space0);
SamplerState s : register(s2, space0);
struct tint_symbol_1 {
float x : TEXCOORD0;
};
void main_inner(float x) {
{
if ((x > 0.0f)) {
}
}
}
void main(tint_symbol_1 tint_symbol) {
main_inner(tint_symbol.x);
return;
}

View File

@ -0,0 +1,30 @@
diagnostic_filtering/compound_statement_attribute.wgsl:8:11 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/compound_statement_attribute.wgsl:7:5 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/compound_statement_attribute.wgsl:7:9 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
Texture2D<float4> t : register(t1, space0);
SamplerState s : register(s2, space0);
struct tint_symbol_1 {
float x : TEXCOORD0;
};
void main_inner(float x) {
{
if ((x > 0.0f)) {
}
}
}
void main(tint_symbol_1 tint_symbol) {
main_inner(tint_symbol.x);
return;
}

View File

@ -0,0 +1,27 @@
diagnostic_filtering/compound_statement_attribute.wgsl:8:11 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/compound_statement_attribute.wgsl:7:5 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/compound_statement_attribute.wgsl:7:9 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
#version 310 es
precision mediump float;
layout(location = 0) in float x_1;
void tint_symbol(float x) {
{
if ((x > 0.0f)) {
}
}
}
void main() {
tint_symbol(x_1);
return;
}

View File

@ -0,0 +1,31 @@
diagnostic_filtering/compound_statement_attribute.wgsl:8:11 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/compound_statement_attribute.wgsl:7:5 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/compound_statement_attribute.wgsl:7:9 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
#include <metal_stdlib>
using namespace metal;
struct tint_symbol_2 {
float x [[user(locn0)]];
};
void tint_symbol_inner(float x) {
{
if ((x > 0.0f)) {
}
}
}
fragment void tint_symbol(tint_symbol_2 tint_symbol_1 [[stage_in]]) {
tint_symbol_inner(tint_symbol_1.x);
return;
}

View File

@ -0,0 +1,63 @@
diagnostic_filtering/compound_statement_attribute.wgsl:8:11 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/compound_statement_attribute.wgsl:7:5 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/compound_statement_attribute.wgsl:7:9 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 0
; Bound: 25
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %x_1
OpExecutionMode %main OriginUpperLeft
OpName %x_1 "x_1"
OpName %t "t"
OpName %s "s"
OpName %main_inner "main_inner"
OpName %x "x"
OpName %main "main"
OpDecorate %x_1 Location 0
OpDecorate %t DescriptorSet 0
OpDecorate %t Binding 1
OpDecorate %s DescriptorSet 0
OpDecorate %s Binding 2
%float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
%x_1 = OpVariable %_ptr_Input_float Input
%6 = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_6 = OpTypePointer UniformConstant %6
%t = OpVariable %_ptr_UniformConstant_6 UniformConstant
%9 = OpTypeSampler
%_ptr_UniformConstant_9 = OpTypePointer UniformConstant %9
%s = OpVariable %_ptr_UniformConstant_9 UniformConstant
%void = OpTypeVoid
%10 = OpTypeFunction %void %float
%15 = OpConstantNull %float
%bool = OpTypeBool
%20 = OpTypeFunction %void
%main_inner = OpFunction %void None %10
%x = OpFunctionParameter %float
%14 = OpLabel
%16 = OpFOrdGreaterThan %bool %x %15
OpSelectionMerge %18 None
OpBranchConditional %16 %19 %18
%19 = OpLabel
OpBranch %18
%18 = OpLabel
OpReturn
OpFunctionEnd
%main = OpFunction %void None %20
%22 = OpLabel
%24 = OpLoad %float %x_1
%23 = OpFunctionCall %void %main_inner %24
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,24 @@
diagnostic_filtering/compound_statement_attribute.wgsl:8:11 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/compound_statement_attribute.wgsl:7:5 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/compound_statement_attribute.wgsl:7:9 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
@group(0) @binding(1) var t : texture_2d<f32>;
@group(0) @binding(2) var s : sampler;
@fragment
fn main(@location(0) x : f32) {
@diagnostic(warning, derivative_uniformity) {
if ((x > 0)) {
_ = textureSample(t, s, vec2(0, 0));
}
}
}

View File

@ -0,0 +1,11 @@
@group(0) @binding(1) var t : texture_2d<f32>;
@group(0) @binding(2) var s : sampler;
@fragment
fn main(@location(0) x : f32) {
switch (i32(x)) {
default @diagnostic(warning, derivative_uniformity) {
_ = textureSample(t, s, vec2(0, 0));
}
}
}

View File

@ -0,0 +1,28 @@
diagnostic_filtering/default_case_body_attribute.wgsl:8:11 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/default_case_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
switch (i32(x)) {
^^^^^^
diagnostic_filtering/default_case_body_attribute.wgsl:6:15 note: user-defined input 'x' of 'main' may be non-uniform
switch (i32(x)) {
^
Texture2D<float4> t : register(t1, space0);
SamplerState s : register(s2, space0);
struct tint_symbol_1 {
float x : TEXCOORD0;
};
void main_inner(float x) {
do {
} while (false);
}
void main(tint_symbol_1 tint_symbol) {
main_inner(tint_symbol.x);
return;
}

View File

@ -0,0 +1,28 @@
diagnostic_filtering/default_case_body_attribute.wgsl:8:11 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/default_case_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
switch (i32(x)) {
^^^^^^
diagnostic_filtering/default_case_body_attribute.wgsl:6:15 note: user-defined input 'x' of 'main' may be non-uniform
switch (i32(x)) {
^
Texture2D<float4> t : register(t1, space0);
SamplerState s : register(s2, space0);
struct tint_symbol_1 {
float x : TEXCOORD0;
};
void main_inner(float x) {
do {
} while (false);
}
void main(tint_symbol_1 tint_symbol) {
main_inner(tint_symbol.x);
return;
}

View File

@ -0,0 +1,28 @@
diagnostic_filtering/default_case_body_attribute.wgsl:8:11 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/default_case_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
switch (i32(x)) {
^^^^^^
diagnostic_filtering/default_case_body_attribute.wgsl:6:15 note: user-defined input 'x' of 'main' may be non-uniform
switch (i32(x)) {
^
#version 310 es
precision mediump float;
layout(location = 0) in float x_1;
void tint_symbol(float x) {
switch(int(x)) {
default: {
break;
}
}
}
void main() {
tint_symbol(x_1);
return;
}

View File

@ -0,0 +1,32 @@
diagnostic_filtering/default_case_body_attribute.wgsl:8:11 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/default_case_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
switch (i32(x)) {
^^^^^^
diagnostic_filtering/default_case_body_attribute.wgsl:6:15 note: user-defined input 'x' of 'main' may be non-uniform
switch (i32(x)) {
^
#include <metal_stdlib>
using namespace metal;
struct tint_symbol_2 {
float x [[user(locn0)]];
};
void tint_symbol_inner(float x) {
switch(int(x)) {
default: {
break;
}
}
}
fragment void tint_symbol(tint_symbol_2 tint_symbol_1 [[stage_in]]) {
tint_symbol_inner(tint_symbol_1.x);
return;
}

View File

@ -0,0 +1,62 @@
diagnostic_filtering/default_case_body_attribute.wgsl:8:11 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/default_case_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
switch (i32(x)) {
^^^^^^
diagnostic_filtering/default_case_body_attribute.wgsl:6:15 note: user-defined input 'x' of 'main' may be non-uniform
switch (i32(x)) {
^
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 0
; Bound: 24
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %x_1
OpExecutionMode %main OriginUpperLeft
OpName %x_1 "x_1"
OpName %t "t"
OpName %s "s"
OpName %main_inner "main_inner"
OpName %x "x"
OpName %main "main"
OpDecorate %x_1 Location 0
OpDecorate %t DescriptorSet 0
OpDecorate %t Binding 1
OpDecorate %s DescriptorSet 0
OpDecorate %s Binding 2
%float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
%x_1 = OpVariable %_ptr_Input_float Input
%6 = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_6 = OpTypePointer UniformConstant %6
%t = OpVariable %_ptr_UniformConstant_6 UniformConstant
%9 = OpTypeSampler
%_ptr_UniformConstant_9 = OpTypePointer UniformConstant %9
%s = OpVariable %_ptr_UniformConstant_9 UniformConstant
%void = OpTypeVoid
%10 = OpTypeFunction %void %float
%int = OpTypeInt 32 1
%19 = OpTypeFunction %void
%main_inner = OpFunction %void None %10
%x = OpFunctionParameter %float
%14 = OpLabel
%16 = OpConvertFToS %int %x
OpSelectionMerge %15 None
OpSwitch %16 %18
%18 = OpLabel
OpBranch %15
%15 = OpLabel
OpReturn
OpFunctionEnd
%main = OpFunction %void None %19
%21 = OpLabel
%23 = OpLoad %float %x_1
%22 = OpFunctionCall %void %main_inner %23
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,24 @@
diagnostic_filtering/default_case_body_attribute.wgsl:8:11 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/default_case_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
switch (i32(x)) {
^^^^^^
diagnostic_filtering/default_case_body_attribute.wgsl:6:15 note: user-defined input 'x' of 'main' may be non-uniform
switch (i32(x)) {
^
@group(0) @binding(1) var t : texture_2d<f32>;
@group(0) @binding(2) var s : sampler;
@fragment
fn main(@location(0) x : f32) {
switch(i32(x)) {
default: @diagnostic(warning, derivative_uniformity) {
_ = textureSample(t, s, vec2(0, 0));
}
}
}

View File

@ -0,0 +1,10 @@
@group(0) @binding(1) var t : texture_2d<f32>;
@group(0) @binding(2) var s : sampler;
@fragment
fn main(@location(0) x : f32) {
if (x > 0) {
} else @diagnostic(warning, derivative_uniformity) {
_ = textureSample(t, s, vec2(0, 0));
}
}

View File

@ -0,0 +1,29 @@
diagnostic_filtering/else_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/else_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/else_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
Texture2D<float4> t : register(t1, space0);
SamplerState s : register(s2, space0);
struct tint_symbol_1 {
float x : TEXCOORD0;
};
void main_inner(float x) {
if ((x > 0.0f)) {
} else {
}
}
void main(tint_symbol_1 tint_symbol) {
main_inner(tint_symbol.x);
return;
}

View File

@ -0,0 +1,29 @@
diagnostic_filtering/else_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/else_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/else_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
Texture2D<float4> t : register(t1, space0);
SamplerState s : register(s2, space0);
struct tint_symbol_1 {
float x : TEXCOORD0;
};
void main_inner(float x) {
if ((x > 0.0f)) {
} else {
}
}
void main(tint_symbol_1 tint_symbol) {
main_inner(tint_symbol.x);
return;
}

View File

@ -0,0 +1,26 @@
diagnostic_filtering/else_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/else_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/else_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
#version 310 es
precision mediump float;
layout(location = 0) in float x_1;
void tint_symbol(float x) {
if ((x > 0.0f)) {
} else {
}
}
void main() {
tint_symbol(x_1);
return;
}

View File

@ -0,0 +1,30 @@
diagnostic_filtering/else_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/else_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/else_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
#include <metal_stdlib>
using namespace metal;
struct tint_symbol_2 {
float x [[user(locn0)]];
};
void tint_symbol_inner(float x) {
if ((x > 0.0f)) {
} else {
}
}
fragment void tint_symbol(tint_symbol_2 tint_symbol_1 [[stage_in]]) {
tint_symbol_inner(tint_symbol_1.x);
return;
}

View File

@ -0,0 +1,65 @@
diagnostic_filtering/else_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/else_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/else_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 0
; Bound: 26
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %x_1
OpExecutionMode %main OriginUpperLeft
OpName %x_1 "x_1"
OpName %t "t"
OpName %s "s"
OpName %main_inner "main_inner"
OpName %x "x"
OpName %main "main"
OpDecorate %x_1 Location 0
OpDecorate %t DescriptorSet 0
OpDecorate %t Binding 1
OpDecorate %s DescriptorSet 0
OpDecorate %s Binding 2
%float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
%x_1 = OpVariable %_ptr_Input_float Input
%6 = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_6 = OpTypePointer UniformConstant %6
%t = OpVariable %_ptr_UniformConstant_6 UniformConstant
%9 = OpTypeSampler
%_ptr_UniformConstant_9 = OpTypePointer UniformConstant %9
%s = OpVariable %_ptr_UniformConstant_9 UniformConstant
%void = OpTypeVoid
%10 = OpTypeFunction %void %float
%15 = OpConstantNull %float
%bool = OpTypeBool
%21 = OpTypeFunction %void
%main_inner = OpFunction %void None %10
%x = OpFunctionParameter %float
%14 = OpLabel
%16 = OpFOrdGreaterThan %bool %x %15
OpSelectionMerge %18 None
OpBranchConditional %16 %19 %20
%19 = OpLabel
OpBranch %18
%20 = OpLabel
OpBranch %18
%18 = OpLabel
OpReturn
OpFunctionEnd
%main = OpFunction %void None %21
%23 = OpLabel
%25 = OpLoad %float %x_1
%24 = OpFunctionCall %void %main_inner %25
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,23 @@
diagnostic_filtering/else_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/else_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/else_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
@group(0) @binding(1) var t : texture_2d<f32>;
@group(0) @binding(2) var s : sampler;
@fragment
fn main(@location(0) x : f32) {
if ((x > 0)) {
} else @diagnostic(warning, derivative_uniformity) {
_ = textureSample(t, s, vec2(0, 0));
}
}

View File

@ -0,0 +1,10 @@
@group(0) @binding(1) var t : texture_2d<f32>;
@group(0) @binding(2) var s : sampler;
@fragment
fn main(@location(0) x : f32) {
if (x > 0) {
} else if (x < 0) @diagnostic(warning, derivative_uniformity) {
_ = textureSample(t, s, vec2(0, 0));
}
}

View File

@ -0,0 +1,31 @@
diagnostic_filtering/else_if_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/else_if_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/else_if_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
Texture2D<float4> t : register(t1, space0);
SamplerState s : register(s2, space0);
struct tint_symbol_1 {
float x : TEXCOORD0;
};
void main_inner(float x) {
if ((x > 0.0f)) {
} else {
if ((x < 0.0f)) {
}
}
}
void main(tint_symbol_1 tint_symbol) {
main_inner(tint_symbol.x);
return;
}

View File

@ -0,0 +1,31 @@
diagnostic_filtering/else_if_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/else_if_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/else_if_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
Texture2D<float4> t : register(t1, space0);
SamplerState s : register(s2, space0);
struct tint_symbol_1 {
float x : TEXCOORD0;
};
void main_inner(float x) {
if ((x > 0.0f)) {
} else {
if ((x < 0.0f)) {
}
}
}
void main(tint_symbol_1 tint_symbol) {
main_inner(tint_symbol.x);
return;
}

View File

@ -0,0 +1,28 @@
diagnostic_filtering/else_if_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/else_if_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/else_if_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
#version 310 es
precision mediump float;
layout(location = 0) in float x_1;
void tint_symbol(float x) {
if ((x > 0.0f)) {
} else {
if ((x < 0.0f)) {
}
}
}
void main() {
tint_symbol(x_1);
return;
}

View File

@ -0,0 +1,32 @@
diagnostic_filtering/else_if_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/else_if_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/else_if_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
#include <metal_stdlib>
using namespace metal;
struct tint_symbol_2 {
float x [[user(locn0)]];
};
void tint_symbol_inner(float x) {
if ((x > 0.0f)) {
} else {
if ((x < 0.0f)) {
}
}
}
fragment void tint_symbol(tint_symbol_2 tint_symbol_1 [[stage_in]]) {
tint_symbol_inner(tint_symbol_1.x);
return;
}

View File

@ -0,0 +1,71 @@
diagnostic_filtering/else_if_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/else_if_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/else_if_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 0
; Bound: 29
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %x_1
OpExecutionMode %main OriginUpperLeft
OpName %x_1 "x_1"
OpName %t "t"
OpName %s "s"
OpName %main_inner "main_inner"
OpName %x "x"
OpName %main "main"
OpDecorate %x_1 Location 0
OpDecorate %t DescriptorSet 0
OpDecorate %t Binding 1
OpDecorate %s DescriptorSet 0
OpDecorate %s Binding 2
%float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
%x_1 = OpVariable %_ptr_Input_float Input
%6 = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_6 = OpTypePointer UniformConstant %6
%t = OpVariable %_ptr_UniformConstant_6 UniformConstant
%9 = OpTypeSampler
%_ptr_UniformConstant_9 = OpTypePointer UniformConstant %9
%s = OpVariable %_ptr_UniformConstant_9 UniformConstant
%void = OpTypeVoid
%10 = OpTypeFunction %void %float
%15 = OpConstantNull %float
%bool = OpTypeBool
%24 = OpTypeFunction %void
%main_inner = OpFunction %void None %10
%x = OpFunctionParameter %float
%14 = OpLabel
%16 = OpFOrdGreaterThan %bool %x %15
OpSelectionMerge %18 None
OpBranchConditional %16 %19 %20
%19 = OpLabel
OpBranch %18
%20 = OpLabel
%21 = OpFOrdLessThan %bool %x %15
OpSelectionMerge %22 None
OpBranchConditional %21 %23 %22
%23 = OpLabel
OpBranch %22
%22 = OpLabel
OpBranch %18
%18 = OpLabel
OpReturn
OpFunctionEnd
%main = OpFunction %void None %24
%26 = OpLabel
%28 = OpLoad %float %x_1
%27 = OpFunctionCall %void %main_inner %28
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,23 @@
diagnostic_filtering/else_if_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/else_if_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/else_if_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
@group(0) @binding(1) var t : texture_2d<f32>;
@group(0) @binding(2) var s : sampler;
@fragment
fn main(@location(0) x : f32) {
if ((x > 0)) {
} else if ((x < 0)) @diagnostic(warning, derivative_uniformity) {
_ = textureSample(t, s, vec2(0, 0));
}
}

View File

@ -0,0 +1,10 @@
@group(0) @binding(1) var t : texture_2d<f32>;
@group(0) @binding(2) var s : sampler;
@fragment
fn main(@location(0) x : f32) {
var v = vec4<f32>(0);
for (; x > v.x; ) @diagnostic(warning, derivative_uniformity) {
v = textureSample(t, s, vec2(0, 0));
}
}

View File

@ -0,0 +1,32 @@
diagnostic_filtering/for_loop_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/for_loop_body_attribute.wgsl:7:3 note: control flow depends on possibly non-uniform value
for (; x > v.x; ) @diagnostic(warning, derivative_uniformity) {
^^^
diagnostic_filtering/for_loop_body_attribute.wgsl:8:9 note: return value of 'textureSample' may be non-uniform
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
Texture2D<float4> t : register(t1, space0);
SamplerState s : register(s2, space0);
struct tint_symbol_1 {
float x : TEXCOORD0;
};
void main_inner(float x) {
float4 v = (0.0f).xxxx;
{
for(; (x > v.x); ) {
v = t.Sample(s, (0.0f).xx);
}
}
}
void main(tint_symbol_1 tint_symbol) {
main_inner(tint_symbol.x);
return;
}

View File

@ -0,0 +1,34 @@
SKIP: FXC rejects non-uniform texture sample operation in output
diagnostic_filtering/for_loop_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/for_loop_body_attribute.wgsl:7:3 note: control flow depends on possibly non-uniform value
for (; x > v.x; ) @diagnostic(warning, derivative_uniformity) {
^^^
diagnostic_filtering/for_loop_body_attribute.wgsl:8:9 note: return value of 'textureSample' may be non-uniform
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
Texture2D<float4> t : register(t1, space0);
SamplerState s : register(s2, space0);
struct tint_symbol_1 {
float x : TEXCOORD0;
};
void main_inner(float x) {
float4 v = (0.0f).xxxx;
{
for(; (x > v.x); ) {
v = t.Sample(s, (0.0f).xx);
}
}
}
void main(tint_symbol_1 tint_symbol) {
main_inner(tint_symbol.x);
return;
}

View File

@ -0,0 +1,31 @@
diagnostic_filtering/for_loop_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/for_loop_body_attribute.wgsl:7:3 note: control flow depends on possibly non-uniform value
for (; x > v.x; ) @diagnostic(warning, derivative_uniformity) {
^^^
diagnostic_filtering/for_loop_body_attribute.wgsl:8:9 note: return value of 'textureSample' may be non-uniform
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
#version 310 es
precision mediump float;
layout(location = 0) in float x_1;
uniform highp sampler2D t_s;
void tint_symbol(float x) {
vec4 v = vec4(0.0f);
{
for(; (x > v.x); ) {
v = texture(t_s, vec2(0.0f));
}
}
}
void main() {
tint_symbol(x_1);
return;
}

View File

@ -0,0 +1,31 @@
diagnostic_filtering/for_loop_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/for_loop_body_attribute.wgsl:7:3 note: control flow depends on possibly non-uniform value
for (; x > v.x; ) @diagnostic(warning, derivative_uniformity) {
^^^
diagnostic_filtering/for_loop_body_attribute.wgsl:8:9 note: return value of 'textureSample' may be non-uniform
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
#include <metal_stdlib>
using namespace metal;
struct tint_symbol_2 {
float x [[user(locn0)]];
};
void tint_symbol_inner(float x, texture2d<float, access::sample> tint_symbol_3, sampler tint_symbol_4) {
float4 v = float4(0.0f);
for(; (x > v[0]); ) {
v = tint_symbol_3.sample(tint_symbol_4, float2(0.0f));
}
}
fragment void tint_symbol(texture2d<float, access::sample> tint_symbol_5 [[texture(0)]], sampler tint_symbol_6 [[sampler(0)]], tint_symbol_2 tint_symbol_1 [[stage_in]]) {
tint_symbol_inner(tint_symbol_1.x, tint_symbol_5, tint_symbol_6);
return;
}

View File

@ -0,0 +1,91 @@
diagnostic_filtering/for_loop_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/for_loop_body_attribute.wgsl:7:3 note: control flow depends on possibly non-uniform value
for (; x > v.x; ) @diagnostic(warning, derivative_uniformity) {
^^^
diagnostic_filtering/for_loop_body_attribute.wgsl:8:9 note: return value of 'textureSample' may be non-uniform
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 0
; Bound: 45
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %x_1
OpExecutionMode %main OriginUpperLeft
OpName %x_1 "x_1"
OpName %t "t"
OpName %s "s"
OpName %main_inner "main_inner"
OpName %x "x"
OpName %v "v"
OpName %main "main"
OpDecorate %x_1 Location 0
OpDecorate %t DescriptorSet 0
OpDecorate %t Binding 1
OpDecorate %s DescriptorSet 0
OpDecorate %s Binding 2
%float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
%x_1 = OpVariable %_ptr_Input_float Input
%6 = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_6 = OpTypePointer UniformConstant %6
%t = OpVariable %_ptr_UniformConstant_6 UniformConstant
%9 = OpTypeSampler
%_ptr_UniformConstant_9 = OpTypePointer UniformConstant %9
%s = OpVariable %_ptr_UniformConstant_9 UniformConstant
%void = OpTypeVoid
%10 = OpTypeFunction %void %float
%v4float = OpTypeVector %float 4
%16 = OpConstantNull %v4float
%_ptr_Function_v4float = OpTypePointer Function %v4float
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Function_float = OpTypePointer Function %float
%bool = OpTypeBool
%36 = OpTypeSampledImage %6
%v2float = OpTypeVector %float 2
%39 = OpConstantNull %v2float
%40 = OpTypeFunction %void
%main_inner = OpFunction %void None %10
%x = OpFunctionParameter %float
%14 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function %16
OpStore %v %16
OpBranch %19
%19 = OpLabel
OpLoopMerge %20 %21 None
OpBranch %22
%22 = OpLabel
%27 = OpAccessChain %_ptr_Function_float %v %uint_0
%28 = OpLoad %float %27
%29 = OpFOrdGreaterThan %bool %x %28
%23 = OpLogicalNot %bool %29
OpSelectionMerge %31 None
OpBranchConditional %23 %32 %31
%32 = OpLabel
OpBranch %20
%31 = OpLabel
%34 = OpLoad %9 %s
%35 = OpLoad %6 %t
%37 = OpSampledImage %36 %35 %34
%33 = OpImageSampleImplicitLod %v4float %37 %39
OpStore %v %33
OpBranch %21
%21 = OpLabel
OpBranch %19
%20 = OpLabel
OpReturn
OpFunctionEnd
%main = OpFunction %void None %40
%42 = OpLabel
%44 = OpLoad %float %x_1
%43 = OpFunctionCall %void %main_inner %44
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,23 @@
diagnostic_filtering/for_loop_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/for_loop_body_attribute.wgsl:7:3 note: control flow depends on possibly non-uniform value
for (; x > v.x; ) @diagnostic(warning, derivative_uniformity) {
^^^
diagnostic_filtering/for_loop_body_attribute.wgsl:8:9 note: return value of 'textureSample' may be non-uniform
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
@group(0) @binding(1) var t : texture_2d<f32>;
@group(0) @binding(2) var s : sampler;
@fragment
fn main(@location(0) x : f32) {
var v = vec4<f32>(0);
for(; (x > v.x); ) @diagnostic(warning, derivative_uniformity) {
v = textureSample(t, s, vec2(0, 0));
}
}

View File

@ -0,0 +1,9 @@
@group(0) @binding(1) var t : texture_2d<f32>;
@group(0) @binding(2) var s : sampler;
@fragment
fn main(@location(0) x : f32) @diagnostic(warning, derivative_uniformity) {
if (x > 0) {
_ = textureSample(t, s, vec2(0, 0));
}
}

View File

@ -0,0 +1,28 @@
diagnostic_filtering/function_body_attribute.wgsl:7:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/function_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/function_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
Texture2D<float4> t : register(t1, space0);
SamplerState s : register(s2, space0);
struct tint_symbol_1 {
float x : TEXCOORD0;
};
void main_inner(float x) {
if ((x > 0.0f)) {
}
}
void main(tint_symbol_1 tint_symbol) {
main_inner(tint_symbol.x);
return;
}

View File

@ -0,0 +1,28 @@
diagnostic_filtering/function_body_attribute.wgsl:7:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/function_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/function_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
Texture2D<float4> t : register(t1, space0);
SamplerState s : register(s2, space0);
struct tint_symbol_1 {
float x : TEXCOORD0;
};
void main_inner(float x) {
if ((x > 0.0f)) {
}
}
void main(tint_symbol_1 tint_symbol) {
main_inner(tint_symbol.x);
return;
}

View File

@ -0,0 +1,25 @@
diagnostic_filtering/function_body_attribute.wgsl:7:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/function_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/function_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
#version 310 es
precision mediump float;
layout(location = 0) in float x_1;
void tint_symbol(float x) {
if ((x > 0.0f)) {
}
}
void main() {
tint_symbol(x_1);
return;
}

View File

@ -0,0 +1,29 @@
diagnostic_filtering/function_body_attribute.wgsl:7:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/function_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/function_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
#include <metal_stdlib>
using namespace metal;
struct tint_symbol_2 {
float x [[user(locn0)]];
};
void tint_symbol_inner(float x) {
if ((x > 0.0f)) {
}
}
fragment void tint_symbol(tint_symbol_2 tint_symbol_1 [[stage_in]]) {
tint_symbol_inner(tint_symbol_1.x);
return;
}

View File

@ -0,0 +1,63 @@
diagnostic_filtering/function_body_attribute.wgsl:7:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/function_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/function_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 0
; Bound: 25
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %x_1
OpExecutionMode %main OriginUpperLeft
OpName %x_1 "x_1"
OpName %t "t"
OpName %s "s"
OpName %main_inner "main_inner"
OpName %x "x"
OpName %main "main"
OpDecorate %x_1 Location 0
OpDecorate %t DescriptorSet 0
OpDecorate %t Binding 1
OpDecorate %s DescriptorSet 0
OpDecorate %s Binding 2
%float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
%x_1 = OpVariable %_ptr_Input_float Input
%6 = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_6 = OpTypePointer UniformConstant %6
%t = OpVariable %_ptr_UniformConstant_6 UniformConstant
%9 = OpTypeSampler
%_ptr_UniformConstant_9 = OpTypePointer UniformConstant %9
%s = OpVariable %_ptr_UniformConstant_9 UniformConstant
%void = OpTypeVoid
%10 = OpTypeFunction %void %float
%15 = OpConstantNull %float
%bool = OpTypeBool
%20 = OpTypeFunction %void
%main_inner = OpFunction %void None %10
%x = OpFunctionParameter %float
%14 = OpLabel
%16 = OpFOrdGreaterThan %bool %x %15
OpSelectionMerge %18 None
OpBranchConditional %16 %19 %18
%19 = OpLabel
OpBranch %18
%18 = OpLabel
OpReturn
OpFunctionEnd
%main = OpFunction %void None %20
%22 = OpLabel
%24 = OpLoad %float %x_1
%23 = OpFunctionCall %void %main_inner %24
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,22 @@
diagnostic_filtering/function_body_attribute.wgsl:7:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/function_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) {
^^
diagnostic_filtering/function_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) {
^
@group(0) @binding(1) var t : texture_2d<f32>;
@group(0) @binding(2) var s : sampler;
@fragment
fn main(@location(0) x : f32) @diagnostic(warning, derivative_uniformity) {
if ((x > 0)) {
_ = textureSample(t, s, vec2(0, 0));
}
}

View File

@ -0,0 +1,9 @@
@group(0) @binding(1) var t : texture_2d<f32>;
@group(0) @binding(2) var s : sampler;
@fragment
fn main(@location(0) x : f32) {
if (x > 0) @diagnostic(warning, derivative_uniformity) {
_ = textureSample(t, s, vec2(0, 0));
}
}

View File

@ -0,0 +1,28 @@
diagnostic_filtering/if_body_attribute.wgsl:7:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/if_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) @diagnostic(warning, derivative_uniformity) {
^^
diagnostic_filtering/if_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) @diagnostic(warning, derivative_uniformity) {
^
Texture2D<float4> t : register(t1, space0);
SamplerState s : register(s2, space0);
struct tint_symbol_1 {
float x : TEXCOORD0;
};
void main_inner(float x) {
if ((x > 0.0f)) {
}
}
void main(tint_symbol_1 tint_symbol) {
main_inner(tint_symbol.x);
return;
}

View File

@ -0,0 +1,28 @@
diagnostic_filtering/if_body_attribute.wgsl:7:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/if_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) @diagnostic(warning, derivative_uniformity) {
^^
diagnostic_filtering/if_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) @diagnostic(warning, derivative_uniformity) {
^
Texture2D<float4> t : register(t1, space0);
SamplerState s : register(s2, space0);
struct tint_symbol_1 {
float x : TEXCOORD0;
};
void main_inner(float x) {
if ((x > 0.0f)) {
}
}
void main(tint_symbol_1 tint_symbol) {
main_inner(tint_symbol.x);
return;
}

View File

@ -0,0 +1,25 @@
diagnostic_filtering/if_body_attribute.wgsl:7:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/if_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) @diagnostic(warning, derivative_uniformity) {
^^
diagnostic_filtering/if_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) @diagnostic(warning, derivative_uniformity) {
^
#version 310 es
precision mediump float;
layout(location = 0) in float x_1;
void tint_symbol(float x) {
if ((x > 0.0f)) {
}
}
void main() {
tint_symbol(x_1);
return;
}

View File

@ -0,0 +1,29 @@
diagnostic_filtering/if_body_attribute.wgsl:7:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/if_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) @diagnostic(warning, derivative_uniformity) {
^^
diagnostic_filtering/if_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) @diagnostic(warning, derivative_uniformity) {
^
#include <metal_stdlib>
using namespace metal;
struct tint_symbol_2 {
float x [[user(locn0)]];
};
void tint_symbol_inner(float x) {
if ((x > 0.0f)) {
}
}
fragment void tint_symbol(tint_symbol_2 tint_symbol_1 [[stage_in]]) {
tint_symbol_inner(tint_symbol_1.x);
return;
}

View File

@ -0,0 +1,63 @@
diagnostic_filtering/if_body_attribute.wgsl:7:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/if_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) @diagnostic(warning, derivative_uniformity) {
^^
diagnostic_filtering/if_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) @diagnostic(warning, derivative_uniformity) {
^
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 0
; Bound: 25
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %x_1
OpExecutionMode %main OriginUpperLeft
OpName %x_1 "x_1"
OpName %t "t"
OpName %s "s"
OpName %main_inner "main_inner"
OpName %x "x"
OpName %main "main"
OpDecorate %x_1 Location 0
OpDecorate %t DescriptorSet 0
OpDecorate %t Binding 1
OpDecorate %s DescriptorSet 0
OpDecorate %s Binding 2
%float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
%x_1 = OpVariable %_ptr_Input_float Input
%6 = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_6 = OpTypePointer UniformConstant %6
%t = OpVariable %_ptr_UniformConstant_6 UniformConstant
%9 = OpTypeSampler
%_ptr_UniformConstant_9 = OpTypePointer UniformConstant %9
%s = OpVariable %_ptr_UniformConstant_9 UniformConstant
%void = OpTypeVoid
%10 = OpTypeFunction %void %float
%15 = OpConstantNull %float
%bool = OpTypeBool
%20 = OpTypeFunction %void
%main_inner = OpFunction %void None %10
%x = OpFunctionParameter %float
%14 = OpLabel
%16 = OpFOrdGreaterThan %bool %x %15
OpSelectionMerge %18 None
OpBranchConditional %16 %19 %18
%19 = OpLabel
OpBranch %18
%18 = OpLabel
OpReturn
OpFunctionEnd
%main = OpFunction %void None %20
%22 = OpLabel
%24 = OpLoad %float %x_1
%23 = OpFunctionCall %void %main_inner %24
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,22 @@
diagnostic_filtering/if_body_attribute.wgsl:7:9 warning: 'textureSample' must only be called from uniform control flow
_ = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/if_body_attribute.wgsl:6:3 note: control flow depends on possibly non-uniform value
if (x > 0) @diagnostic(warning, derivative_uniformity) {
^^
diagnostic_filtering/if_body_attribute.wgsl:6:7 note: user-defined input 'x' of 'main' may be non-uniform
if (x > 0) @diagnostic(warning, derivative_uniformity) {
^
@group(0) @binding(1) var t : texture_2d<f32>;
@group(0) @binding(2) var s : sampler;
@fragment
fn main(@location(0) x : f32) {
if ((x > 0)) @diagnostic(warning, derivative_uniformity) {
_ = textureSample(t, s, vec2(0, 0));
}
}

View File

@ -0,0 +1,10 @@
@group(0) @binding(1) var t : texture_2d<f32>;
@group(0) @binding(2) var s : sampler;
@fragment
fn main(@location(0) x : f32) {
var v = vec4<f32>(0);
while (x > v.x) @diagnostic(warning, derivative_uniformity) {
v = textureSample(t, s, vec2(0, 0));
}
}

View File

@ -0,0 +1,30 @@
diagnostic_filtering/while_loop_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/while_loop_body_attribute.wgsl:7:3 note: control flow depends on possibly non-uniform value
while (x > v.x) @diagnostic(warning, derivative_uniformity) {
^^^^^
diagnostic_filtering/while_loop_body_attribute.wgsl:8:9 note: return value of 'textureSample' may be non-uniform
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
Texture2D<float4> t : register(t1, space0);
SamplerState s : register(s2, space0);
struct tint_symbol_1 {
float x : TEXCOORD0;
};
void main_inner(float x) {
float4 v = (0.0f).xxxx;
while((x > v.x)) {
v = t.Sample(s, (0.0f).xx);
}
}
void main(tint_symbol_1 tint_symbol) {
main_inner(tint_symbol.x);
return;
}

View File

@ -0,0 +1,32 @@
SKIP: FXC rejects non-uniform texture sample operation in output
diagnostic_filtering/while_loop_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/while_loop_body_attribute.wgsl:7:3 note: control flow depends on possibly non-uniform value
while (x > v.x) @diagnostic(warning, derivative_uniformity) {
^^^^^
diagnostic_filtering/while_loop_body_attribute.wgsl:8:9 note: return value of 'textureSample' may be non-uniform
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
Texture2D<float4> t : register(t1, space0);
SamplerState s : register(s2, space0);
struct tint_symbol_1 {
float x : TEXCOORD0;
};
void main_inner(float x) {
float4 v = (0.0f).xxxx;
while((x > v.x)) {
v = t.Sample(s, (0.0f).xx);
}
}
void main(tint_symbol_1 tint_symbol) {
main_inner(tint_symbol.x);
return;
}

View File

@ -0,0 +1,29 @@
diagnostic_filtering/while_loop_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/while_loop_body_attribute.wgsl:7:3 note: control flow depends on possibly non-uniform value
while (x > v.x) @diagnostic(warning, derivative_uniformity) {
^^^^^
diagnostic_filtering/while_loop_body_attribute.wgsl:8:9 note: return value of 'textureSample' may be non-uniform
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
#version 310 es
precision mediump float;
layout(location = 0) in float x_1;
uniform highp sampler2D t_s;
void tint_symbol(float x) {
vec4 v = vec4(0.0f);
while((x > v.x)) {
v = texture(t_s, vec2(0.0f));
}
}
void main() {
tint_symbol(x_1);
return;
}

View File

@ -0,0 +1,31 @@
diagnostic_filtering/while_loop_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/while_loop_body_attribute.wgsl:7:3 note: control flow depends on possibly non-uniform value
while (x > v.x) @diagnostic(warning, derivative_uniformity) {
^^^^^
diagnostic_filtering/while_loop_body_attribute.wgsl:8:9 note: return value of 'textureSample' may be non-uniform
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
#include <metal_stdlib>
using namespace metal;
struct tint_symbol_2 {
float x [[user(locn0)]];
};
void tint_symbol_inner(float x, texture2d<float, access::sample> tint_symbol_3, sampler tint_symbol_4) {
float4 v = float4(0.0f);
while((x > v[0])) {
v = tint_symbol_3.sample(tint_symbol_4, float2(0.0f));
}
}
fragment void tint_symbol(texture2d<float, access::sample> tint_symbol_5 [[texture(0)]], sampler tint_symbol_6 [[sampler(0)]], tint_symbol_2 tint_symbol_1 [[stage_in]]) {
tint_symbol_inner(tint_symbol_1.x, tint_symbol_5, tint_symbol_6);
return;
}

View File

@ -0,0 +1,91 @@
diagnostic_filtering/while_loop_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/while_loop_body_attribute.wgsl:7:3 note: control flow depends on possibly non-uniform value
while (x > v.x) @diagnostic(warning, derivative_uniformity) {
^^^^^
diagnostic_filtering/while_loop_body_attribute.wgsl:8:9 note: return value of 'textureSample' may be non-uniform
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
; SPIR-V
; Version: 1.3
; Generator: Google Tint Compiler; 0
; Bound: 45
; Schema: 0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %x_1
OpExecutionMode %main OriginUpperLeft
OpName %x_1 "x_1"
OpName %t "t"
OpName %s "s"
OpName %main_inner "main_inner"
OpName %x "x"
OpName %v "v"
OpName %main "main"
OpDecorate %x_1 Location 0
OpDecorate %t DescriptorSet 0
OpDecorate %t Binding 1
OpDecorate %s DescriptorSet 0
OpDecorate %s Binding 2
%float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
%x_1 = OpVariable %_ptr_Input_float Input
%6 = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_6 = OpTypePointer UniformConstant %6
%t = OpVariable %_ptr_UniformConstant_6 UniformConstant
%9 = OpTypeSampler
%_ptr_UniformConstant_9 = OpTypePointer UniformConstant %9
%s = OpVariable %_ptr_UniformConstant_9 UniformConstant
%void = OpTypeVoid
%10 = OpTypeFunction %void %float
%v4float = OpTypeVector %float 4
%16 = OpConstantNull %v4float
%_ptr_Function_v4float = OpTypePointer Function %v4float
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%_ptr_Function_float = OpTypePointer Function %float
%bool = OpTypeBool
%36 = OpTypeSampledImage %6
%v2float = OpTypeVector %float 2
%39 = OpConstantNull %v2float
%40 = OpTypeFunction %void
%main_inner = OpFunction %void None %10
%x = OpFunctionParameter %float
%14 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function %16
OpStore %v %16
OpBranch %19
%19 = OpLabel
OpLoopMerge %20 %21 None
OpBranch %22
%22 = OpLabel
%27 = OpAccessChain %_ptr_Function_float %v %uint_0
%28 = OpLoad %float %27
%29 = OpFOrdGreaterThan %bool %x %28
%23 = OpLogicalNot %bool %29
OpSelectionMerge %31 None
OpBranchConditional %23 %32 %31
%32 = OpLabel
OpBranch %20
%31 = OpLabel
%34 = OpLoad %9 %s
%35 = OpLoad %6 %t
%37 = OpSampledImage %36 %35 %34
%33 = OpImageSampleImplicitLod %v4float %37 %39
OpStore %v %33
OpBranch %21
%21 = OpLabel
OpBranch %19
%20 = OpLabel
OpReturn
OpFunctionEnd
%main = OpFunction %void None %40
%42 = OpLabel
%44 = OpLoad %float %x_1
%43 = OpFunctionCall %void %main_inner %44
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,23 @@
diagnostic_filtering/while_loop_body_attribute.wgsl:8:9 warning: 'textureSample' must only be called from uniform control flow
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
diagnostic_filtering/while_loop_body_attribute.wgsl:7:3 note: control flow depends on possibly non-uniform value
while (x > v.x) @diagnostic(warning, derivative_uniformity) {
^^^^^
diagnostic_filtering/while_loop_body_attribute.wgsl:8:9 note: return value of 'textureSample' may be non-uniform
v = textureSample(t, s, vec2(0, 0));
^^^^^^^^^^^^^
@group(0) @binding(1) var t : texture_2d<f32>;
@group(0) @binding(2) var s : sampler;
@fragment
fn main(@location(0) x : f32) {
var v = vec4<f32>(0);
while((x > v.x)) @diagnostic(warning, derivative_uniformity) {
v = textureSample(t, s, vec2(0, 0));
}
}