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>
This commit is contained in:
parent
14c0b8acbf
commit
c3f19081f8
|
@ -126,6 +126,105 @@ static ast::DecorationList createDecorations(const Source& source,
|
||||||
return {};
|
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;
|
using FunctionReturnTypeDecorationTest = TestWithParams;
|
||||||
TEST_P(FunctionReturnTypeDecorationTest, IsValid) {
|
TEST_P(FunctionReturnTypeDecorationTest, IsValid) {
|
||||||
auto& params = GetParam();
|
auto& params = GetParam();
|
||||||
|
@ -385,22 +484,6 @@ TEST_F(VariableDecorationTest, DuplicateDecoration) {
|
||||||
12:34 note: first decoration declared here)");
|
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;
|
using ConstantDecorationTest = TestWithParams;
|
||||||
TEST_P(ConstantDecorationTest, IsValid) {
|
TEST_P(ConstantDecorationTest, IsValid) {
|
||||||
auto& params = GetParam();
|
auto& params = GetParam();
|
||||||
|
|
|
@ -927,15 +927,30 @@ bool Resolver::ValidateVariable(const VariableInfo* info) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resolver::ValidateParameter(const VariableInfo* info) {
|
bool Resolver::ValidateParameter(const ast::Function* func,
|
||||||
|
const VariableInfo* info) {
|
||||||
if (!ValidateVariable(info)) {
|
if (!ValidateVariable(info)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (auto* deco : info->declaration->decorations()) {
|
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 (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
|
||||||
if (!ValidateBuiltinDecoration(builtin, info->type)) {
|
if (!ValidateBuiltinDecoration(builtin, info->type)) {
|
||||||
return false;
|
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;
|
return true;
|
||||||
|
@ -1041,7 +1056,7 @@ bool Resolver::ValidateFunction(const ast::Function* func,
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto* param : func->params()) {
|
for (auto* param : func->params()) {
|
||||||
if (!ValidateParameter(variable_to_info_.at(param))) {
|
if (!ValidateParameter(func, variable_to_info_.at(param))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -279,7 +279,7 @@ class Resolver {
|
||||||
bool ValidateMatrix(const sem::Matrix* matirx_type, const Source& source);
|
bool ValidateMatrix(const sem::Matrix* matirx_type, const Source& source);
|
||||||
bool ValidateMatrixConstructor(const ast::TypeConstructorExpression* ctor,
|
bool ValidateMatrixConstructor(const ast::TypeConstructorExpression* ctor,
|
||||||
const sem::Matrix* matrix_type);
|
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 ValidateReturn(const ast::ReturnStatement* ret);
|
||||||
bool ValidateStatements(const ast::StatementList& stmts);
|
bool ValidateStatements(const ast::StatementList& stmts);
|
||||||
bool ValidateStorageTexture(const ast::StorageTexture* t);
|
bool ValidateStorageTexture(const ast::StorageTexture* t);
|
||||||
|
|
|
@ -151,6 +151,13 @@ Output CanonicalizeEntryPointIO::Run(const Program* in, const DataMap& data) {
|
||||||
<< "nested pipeline IO struct";
|
<< "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 &&
|
if (cfg->builtin_style == BuiltinStyle::kParameter &&
|
||||||
ast::HasDecoration<ast::BuiltinDecoration>(
|
ast::HasDecoration<ast::BuiltinDecoration>(
|
||||||
member->Declaration()->decorations())) {
|
member->Declaration()->decorations())) {
|
||||||
|
@ -158,19 +165,12 @@ Output CanonicalizeEntryPointIO::Run(const Program* in, const DataMap& data) {
|
||||||
// parameters, then move it to the parameter list.
|
// parameters, then move it to the parameter list.
|
||||||
auto* member_ty = CreateASTTypeFor(&ctx, member->Type());
|
auto* member_ty = CreateASTTypeFor(&ctx, member->Type());
|
||||||
auto new_param_name = ctx.dst->Sym();
|
auto new_param_name = ctx.dst->Sym();
|
||||||
new_parameters.push_back(ctx.dst->Param(
|
new_parameters.push_back(
|
||||||
new_param_name, member_ty,
|
ctx.dst->Param(new_param_name, member_ty, new_decorations));
|
||||||
ctx.Clone(member->Declaration()->decorations())));
|
|
||||||
init_values.push_back(ctx.dst->Expr(new_param_name));
|
init_values.push_back(ctx.dst->Expr(new_param_name));
|
||||||
continue;
|
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_name = ctx.Clone(member->Declaration()->symbol());
|
||||||
auto* member_type = ctx.Clone(member->Declaration()->type());
|
auto* member_type = ctx.Clone(member->Declaration()->type());
|
||||||
new_struct_members.push_back(
|
new_struct_members.push_back(
|
||||||
|
|
Loading…
Reference in New Issue