Validate attributes on function return types

Functions that are not entry points cannot have any attributes on
their return types.

Validate the builtin store types for return types.

Bug: tint:851
Change-Id: I718356b3ab06db4b4502a53b81790e4de0ecfeac
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/56100
Auto-Submit: James Price <jrprice@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
James Price 2021-06-28 09:53:37 +00:00 committed by Tint LUCI CQ
parent 67993b955e
commit 1b1d963d7b
3 changed files with 73 additions and 5 deletions

View File

@ -39,6 +39,18 @@ TEST_F(ResolverBuiltinsValidationTest, PositionNotF32_Struct_Fail) {
"12:34 error: store type of builtin(position) must be 'vec4<f32>'"); "12:34 error: store type of builtin(position) must be 'vec4<f32>'");
} }
TEST_F(ResolverBuiltinsValidationTest, PositionNotF32_ReturnType_Fail) {
// [[stage(vertex)]]
// fn main() -> [[builtin(position)]] f32 { return 1.0; }
Func("main", {}, ty.f32(), {Return(1.0f)},
{Stage(ast::PipelineStage::kVertex)},
{Builtin(Source{{12, 34}}, ast::Builtin::kPosition)});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(position) must be 'vec4<f32>'");
}
TEST_F(ResolverBuiltinsValidationTest, FragDepthNotF32_Struct_Fail) { TEST_F(ResolverBuiltinsValidationTest, FragDepthNotF32_Struct_Fail) {
// struct MyInputs { // struct MyInputs {
// [[builtin(kFragDepth)]] p: i32; // [[builtin(kFragDepth)]] p: i32;
@ -77,6 +89,18 @@ TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_Struct_Fail) {
"12:34 error: store type of builtin(sample_mask) must be 'u32'"); "12:34 error: store type of builtin(sample_mask) must be 'u32'");
} }
TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_ReturnType_Fail) {
// [[stage(fragment)]]
// fn main() -> [[builtin(sample_mask)]] i32 { return 1; }
Func("main", {}, ty.i32(), {Return(1)},
{Stage(ast::PipelineStage::kFragment)},
{Builtin(Source{{12, 34}}, ast::Builtin::kSampleMask)});
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) { TEST_F(ResolverBuiltinsValidationTest, SampleMaskIsNotU32_Fail) {
// [[stage(fragment)]] // [[stage(fragment)]]
// fn fs_main( // fn fs_main(

View File

@ -230,8 +230,7 @@ TEST_P(FunctionReturnTypeDecorationTest, IsValid) {
auto& params = GetParam(); auto& params = GetParam();
Func("main", ast::VariableList{}, ty.f32(), ast::StatementList{Return(1.f)}, Func("main", ast::VariableList{}, ty.f32(), ast::StatementList{Return(1.f)},
ast::DecorationList{Stage(ast::PipelineStage::kCompute)}, {}, createDecorations({}, *this, params.kind));
createDecorations({}, *this, params.kind));
if (params.should_pass) { if (params.should_pass) {
EXPECT_TRUE(r()->Resolve()) << r()->error(); EXPECT_TRUE(r()->Resolve()) << r()->error();
@ -244,6 +243,41 @@ TEST_P(FunctionReturnTypeDecorationTest, IsValid) {
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(
ResolverDecorationValidationTest, ResolverDecorationValidationTest,
FunctionReturnTypeDecorationTest, FunctionReturnTypeDecorationTest,
testing::Values(TestParams{DecorationKind::kAlign, false},
TestParams{DecorationKind::kBinding, false},
TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false},
TestParams{DecorationKind::kSize, false},
TestParams{DecorationKind::kStage, false},
TestParams{DecorationKind::kStride, false},
TestParams{DecorationKind::kStructBlock, false},
TestParams{DecorationKind::kWorkgroup, false},
TestParams{DecorationKind::kBindingAndGroup, false}));
using EntryPointReturnTypeDecorationTest = TestWithParams;
TEST_P(EntryPointReturnTypeDecorationTest, IsValid) {
auto& params = GetParam();
Func("main", ast::VariableList{}, ty.vec4<f32>(),
{Return(Construct(ty.vec4<f32>(), 1.f))},
{Stage(ast::PipelineStage::kCompute)},
createDecorations({}, *this, params.kind));
if (params.should_pass) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
} else {
EXPECT_FALSE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(),
"error: decoration is not valid for entry point return types");
}
}
INSTANTIATE_TEST_SUITE_P(
ResolverDecorationValidationTest,
EntryPointReturnTypeDecorationTest,
testing::Values(TestParams{DecorationKind::kAlign, false}, testing::Values(TestParams{DecorationKind::kAlign, false},
TestParams{DecorationKind::kBinding, false}, TestParams{DecorationKind::kBinding, false},
TestParams{DecorationKind::kBuiltin, true}, TestParams{DecorationKind::kBuiltin, true},
@ -258,9 +292,9 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kWorkgroup, false}, TestParams{DecorationKind::kWorkgroup, false},
TestParams{DecorationKind::kBindingAndGroup, false})); TestParams{DecorationKind::kBindingAndGroup, false}));
TEST_F(FunctionReturnTypeDecorationTest, DuplicateDecoration) { TEST_F(EntryPointReturnTypeDecorationTest, DuplicateDecoration) {
Func("main", ast::VariableList{}, ty.f32(), ast::StatementList{Return(1.f)}, Func("main", ast::VariableList{}, ty.f32(), ast::StatementList{Return(1.f)},
ast::DecorationList{Stage(ast::PipelineStage::kCompute)}, ast::DecorationList{Stage(ast::PipelineStage::kFragment)},
ast::DecorationList{ ast::DecorationList{
Location(Source{{12, 34}}, 2), Location(Source{{12, 34}}, 2),
Location(Source{{56, 78}}, 3), Location(Source{{56, 78}}, 3),

View File

@ -1084,11 +1084,21 @@ bool Resolver::ValidateFunction(const ast::Function* func,
} }
for (auto* deco : func->return_type_decorations()) { for (auto* deco : func->return_type_decorations()) {
if (!deco->IsAnyOf<ast::BuiltinDecoration, ast::LocationDecoration>()) { if (!func->IsEntryPoint()) {
AddError("decoration is not valid for function return types", AddError("decoration is not valid for function return types",
deco->source()); deco->source());
return false; return false;
} }
if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
if (!ValidateBuiltinDecoration(builtin, info->return_type)) {
return false;
}
} else if (!deco->Is<ast::LocationDecoration>()) {
AddError("decoration is not valid for entry point return types",
deco->source());
return false;
}
} }
} }