Validate attributes on function parameters
Entry point function parameter can only have builtin and location
attributes (unless a disable validation attribute is present). Other
functions cannot have any decorations on their parameters.
Fix parameter creation in the CanonicalizeEntryPointIO transform for a
case where it was not stripping attributes that are not valid for
function parameters.
Change-Id: I000908aa2dc007c52d100c5f65bca051a49bec9a
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/55863
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: James Price <jrprice@google.com>
Auto-Submit: James Price <jrprice@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/resolver/decoration_validation_test.cc b/src/resolver/decoration_validation_test.cc
index ddf7b71..8164445 100644
--- a/src/resolver/decoration_validation_test.cc
+++ b/src/resolver/decoration_validation_test.cc
@@ -126,6 +126,105 @@
return {};
}
+using FunctionParameterDecorationTest = TestWithParams;
+TEST_P(FunctionParameterDecorationTest, IsValid) {
+ auto& params = GetParam();
+
+ Func("main",
+ ast::VariableList{Param("a", ty.vec4<f32>(),
+ createDecorations({}, *this, params.kind))},
+ ty.void_(), {});
+
+ 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 function parameters");
+ }
+}
+INSTANTIATE_TEST_SUITE_P(
+ ResolverDecorationValidationTest,
+ FunctionParameterDecorationTest,
+ 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 EntryPointParameterDecorationTest = TestWithParams;
+TEST_P(EntryPointParameterDecorationTest, IsValid) {
+ auto& params = GetParam();
+
+ Func("main",
+ ast::VariableList{Param("a", ty.vec4<f32>(),
+ createDecorations({}, *this, params.kind))},
+ ty.void_(), {},
+ ast::DecorationList{Stage(ast::PipelineStage::kFragment)});
+
+ 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 function parameters");
+ }
+}
+INSTANTIATE_TEST_SUITE_P(
+ ResolverDecorationValidationTest,
+ EntryPointParameterDecorationTest,
+ testing::Values(TestParams{DecorationKind::kAlign, false},
+ TestParams{DecorationKind::kBinding, false},
+ TestParams{DecorationKind::kBuiltin, true},
+ TestParams{DecorationKind::kGroup, false},
+ TestParams{DecorationKind::kLocation, true},
+ 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}));
+
+TEST_F(EntryPointParameterDecorationTest, DuplicateDecoration) {
+ Func("main", ast::VariableList{}, ty.f32(), ast::StatementList{Return(1.f)},
+ {Stage(ast::PipelineStage::kFragment)},
+ {
+ Location(Source{{12, 34}}, 2),
+ Location(Source{{56, 78}}, 3),
+ });
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(56:78 error: duplicate location decoration
+12:34 note: first decoration declared here)");
+}
+
+TEST_F(EntryPointParameterDecorationTest, DuplicateInternalDecoration) {
+ auto* s =
+ Param("s", ty.sampler(ast::SamplerKind::kSampler),
+ ast::DecorationList{
+ create<ast::BindingDecoration>(0),
+ create<ast::GroupDecoration>(0),
+ ASTNodes().Create<ast::DisableValidationDecoration>(
+ ID(), ast::DisabledValidation::kBindingPointCollision),
+ ASTNodes().Create<ast::DisableValidationDecoration>(
+ ID(), ast::DisabledValidation::kEntryPointParameter),
+ });
+ Func("f", {s}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
using FunctionReturnTypeDecorationTest = TestWithParams;
TEST_P(FunctionReturnTypeDecorationTest, IsValid) {
auto& params = GetParam();
@@ -385,22 +484,6 @@
12:34 note: first decoration declared here)");
}
-TEST_F(VariableDecorationTest, DuplicateInternalDecoration) {
- auto* s =
- Param("s", ty.sampler(ast::SamplerKind::kSampler),
- ast::DecorationList{
- create<ast::BindingDecoration>(0),
- create<ast::GroupDecoration>(0),
- ASTNodes().Create<ast::DisableValidationDecoration>(
- ID(), ast::DisabledValidation::kBindingPointCollision),
- ASTNodes().Create<ast::DisableValidationDecoration>(
- ID(), ast::DisabledValidation::kEntryPointParameter),
- });
- Func("f", {s}, ty.void_(), {});
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-}
-
using ConstantDecorationTest = TestWithParams;
TEST_P(ConstantDecorationTest, IsValid) {
auto& params = GetParam();
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index ce2e608..405f15c 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -927,15 +927,30 @@
return true;
}
-bool Resolver::ValidateParameter(const VariableInfo* info) {
+bool Resolver::ValidateParameter(const ast::Function* func,
+ const VariableInfo* info) {
if (!ValidateVariable(info)) {
return false;
}
for (auto* deco : info->declaration->decorations()) {
+ if (!func->IsEntryPoint()) {
+ AddError("decoration is not valid for function parameters",
+ deco->source());
+ return false;
+ }
+
if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
if (!ValidateBuiltinDecoration(builtin, info->type)) {
return false;
}
+ } else if (!deco->IsAnyOf<ast::LocationDecoration,
+ ast::InternalDecoration>() &&
+ !IsValidationDisabled(
+ info->declaration->decorations(),
+ ast::DisabledValidation::kEntryPointParameter)) {
+ AddError("decoration is not valid for function parameters",
+ deco->source());
+ return false;
}
}
return true;
@@ -1041,7 +1056,7 @@
}
for (auto* param : func->params()) {
- if (!ValidateParameter(variable_to_info_.at(param))) {
+ if (!ValidateParameter(func, variable_to_info_.at(param))) {
return false;
}
}
diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h
index 531912f..336b44c 100644
--- a/src/resolver/resolver.h
+++ b/src/resolver/resolver.h
@@ -279,7 +279,7 @@
bool ValidateMatrix(const sem::Matrix* matirx_type, const Source& source);
bool ValidateMatrixConstructor(const ast::TypeConstructorExpression* ctor,
const sem::Matrix* matrix_type);
- bool ValidateParameter(const VariableInfo* info);
+ bool ValidateParameter(const ast::Function* func, const VariableInfo* info);
bool ValidateReturn(const ast::ReturnStatement* ret);
bool ValidateStatements(const ast::StatementList& stmts);
bool ValidateStorageTexture(const ast::StorageTexture* t);
diff --git a/src/transform/canonicalize_entry_point_io.cc b/src/transform/canonicalize_entry_point_io.cc
index ebac0b9..eaf9ce9 100644
--- a/src/transform/canonicalize_entry_point_io.cc
+++ b/src/transform/canonicalize_entry_point_io.cc
@@ -151,6 +151,13 @@
<< "nested pipeline IO struct";
}
+ ast::DecorationList new_decorations = RemoveDecorations(
+ &ctx, member->Declaration()->decorations(),
+ [](const ast::Decoration* deco) {
+ return !deco->IsAnyOf<ast::BuiltinDecoration,
+ ast::LocationDecoration>();
+ });
+
if (cfg->builtin_style == BuiltinStyle::kParameter &&
ast::HasDecoration<ast::BuiltinDecoration>(
member->Declaration()->decorations())) {
@@ -158,19 +165,12 @@
// parameters, then move it to the parameter list.
auto* member_ty = CreateASTTypeFor(&ctx, member->Type());
auto new_param_name = ctx.dst->Sym();
- new_parameters.push_back(ctx.dst->Param(
- new_param_name, member_ty,
- ctx.Clone(member->Declaration()->decorations())));
+ new_parameters.push_back(
+ ctx.dst->Param(new_param_name, member_ty, new_decorations));
init_values.push_back(ctx.dst->Expr(new_param_name));
continue;
}
- ast::DecorationList new_decorations = RemoveDecorations(
- &ctx, member->Declaration()->decorations(),
- [](const ast::Decoration* deco) {
- return !deco->IsAnyOf<ast::BuiltinDecoration,
- ast::LocationDecoration>();
- });
auto member_name = ctx.Clone(member->Declaration()->symbol());
auto* member_type = ctx.Clone(member->Declaration()->type());
new_struct_members.push_back(