tint: Support @diagnostic on while statements
Bug: tint:1809 Change-Id: I9593539d258d16a68c9ff1cfd2cad52e28fad65a Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/124100 Commit-Queue: James Price <jrprice@google.com> Reviewed-by: Ben Clayton <bclayton@google.com> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
d0ed63eb64
commit
9536ced014
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include "src/tint/ast/while_statement.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "src/tint/program_builder.h"
|
||||
|
||||
TINT_INSTANTIATE_TYPEINFO(tint::ast::WhileStatement);
|
||||
|
@ -24,13 +26,18 @@ WhileStatement::WhileStatement(ProgramID pid,
|
|||
NodeID nid,
|
||||
const Source& src,
|
||||
const Expression* cond,
|
||||
const BlockStatement* b)
|
||||
: Base(pid, nid, src), condition(cond), body(b) {
|
||||
const BlockStatement* b,
|
||||
utils::VectorRef<const ast::Attribute*> attrs)
|
||||
: Base(pid, nid, src), condition(cond), body(b), attributes(std::move(attrs)) {
|
||||
TINT_ASSERT(AST, cond);
|
||||
TINT_ASSERT(AST, body);
|
||||
|
||||
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, condition, program_id);
|
||||
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, body, program_id);
|
||||
for (auto* attr : attributes) {
|
||||
TINT_ASSERT(AST, attr);
|
||||
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, attr, program_id);
|
||||
}
|
||||
}
|
||||
|
||||
WhileStatement::WhileStatement(WhileStatement&&) = default;
|
||||
|
@ -43,7 +50,8 @@ const WhileStatement* WhileStatement::Clone(CloneContext* ctx) const {
|
|||
|
||||
auto* cond = ctx->Clone(condition);
|
||||
auto* b = ctx->Clone(body);
|
||||
return ctx->dst->create<WhileStatement>(src, cond, b);
|
||||
auto attrs = ctx->Clone(attributes);
|
||||
return ctx->dst->create<WhileStatement>(src, cond, b, std::move(attrs));
|
||||
}
|
||||
|
||||
} // namespace tint::ast
|
||||
|
|
|
@ -30,11 +30,13 @@ class WhileStatement final : public Castable<WhileStatement, Statement> {
|
|||
/// @param source the for loop statement source
|
||||
/// @param condition the optional loop condition expression
|
||||
/// @param body the loop body
|
||||
/// @param attributes the while statement attributes
|
||||
WhileStatement(ProgramID pid,
|
||||
NodeID nid,
|
||||
const Source& source,
|
||||
const Expression* condition,
|
||||
const BlockStatement* body);
|
||||
const BlockStatement* body,
|
||||
utils::VectorRef<const ast::Attribute*> attributes);
|
||||
/// Move constructor
|
||||
WhileStatement(WhileStatement&&);
|
||||
~WhileStatement() override;
|
||||
|
@ -50,6 +52,9 @@ class WhileStatement final : public Castable<WhileStatement, Statement> {
|
|||
|
||||
/// The loop body block
|
||||
const BlockStatement* const body;
|
||||
|
||||
/// The attribute list
|
||||
const utils::Vector<const Attribute*, 1> attributes;
|
||||
};
|
||||
|
||||
} // namespace tint::ast
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest-spi.h"
|
||||
#include "src/tint/ast/binary_expression.h"
|
||||
#include "src/tint/ast/test_helper.h"
|
||||
|
@ -41,6 +42,16 @@ TEST_F(WhileStatementTest, Creation_WithSource) {
|
|||
EXPECT_EQ(src.range.begin.column, 2u);
|
||||
}
|
||||
|
||||
TEST_F(WhileStatementTest, Creation_WithAttributes) {
|
||||
auto* attr1 = DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "foo");
|
||||
auto* attr2 = DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "bar");
|
||||
auto* cond = create<BinaryExpression>(BinaryOp::kLessThan, Expr("i"), Expr(5_u));
|
||||
auto* body = Block(Return());
|
||||
auto* l = While(cond, body, utils::Vector{attr1, attr2});
|
||||
|
||||
EXPECT_THAT(l->attributes, testing::ElementsAre(attr1, attr2));
|
||||
}
|
||||
|
||||
TEST_F(WhileStatementTest, Assert_Null_Cond) {
|
||||
EXPECT_FATAL_FAILURE(
|
||||
{
|
||||
|
|
|
@ -3311,25 +3311,34 @@ class ProgramBuilder {
|
|||
return create<ast::ForLoopStatement>(init, Expr(std::forward<COND>(cond)), cont, body);
|
||||
}
|
||||
|
||||
/// Creates a ast::WhileStatement with input body and condition.
|
||||
/// Creates a ast::WhileStatement with input body, condition, and optional attributes.
|
||||
/// @param source the source information
|
||||
/// @param cond the loop condition
|
||||
/// @param body the loop body
|
||||
/// @param attributes optional attributes
|
||||
/// @returns the while statement pointer
|
||||
template <typename COND>
|
||||
const ast::WhileStatement* While(const Source& source,
|
||||
COND&& cond,
|
||||
const ast::BlockStatement* body) {
|
||||
return create<ast::WhileStatement>(source, Expr(std::forward<COND>(cond)), body);
|
||||
const ast::WhileStatement* While(
|
||||
const Source& source,
|
||||
COND&& cond,
|
||||
const ast::BlockStatement* body,
|
||||
utils::VectorRef<const ast::Attribute*> attributes = utils::Empty) {
|
||||
return create<ast::WhileStatement>(source, Expr(std::forward<COND>(cond)), body,
|
||||
std::move(attributes));
|
||||
}
|
||||
|
||||
/// Creates a ast::WhileStatement with given condition and body.
|
||||
/// Creates a ast::WhileStatement with input body, condition, and optional attributes.
|
||||
/// @param cond the condition
|
||||
/// @param body the loop body
|
||||
/// @param attributes optional attributes
|
||||
/// @returns the while loop statement pointer
|
||||
template <typename COND>
|
||||
const ast::WhileStatement* While(COND&& cond, const ast::BlockStatement* body) {
|
||||
return create<ast::WhileStatement>(Expr(std::forward<COND>(cond)), body);
|
||||
const ast::WhileStatement* While(
|
||||
COND&& cond,
|
||||
const ast::BlockStatement* body,
|
||||
utils::VectorRef<const ast::Attribute*> attributes = utils::Empty) {
|
||||
return create<ast::WhileStatement>(Expr(std::forward<COND>(cond)), body,
|
||||
std::move(attributes));
|
||||
}
|
||||
|
||||
/// Creates a ast::VariableDeclStatement for the input variable
|
||||
|
|
|
@ -1296,7 +1296,7 @@ Maybe<const ast::Statement*> ParserImpl::statement() {
|
|||
return stmt_for.value;
|
||||
}
|
||||
|
||||
auto stmt_while = while_statement();
|
||||
auto stmt_while = while_statement(attrs.value);
|
||||
if (stmt_while.errored) {
|
||||
return Failure::kErrored;
|
||||
}
|
||||
|
@ -1902,8 +1902,8 @@ Maybe<const ast::ForLoopStatement*> ParserImpl::for_statement() {
|
|||
}
|
||||
|
||||
// while_statement
|
||||
// : WHILE expression compound_statement
|
||||
Maybe<const ast::WhileStatement*> ParserImpl::while_statement() {
|
||||
// : attribute* WHILE expression compound_statement
|
||||
Maybe<const ast::WhileStatement*> ParserImpl::while_statement(AttributeList& attrs) {
|
||||
Source source;
|
||||
if (!match(Token::Type::kWhile, &source)) {
|
||||
return Failure::kNoMatch;
|
||||
|
@ -1922,7 +1922,8 @@ Maybe<const ast::WhileStatement*> ParserImpl::while_statement() {
|
|||
return Failure::kErrored;
|
||||
}
|
||||
|
||||
return create<ast::WhileStatement>(source, condition.value, body.value);
|
||||
TINT_DEFER(attrs.Clear());
|
||||
return create<ast::WhileStatement>(source, condition.value, body.value, std::move(attrs));
|
||||
}
|
||||
|
||||
// func_call_statement
|
||||
|
|
|
@ -520,9 +520,10 @@ class ParserImpl {
|
|||
/// Parses a `for_statement` grammar element
|
||||
/// @returns the parsed for loop or nullptr
|
||||
Maybe<const ast::ForLoopStatement*> for_statement();
|
||||
/// Parses a `while_statement` grammar element
|
||||
/// Parses a `while_statement` grammar element, with the attribute list provided as `attrs`.
|
||||
/// @param attrs the list of attributes for the statement
|
||||
/// @returns the parsed while loop or nullptr
|
||||
Maybe<const ast::WhileStatement*> while_statement();
|
||||
Maybe<const ast::WhileStatement*> while_statement(AttributeList& attrs);
|
||||
/// Parses a `break_if_statement` grammar element
|
||||
/// @returns the parsed statement or nullptr
|
||||
Maybe<const ast::Statement*> break_if_statement();
|
||||
|
|
|
@ -350,6 +350,18 @@ TEST_F(ParserImplTest, Statement_ConsumedAttributes_Switch) {
|
|||
EXPECT_EQ(s->attributes.Length(), 1u);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, Statement_ConsumedAttributes_While) {
|
||||
auto p = parser("@diagnostic(off, derivative_uniformity) while (false) {}");
|
||||
auto e = p->statement();
|
||||
ASSERT_FALSE(p->has_error()) << p->error();
|
||||
EXPECT_TRUE(e.matched);
|
||||
EXPECT_FALSE(e.errored);
|
||||
|
||||
auto* s = As<ast::WhileStatement>(e.value);
|
||||
ASSERT_NE(s, nullptr);
|
||||
EXPECT_EQ(s->attributes.Length(), 1u);
|
||||
}
|
||||
|
||||
TEST_F(ParserImplTest, Statement_UnexpectedAttributes) {
|
||||
auto p = parser("@diagnostic(off, derivative_uniformity) return;");
|
||||
auto e = p->statement();
|
||||
|
|
|
@ -24,7 +24,8 @@ using WhileStmtTest = ParserImplTest;
|
|||
// Test an empty while loop.
|
||||
TEST_F(WhileStmtTest, Empty) {
|
||||
auto p = parser("while true { }");
|
||||
auto wl = p->while_statement();
|
||||
ParserImpl::AttributeList attrs;
|
||||
auto wl = p->while_statement(attrs);
|
||||
EXPECT_FALSE(p->has_error()) << p->error();
|
||||
EXPECT_FALSE(wl.errored);
|
||||
ASSERT_TRUE(wl.matched);
|
||||
|
@ -35,7 +36,8 @@ TEST_F(WhileStmtTest, Empty) {
|
|||
// Test an empty while loop with parentheses.
|
||||
TEST_F(WhileStmtTest, EmptyWithParentheses) {
|
||||
auto p = parser("while (true) { }");
|
||||
auto wl = p->while_statement();
|
||||
ParserImpl::AttributeList attrs;
|
||||
auto wl = p->while_statement(attrs);
|
||||
EXPECT_FALSE(p->has_error()) << p->error();
|
||||
EXPECT_FALSE(wl.errored);
|
||||
ASSERT_TRUE(wl.matched);
|
||||
|
@ -46,7 +48,8 @@ TEST_F(WhileStmtTest, EmptyWithParentheses) {
|
|||
// Test a while loop with non-empty body.
|
||||
TEST_F(WhileStmtTest, Body) {
|
||||
auto p = parser("while (true) { discard; }");
|
||||
auto wl = p->while_statement();
|
||||
ParserImpl::AttributeList attrs;
|
||||
auto wl = p->while_statement(attrs);
|
||||
EXPECT_FALSE(p->has_error()) << p->error();
|
||||
EXPECT_FALSE(wl.errored);
|
||||
ASSERT_TRUE(wl.matched);
|
||||
|
@ -58,7 +61,8 @@ TEST_F(WhileStmtTest, Body) {
|
|||
// Test a while loop with complex condition.
|
||||
TEST_F(WhileStmtTest, ComplexCondition) {
|
||||
auto p = parser("while (a + 1 - 2) == 3 { }");
|
||||
auto wl = p->while_statement();
|
||||
ParserImpl::AttributeList attrs;
|
||||
auto wl = p->while_statement(attrs);
|
||||
EXPECT_FALSE(p->has_error()) << p->error();
|
||||
EXPECT_FALSE(wl.errored);
|
||||
ASSERT_TRUE(wl.matched);
|
||||
|
@ -69,7 +73,8 @@ TEST_F(WhileStmtTest, ComplexCondition) {
|
|||
// Test a while loop with complex condition, with parentheses.
|
||||
TEST_F(WhileStmtTest, ComplexConditionWithParentheses) {
|
||||
auto p = parser("while ((a + 1 - 2) == 3) { }");
|
||||
auto wl = p->while_statement();
|
||||
ParserImpl::AttributeList attrs;
|
||||
auto wl = p->while_statement(attrs);
|
||||
EXPECT_FALSE(p->has_error()) << p->error();
|
||||
EXPECT_FALSE(wl.errored);
|
||||
ASSERT_TRUE(wl.matched);
|
||||
|
@ -77,11 +82,26 @@ TEST_F(WhileStmtTest, ComplexConditionWithParentheses) {
|
|||
EXPECT_TRUE(wl->body->Empty());
|
||||
}
|
||||
|
||||
// Test a while loop with attributes.
|
||||
TEST_F(WhileStmtTest, WithAttributes) {
|
||||
auto p = parser("@diagnostic(off, derivative_uniformity) while true { }");
|
||||
auto attrs = p->attribute_list();
|
||||
auto wl = p->while_statement(attrs.value);
|
||||
EXPECT_FALSE(p->has_error()) << p->error();
|
||||
EXPECT_FALSE(wl.errored);
|
||||
ASSERT_TRUE(wl.matched);
|
||||
|
||||
EXPECT_TRUE(attrs->IsEmpty());
|
||||
ASSERT_EQ(wl->attributes.Length(), 1u);
|
||||
EXPECT_TRUE(wl->attributes[0]->Is<ast::DiagnosticAttribute>());
|
||||
}
|
||||
|
||||
class WhileStmtErrorTest : public ParserImplTest {
|
||||
public:
|
||||
void TestWhileWithError(std::string for_str, std::string error_str) {
|
||||
auto p_for = parser(for_str);
|
||||
auto e_for = p_for->while_statement();
|
||||
ParserImpl::AttributeList attrs;
|
||||
auto e_for = p_for->while_statement(attrs);
|
||||
|
||||
EXPECT_FALSE(e_for.matched);
|
||||
EXPECT_TRUE(e_for.errored);
|
||||
|
|
|
@ -1111,6 +1111,39 @@ INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
|||
TestParams{AttributeKind::kWorkgroup, false},
|
||||
TestParams{AttributeKind::kBindingAndGroup, false}));
|
||||
|
||||
using WhileStatementAttributeTest = TestWithParams;
|
||||
TEST_P(WhileStatementAttributeTest, IsValid) {
|
||||
auto& params = GetParam();
|
||||
|
||||
WrapInFunction(
|
||||
While(Expr(false), Block(), createAttributes(Source{{12, 34}}, *this, params.kind)));
|
||||
|
||||
if (params.should_pass) {
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
} else {
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for while statements");
|
||||
}
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||
WhileStatementAttributeTest,
|
||||
testing::Values(TestParams{AttributeKind::kAlign, false},
|
||||
TestParams{AttributeKind::kBinding, false},
|
||||
TestParams{AttributeKind::kBuiltin, false},
|
||||
TestParams{AttributeKind::kDiagnostic, true},
|
||||
TestParams{AttributeKind::kGroup, false},
|
||||
TestParams{AttributeKind::kId, false},
|
||||
TestParams{AttributeKind::kInterpolate, false},
|
||||
TestParams{AttributeKind::kInvariant, false},
|
||||
TestParams{AttributeKind::kLocation, false},
|
||||
TestParams{AttributeKind::kMustUse, false},
|
||||
TestParams{AttributeKind::kOffset, false},
|
||||
TestParams{AttributeKind::kSize, false},
|
||||
TestParams{AttributeKind::kStage, false},
|
||||
TestParams{AttributeKind::kStride, false},
|
||||
TestParams{AttributeKind::kWorkgroup, false},
|
||||
TestParams{AttributeKind::kBindingAndGroup, false}));
|
||||
|
||||
namespace BlockStatementTests {
|
||||
class BlockStatementTest : public TestWithParams {
|
||||
protected:
|
||||
|
|
|
@ -4278,6 +4278,9 @@ SEM* Resolver::StatementScope(const ast::Statement* ast, SEM* sem, F&& callback)
|
|||
[&](const ast::SwitchStatement* s) {
|
||||
return handle_attributes(s, sem, "switch statements");
|
||||
},
|
||||
[&](const ast::WhileStatement* w) {
|
||||
return handle_attributes(w, sem, "while statements");
|
||||
},
|
||||
[&](Default) { return true; })) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -8504,6 +8504,55 @@ fn foo() {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnWhileStatement_CallInCondition) {
|
||||
auto& param = GetParam();
|
||||
utils::StringStream ss;
|
||||
ss << R"(
|
||||
@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
|
||||
fn foo() {
|
||||
)"
|
||||
<< "@diagnostic(" << param << ", derivative_uniformity)"
|
||||
<< R"(while (non_uniform == 42 && dpdx(1.0) > 0.0) {
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
RunTest(ss.str(), param != builtin::DiagnosticSeverity::kError);
|
||||
if (param == builtin::DiagnosticSeverity::kOff) {
|
||||
EXPECT_TRUE(error_.empty());
|
||||
} else {
|
||||
utils::StringStream err;
|
||||
err << ToStr(param) << ": 'dpdx' must only be called";
|
||||
EXPECT_THAT(error_, ::testing::HasSubstr(err.str()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnWhileStatement_CallInBody) {
|
||||
auto& param = GetParam();
|
||||
utils::StringStream ss;
|
||||
ss << R"(
|
||||
@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
|
||||
@group(0) @binding(1) var t : texture_2d<f32>;
|
||||
@group(0) @binding(2) var s : sampler;
|
||||
fn foo() {
|
||||
)"
|
||||
<< "@diagnostic(" << param << ", derivative_uniformity)"
|
||||
<< R"(while (non_uniform == 42) {
|
||||
let color = textureSample(t, s, vec2(0, 0));
|
||||
}
|
||||
}
|
||||
)";
|
||||
|
||||
RunTest(ss.str(), param != builtin::DiagnosticSeverity::kError);
|
||||
if (param == builtin::DiagnosticSeverity::kOff) {
|
||||
EXPECT_TRUE(error_.empty());
|
||||
} else {
|
||||
utils::StringStream err;
|
||||
err << ToStr(param) << ": 'textureSample' must only be called";
|
||||
EXPECT_THAT(error_, ::testing::HasSubstr(err.str()));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(UniformityAnalysisTest,
|
||||
UniformityAnalysisDiagnosticFilterTest,
|
||||
::testing::Values(builtin::DiagnosticSeverity::kError,
|
||||
|
|
|
@ -50,6 +50,11 @@ class DiagnosticSeverityTest : public TestHelper {
|
|||
// return;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @diagnostic(error, chromium_unreachable_code)
|
||||
// while (false) @diagnostic(warning, chromium_unreachable_code) {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
@ -64,6 +69,8 @@ class DiagnosticSeverityTest : public TestHelper {
|
|||
auto else_body_severity = builtin::DiagnosticSeverity::kInfo;
|
||||
auto switch_severity = builtin::DiagnosticSeverity::kError;
|
||||
auto case_severity = builtin::DiagnosticSeverity::kWarning;
|
||||
auto while_severity = builtin::DiagnosticSeverity::kError;
|
||||
auto while_body_severity = builtin::DiagnosticSeverity::kWarning;
|
||||
auto attr = [&](auto severity) {
|
||||
return utils::Vector{DiagnosticAttribute(severity, "chromium_unreachable_code")};
|
||||
};
|
||||
|
@ -74,6 +81,7 @@ class DiagnosticSeverityTest : public TestHelper {
|
|||
auto* return_foo_block = Return();
|
||||
auto* return_foo_case = Return();
|
||||
auto* return_foo_default = Return();
|
||||
auto* return_foo_while = Return();
|
||||
auto* else_stmt = Block(utils::Vector{return_foo_else}, attr(else_body_severity));
|
||||
auto* elseif = If(Expr(false), Block(return_foo_elseif), Else(else_stmt));
|
||||
auto* if_foo = If(Expr(true), Block(utils::Vector{return_foo_if}, attr(if_body_severity)),
|
||||
|
@ -82,7 +90,10 @@ class DiagnosticSeverityTest : public TestHelper {
|
|||
Case(CaseSelector(0_a), Block(utils::Vector{return_foo_case}, attr(case_severity)));
|
||||
auto* swtch = Switch(42_a, utils::Vector{case_stmt, DefaultCase(Block(return_foo_default))},
|
||||
attr(switch_severity));
|
||||
auto* block_1 = Block(utils::Vector{if_foo, return_foo_block, swtch}, attr(block_severity));
|
||||
auto* wl = While(false, Block(utils::Vector{return_foo_while}, attr(while_body_severity)),
|
||||
attr(while_severity));
|
||||
auto* block_1 =
|
||||
Block(utils::Vector{if_foo, return_foo_block, swtch, wl}, attr(block_severity));
|
||||
auto* func_attr = DiagnosticAttribute(func_severity, "chromium_unreachable_code");
|
||||
auto* foo = Func("foo", {}, ty.void_(), utils::Vector{block_1}, utils::Vector{func_attr});
|
||||
|
||||
|
@ -110,6 +121,10 @@ class DiagnosticSeverityTest : public TestHelper {
|
|||
EXPECT_EQ(p.Sem().DiagnosticSeverity(case_stmt->body, rule), case_severity);
|
||||
EXPECT_EQ(p.Sem().DiagnosticSeverity(return_foo_case, rule), case_severity);
|
||||
EXPECT_EQ(p.Sem().DiagnosticSeverity(return_foo_default, rule), switch_severity);
|
||||
EXPECT_EQ(p.Sem().DiagnosticSeverity(wl, rule), while_severity);
|
||||
EXPECT_EQ(p.Sem().DiagnosticSeverity(wl->condition, rule), while_severity);
|
||||
EXPECT_EQ(p.Sem().DiagnosticSeverity(wl->body, rule), while_body_severity);
|
||||
EXPECT_EQ(p.Sem().DiagnosticSeverity(return_foo_while, rule), while_body_severity);
|
||||
|
||||
EXPECT_EQ(p.Sem().DiagnosticSeverity(bar, rule), global_severity);
|
||||
EXPECT_EQ(p.Sem().DiagnosticSeverity(return_bar, rule), global_severity);
|
||||
|
|
|
@ -1120,6 +1120,14 @@ bool GeneratorImpl::EmitForLoop(const ast::ForLoopStatement* stmt) {
|
|||
bool GeneratorImpl::EmitWhile(const ast::WhileStatement* stmt) {
|
||||
{
|
||||
auto out = line();
|
||||
|
||||
if (!stmt->attributes.IsEmpty()) {
|
||||
if (!EmitAttributes(out, stmt->attributes)) {
|
||||
return false;
|
||||
}
|
||||
out << " ";
|
||||
}
|
||||
|
||||
out << "while";
|
||||
{
|
||||
ScopedParen sp(out);
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
@fragment
|
||||
fn main(@location(0) x : f32) {
|
||||
var v = vec4<f32>(0);
|
||||
@diagnostic(warning, derivative_uniformity)
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
diagnostic_filtering/while_loop_attribute.wgsl:5:21 warning: 'dpdx' must only be called from uniform control flow
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
^^^^^^^^^
|
||||
|
||||
diagnostic_filtering/while_loop_attribute.wgsl:5:3 note: control flow depends on possibly non-uniform value
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
^^^^^
|
||||
|
||||
diagnostic_filtering/while_loop_attribute.wgsl:5:21 note: return value of 'dpdx' may be non-uniform
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
^^^^^^^^^
|
||||
|
||||
struct tint_symbol_1 {
|
||||
float x : TEXCOORD0;
|
||||
};
|
||||
|
||||
void main_inner(float x) {
|
||||
float4 v = (0.0f).xxxx;
|
||||
while (true) {
|
||||
bool tint_tmp = (x > 0.0f);
|
||||
if (tint_tmp) {
|
||||
tint_tmp = (ddx(1.0f) > 0.0f);
|
||||
}
|
||||
if (!((tint_tmp))) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
void main(tint_symbol_1 tint_symbol) {
|
||||
main_inner(tint_symbol.x);
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
diagnostic_filtering/while_loop_attribute.wgsl:5:21 warning: 'dpdx' must only be called from uniform control flow
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
^^^^^^^^^
|
||||
|
||||
diagnostic_filtering/while_loop_attribute.wgsl:5:3 note: control flow depends on possibly non-uniform value
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
^^^^^
|
||||
|
||||
diagnostic_filtering/while_loop_attribute.wgsl:5:21 note: return value of 'dpdx' may be non-uniform
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
^^^^^^^^^
|
||||
|
||||
struct tint_symbol_1 {
|
||||
float x : TEXCOORD0;
|
||||
};
|
||||
|
||||
void main_inner(float x) {
|
||||
float4 v = (0.0f).xxxx;
|
||||
while (true) {
|
||||
bool tint_tmp = (x > 0.0f);
|
||||
if (tint_tmp) {
|
||||
tint_tmp = (ddx(1.0f) > 0.0f);
|
||||
}
|
||||
if (!((tint_tmp))) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
void main(tint_symbol_1 tint_symbol) {
|
||||
main_inner(tint_symbol.x);
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
diagnostic_filtering/while_loop_attribute.wgsl:5:21 warning: 'dpdx' must only be called from uniform control flow
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
^^^^^^^^^
|
||||
|
||||
diagnostic_filtering/while_loop_attribute.wgsl:5:3 note: control flow depends on possibly non-uniform value
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
^^^^^
|
||||
|
||||
diagnostic_filtering/while_loop_attribute.wgsl:5:21 note: return value of 'dpdx' may be non-uniform
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
^^^^^^^^^
|
||||
|
||||
#version 310 es
|
||||
precision highp float;
|
||||
|
||||
layout(location = 0) in float x_1;
|
||||
void tint_symbol(float x) {
|
||||
vec4 v = vec4(0.0f);
|
||||
while (true) {
|
||||
bool tint_tmp = (x > 0.0f);
|
||||
if (tint_tmp) {
|
||||
tint_tmp = (dFdx(1.0f) > 0.0f);
|
||||
}
|
||||
if (!((tint_tmp))) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
tint_symbol(x_1);
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
diagnostic_filtering/while_loop_attribute.wgsl:5:21 warning: 'dpdx' must only be called from uniform control flow
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
^^^^^^^^^
|
||||
|
||||
diagnostic_filtering/while_loop_attribute.wgsl:5:3 note: control flow depends on possibly non-uniform value
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
^^^^^
|
||||
|
||||
diagnostic_filtering/while_loop_attribute.wgsl:5:21 note: return value of 'dpdx' may be non-uniform
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
^^^^^^^^^
|
||||
|
||||
#include <metal_stdlib>
|
||||
|
||||
using namespace metal;
|
||||
struct tint_symbol_2 {
|
||||
float x [[user(locn0)]];
|
||||
};
|
||||
|
||||
void tint_symbol_inner(float x) {
|
||||
float4 v = float4(0.0f);
|
||||
while(((x > 0.0f) && (dfdx(1.0f) > 0.0f))) {
|
||||
}
|
||||
}
|
||||
|
||||
fragment void tint_symbol(tint_symbol_2 tint_symbol_1 [[stage_in]]) {
|
||||
tint_symbol_inner(tint_symbol_1.x);
|
||||
return;
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
diagnostic_filtering/while_loop_attribute.wgsl:5:21 warning: 'dpdx' must only be called from uniform control flow
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
^^^^^^^^^
|
||||
|
||||
diagnostic_filtering/while_loop_attribute.wgsl:5:3 note: control flow depends on possibly non-uniform value
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
^^^^^
|
||||
|
||||
diagnostic_filtering/while_loop_attribute.wgsl:5:21 note: return value of 'dpdx' may be non-uniform
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
^^^^^^^^^
|
||||
|
||||
; SPIR-V
|
||||
; Version: 1.3
|
||||
; Generator: Google Tint Compiler; 0
|
||||
; Bound: 34
|
||||
; Schema: 0
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main" %x_1
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpName %x_1 "x_1"
|
||||
OpName %main_inner "main_inner"
|
||||
OpName %x "x"
|
||||
OpName %v "v"
|
||||
OpName %main "main"
|
||||
OpDecorate %x_1 Location 0
|
||||
%float = OpTypeFloat 32
|
||||
%_ptr_Input_float = OpTypePointer Input %float
|
||||
%x_1 = OpVariable %_ptr_Input_float Input
|
||||
%void = OpTypeVoid
|
||||
%4 = OpTypeFunction %void %float
|
||||
%v4float = OpTypeVector %float 4
|
||||
%10 = OpConstantNull %v4float
|
||||
%_ptr_Function_v4float = OpTypePointer Function %v4float
|
||||
%18 = OpConstantNull %float
|
||||
%bool = OpTypeBool
|
||||
%float_1 = OpConstant %float 1
|
||||
%29 = OpTypeFunction %void
|
||||
%main_inner = OpFunction %void None %4
|
||||
%x = OpFunctionParameter %float
|
||||
%8 = OpLabel
|
||||
%v = OpVariable %_ptr_Function_v4float Function %10
|
||||
OpStore %v %10
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
OpLoopMerge %14 %15 None
|
||||
OpBranch %16
|
||||
%16 = OpLabel
|
||||
%19 = OpFOrdGreaterThan %bool %x %18
|
||||
OpSelectionMerge %21 None
|
||||
OpBranchConditional %19 %22 %21
|
||||
%22 = OpLabel
|
||||
%23 = OpDPdx %float %float_1
|
||||
%25 = OpFOrdGreaterThan %bool %23 %18
|
||||
OpBranch %21
|
||||
%21 = OpLabel
|
||||
%26 = OpPhi %bool %19 %16 %25 %22
|
||||
%17 = OpLogicalNot %bool %26
|
||||
OpSelectionMerge %27 None
|
||||
OpBranchConditional %17 %28 %27
|
||||
%28 = OpLabel
|
||||
OpBranch %14
|
||||
%27 = OpLabel
|
||||
OpBranch %15
|
||||
%15 = OpLabel
|
||||
OpBranch %13
|
||||
%14 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%main = OpFunction %void None %29
|
||||
%31 = OpLabel
|
||||
%33 = OpLoad %float %x_1
|
||||
%32 = OpFunctionCall %void %main_inner %33
|
||||
OpReturn
|
||||
OpFunctionEnd
|
|
@ -0,0 +1,18 @@
|
|||
diagnostic_filtering/while_loop_attribute.wgsl:5:21 warning: 'dpdx' must only be called from uniform control flow
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
^^^^^^^^^
|
||||
|
||||
diagnostic_filtering/while_loop_attribute.wgsl:5:3 note: control flow depends on possibly non-uniform value
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
^^^^^
|
||||
|
||||
diagnostic_filtering/while_loop_attribute.wgsl:5:21 note: return value of 'dpdx' may be non-uniform
|
||||
while (x > 0.0 && dpdx(1.0) > 0.0) {
|
||||
^^^^^^^^^
|
||||
|
||||
@fragment
|
||||
fn main(@location(0) x : f32) {
|
||||
var v = vec4<f32>(0);
|
||||
@diagnostic(warning, derivative_uniformity) while(((x > 0.0) && (dpdx(1.0) > 0.0))) {
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue