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,
|
class ResolverBuiltinsValidationTest : public resolver::TestHelper,
|
||||||
public testing::Test {};
|
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)]]
|
// [[stage(fragment)]]
|
||||||
// fn fs_main(
|
// fn fs_main(
|
||||||
// [[builtin(front_facing)]] is_front: bool
|
// [[builtin(sample_mask)]] arg: bool
|
||||||
// ) -> [[location(0)]] f32 { return 1.0; }
|
// ) -> [[location(0)]] f32 { return 1.0; }
|
||||||
auto* is_front =
|
auto* arg = Param("arg", ty.bool_(),
|
||||||
Param("is_front", ty.bool_(),
|
ast::DecorationList{
|
||||||
ast::DecorationList{Builtin(ast::Builtin::kFrontFacing)});
|
Builtin(Source{{12, 34}}, ast::Builtin::kSampleMask)});
|
||||||
Func("fs_main", ast::VariableList{is_front}, ty.f32(), {Return(1.0f)},
|
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)},
|
ast::DecorationList{Stage(ast::PipelineStage::kFragment)},
|
||||||
{Location(0)});
|
{Location(0)});
|
||||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverBuiltinsValidationTest, FrontFacingMemberIsBool_Pass) {
|
TEST_F(ResolverBuiltinsValidationTest, FragmentBuiltinStruct_Pass) {
|
||||||
// struct MyInputs {
|
// Struct MyInputs {
|
||||||
// [[builtin(front_facing)]] pos: bool;
|
// [[builtin(front_facing)]] ff: bool;
|
||||||
|
// [[builtin(sample_index)]] si: u32;
|
||||||
|
// [[builtin(sample_mask)]] sm : u32;
|
||||||
// };
|
// };
|
||||||
// [[stage(fragment)]]
|
// [[stage(fragment)]]
|
||||||
// fn fragShader(is_front: MyInputs) -> [[location(0)]] f32 { return 1.0; }
|
// fn fragShader(is_front: MyInputs) -> [[location(0)]] f32 { return 1.0; }
|
||||||
|
|
||||||
auto* s = Structure(
|
auto* s = Structure(
|
||||||
"MyInputs",
|
"MyInputs",
|
||||||
{Member("pos", ty.bool_(),
|
{Member("ff", ty.bool_(),
|
||||||
ast::DecorationList{Builtin(ast::Builtin::kFrontFacing)})});
|
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)},
|
Func("fragShader", {Param("is_front", ty.Of(s))}, ty.f32(), {Return(1.0f)},
|
||||||
{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
|
{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
|
||||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
@ -65,7 +146,7 @@ TEST_F(ResolverBuiltinsValidationTest, FrontFacingParamIsNotBool_Fail) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
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) {
|
TEST_F(ResolverBuiltinsValidationTest, FrontFacingMemberIsNotBool_Fail) {
|
||||||
|
@ -84,7 +165,7 @@ TEST_F(ResolverBuiltinsValidationTest, FrontFacingMemberIsNotBool_Fail) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
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) {
|
TEST_F(ResolverBuiltinsValidationTest, Length_Float_Scalar) {
|
||||||
|
|
|
@ -370,7 +370,7 @@ TEST_F(ResolverEntryPointValidationTest,
|
||||||
// fn main(param : Input) {}
|
// fn main(param : Input) {}
|
||||||
auto* input = Structure(
|
auto* input = Structure(
|
||||||
"Input",
|
"Input",
|
||||||
{Member("a", ty.f32(),
|
{Member("a", ty.u32(),
|
||||||
{Location(Source{{13, 43}}, 0),
|
{Location(Source{{13, 43}}, 0),
|
||||||
Builtin(Source{{14, 52}}, ast::Builtin::kSampleIndex)})});
|
Builtin(Source{{14, 52}}, ast::Builtin::kSampleIndex)})});
|
||||||
auto* param = Param("param", ty.Of(input));
|
auto* param = Param("param", ty.Of(input));
|
||||||
|
|
|
@ -121,6 +121,16 @@ bool IsValidationDisabled(const ast::DecorationList& decorations,
|
||||||
return false;
|
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
|
} // namespace
|
||||||
|
|
||||||
Resolver::Resolver(ProgramBuilder* builder)
|
Resolver::Resolver(ProgramBuilder* builder)
|
||||||
|
@ -891,22 +901,44 @@ bool Resolver::ValidateVariable(const VariableInfo* info) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resolver::ValidateParameter(const VariableInfo* info) {
|
bool Resolver::ValidateParameter(const VariableInfo* info) {
|
||||||
auto* var = info->declaration;
|
if (!ValidateVariable(info)) {
|
||||||
for (auto* deco : var->decorations()) {
|
return false;
|
||||||
|
}
|
||||||
|
for (auto* deco : info->declaration->decorations()) {
|
||||||
if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
||||||
if (builtin->value() == ast::Builtin::kFrontFacing) {
|
if (!ValidateBuiltinDecoration(builtin, info->type)) {
|
||||||
auto* storage_type = info->type->UnwrapRef();
|
return false;
|
||||||
if (!(storage_type->Is<sem::Bool>())) {
|
|
||||||
diagnostics_.add_error("v-15001",
|
|
||||||
"front_facing builtin must be boolean",
|
|
||||||
deco->source());
|
|
||||||
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,
|
bool Resolver::ValidateFunction(const ast::Function* func,
|
||||||
|
@ -1014,16 +1046,7 @@ bool Resolver::ValidateEntryPoint(const ast::Function* func,
|
||||||
kParameter,
|
kParameter,
|
||||||
kReturnType,
|
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.
|
// Inner lambda that is applied to a type and all of its members.
|
||||||
auto validate_entry_point_decorations_inner =
|
auto validate_entry_point_decorations_inner =
|
||||||
[&](const ast::DecorationList& decos, sem::Type* ty, Source source,
|
[&](const ast::DecorationList& decos, sem::Type* ty, Source source,
|
||||||
|
@ -3178,13 +3201,8 @@ bool Resolver::ValidateStructure(const sem::Struct* str) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
||||||
if (builtin->value() == ast::Builtin::kFrontFacing) {
|
if (!ValidateBuiltinDecoration(builtin, member->Type())) {
|
||||||
if (!(member->Type()->Is<sem::Bool>())) {
|
return false;
|
||||||
diagnostics_.add_error("v-15001",
|
|
||||||
"front_facing builtin must be boolean",
|
|
||||||
deco->source());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -266,6 +266,8 @@ class Resolver {
|
||||||
bool ValidateAtomic(const ast::Atomic* a, const sem::Atomic* s);
|
bool ValidateAtomic(const ast::Atomic* a, const sem::Atomic* s);
|
||||||
bool ValidateAtomicUses();
|
bool ValidateAtomicUses();
|
||||||
bool ValidateAssignment(const ast::AssignmentStatement* a);
|
bool ValidateAssignment(const ast::AssignmentStatement* a);
|
||||||
|
bool ValidateBuiltinDecoration(const ast::BuiltinDecoration* deco,
|
||||||
|
const sem::Type* storage_type);
|
||||||
bool ValidateCallStatement(ast::CallStatement* stmt);
|
bool ValidateCallStatement(ast::CallStatement* stmt);
|
||||||
bool ValidateEntryPoint(const ast::Function* func, const FunctionInfo* info);
|
bool ValidateEntryPoint(const ast::Function* func, const FunctionInfo* info);
|
||||||
bool ValidateFunction(const ast::Function* func, const FunctionInfo* info);
|
bool ValidateFunction(const ast::Function* func, const FunctionInfo* info);
|
||||||
|
|
Loading…
Reference in New Issue