mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-20 10:25:28 +00:00
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:
committed by
Dawn LUCI CQ
parent
e60a579c19
commit
d9f659670d
@@ -1030,6 +1030,104 @@ TEST_F(OverrideAttributeTest, DuplicateAttribute) {
|
||||
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 AttributeTests
|
||||
|
||||
|
||||
@@ -181,6 +181,66 @@ TEST_F(ResolverDiagnosticControlTest, FunctionAttributeScope) {
|
||||
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) {
|
||||
DiagnosticDirective(ast::DiagnosticSeverity::kError,
|
||||
Expr(Source{{12, 34}}, "chromium_unreachable_cod"));
|
||||
|
||||
@@ -3845,6 +3845,43 @@ SEM* Resolver::StatementScope(const ast::Statement* ast, SEM* sem, F&& callback)
|
||||
|
||||
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_compound_statement_,
|
||||
as_compound ? as_compound : current_compound_statement_);
|
||||
|
||||
@@ -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,
|
||||
UniformityAnalysisDiagnosticFilterTest,
|
||||
::testing::Values(ast::DiagnosticSeverity::kError,
|
||||
|
||||
Reference in New Issue
Block a user