validation: store type of sample_index and sample_mask builtins must be 'u32'
Bug: tint:506 Change-Id: I786a74640d9ad9d98a76cdbe4dfcbfffdb0ccf2c Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/55121 Kokoro: Kokoro <noreply+kokoro@google.com> Auto-Submit: Sarah Mashayekhi <sarahmashay@google.com> Reviewed-by: Ben Clayton <bclayton@google.com> Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
8b2be2d1e2
commit
443c41de33
|
@ -20,31 +20,112 @@ namespace {
|
|||
class ResolverBuiltinsValidationTest : public resolver::TestHelper,
|
||||
public testing::Test {};
|
||||
|
||||
TEST_F(ResolverBuiltinsValidationTest, FrontFacingParamIsBool_Pass) {
|
||||
TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_Struct_Fail) {
|
||||
// struct MyInputs {
|
||||
// [[builtin(sample_mask)]] m: f32;
|
||||
// };
|
||||
// [[stage(fragment)]]
|
||||
// fn fragShader(is_front: MyInputs) -> [[location(0)]] f32 { return 1.0; }
|
||||
|
||||
auto* s = Structure(
|
||||
"MyInputs", {Member("m", ty.f32(),
|
||||
ast::DecorationList{Builtin(
|
||||
Source{{12, 34}}, ast::Builtin::kSampleMask)})});
|
||||
Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
|
||||
{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: store type of builtin(sample_mask) must be 'u32'");
|
||||
}
|
||||
|
||||
TEST_F(ResolverBuiltinsValidationTest, SampleMaskIsNotU32_Fail) {
|
||||
// [[stage(fragment)]]
|
||||
// fn fs_main(
|
||||
// [[builtin(front_facing)]] is_front: bool
|
||||
// [[builtin(sample_mask)]] arg: bool
|
||||
// ) -> [[location(0)]] f32 { return 1.0; }
|
||||
auto* is_front =
|
||||
Param("is_front", ty.bool_(),
|
||||
ast::DecorationList{Builtin(ast::Builtin::kFrontFacing)});
|
||||
Func("fs_main", ast::VariableList{is_front}, ty.f32(), {Return(1.0f)},
|
||||
auto* arg = Param("arg", ty.bool_(),
|
||||
ast::DecorationList{
|
||||
Builtin(Source{{12, 34}}, ast::Builtin::kSampleMask)});
|
||||
Func("fs_main", ast::VariableList{arg}, ty.f32(), {Return(1.0f)},
|
||||
ast::DecorationList{Stage(ast::PipelineStage::kFragment)},
|
||||
{Location(0)});
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: store type of builtin(sample_mask) must be 'u32'");
|
||||
}
|
||||
|
||||
TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Struct_Fail) {
|
||||
// struct MyInputs {
|
||||
// [[builtin(sample_index)]] m: f32;
|
||||
// };
|
||||
// [[stage(fragment)]]
|
||||
// fn fragShader(is_front: MyInputs) -> [[location(0)]] f32 { return 1.0; }
|
||||
|
||||
auto* s = Structure(
|
||||
"MyInputs", {Member("m", ty.f32(),
|
||||
ast::DecorationList{Builtin(
|
||||
Source{{12, 34}}, ast::Builtin::kSampleIndex)})});
|
||||
Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
|
||||
{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: store type of builtin(sample_index) must be 'u32'");
|
||||
}
|
||||
|
||||
TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Fail) {
|
||||
// [[stage(fragment)]]
|
||||
// fn fs_main(
|
||||
// [[builtin(sample_index)]] arg: bool
|
||||
// ) -> [[location(0)]] f32 { return 1.0; }
|
||||
auto* arg = Param("arg", ty.bool_(),
|
||||
ast::DecorationList{
|
||||
Builtin(Source{{12, 34}}, ast::Builtin::kSampleIndex)});
|
||||
Func("fs_main", ast::VariableList{arg}, ty.f32(), {Return(1.0f)},
|
||||
ast::DecorationList{Stage(ast::PipelineStage::kFragment)},
|
||||
{Location(0)});
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: store type of builtin(sample_index) must be 'u32'");
|
||||
}
|
||||
|
||||
TEST_F(ResolverBuiltinsValidationTest, FragmentBuiltin_Pass) {
|
||||
// [[stage(fragment)]]
|
||||
// fn fs_main(
|
||||
// [[builtin(front_facing)]] ff : bool,
|
||||
// [[builtin(sample_index)]] si: u32,
|
||||
// [[builtin(sample_mask)]] sm : u32
|
||||
// ) -> [[location(0)]] f32 { return 1.0; }
|
||||
auto* ff = Param("ff", ty.bool_(),
|
||||
ast::DecorationList{Builtin(ast::Builtin::kFrontFacing)});
|
||||
auto* si = Param("si", ty.u32(),
|
||||
ast::DecorationList{Builtin(ast::Builtin::kSampleIndex)});
|
||||
auto* sm = Param("sm", ty.u32(),
|
||||
ast::DecorationList{Builtin(ast::Builtin::kSampleMask)});
|
||||
Func("fs_main", ast::VariableList{ff, si, sm}, ty.f32(), {Return(1.0f)},
|
||||
ast::DecorationList{Stage(ast::PipelineStage::kFragment)},
|
||||
{Location(0)});
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverBuiltinsValidationTest, FrontFacingMemberIsBool_Pass) {
|
||||
// struct MyInputs {
|
||||
// [[builtin(front_facing)]] pos: bool;
|
||||
TEST_F(ResolverBuiltinsValidationTest, FragmentBuiltinStruct_Pass) {
|
||||
// Struct MyInputs {
|
||||
// [[builtin(front_facing)]] ff: bool;
|
||||
// [[builtin(sample_index)]] si: u32;
|
||||
// [[builtin(sample_mask)]] sm : u32;
|
||||
// };
|
||||
// [[stage(fragment)]]
|
||||
// fn fragShader(is_front: MyInputs) -> [[location(0)]] f32 { return 1.0; }
|
||||
|
||||
auto* s = Structure(
|
||||
"MyInputs",
|
||||
{Member("pos", ty.bool_(),
|
||||
ast::DecorationList{Builtin(ast::Builtin::kFrontFacing)})});
|
||||
{Member("ff", ty.bool_(),
|
||||
ast::DecorationList{Builtin(ast::Builtin::kFrontFacing)}),
|
||||
Member("si", ty.u32(),
|
||||
ast::DecorationList{Builtin(ast::Builtin::kSampleIndex)}),
|
||||
Member("sm", ty.u32(),
|
||||
ast::DecorationList{Builtin(ast::Builtin::kSampleMask)})});
|
||||
Func("fragShader", {Param("is_front", ty.Of(s))}, ty.f32(), {Return(1.0f)},
|
||||
{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
@ -65,7 +146,7 @@ TEST_F(ResolverBuiltinsValidationTest, FrontFacingParamIsNotBool_Fail) {
|
|||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error v-15001: front_facing builtin must be boolean");
|
||||
"12:34 error: store type of builtin(front_facing) must be 'bool'");
|
||||
}
|
||||
|
||||
TEST_F(ResolverBuiltinsValidationTest, FrontFacingMemberIsNotBool_Fail) {
|
||||
|
@ -84,7 +165,7 @@ TEST_F(ResolverBuiltinsValidationTest, FrontFacingMemberIsNotBool_Fail) {
|
|||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error v-15001: front_facing builtin must be boolean");
|
||||
"12:34 error: store type of builtin(front_facing) must be 'bool'");
|
||||
}
|
||||
|
||||
TEST_F(ResolverBuiltinsValidationTest, Length_Float_Scalar) {
|
||||
|
|
|
@ -370,7 +370,7 @@ TEST_F(ResolverEntryPointValidationTest,
|
|||
// fn main(param : Input) {}
|
||||
auto* input = Structure(
|
||||
"Input",
|
||||
{Member("a", ty.f32(),
|
||||
{Member("a", ty.u32(),
|
||||
{Location(Source{{13, 43}}, 0),
|
||||
Builtin(Source{{14, 52}}, ast::Builtin::kSampleIndex)})});
|
||||
auto* param = Param("param", ty.Of(input));
|
||||
|
|
|
@ -121,6 +121,16 @@ bool IsValidationDisabled(const ast::DecorationList& decorations,
|
|||
return false;
|
||||
}
|
||||
|
||||
// Helper to stringify a pipeline IO decoration.
|
||||
std::string deco_to_str(const ast::Decoration* deco) {
|
||||
std::stringstream str;
|
||||
if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
||||
str << "builtin(" << builtin->value() << ")";
|
||||
} else if (auto* location = deco->As<ast::LocationDecoration>()) {
|
||||
str << "location(" << location->value() << ")";
|
||||
}
|
||||
return str.str();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Resolver::Resolver(ProgramBuilder* builder)
|
||||
|
@ -891,22 +901,44 @@ bool Resolver::ValidateVariable(const VariableInfo* info) {
|
|||
}
|
||||
|
||||
bool Resolver::ValidateParameter(const VariableInfo* info) {
|
||||
auto* var = info->declaration;
|
||||
for (auto* deco : var->decorations()) {
|
||||
if (!ValidateVariable(info)) {
|
||||
return false;
|
||||
}
|
||||
for (auto* deco : info->declaration->decorations()) {
|
||||
if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
||||
if (builtin->value() == ast::Builtin::kFrontFacing) {
|
||||
auto* storage_type = info->type->UnwrapRef();
|
||||
if (!(storage_type->Is<sem::Bool>())) {
|
||||
diagnostics_.add_error("v-15001",
|
||||
"front_facing builtin must be boolean",
|
||||
deco->source());
|
||||
return false;
|
||||
}
|
||||
if (!ValidateBuiltinDecoration(builtin, info->type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return ValidateVariable(info);
|
||||
bool Resolver::ValidateBuiltinDecoration(const ast::BuiltinDecoration* deco,
|
||||
const sem::Type* storage_type) {
|
||||
auto* type = storage_type->UnwrapRef();
|
||||
switch (deco->value()) {
|
||||
case ast::Builtin::kFrontFacing:
|
||||
if (!type->Is<sem::Bool>()) {
|
||||
diagnostics_.add_error(
|
||||
"store type of " + deco_to_str(deco) + " must be 'bool'",
|
||||
deco->source());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case ast::Builtin::kSampleMask:
|
||||
case ast::Builtin::kSampleIndex:
|
||||
if (!type->Is<sem::U32>()) {
|
||||
diagnostics_.add_error(
|
||||
"store type of " + deco_to_str(deco) + " must be 'u32'",
|
||||
deco->source());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Resolver::ValidateFunction(const ast::Function* func,
|
||||
|
@ -1014,16 +1046,7 @@ bool Resolver::ValidateEntryPoint(const ast::Function* func,
|
|||
kParameter,
|
||||
kReturnType,
|
||||
};
|
||||
// Helper to stringify a pipeline IO decoration.
|
||||
auto deco_to_str = [](const ast::Decoration* deco) {
|
||||
std::stringstream str;
|
||||
if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
||||
str << "builtin(" << builtin->value() << ")";
|
||||
} else if (auto* location = deco->As<ast::LocationDecoration>()) {
|
||||
str << "location(" << location->value() << ")";
|
||||
}
|
||||
return str.str();
|
||||
};
|
||||
|
||||
// Inner lambda that is applied to a type and all of its members.
|
||||
auto validate_entry_point_decorations_inner =
|
||||
[&](const ast::DecorationList& decos, sem::Type* ty, Source source,
|
||||
|
@ -3178,13 +3201,8 @@ bool Resolver::ValidateStructure(const sem::Struct* str) {
|
|||
return false;
|
||||
}
|
||||
if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
||||
if (builtin->value() == ast::Builtin::kFrontFacing) {
|
||||
if (!(member->Type()->Is<sem::Bool>())) {
|
||||
diagnostics_.add_error("v-15001",
|
||||
"front_facing builtin must be boolean",
|
||||
deco->source());
|
||||
return false;
|
||||
}
|
||||
if (!ValidateBuiltinDecoration(builtin, member->Type())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -266,6 +266,8 @@ class Resolver {
|
|||
bool ValidateAtomic(const ast::Atomic* a, const sem::Atomic* s);
|
||||
bool ValidateAtomicUses();
|
||||
bool ValidateAssignment(const ast::AssignmentStatement* a);
|
||||
bool ValidateBuiltinDecoration(const ast::BuiltinDecoration* deco,
|
||||
const sem::Type* storage_type);
|
||||
bool ValidateCallStatement(ast::CallStatement* stmt);
|
||||
bool ValidateEntryPoint(const ast::Function* func, const FunctionInfo* info);
|
||||
bool ValidateFunction(const ast::Function* func, const FunctionInfo* info);
|
||||
|
|
Loading…
Reference in New Issue