tint/resolver: Clean up attribute resolving
Attributes resolving was done ad-hoc throughout the resolver, with the validator ensuring that attributes were only applied to the correct nodes. The ad-hoc nature meant that attributes were inconsistently marked and resolved, and the attribute arguments were not always validated (especially when used internally). This change inlines the attribute processing into the appropriate places in the resolver, and uses a standardized error message for attributes that cannot be applied. Change-Id: Ic084820949bbf8276fb2d33c103fa29b77824a69 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/129620 Commit-Queue: Ben Clayton <bclayton@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
This commit is contained in:
parent
fe8a76cbbc
commit
333cea405c
|
@ -131,6 +131,43 @@ static utils::Vector<const ast::Attribute*, 2> createAttributes(const Source& so
|
|||
return {};
|
||||
}
|
||||
|
||||
static std::string name(AttributeKind kind) {
|
||||
switch (kind) {
|
||||
case AttributeKind::kAlign:
|
||||
return "@align";
|
||||
case AttributeKind::kBinding:
|
||||
return "@binding";
|
||||
case AttributeKind::kBuiltin:
|
||||
return "@builtin";
|
||||
case AttributeKind::kDiagnostic:
|
||||
return "@diagnostic";
|
||||
case AttributeKind::kGroup:
|
||||
return "@group";
|
||||
case AttributeKind::kId:
|
||||
return "@id";
|
||||
case AttributeKind::kInterpolate:
|
||||
return "@interpolate";
|
||||
case AttributeKind::kInvariant:
|
||||
return "@invariant";
|
||||
case AttributeKind::kLocation:
|
||||
return "@location";
|
||||
case AttributeKind::kOffset:
|
||||
return "@offset";
|
||||
case AttributeKind::kMustUse:
|
||||
return "@must_use";
|
||||
case AttributeKind::kSize:
|
||||
return "@size";
|
||||
case AttributeKind::kStage:
|
||||
return "@stage";
|
||||
case AttributeKind::kStride:
|
||||
return "@stride";
|
||||
case AttributeKind::kWorkgroup:
|
||||
return "@workgroup_size";
|
||||
case AttributeKind::kBindingAndGroup:
|
||||
return "@binding";
|
||||
}
|
||||
return "<unknown>";
|
||||
}
|
||||
namespace FunctionInputAndOutputTests {
|
||||
using FunctionParameterAttributeTest = TestWithParams;
|
||||
TEST_P(FunctionParameterAttributeTest, IsValid) {
|
||||
|
@ -144,11 +181,16 @@ TEST_P(FunctionParameterAttributeTest, IsValid) {
|
|||
|
||||
if (params.should_pass) {
|
||||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
} else if (params.kind == AttributeKind::kLocation || params.kind == AttributeKind::kBuiltin ||
|
||||
params.kind == AttributeKind::kInvariant ||
|
||||
params.kind == AttributeKind::kInterpolate) {
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "error: " + name(params.kind) +
|
||||
" is not valid for non-entry point function parameters");
|
||||
} else {
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(),
|
||||
"error: attribute is not valid for non-entry point function "
|
||||
"parameters");
|
||||
"error: " + name(params.kind) + " is not valid for function parameters");
|
||||
}
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||
|
@ -184,9 +226,9 @@ TEST_P(FunctionReturnTypeAttributeTest, IsValid) {
|
|||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
} else {
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(),
|
||||
"error: attribute is not valid for non-entry point function "
|
||||
"return types");
|
||||
EXPECT_EQ(r()->error(), "error: " + name(params.kind) +
|
||||
" is not valid for non-entry point function "
|
||||
"return types");
|
||||
}
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||
|
@ -234,10 +276,11 @@ TEST_P(ComputeShaderParameterAttributeTest, IsValid) {
|
|||
} else if (params.kind == AttributeKind::kInterpolate ||
|
||||
params.kind == AttributeKind::kLocation ||
|
||||
params.kind == AttributeKind::kInvariant) {
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: attribute is not valid for compute shader inputs");
|
||||
EXPECT_EQ(r()->error(), "12:34 error: " + name(params.kind) +
|
||||
" is not valid for compute shader inputs");
|
||||
} else {
|
||||
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for function parameters");
|
||||
EXPECT_EQ(r()->error(), "12:34 error: " + name(params.kind) +
|
||||
" is not valid for function parameters");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -277,7 +320,8 @@ TEST_P(FragmentShaderParameterAttributeTest, IsValid) {
|
|||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
} else {
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for function parameters");
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: " + name(params.kind) + " is not valid for function parameters");
|
||||
}
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||
|
@ -331,7 +375,8 @@ TEST_P(VertexShaderParameterAttributeTest, IsValid) {
|
|||
"12:34 error: invariant attribute must only be applied to a "
|
||||
"position builtin");
|
||||
} else {
|
||||
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for function parameters");
|
||||
EXPECT_EQ(r()->error(), "12:34 error: " + name(params.kind) +
|
||||
" is not valid for function parameters");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -378,12 +423,12 @@ TEST_P(ComputeShaderReturnTypeAttributeTest, IsValid) {
|
|||
} else if (params.kind == AttributeKind::kInterpolate ||
|
||||
params.kind == AttributeKind::kLocation ||
|
||||
params.kind == AttributeKind::kInvariant) {
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: attribute is not valid for compute shader output");
|
||||
EXPECT_EQ(r()->error(), "12:34 error: " + name(params.kind) +
|
||||
" is not valid for compute shader output");
|
||||
} else {
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: attribute is not valid for entry point return "
|
||||
"types");
|
||||
EXPECT_EQ(r()->error(), "12:34 error: " + name(params.kind) +
|
||||
" is not valid for entry point return "
|
||||
"types");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -434,8 +479,8 @@ TEST_P(FragmentShaderReturnTypeAttributeTest, IsValid) {
|
|||
R"(34:56 error: duplicate location attribute
|
||||
12:34 note: first attribute declared here)");
|
||||
} else {
|
||||
EXPECT_EQ(r()->error(),
|
||||
R"(12:34 error: attribute is not valid for entry point return types)");
|
||||
EXPECT_EQ(r()->error(), "12:34 error: " + name(params.kind) +
|
||||
" is not valid for entry point return types");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -484,8 +529,8 @@ TEST_P(VertexShaderReturnTypeAttributeTest, IsValid) {
|
|||
R"(34:56 error: multiple entry point IO attributes
|
||||
12:34 note: previously consumed @location)");
|
||||
} else {
|
||||
EXPECT_EQ(r()->error(),
|
||||
R"(12:34 error: attribute is not valid for entry point return types)");
|
||||
EXPECT_EQ(r()->error(), "12:34 error: " + name(params.kind) +
|
||||
" is not valid for entry point return types");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -591,7 +636,8 @@ TEST_P(StructAttributeTest, IsValid) {
|
|||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
} else {
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for struct declarations");
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: " + name(params.kind) + " is not valid for struct declarations");
|
||||
}
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||
|
@ -628,7 +674,8 @@ TEST_P(StructMemberAttributeTest, IsValid) {
|
|||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
} else {
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for structure members");
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: " + name(params.kind) + " is not valid for struct members");
|
||||
}
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||
|
@ -871,7 +918,8 @@ TEST_P(ArrayAttributeTest, IsValid) {
|
|||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
} else {
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for array types");
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: " + name(params.kind) + " is not valid for array types");
|
||||
}
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||
|
@ -898,7 +946,6 @@ TEST_P(VariableAttributeTest, IsValid) {
|
|||
auto& params = GetParam();
|
||||
|
||||
auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
|
||||
auto* attr = attrs[0];
|
||||
if (IsBindingAttribute(params.kind)) {
|
||||
GlobalVar("a", ty.sampler(type::SamplerKind::kSampler), attrs);
|
||||
} else {
|
||||
|
@ -910,8 +957,8 @@ TEST_P(VariableAttributeTest, IsValid) {
|
|||
} else {
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
if (!IsBindingAttribute(params.kind)) {
|
||||
EXPECT_EQ(r()->error(), "12:34 error: attribute '" + attr->Name() +
|
||||
"' is not valid for module-scope 'var'");
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: " + name(params.kind) + " is not valid for module-scope 'var'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -944,13 +991,22 @@ TEST_F(VariableAttributeTest, DuplicateAttribute) {
|
|||
12:34 note: first attribute declared here)");
|
||||
}
|
||||
|
||||
TEST_F(VariableAttributeTest, LocalVariable) {
|
||||
TEST_F(VariableAttributeTest, LocalVar) {
|
||||
auto* v = Var("a", ty.f32(), utils::Vector{Binding(Source{{12, 34}}, 2_a)});
|
||||
|
||||
WrapInFunction(v);
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: attributes are not valid on local variables");
|
||||
EXPECT_EQ(r()->error(), "12:34 error: @binding is not valid for function-scope 'var'");
|
||||
}
|
||||
|
||||
TEST_F(VariableAttributeTest, LocalLet) {
|
||||
auto* v = Let("a", utils::Vector{Binding(Source{{12, 34}}, 2_a)}, Expr(1_a));
|
||||
|
||||
WrapInFunction(v);
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: @binding is not valid for 'let' declaration");
|
||||
}
|
||||
|
||||
using ConstantAttributeTest = TestWithParams;
|
||||
|
@ -965,7 +1021,7 @@ TEST_P(ConstantAttributeTest, IsValid) {
|
|||
} else {
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: attribute is not valid for module-scope 'const' declaration");
|
||||
"12:34 error: " + name(params.kind) + " is not valid for 'const' declaration");
|
||||
}
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||
|
@ -987,17 +1043,14 @@ INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
|||
TestParams{AttributeKind::kWorkgroup, false},
|
||||
TestParams{AttributeKind::kBindingAndGroup, false}));
|
||||
|
||||
TEST_F(ConstantAttributeTest, DuplicateAttribute) {
|
||||
TEST_F(ConstantAttributeTest, InvalidAttribute) {
|
||||
GlobalConst("a", ty.f32(), Expr(1.23_f),
|
||||
utils::Vector{
|
||||
Id(Source{{12, 34}}, 0_a),
|
||||
Id(Source{{56, 78}}, 1_a),
|
||||
});
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(),
|
||||
R"(56:78 error: duplicate id attribute
|
||||
12:34 note: first attribute declared here)");
|
||||
EXPECT_EQ(r()->error(), "12:34 error: @id is not valid for 'const' declaration");
|
||||
}
|
||||
|
||||
using OverrideAttributeTest = TestWithParams;
|
||||
|
@ -1010,7 +1063,8 @@ TEST_P(OverrideAttributeTest, IsValid) {
|
|||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
} else {
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for 'override' declaration");
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: " + name(params.kind) + " is not valid for 'override' declaration");
|
||||
}
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||
|
@ -1056,7 +1110,8 @@ TEST_P(SwitchStatementAttributeTest, IsValid) {
|
|||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
} else {
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for switch statements");
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: " + name(params.kind) + " is not valid for switch statements");
|
||||
}
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||
|
@ -1089,7 +1144,8 @@ TEST_P(SwitchBodyAttributeTest, IsValid) {
|
|||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
} else {
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for switch body");
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: " + name(params.kind) + " is not valid for switch body");
|
||||
}
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||
|
@ -1122,7 +1178,8 @@ TEST_P(IfStatementAttributeTest, IsValid) {
|
|||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
} else {
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for if statements");
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: " + name(params.kind) + " is not valid for if statements");
|
||||
}
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||
|
@ -1155,7 +1212,8 @@ TEST_P(ForStatementAttributeTest, IsValid) {
|
|||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
} else {
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for for statements");
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: " + name(params.kind) + " is not valid for for statements");
|
||||
}
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||
|
@ -1188,7 +1246,8 @@ TEST_P(LoopStatementAttributeTest, IsValid) {
|
|||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
} else {
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for loop statements");
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: " + name(params.kind) + " is not valid for loop statements");
|
||||
}
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||
|
@ -1221,7 +1280,8 @@ TEST_P(WhileStatementAttributeTest, IsValid) {
|
|||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
} else {
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for while statements");
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: " + name(params.kind) + " is not valid for while statements");
|
||||
}
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
|
||||
|
@ -1251,7 +1311,8 @@ class BlockStatementTest : public TestWithParams {
|
|||
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||
} else {
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), "error: attribute is not valid for block statements");
|
||||
EXPECT_EQ(r()->error(),
|
||||
"error: " + name(GetParam().kind) + " is not valid for block statements");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1084,7 +1084,7 @@ TEST_F(LocationAttributeTests, ComputeShaderLocation_Input) {
|
|||
});
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), R"(12:34 error: attribute is not valid for compute shader output)");
|
||||
EXPECT_EQ(r()->error(), R"(12:34 error: @location is not valid for compute shader output)");
|
||||
}
|
||||
|
||||
TEST_F(LocationAttributeTests, ComputeShaderLocation_Output) {
|
||||
|
@ -1099,7 +1099,7 @@ TEST_F(LocationAttributeTests, ComputeShaderLocation_Output) {
|
|||
});
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), R"(12:34 error: attribute is not valid for compute shader inputs)");
|
||||
EXPECT_EQ(r()->error(), R"(12:34 error: @location is not valid for compute shader inputs)");
|
||||
}
|
||||
|
||||
TEST_F(LocationAttributeTests, ComputeShaderLocationStructMember_Output) {
|
||||
|
@ -1119,7 +1119,7 @@ TEST_F(LocationAttributeTests, ComputeShaderLocationStructMember_Output) {
|
|||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: attribute is not valid for compute shader output\n"
|
||||
"12:34 error: @location is not valid for compute shader output\n"
|
||||
"56:78 note: while analyzing entry point 'main'");
|
||||
}
|
||||
|
||||
|
@ -1138,7 +1138,7 @@ TEST_F(LocationAttributeTests, ComputeShaderLocationStructMember_Input) {
|
|||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(),
|
||||
"12:34 error: attribute is not valid for compute shader inputs\n"
|
||||
"12:34 error: @location is not valid for compute shader inputs\n"
|
||||
"56:78 note: while analyzing entry point 'main'");
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -312,13 +312,45 @@ class Resolver {
|
|||
/// current_function_
|
||||
bool WorkgroupSize(const ast::Function*);
|
||||
|
||||
/// Resolves the attribute @p attr
|
||||
/// @returns true on success, false on failure
|
||||
bool Attribute(const ast::Attribute* attr);
|
||||
|
||||
/// Resolves the `@builtin` attribute @p attr
|
||||
/// @returns the builtin value on success
|
||||
utils::Result<tint::builtin::BuiltinValue> BuiltinAttribute(const ast::BuiltinAttribute* attr);
|
||||
|
||||
/// Resolves the `@location` attribute @p attr
|
||||
/// @returns the location value on success.
|
||||
utils::Result<uint32_t> LocationAttribute(const ast::LocationAttribute* attr);
|
||||
|
||||
/// Resolves the `@binding` attribute @p attr
|
||||
/// @returns the binding value on success.
|
||||
utils::Result<uint32_t> BindingAttribute(const ast::BindingAttribute* attr);
|
||||
|
||||
/// Resolves the `@group` attribute @p attr
|
||||
/// @returns the group value on success.
|
||||
utils::Result<uint32_t> GroupAttribute(const ast::GroupAttribute* attr);
|
||||
|
||||
/// Resolves the `@workgroup_size` attribute @p attr
|
||||
/// @returns the workgroup size on success.
|
||||
utils::Result<sem::WorkgroupSize> WorkgroupAttribute(const ast::WorkgroupAttribute* attr);
|
||||
|
||||
/// Resolves the `@diagnostic` attribute @p attr
|
||||
/// @returns true on success, false on failure
|
||||
bool BuiltinAttribute(const ast::BuiltinAttribute* attr);
|
||||
bool DiagnosticAttribute(const ast::DiagnosticAttribute* attr);
|
||||
|
||||
/// Resolves the stage attribute @p attr
|
||||
/// @returns true on success, false on failure
|
||||
bool StageAttribute(const ast::StageAttribute* attr);
|
||||
|
||||
/// Resolves the `@must_use` attribute @p attr
|
||||
/// @returns true on success, false on failure
|
||||
bool MustUseAttribute(const ast::MustUseAttribute* attr);
|
||||
|
||||
/// Resolves the `@invariant` attribute @p attr
|
||||
/// @returns true on success, false on failure
|
||||
bool InvariantAttribute(const ast::InvariantAttribute*);
|
||||
|
||||
/// Resolves the `@stride` attribute @p attr
|
||||
/// @returns true on success, false on failure
|
||||
bool StrideAttribute(const ast::StrideAttribute*);
|
||||
|
||||
/// Resolves the `@interpolate` attribute @p attr
|
||||
/// @returns true on success, false on failure
|
||||
|
@ -427,12 +459,11 @@ class Resolver {
|
|||
/// nullptr is returned.
|
||||
/// @note the caller is expected to validate the parameter
|
||||
/// @param param the AST parameter
|
||||
/// @param func the AST function that owns the parameter
|
||||
/// @param index the index of the parameter
|
||||
sem::Parameter* Parameter(const ast::Parameter* param, uint32_t index);
|
||||
|
||||
/// @returns the location value for a `@location` attribute, validating the value's range and
|
||||
/// type.
|
||||
utils::Result<uint32_t> LocationAttribute(const ast::LocationAttribute* attr);
|
||||
sem::Parameter* Parameter(const ast::Parameter* param,
|
||||
const ast::Function* func,
|
||||
uint32_t index);
|
||||
|
||||
/// Records the address space usage for the given type, and any transient
|
||||
/// dependencies of the type. Validates that the type can be used for the
|
||||
|
@ -497,6 +528,11 @@ class Resolver {
|
|||
const ResolvedIdentifier& resolved,
|
||||
std::string_view wanted);
|
||||
|
||||
/// Raises an error that the attribute is not valid for the given use.
|
||||
/// @param attr the invalue attribute
|
||||
/// @param use the thing that the attribute was applied to
|
||||
void ErrorInvalidAttribute(const ast::Attribute* attr, std::string_view use);
|
||||
|
||||
/// Adds the given error message to the diagnostics
|
||||
void AddError(const std::string& msg, const Source& source) const;
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ TEST_F(ResolverUnresolvedIdentifierSuggestions, BuiltinValue) {
|
|||
Func("f",
|
||||
utils::Vector{
|
||||
Param("p", ty.i32(), utils::Vector{Builtin(Expr(Source{{12, 34}}, "positon"))})},
|
||||
ty.void_(), utils::Empty);
|
||||
ty.void_(), utils::Empty, utils::Vector{Stage(ast::PipelineStage::kVertex)});
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(r()->error(), R"(12:34 error: unresolved builtin value 'positon'
|
||||
|
|
|
@ -606,32 +606,10 @@ bool Validator::GlobalVariable(
|
|||
return false;
|
||||
}
|
||||
|
||||
for (auto* attr : decl->attributes) {
|
||||
bool is_shader_io_attribute =
|
||||
attr->IsAnyOf<ast::BuiltinAttribute, ast::InterpolateAttribute,
|
||||
ast::InvariantAttribute, ast::LocationAttribute>();
|
||||
bool has_io_address_space = global->AddressSpace() == builtin::AddressSpace::kIn ||
|
||||
global->AddressSpace() == builtin::AddressSpace::kOut;
|
||||
if (!attr->IsAnyOf<ast::BindingAttribute, ast::GroupAttribute,
|
||||
ast::InternalAttribute>() &&
|
||||
(!is_shader_io_attribute || !has_io_address_space)) {
|
||||
AddError("attribute '" + attr->Name() + "' is not valid for module-scope 'var'",
|
||||
attr->source);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return Var(global);
|
||||
},
|
||||
[&](const ast::Override*) { return Override(global, override_ids); },
|
||||
[&](const ast::Const*) {
|
||||
if (!decl->attributes.IsEmpty()) {
|
||||
AddError("attribute is not valid for module-scope 'const' declaration",
|
||||
decl->attributes[0]->source);
|
||||
return false;
|
||||
}
|
||||
return Const(global);
|
||||
},
|
||||
[&](const ast::Const*) { return Const(global); },
|
||||
[&](Default) {
|
||||
TINT_ICE(Resolver, diagnostics_)
|
||||
<< "Validator::GlobalVariable() called with a unknown variable type: "
|
||||
|
@ -773,9 +751,6 @@ bool Validator::Override(
|
|||
ast::GetAttribute<ast::IdAttribute>((*var)->Declaration()->attributes)->source);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
AddError("attribute is not valid for 'override' declaration", attr->source);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -792,28 +767,13 @@ bool Validator::Const(const sem::Variable*) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Validator::Parameter(const ast::Function* func, const sem::Variable* var) const {
|
||||
bool Validator::Parameter(const sem::Variable* var) const {
|
||||
auto* decl = var->Declaration();
|
||||
|
||||
if (IsValidationDisabled(decl->attributes, ast::DisabledValidation::kFunctionParameter)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto* attr : decl->attributes) {
|
||||
if (!func->IsEntryPoint() && !attr->Is<ast::InternalAttribute>()) {
|
||||
AddError("attribute is not valid for non-entry point function parameters",
|
||||
attr->source);
|
||||
return false;
|
||||
}
|
||||
if (!attr->IsAnyOf<ast::BuiltinAttribute, ast::InvariantAttribute, ast::LocationAttribute,
|
||||
ast::InterpolateAttribute, ast::InternalAttribute>() &&
|
||||
(IsValidationEnabled(decl->attributes,
|
||||
ast::DisabledValidation::kEntryPointParameter))) {
|
||||
AddError("attribute is not valid for function parameters", attr->source);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto* ref = var->Type()->As<type::Pointer>()) {
|
||||
if (IsValidationEnabled(decl->attributes, ast::DisabledValidation::kIgnoreAddressSpace)) {
|
||||
bool ok = false;
|
||||
|
@ -1028,14 +988,7 @@ bool Validator::Function(const sem::Function* func, ast::PipelineStage stage) co
|
|||
}
|
||||
return true;
|
||||
},
|
||||
[&](Default) {
|
||||
if (!attr->IsAnyOf<ast::DiagnosticAttribute, ast::StageAttribute,
|
||||
ast::InternalAttribute>()) {
|
||||
AddError("attribute is not valid for functions", attr->source);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
[&](Default) { return true; });
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1069,24 +1022,6 @@ bool Validator::Function(const sem::Function* func, ast::PipelineStage stage) co
|
|||
TINT_ICE(Resolver, diagnostics_)
|
||||
<< "Function " << decl->name->symbol.Name() << " has no body";
|
||||
}
|
||||
|
||||
for (auto* attr : decl->return_type_attributes) {
|
||||
if (!decl->IsEntryPoint()) {
|
||||
AddError("attribute is not valid for non-entry point function return types",
|
||||
attr->source);
|
||||
return false;
|
||||
}
|
||||
if (!attr->IsAnyOf<ast::BuiltinAttribute, ast::InternalAttribute,
|
||||
ast::LocationAttribute, ast::InterpolateAttribute,
|
||||
ast::InvariantAttribute>() &&
|
||||
(IsValidationEnabled(decl->attributes,
|
||||
ast::DisabledValidation::kEntryPointParameter) &&
|
||||
IsValidationEnabled(decl->attributes,
|
||||
ast::DisabledValidation::kFunctionParameter))) {
|
||||
AddError("attribute is not valid for entry point return types", attr->source);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (decl->IsEntryPoint()) {
|
||||
|
@ -1196,7 +1131,7 @@ bool Validator::EntryPoint(const sem::Function* func, ast::PipelineStage stage)
|
|||
if (is_invalid_compute_shader_attribute) {
|
||||
std::string input_or_output =
|
||||
param_or_ret == ParamOrRetType::kParameter ? "inputs" : "output";
|
||||
AddError("attribute is not valid for compute shader " + input_or_output,
|
||||
AddError("@" + attr->Name() + " is not valid for compute shader " + input_or_output,
|
||||
attr->source);
|
||||
return false;
|
||||
}
|
||||
|
@ -2205,24 +2140,7 @@ bool Validator::Structure(const sem::Struct* str, ast::PipelineStage stage) cons
|
|||
}
|
||||
return true;
|
||||
},
|
||||
[&](Default) {
|
||||
if (!attr->IsAnyOf<ast::BuiltinAttribute, //
|
||||
ast::InternalAttribute, //
|
||||
ast::InterpolateAttribute, //
|
||||
ast::InvariantAttribute, //
|
||||
ast::LocationAttribute, //
|
||||
ast::StructMemberOffsetAttribute, //
|
||||
ast::StructMemberAlignAttribute>()) {
|
||||
if (attr->Is<ast::StrideAttribute>() &&
|
||||
IsValidationDisabled(member->Declaration()->attributes,
|
||||
ast::DisabledValidation::kIgnoreStrideAttribute)) {
|
||||
return true;
|
||||
}
|
||||
AddError("attribute is not valid for structure members", attr->source);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
[&](Default) { return true; });
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
@ -2241,13 +2159,6 @@ bool Validator::Structure(const sem::Struct* str, ast::PipelineStage stage) cons
|
|||
}
|
||||
}
|
||||
|
||||
for (auto* attr : str->Declaration()->attributes) {
|
||||
if (!(attr->IsAnyOf<ast::InternalAttribute>())) {
|
||||
AddError("attribute is not valid for struct declarations", attr->source);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2260,7 +2171,8 @@ bool Validator::LocationAttribute(const ast::LocationAttribute* loc_attr,
|
|||
const bool is_input) const {
|
||||
std::string inputs_or_output = is_input ? "inputs" : "output";
|
||||
if (stage == ast::PipelineStage::kCompute) {
|
||||
AddError("attribute is not valid for compute shader " + inputs_or_output, loc_attr->source);
|
||||
AddError("@" + loc_attr->Name() + " is not valid for compute shader " + inputs_or_output,
|
||||
loc_attr->source);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -348,10 +348,9 @@ class Validator {
|
|||
bool Matrix(const type::Type* el_ty, const Source& source) const;
|
||||
|
||||
/// Validates a function parameter
|
||||
/// @param func the function the variable is for
|
||||
/// @param var the variable to validate
|
||||
/// @returns true on success, false otherwise
|
||||
bool Parameter(const ast::Function* func, const sem::Variable* var) const;
|
||||
bool Parameter(const sem::Variable* var) const;
|
||||
|
||||
/// Validates a return
|
||||
/// @param ret the return statement to validate
|
||||
|
|
|
@ -383,7 +383,7 @@ TEST_F(ResolverVariableTest, LocalVar_ShadowsParam) {
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Function-scope 'let'
|
||||
// 'let' declaration
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
TEST_F(ResolverVariableTest, LocalLet) {
|
||||
// struct S { i : i32; }
|
||||
|
|
Loading…
Reference in New Issue