tint: Support @diagnostic on switch body
Bug: tint:1809 Change-Id: I33259080c143b0cf7afe01343a1f179f61078dd3 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/124520 Reviewed-by: Ben Clayton <bclayton@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: James Price <jrprice@google.com>
This commit is contained in:
parent
ae7a2a94c0
commit
8cf01eef04
|
@ -27,8 +27,13 @@ SwitchStatement::SwitchStatement(ProgramID pid,
|
||||||
const Source& src,
|
const Source& src,
|
||||||
const Expression* cond,
|
const Expression* cond,
|
||||||
utils::VectorRef<const CaseStatement*> b,
|
utils::VectorRef<const CaseStatement*> b,
|
||||||
utils::VectorRef<const Attribute*> attrs)
|
utils::VectorRef<const Attribute*> stmt_attrs,
|
||||||
: Base(pid, nid, src), condition(cond), body(std::move(b)), attributes(std::move(attrs)) {
|
utils::VectorRef<const Attribute*> body_attrs)
|
||||||
|
: Base(pid, nid, src),
|
||||||
|
condition(cond),
|
||||||
|
body(std::move(b)),
|
||||||
|
attributes(std::move(stmt_attrs)),
|
||||||
|
body_attributes(std::move(body_attrs)) {
|
||||||
TINT_ASSERT(AST, condition);
|
TINT_ASSERT(AST, condition);
|
||||||
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, condition, program_id);
|
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, condition, program_id);
|
||||||
for (auto* stmt : body) {
|
for (auto* stmt : body) {
|
||||||
|
@ -39,6 +44,10 @@ SwitchStatement::SwitchStatement(ProgramID pid,
|
||||||
TINT_ASSERT(AST, attr);
|
TINT_ASSERT(AST, attr);
|
||||||
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, attr, program_id);
|
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, attr, program_id);
|
||||||
}
|
}
|
||||||
|
for (auto* attr : body_attributes) {
|
||||||
|
TINT_ASSERT(AST, attr);
|
||||||
|
TINT_ASSERT_PROGRAM_IDS_EQUAL_IF_VALID(AST, attr, program_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SwitchStatement::~SwitchStatement() = default;
|
SwitchStatement::~SwitchStatement() = default;
|
||||||
|
@ -49,7 +58,9 @@ const SwitchStatement* SwitchStatement::Clone(CloneContext* ctx) const {
|
||||||
auto* cond = ctx->Clone(condition);
|
auto* cond = ctx->Clone(condition);
|
||||||
auto b = ctx->Clone(body);
|
auto b = ctx->Clone(body);
|
||||||
auto attrs = ctx->Clone(attributes);
|
auto attrs = ctx->Clone(attributes);
|
||||||
return ctx->dst->create<SwitchStatement>(src, cond, std::move(b), std::move(attrs));
|
auto body_attrs = ctx->Clone(body_attributes);
|
||||||
|
return ctx->dst->create<SwitchStatement>(src, cond, std::move(b), std::move(attrs),
|
||||||
|
std::move(body_attrs));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tint::ast
|
} // namespace tint::ast
|
||||||
|
|
|
@ -29,13 +29,15 @@ class SwitchStatement final : public Castable<SwitchStatement, Statement> {
|
||||||
/// @param src the source of this node
|
/// @param src the source of this node
|
||||||
/// @param condition the switch condition
|
/// @param condition the switch condition
|
||||||
/// @param body the switch body
|
/// @param body the switch body
|
||||||
/// @param attributes the switch statement attributes
|
/// @param stmt_attributes the switch statement attributes
|
||||||
|
/// @param body_attributes the switch body attributes
|
||||||
SwitchStatement(ProgramID pid,
|
SwitchStatement(ProgramID pid,
|
||||||
NodeID nid,
|
NodeID nid,
|
||||||
const Source& src,
|
const Source& src,
|
||||||
const Expression* condition,
|
const Expression* condition,
|
||||||
utils::VectorRef<const CaseStatement*> body,
|
utils::VectorRef<const CaseStatement*> body,
|
||||||
utils::VectorRef<const Attribute*> attributes);
|
utils::VectorRef<const Attribute*> stmt_attributes,
|
||||||
|
utils::VectorRef<const Attribute*> body_attributes);
|
||||||
|
|
||||||
/// Destructor
|
/// Destructor
|
||||||
~SwitchStatement() override;
|
~SwitchStatement() override;
|
||||||
|
@ -53,8 +55,11 @@ class SwitchStatement final : public Castable<SwitchStatement, Statement> {
|
||||||
const utils::Vector<const CaseStatement*, 4> body;
|
const utils::Vector<const CaseStatement*, 4> body;
|
||||||
SwitchStatement(const SwitchStatement&) = delete;
|
SwitchStatement(const SwitchStatement&) = delete;
|
||||||
|
|
||||||
/// The attribute list
|
/// The attribute list for the statement
|
||||||
const utils::Vector<const Attribute*, 1> attributes;
|
const utils::Vector<const Attribute*, 1> attributes;
|
||||||
|
|
||||||
|
/// The attribute list for the body
|
||||||
|
const utils::Vector<const Attribute*, 1> body_attributes;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tint::ast
|
} // namespace tint::ast
|
||||||
|
|
|
@ -30,7 +30,7 @@ TEST_F(SwitchStatementTest, Creation) {
|
||||||
auto* ident = Expr("ident");
|
auto* ident = Expr("ident");
|
||||||
utils::Vector body{case_stmt};
|
utils::Vector body{case_stmt};
|
||||||
|
|
||||||
auto* stmt = create<SwitchStatement>(ident, body, utils::Empty);
|
auto* stmt = create<SwitchStatement>(ident, body, utils::Empty, utils::Empty);
|
||||||
EXPECT_EQ(stmt->condition, ident);
|
EXPECT_EQ(stmt->condition, ident);
|
||||||
ASSERT_EQ(stmt->body.Length(), 1u);
|
ASSERT_EQ(stmt->body.Length(), 1u);
|
||||||
EXPECT_EQ(stmt->body[0], case_stmt);
|
EXPECT_EQ(stmt->body[0], case_stmt);
|
||||||
|
@ -38,8 +38,8 @@ TEST_F(SwitchStatementTest, Creation) {
|
||||||
|
|
||||||
TEST_F(SwitchStatementTest, Creation_WithSource) {
|
TEST_F(SwitchStatementTest, Creation_WithSource) {
|
||||||
auto* ident = Expr("ident");
|
auto* ident = Expr("ident");
|
||||||
auto* stmt =
|
auto* stmt = create<SwitchStatement>(Source{Source::Location{20, 2}}, ident, utils::Empty,
|
||||||
create<SwitchStatement>(Source{Source::Location{20, 2}}, ident, utils::Empty, utils::Empty);
|
utils::Empty, utils::Empty);
|
||||||
auto src = stmt->source;
|
auto src = stmt->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);
|
||||||
|
@ -49,17 +49,28 @@ TEST_F(SwitchStatementTest, Creation_WithAttributes) {
|
||||||
auto* attr1 = DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "foo");
|
auto* attr1 = DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "foo");
|
||||||
auto* attr2 = DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "bar");
|
auto* attr2 = DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "bar");
|
||||||
auto* ident = Expr("ident");
|
auto* ident = Expr("ident");
|
||||||
auto* stmt = create<SwitchStatement>(ident, utils::Empty, utils::Vector{attr1, attr2});
|
auto* stmt =
|
||||||
|
create<SwitchStatement>(ident, utils::Empty, utils::Vector{attr1, attr2}, utils::Empty);
|
||||||
|
|
||||||
EXPECT_THAT(stmt->attributes, testing::ElementsAre(attr1, attr2));
|
EXPECT_THAT(stmt->attributes, testing::ElementsAre(attr1, attr2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SwitchStatementTest, Creation_WithBodyAttributes) {
|
||||||
|
auto* attr1 = DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "foo");
|
||||||
|
auto* attr2 = DiagnosticAttribute(builtin::DiagnosticSeverity::kOff, "bar");
|
||||||
|
auto* ident = Expr("ident");
|
||||||
|
auto* stmt =
|
||||||
|
create<SwitchStatement>(ident, utils::Empty, utils::Empty, utils::Vector{attr1, attr2});
|
||||||
|
|
||||||
|
EXPECT_THAT(stmt->body_attributes, testing::ElementsAre(attr1, attr2));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SwitchStatementTest, IsSwitch) {
|
TEST_F(SwitchStatementTest, IsSwitch) {
|
||||||
utils::Vector lit{CaseSelector(2_i)};
|
utils::Vector lit{CaseSelector(2_i)};
|
||||||
auto* ident = Expr("ident");
|
auto* ident = Expr("ident");
|
||||||
utils::Vector body{create<CaseStatement>(lit, Block())};
|
utils::Vector body{create<CaseStatement>(lit, Block())};
|
||||||
|
|
||||||
auto* stmt = create<SwitchStatement>(ident, body, utils::Empty);
|
auto* stmt = create<SwitchStatement>(ident, body, utils::Empty, utils::Empty);
|
||||||
EXPECT_TRUE(stmt->Is<SwitchStatement>());
|
EXPECT_TRUE(stmt->Is<SwitchStatement>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +82,7 @@ TEST_F(SwitchStatementTest, Assert_Null_Condition) {
|
||||||
CaseStatementList cases;
|
CaseStatementList cases;
|
||||||
cases.Push(
|
cases.Push(
|
||||||
b.create<CaseStatement>(utils::Vector{b.CaseSelector(b.Expr(1_i))}, b.Block()));
|
b.create<CaseStatement>(utils::Vector{b.CaseSelector(b.Expr(1_i))}, b.Block()));
|
||||||
b.create<SwitchStatement>(nullptr, cases, utils::Empty);
|
b.create<SwitchStatement>(nullptr, cases, utils::Empty, utils::Empty);
|
||||||
},
|
},
|
||||||
"internal compiler error");
|
"internal compiler error");
|
||||||
}
|
}
|
||||||
|
@ -81,7 +92,8 @@ TEST_F(SwitchStatementTest, Assert_Null_CaseStatement) {
|
||||||
EXPECT_FATAL_FAILURE(
|
EXPECT_FATAL_FAILURE(
|
||||||
{
|
{
|
||||||
ProgramBuilder b;
|
ProgramBuilder b;
|
||||||
b.create<SwitchStatement>(b.Expr(true), CaseStatementList{nullptr}, utils::Empty);
|
b.create<SwitchStatement>(b.Expr(true), CaseStatementList{nullptr}, utils::Empty,
|
||||||
|
utils::Empty);
|
||||||
},
|
},
|
||||||
"internal compiler error");
|
"internal compiler error");
|
||||||
}
|
}
|
||||||
|
@ -99,7 +111,7 @@ TEST_F(SwitchStatementTest, Assert_DifferentProgramID_Condition) {
|
||||||
},
|
},
|
||||||
b1.Block()),
|
b1.Block()),
|
||||||
},
|
},
|
||||||
utils::Empty);
|
utils::Empty, utils::Empty);
|
||||||
},
|
},
|
||||||
"internal compiler error");
|
"internal compiler error");
|
||||||
}
|
}
|
||||||
|
@ -117,7 +129,7 @@ TEST_F(SwitchStatementTest, Assert_DifferentProgramID_CaseStatement) {
|
||||||
},
|
},
|
||||||
b2.Block()),
|
b2.Block()),
|
||||||
},
|
},
|
||||||
utils::Empty);
|
utils::Empty, utils::Empty);
|
||||||
},
|
},
|
||||||
"internal compiler error");
|
"internal compiler error");
|
||||||
}
|
}
|
||||||
|
|
|
@ -3384,7 +3384,7 @@ class ProgramBuilder {
|
||||||
source, Expr(std::forward<ExpressionInit>(condition)),
|
source, Expr(std::forward<ExpressionInit>(condition)),
|
||||||
utils::Vector<const ast::CaseStatement*, sizeof...(cases)>{
|
utils::Vector<const ast::CaseStatement*, sizeof...(cases)>{
|
||||||
std::forward<Cases>(cases)...},
|
std::forward<Cases>(cases)...},
|
||||||
utils::Empty);
|
utils::Empty, utils::Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a ast::SwitchStatement with input expression and cases
|
/// Creates a ast::SwitchStatement with input expression and cases
|
||||||
|
@ -3400,37 +3400,42 @@ class ProgramBuilder {
|
||||||
Expr(std::forward<ExpressionInit>(condition)),
|
Expr(std::forward<ExpressionInit>(condition)),
|
||||||
utils::Vector<const ast::CaseStatement*, sizeof...(cases)>{
|
utils::Vector<const ast::CaseStatement*, sizeof...(cases)>{
|
||||||
std::forward<Cases>(cases)...},
|
std::forward<Cases>(cases)...},
|
||||||
utils::Empty);
|
utils::Empty, utils::Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a ast::SwitchStatement with input expression, cases, and optional attributes
|
/// Creates a ast::SwitchStatement with input expression, cases, and optional attributes
|
||||||
/// @param source the source information
|
/// @param source the source information
|
||||||
/// @param condition the condition expression initializer
|
/// @param condition the condition expression initializer
|
||||||
/// @param cases case statements
|
/// @param cases case statements
|
||||||
/// @param attributes optional attributes
|
/// @param stmt_attributes optional statement attributes
|
||||||
|
/// @param body_attributes optional body attributes
|
||||||
/// @returns the switch statement pointer
|
/// @returns the switch statement pointer
|
||||||
template <typename ExpressionInit>
|
template <typename ExpressionInit>
|
||||||
const ast::SwitchStatement* Switch(
|
const ast::SwitchStatement* Switch(
|
||||||
const Source& source,
|
const Source& source,
|
||||||
ExpressionInit&& condition,
|
ExpressionInit&& condition,
|
||||||
utils::VectorRef<const ast::CaseStatement*> cases,
|
utils::VectorRef<const ast::CaseStatement*> cases,
|
||||||
utils::VectorRef<const ast::Attribute*> attributes = utils::Empty) {
|
utils::VectorRef<const ast::Attribute*> stmt_attributes = utils::Empty,
|
||||||
|
utils::VectorRef<const ast::Attribute*> body_attributes = utils::Empty) {
|
||||||
return create<ast::SwitchStatement>(source, Expr(std::forward<ExpressionInit>(condition)),
|
return create<ast::SwitchStatement>(source, Expr(std::forward<ExpressionInit>(condition)),
|
||||||
cases, std::move(attributes));
|
cases, std::move(stmt_attributes),
|
||||||
|
std::move(body_attributes));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a ast::SwitchStatement with input expression, cases, and optional attributes
|
/// Creates a ast::SwitchStatement with input expression, cases, and optional attributes
|
||||||
/// @param condition the condition expression initializer
|
/// @param condition the condition expression initializer
|
||||||
/// @param cases case statements
|
/// @param cases case statements
|
||||||
/// @param attributes optional attributes
|
/// @param stmt_attributes optional statement attributes
|
||||||
|
/// @param body_attributes optional body attributes
|
||||||
/// @returns the switch statement pointer
|
/// @returns the switch statement pointer
|
||||||
template <typename ExpressionInit, typename = DisableIfSource<ExpressionInit>>
|
template <typename ExpressionInit, typename = DisableIfSource<ExpressionInit>>
|
||||||
const ast::SwitchStatement* Switch(
|
const ast::SwitchStatement* Switch(
|
||||||
ExpressionInit&& condition,
|
ExpressionInit&& condition,
|
||||||
utils::VectorRef<const ast::CaseStatement*> cases,
|
utils::VectorRef<const ast::CaseStatement*> cases,
|
||||||
utils::VectorRef<const ast::Attribute*> attributes = utils::Empty) {
|
utils::VectorRef<const ast::Attribute*> stmt_attributes = utils::Empty,
|
||||||
|
utils::VectorRef<const ast::Attribute*> body_attributes = utils::Empty) {
|
||||||
return create<ast::SwitchStatement>(Expr(std::forward<ExpressionInit>(condition)), cases,
|
return create<ast::SwitchStatement>(Expr(std::forward<ExpressionInit>(condition)), cases,
|
||||||
std::move(attributes));
|
std::move(stmt_attributes), std::move(body_attributes));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a ast::CaseStatement with input list of selectors, and body
|
/// Creates a ast::CaseStatement with input list of selectors, and body
|
||||||
|
|
|
@ -706,8 +706,7 @@ struct SwitchStatementBuilder final : public Castable<SwitchStatementBuilder, St
|
||||||
auto reversed_cases = cases;
|
auto reversed_cases = cases;
|
||||||
std::reverse(reversed_cases.begin(), reversed_cases.end());
|
std::reverse(reversed_cases.begin(), reversed_cases.end());
|
||||||
|
|
||||||
return builder->create<ast::SwitchStatement>(Source{}, condition, std::move(reversed_cases),
|
return builder->Switch(Source{}, condition, std::move(reversed_cases));
|
||||||
utils::Empty);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Switch statement condition
|
/// Switch statement condition
|
||||||
|
|
|
@ -1620,6 +1620,11 @@ Maybe<const ast::SwitchStatement*> ParserImpl::switch_statement(AttributeList& a
|
||||||
return add_error(peek(), "unable to parse selector expression");
|
return add_error(peek(), "unable to parse selector expression");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto body_attrs = attribute_list();
|
||||||
|
if (body_attrs.errored) {
|
||||||
|
return Failure::kErrored;
|
||||||
|
}
|
||||||
|
|
||||||
auto body = expect_brace_block("switch statement", [&]() -> Expect<CaseStatementList> {
|
auto body = expect_brace_block("switch statement", [&]() -> Expect<CaseStatementList> {
|
||||||
bool errored = false;
|
bool errored = false;
|
||||||
CaseStatementList list;
|
CaseStatementList list;
|
||||||
|
@ -1645,7 +1650,8 @@ Maybe<const ast::SwitchStatement*> ParserImpl::switch_statement(AttributeList& a
|
||||||
}
|
}
|
||||||
|
|
||||||
TINT_DEFER(attrs.Clear());
|
TINT_DEFER(attrs.Clear());
|
||||||
return create<ast::SwitchStatement>(source, condition.value, body.value, std::move(attrs));
|
return create<ast::SwitchStatement>(source, condition.value, body.value, std::move(attrs),
|
||||||
|
std::move(body_attrs.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
// switch_body
|
// switch_body
|
||||||
|
|
|
@ -95,7 +95,7 @@ TEST_F(ParserImplTest, SwitchStmt_WithParens) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, SwitchStmt_WithAttributes) {
|
TEST_F(ParserImplTest, SwitchStmt_WithAttributes) {
|
||||||
auto p = parser(R"(@diagnostic(off, derivative_uniformity) switch a { default{} })");
|
auto p = parser("@diagnostic(off, derivative_uniformity) switch a { default{} }");
|
||||||
auto a = p->attribute_list();
|
auto a = p->attribute_list();
|
||||||
auto e = p->switch_statement(a.value);
|
auto e = p->switch_statement(a.value);
|
||||||
EXPECT_TRUE(e.matched);
|
EXPECT_TRUE(e.matched);
|
||||||
|
@ -109,6 +109,21 @@ TEST_F(ParserImplTest, SwitchStmt_WithAttributes) {
|
||||||
EXPECT_TRUE(e->attributes[0]->Is<ast::DiagnosticAttribute>());
|
EXPECT_TRUE(e->attributes[0]->Is<ast::DiagnosticAttribute>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ParserImplTest, SwitchStmt_WithBodyAttributes) {
|
||||||
|
auto p = parser("switch a @diagnostic(off, derivative_uniformity) { default{} }");
|
||||||
|
ParserImpl::AttributeList attrs;
|
||||||
|
auto e = p->switch_statement(attrs);
|
||||||
|
EXPECT_TRUE(e.matched);
|
||||||
|
EXPECT_FALSE(e.errored);
|
||||||
|
EXPECT_FALSE(p->has_error()) << p->error();
|
||||||
|
ASSERT_NE(e.value, nullptr);
|
||||||
|
ASSERT_TRUE(e->Is<ast::SwitchStatement>());
|
||||||
|
|
||||||
|
EXPECT_TRUE(e->attributes.IsEmpty());
|
||||||
|
ASSERT_EQ(e->body_attributes.Length(), 1u);
|
||||||
|
EXPECT_TRUE(e->body_attributes[0]->Is<ast::DiagnosticAttribute>());
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, SwitchStmt_InvalidExpression) {
|
TEST_F(ParserImplTest, SwitchStmt_InvalidExpression) {
|
||||||
auto p = parser("switch a=b {}");
|
auto p = parser("switch a=b {}");
|
||||||
ParserImpl::AttributeList attrs;
|
ParserImpl::AttributeList attrs;
|
||||||
|
|
|
@ -1078,6 +1078,39 @@ INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||||
TestParams{AttributeKind::kWorkgroup, false},
|
TestParams{AttributeKind::kWorkgroup, false},
|
||||||
TestParams{AttributeKind::kBindingAndGroup, false}));
|
TestParams{AttributeKind::kBindingAndGroup, false}));
|
||||||
|
|
||||||
|
using SwitchBodyAttributeTest = TestWithParams;
|
||||||
|
TEST_P(SwitchBodyAttributeTest, IsValid) {
|
||||||
|
auto& params = GetParam();
|
||||||
|
|
||||||
|
WrapInFunction(Switch(Expr(0_a), utils::Vector{DefaultCase()}, utils::Empty,
|
||||||
|
createAttributes(Source{{12, 34}}, *this, params.kind)));
|
||||||
|
|
||||||
|
if (params.should_pass) {
|
||||||
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
} else {
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for switch body");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||||
|
SwitchBodyAttributeTest,
|
||||||
|
testing::Values(TestParams{AttributeKind::kAlign, false},
|
||||||
|
TestParams{AttributeKind::kBinding, false},
|
||||||
|
TestParams{AttributeKind::kBuiltin, false},
|
||||||
|
TestParams{AttributeKind::kDiagnostic, true},
|
||||||
|
TestParams{AttributeKind::kGroup, false},
|
||||||
|
TestParams{AttributeKind::kId, false},
|
||||||
|
TestParams{AttributeKind::kInterpolate, false},
|
||||||
|
TestParams{AttributeKind::kInvariant, false},
|
||||||
|
TestParams{AttributeKind::kLocation, false},
|
||||||
|
TestParams{AttributeKind::kMustUse, false},
|
||||||
|
TestParams{AttributeKind::kOffset, false},
|
||||||
|
TestParams{AttributeKind::kSize, false},
|
||||||
|
TestParams{AttributeKind::kStage, false},
|
||||||
|
TestParams{AttributeKind::kStride, false},
|
||||||
|
TestParams{AttributeKind::kWorkgroup, false},
|
||||||
|
TestParams{AttributeKind::kBindingAndGroup, false}));
|
||||||
|
|
||||||
using IfStatementAttributeTest = TestWithParams;
|
using IfStatementAttributeTest = TestWithParams;
|
||||||
TEST_P(IfStatementAttributeTest, IsValid) {
|
TEST_P(IfStatementAttributeTest, IsValid) {
|
||||||
auto& params = GetParam();
|
auto& params = GetParam();
|
||||||
|
|
|
@ -3975,6 +3975,22 @@ sem::SwitchStatement* Resolver::SwitchStatement(const ast::SwitchStatement* stmt
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle switch body attributes.
|
||||||
|
for (auto* attr : stmt->body_attributes) {
|
||||||
|
Mark(attr);
|
||||||
|
if (auto* dc = attr->As<ast::DiagnosticAttribute>()) {
|
||||||
|
if (!DiagnosticControl(dc->control)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AddError("attribute is not valid for switch body", attr->source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!validator_.NoDuplicateAttributes(stmt->body_attributes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
utils::Vector<sem::CaseStatement*, 4> cases;
|
utils::Vector<sem::CaseStatement*, 4> cases;
|
||||||
cases.Reserve(stmt->body.Length());
|
cases.Reserve(stmt->body.Length());
|
||||||
for (auto* case_stmt : stmt->body) {
|
for (auto* case_stmt : stmt->body) {
|
||||||
|
@ -3986,6 +4002,8 @@ sem::SwitchStatement* Resolver::SwitchStatement(const ast::SwitchStatement* stmt
|
||||||
cases.Push(c);
|
cases.Push(c);
|
||||||
behaviors.Add(c->Behaviors());
|
behaviors.Add(c->Behaviors());
|
||||||
sem->Cases().emplace_back(c);
|
sem->Cases().emplace_back(c);
|
||||||
|
|
||||||
|
ApplyDiagnosticSeverities(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (behaviors.Contains(sem::Behavior::kBreak)) {
|
if (behaviors.Contains(sem::Behavior::kBreak)) {
|
||||||
|
|
|
@ -8734,6 +8734,34 @@ fn foo() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnSwitchBody_CallInBody) {
|
||||||
|
auto& param = GetParam();
|
||||||
|
utils::StringStream ss;
|
||||||
|
ss << R"(
|
||||||
|
@group(0) @binding(0) var<storage, read_write> non_uniform : i32;
|
||||||
|
@group(0) @binding(1) var t : texture_2d<f32>;
|
||||||
|
@group(0) @binding(2) var s : sampler;
|
||||||
|
fn foo() {
|
||||||
|
switch (non_uniform))"
|
||||||
|
<< "@diagnostic(" << param << ", derivative_uniformity)"
|
||||||
|
<< R"( {
|
||||||
|
default {
|
||||||
|
let color = textureSample(t, s, vec2(0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
RunTest(ss.str(), param != builtin::DiagnosticSeverity::kError);
|
||||||
|
if (param == builtin::DiagnosticSeverity::kOff) {
|
||||||
|
EXPECT_TRUE(error_.empty());
|
||||||
|
} else {
|
||||||
|
utils::StringStream err;
|
||||||
|
err << ToStr(param) << ": 'textureSample' must only be called";
|
||||||
|
EXPECT_THAT(error_, ::testing::HasSubstr(err.str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnWhileStatement_CallInCondition) {
|
TEST_P(UniformityAnalysisDiagnosticFilterTest, AttributeOnWhileStatement_CallInCondition) {
|
||||||
auto& param = GetParam();
|
auto& param = GetParam();
|
||||||
utils::StringStream ss;
|
utils::StringStream ss;
|
||||||
|
|
|
@ -42,7 +42,7 @@ class DiagnosticSeverityTest : public TestHelper {
|
||||||
// return;
|
// return;
|
||||||
//
|
//
|
||||||
// @diagnostic(error, chromium_unreachable_code)
|
// @diagnostic(error, chromium_unreachable_code)
|
||||||
// switch (42) {
|
// switch (42) @diagnostic(off, chromium_unreachable_code) {
|
||||||
// case 0 @diagnostic(warning, chromium_unreachable_code) {
|
// case 0 @diagnostic(warning, chromium_unreachable_code) {
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
|
@ -81,6 +81,7 @@ class DiagnosticSeverityTest : public TestHelper {
|
||||||
auto if_body_severity = builtin::DiagnosticSeverity::kWarning;
|
auto if_body_severity = builtin::DiagnosticSeverity::kWarning;
|
||||||
auto else_body_severity = builtin::DiagnosticSeverity::kInfo;
|
auto else_body_severity = builtin::DiagnosticSeverity::kInfo;
|
||||||
auto switch_severity = builtin::DiagnosticSeverity::kError;
|
auto switch_severity = builtin::DiagnosticSeverity::kError;
|
||||||
|
auto switch_body_severity = builtin::DiagnosticSeverity::kOff;
|
||||||
auto case_severity = builtin::DiagnosticSeverity::kWarning;
|
auto case_severity = builtin::DiagnosticSeverity::kWarning;
|
||||||
auto for_severity = builtin::DiagnosticSeverity::kError;
|
auto for_severity = builtin::DiagnosticSeverity::kError;
|
||||||
auto for_body_severity = builtin::DiagnosticSeverity::kWarning;
|
auto for_body_severity = builtin::DiagnosticSeverity::kWarning;
|
||||||
|
@ -109,8 +110,9 @@ class DiagnosticSeverityTest : public TestHelper {
|
||||||
Else(elseif), attr(if_severity));
|
Else(elseif), attr(if_severity));
|
||||||
auto* case_stmt =
|
auto* case_stmt =
|
||||||
Case(CaseSelector(0_a), Block(utils::Vector{return_foo_case}, attr(case_severity)));
|
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))},
|
auto* default_stmt = DefaultCase(Block(return_foo_default));
|
||||||
attr(switch_severity));
|
auto* swtch = Switch(42_a, utils::Vector{case_stmt, default_stmt}, attr(switch_severity),
|
||||||
|
attr(switch_body_severity));
|
||||||
auto* fl =
|
auto* fl =
|
||||||
For(Decl(Var("i", ty.i32())), false, Increment("i"),
|
For(Decl(Var("i", ty.i32())), false, Increment("i"),
|
||||||
Block(utils::Vector{return_foo_for}, attr(for_body_severity)), attr(for_severity));
|
Block(utils::Vector{return_foo_for}, attr(for_body_severity)), attr(for_severity));
|
||||||
|
@ -144,10 +146,11 @@ class DiagnosticSeverityTest : public TestHelper {
|
||||||
EXPECT_EQ(p.Sem().DiagnosticSeverity(return_foo_else, rule), else_body_severity);
|
EXPECT_EQ(p.Sem().DiagnosticSeverity(return_foo_else, rule), else_body_severity);
|
||||||
EXPECT_EQ(p.Sem().DiagnosticSeverity(swtch, rule), switch_severity);
|
EXPECT_EQ(p.Sem().DiagnosticSeverity(swtch, rule), switch_severity);
|
||||||
EXPECT_EQ(p.Sem().DiagnosticSeverity(swtch->condition, rule), switch_severity);
|
EXPECT_EQ(p.Sem().DiagnosticSeverity(swtch->condition, rule), switch_severity);
|
||||||
EXPECT_EQ(p.Sem().DiagnosticSeverity(case_stmt, rule), switch_severity);
|
EXPECT_EQ(p.Sem().DiagnosticSeverity(case_stmt, rule), switch_body_severity);
|
||||||
EXPECT_EQ(p.Sem().DiagnosticSeverity(case_stmt->body, rule), case_severity);
|
EXPECT_EQ(p.Sem().DiagnosticSeverity(case_stmt->body, rule), case_severity);
|
||||||
EXPECT_EQ(p.Sem().DiagnosticSeverity(return_foo_case, rule), case_severity);
|
EXPECT_EQ(p.Sem().DiagnosticSeverity(return_foo_case, rule), case_severity);
|
||||||
EXPECT_EQ(p.Sem().DiagnosticSeverity(return_foo_default, rule), switch_severity);
|
EXPECT_EQ(p.Sem().DiagnosticSeverity(default_stmt, rule), switch_body_severity);
|
||||||
|
EXPECT_EQ(p.Sem().DiagnosticSeverity(return_foo_default, rule), switch_body_severity);
|
||||||
EXPECT_EQ(p.Sem().DiagnosticSeverity(fl, rule), while_severity);
|
EXPECT_EQ(p.Sem().DiagnosticSeverity(fl, rule), while_severity);
|
||||||
EXPECT_EQ(p.Sem().DiagnosticSeverity(fl->initializer, rule), for_severity);
|
EXPECT_EQ(p.Sem().DiagnosticSeverity(fl->initializer, rule), for_severity);
|
||||||
EXPECT_EQ(p.Sem().DiagnosticSeverity(fl->condition, rule), for_severity);
|
EXPECT_EQ(p.Sem().DiagnosticSeverity(fl->condition, rule), for_severity);
|
||||||
|
|
|
@ -31,7 +31,7 @@ TEST_F(GlslGeneratorImplTest_Switch, Emit_Switch) {
|
||||||
auto* case_stmt = create<ast::CaseStatement>(utils::Vector{CaseSelector(5_i)}, case_body);
|
auto* case_stmt = create<ast::CaseStatement>(utils::Vector{CaseSelector(5_i)}, case_body);
|
||||||
|
|
||||||
auto* cond = Expr("cond");
|
auto* cond = Expr("cond");
|
||||||
auto* s = create<ast::SwitchStatement>(cond, utils::Vector{case_stmt, def}, utils::Empty);
|
auto* s = Switch(cond, utils::Vector{case_stmt, def});
|
||||||
WrapInFunction(s);
|
WrapInFunction(s);
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
@ -58,7 +58,7 @@ TEST_F(GlslGeneratorImplTest_Switch, Emit_Switch_MixedDefault) {
|
||||||
def_body);
|
def_body);
|
||||||
|
|
||||||
auto* cond = Expr("cond");
|
auto* cond = Expr("cond");
|
||||||
auto* s = create<ast::SwitchStatement>(cond, utils::Vector{def}, utils::Empty);
|
auto* s = Switch(cond, utils::Vector{def});
|
||||||
WrapInFunction(s);
|
WrapInFunction(s);
|
||||||
|
|
||||||
GeneratorImpl& gen = Build();
|
GeneratorImpl& gen = Build();
|
||||||
|
|
|
@ -1229,7 +1229,16 @@ bool GeneratorImpl::EmitSwitch(const ast::SwitchStatement* stmt) {
|
||||||
if (!EmitExpression(out, stmt->condition)) {
|
if (!EmitExpression(out, stmt->condition)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out << ") {";
|
out << ") ";
|
||||||
|
|
||||||
|
if (!stmt->body_attributes.IsEmpty()) {
|
||||||
|
if (!EmitAttributes(out, stmt->body_attributes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out << " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
out << "{";
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
@fragment
|
||||||
|
fn main(@location(0) x : f32) {
|
||||||
|
switch (i32(x)) @diagnostic(warning, derivative_uniformity) {
|
||||||
|
default {
|
||||||
|
_ = dpdx(1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
diagnostic_filtering/switch_body_attribute.wgsl:5:11 warning: 'dpdx' must only be called from uniform control flow
|
||||||
|
_ = dpdx(1.0);
|
||||||
|
^^^^^^^^^
|
||||||
|
|
||||||
|
diagnostic_filtering/switch_body_attribute.wgsl:3:3 note: control flow depends on possibly non-uniform value
|
||||||
|
switch (i32(x)) @diagnostic(warning, derivative_uniformity) {
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
diagnostic_filtering/switch_body_attribute.wgsl:3:15 note: user-defined input 'x' of 'main' may be non-uniform
|
||||||
|
switch (i32(x)) @diagnostic(warning, derivative_uniformity) {
|
||||||
|
^
|
||||||
|
|
||||||
|
int tint_ftoi(float v) {
|
||||||
|
return ((v < 2147483520.0f) ? ((v < -2147483648.0f) ? -2147483648 : int(v)) : 2147483647);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tint_symbol_1 {
|
||||||
|
float x : TEXCOORD0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void main_inner(float x) {
|
||||||
|
tint_ftoi(x);
|
||||||
|
do {
|
||||||
|
} while (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(tint_symbol_1 tint_symbol) {
|
||||||
|
main_inner(tint_symbol.x);
|
||||||
|
return;
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
diagnostic_filtering/switch_body_attribute.wgsl:5:11 warning: 'dpdx' must only be called from uniform control flow
|
||||||
|
_ = dpdx(1.0);
|
||||||
|
^^^^^^^^^
|
||||||
|
|
||||||
|
diagnostic_filtering/switch_body_attribute.wgsl:3:3 note: control flow depends on possibly non-uniform value
|
||||||
|
switch (i32(x)) @diagnostic(warning, derivative_uniformity) {
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
diagnostic_filtering/switch_body_attribute.wgsl:3:15 note: user-defined input 'x' of 'main' may be non-uniform
|
||||||
|
switch (i32(x)) @diagnostic(warning, derivative_uniformity) {
|
||||||
|
^
|
||||||
|
|
||||||
|
int tint_ftoi(float v) {
|
||||||
|
return ((v < 2147483520.0f) ? ((v < -2147483648.0f) ? -2147483648 : int(v)) : 2147483647);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tint_symbol_1 {
|
||||||
|
float x : TEXCOORD0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void main_inner(float x) {
|
||||||
|
tint_ftoi(x);
|
||||||
|
do {
|
||||||
|
} while (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(tint_symbol_1 tint_symbol) {
|
||||||
|
main_inner(tint_symbol.x);
|
||||||
|
return;
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
diagnostic_filtering/switch_body_attribute.wgsl:5:11 warning: 'dpdx' must only be called from uniform control flow
|
||||||
|
_ = dpdx(1.0);
|
||||||
|
^^^^^^^^^
|
||||||
|
|
||||||
|
diagnostic_filtering/switch_body_attribute.wgsl:3:3 note: control flow depends on possibly non-uniform value
|
||||||
|
switch (i32(x)) @diagnostic(warning, derivative_uniformity) {
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
diagnostic_filtering/switch_body_attribute.wgsl:3:15 note: user-defined input 'x' of 'main' may be non-uniform
|
||||||
|
switch (i32(x)) @diagnostic(warning, derivative_uniformity) {
|
||||||
|
^
|
||||||
|
|
||||||
|
#version 310 es
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
layout(location = 0) in float x_1;
|
||||||
|
int tint_ftoi(float v) {
|
||||||
|
return ((v < 2147483520.0f) ? ((v < -2147483648.0f) ? (-2147483647 - 1) : int(v)) : 2147483647);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tint_symbol(float x) {
|
||||||
|
switch(tint_ftoi(x)) {
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
tint_symbol(x_1);
|
||||||
|
return;
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
diagnostic_filtering/switch_body_attribute.wgsl:5:11 warning: 'dpdx' must only be called from uniform control flow
|
||||||
|
_ = dpdx(1.0);
|
||||||
|
^^^^^^^^^
|
||||||
|
|
||||||
|
diagnostic_filtering/switch_body_attribute.wgsl:3:3 note: control flow depends on possibly non-uniform value
|
||||||
|
switch (i32(x)) @diagnostic(warning, derivative_uniformity) {
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
diagnostic_filtering/switch_body_attribute.wgsl:3:15 note: user-defined input 'x' of 'main' may be non-uniform
|
||||||
|
switch (i32(x)) @diagnostic(warning, derivative_uniformity) {
|
||||||
|
^
|
||||||
|
|
||||||
|
#include <metal_stdlib>
|
||||||
|
|
||||||
|
using namespace metal;
|
||||||
|
int tint_ftoi(float v) {
|
||||||
|
return select(2147483647, select(int(v), (-2147483647 - 1), (v < -2147483648.0f)), (v < 2147483520.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tint_symbol_2 {
|
||||||
|
float x [[user(locn0)]];
|
||||||
|
};
|
||||||
|
|
||||||
|
void tint_symbol_inner(float x) {
|
||||||
|
switch(tint_ftoi(x)) {
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment void tint_symbol(tint_symbol_2 tint_symbol_1 [[stage_in]]) {
|
||||||
|
tint_symbol_inner(tint_symbol_1.x);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
diagnostic_filtering/switch_body_attribute.wgsl:5:11 warning: 'dpdx' must only be called from uniform control flow
|
||||||
|
_ = dpdx(1.0);
|
||||||
|
^^^^^^^^^
|
||||||
|
|
||||||
|
diagnostic_filtering/switch_body_attribute.wgsl:3:3 note: control flow depends on possibly non-uniform value
|
||||||
|
switch (i32(x)) @diagnostic(warning, derivative_uniformity) {
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
diagnostic_filtering/switch_body_attribute.wgsl:3:15 note: user-defined input 'x' of 'main' may be non-uniform
|
||||||
|
switch (i32(x)) @diagnostic(warning, derivative_uniformity) {
|
||||||
|
^
|
||||||
|
|
||||||
|
; SPIR-V
|
||||||
|
; Version: 1.3
|
||||||
|
; Generator: Google Tint Compiler; 0
|
||||||
|
; Bound: 32
|
||||||
|
; Schema: 0
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Fragment %main "main" %x_1
|
||||||
|
OpExecutionMode %main OriginUpperLeft
|
||||||
|
OpName %x_1 "x_1"
|
||||||
|
OpName %tint_ftoi "tint_ftoi"
|
||||||
|
OpName %v "v"
|
||||||
|
OpName %main_inner "main_inner"
|
||||||
|
OpName %x "x"
|
||||||
|
OpName %main "main"
|
||||||
|
OpDecorate %x_1 Location 0
|
||||||
|
%float = OpTypeFloat 32
|
||||||
|
%_ptr_Input_float = OpTypePointer Input %float
|
||||||
|
%x_1 = OpVariable %_ptr_Input_float Input
|
||||||
|
%int = OpTypeInt 32 1
|
||||||
|
%4 = OpTypeFunction %int %float
|
||||||
|
%float_2_14748352e_09 = OpConstant %float 2.14748352e+09
|
||||||
|
%bool = OpTypeBool
|
||||||
|
%float_n2_14748365e_09 = OpConstant %float -2.14748365e+09
|
||||||
|
%int_n2147483648 = OpConstant %int -2147483648
|
||||||
|
%int_2147483647 = OpConstant %int 2147483647
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%19 = OpTypeFunction %void %float
|
||||||
|
%27 = OpTypeFunction %void
|
||||||
|
%tint_ftoi = OpFunction %int None %4
|
||||||
|
%v = OpFunctionParameter %float
|
||||||
|
%8 = OpLabel
|
||||||
|
%11 = OpFOrdLessThan %bool %v %float_2_14748352e_09
|
||||||
|
%15 = OpFOrdLessThan %bool %v %float_n2_14748365e_09
|
||||||
|
%17 = OpConvertFToS %int %v
|
||||||
|
%13 = OpSelect %int %15 %int_n2147483648 %17
|
||||||
|
%9 = OpSelect %int %11 %13 %int_2147483647
|
||||||
|
OpReturnValue %9
|
||||||
|
OpFunctionEnd
|
||||||
|
%main_inner = OpFunction %void None %19
|
||||||
|
%x = OpFunctionParameter %float
|
||||||
|
%23 = OpLabel
|
||||||
|
%25 = OpFunctionCall %int %tint_ftoi %x
|
||||||
|
OpSelectionMerge %24 None
|
||||||
|
OpSwitch %25 %26
|
||||||
|
%26 = OpLabel
|
||||||
|
OpBranch %24
|
||||||
|
%24 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
%main = OpFunction %void None %27
|
||||||
|
%29 = OpLabel
|
||||||
|
%31 = OpLoad %float %x_1
|
||||||
|
%30 = OpFunctionCall %void %main_inner %31
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
|
@ -0,0 +1,20 @@
|
||||||
|
diagnostic_filtering/switch_body_attribute.wgsl:5:11 warning: 'dpdx' must only be called from uniform control flow
|
||||||
|
_ = dpdx(1.0);
|
||||||
|
^^^^^^^^^
|
||||||
|
|
||||||
|
diagnostic_filtering/switch_body_attribute.wgsl:3:3 note: control flow depends on possibly non-uniform value
|
||||||
|
switch (i32(x)) @diagnostic(warning, derivative_uniformity) {
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
diagnostic_filtering/switch_body_attribute.wgsl:3:15 note: user-defined input 'x' of 'main' may be non-uniform
|
||||||
|
switch (i32(x)) @diagnostic(warning, derivative_uniformity) {
|
||||||
|
^
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn main(@location(0) x : f32) {
|
||||||
|
switch(i32(x)) @diagnostic(warning, derivative_uniformity) {
|
||||||
|
default: {
|
||||||
|
_ = dpdx(1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue