validation: Validate invariant attribute

This is only valid on structure members and entry point return
types. Additionally, this can only be applied to a position builtin.

Bug: tint:772
Change-Id: Iffd774ca768ab66373678ecf0eb57c766767e92b
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/57641
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-07-12 12:12:32 +00:00 committed by Tint LUCI CQ
parent 2e6fefb858
commit 508d2491d0
3 changed files with 69 additions and 2 deletions

View File

@ -39,6 +39,7 @@
#include "src/ast/i32.h" #include "src/ast/i32.h"
#include "src/ast/if_statement.h" #include "src/ast/if_statement.h"
#include "src/ast/interpolate_decoration.h" #include "src/ast/interpolate_decoration.h"
#include "src/ast/invariant_decoration.h"
#include "src/ast/loop_statement.h" #include "src/ast/loop_statement.h"
#include "src/ast/matrix.h" #include "src/ast/matrix.h"
#include "src/ast/member_accessor_expression.h" #include "src/ast/member_accessor_expression.h"
@ -2042,6 +2043,19 @@ class ProgramBuilder {
return create<ast::InterpolateDecoration>(source_, type, sampling); return create<ast::InterpolateDecoration>(source_, type, sampling);
} }
/// Creates an ast::InvariantDecoration
/// @param source the source information
/// @returns the invariant decoration pointer
ast::InvariantDecoration* Invariant(const Source& source) {
return create<ast::InvariantDecoration>(source);
}
/// Creates an ast::InvariantDecoration
/// @returns the invariant decoration pointer
ast::InvariantDecoration* Invariant() {
return create<ast::InvariantDecoration>(source_);
}
/// Creates an ast::LocationDecoration /// Creates an ast::LocationDecoration
/// @param source the source information /// @param source the source information
/// @param location the location value /// @param location the location value

View File

@ -62,6 +62,7 @@ enum class DecorationKind {
kBuiltin, kBuiltin,
kGroup, kGroup,
kInterpolate, kInterpolate,
kInvariant,
kLocation, kLocation,
kOverride, kOverride,
kOffset, kOffset,
@ -107,6 +108,8 @@ static ast::DecorationList createDecorations(const Source& source,
return {builder.Interpolate(source, ast::InterpolationType::kLinear, return {builder.Interpolate(source, ast::InterpolationType::kLinear,
ast::InterpolationSampling::kCenter), ast::InterpolationSampling::kCenter),
builder.Location(0)}; builder.Location(0)};
case DecorationKind::kInvariant:
return {builder.Invariant(source)};
case DecorationKind::kLocation: case DecorationKind::kLocation:
return {builder.Location(source, 1)}; return {builder.Location(source, 1)};
case DecorationKind::kOverride: case DecorationKind::kOverride:
@ -157,6 +160,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBuiltin, false}, TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false}, TestParams{DecorationKind::kInterpolate, false},
TestParams{DecorationKind::kInvariant, false},
TestParams{DecorationKind::kLocation, false}, TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false}, TestParams{DecorationKind::kOffset, false},
@ -193,6 +197,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBuiltin, true}, TestParams{DecorationKind::kBuiltin, true},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, true}, TestParams{DecorationKind::kInterpolate, true},
TestParams{DecorationKind::kInvariant, false},
TestParams{DecorationKind::kLocation, true}, TestParams{DecorationKind::kLocation, true},
TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false}, TestParams{DecorationKind::kOffset, false},
@ -257,6 +262,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBuiltin, false}, TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false}, TestParams{DecorationKind::kInterpolate, false},
TestParams{DecorationKind::kInvariant, false},
TestParams{DecorationKind::kLocation, false}, TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false}, TestParams{DecorationKind::kOffset, false},
@ -293,6 +299,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBuiltin, true}, TestParams{DecorationKind::kBuiltin, true},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, true}, TestParams{DecorationKind::kInterpolate, true},
// kInvariant tested separately (requires position builtin)
TestParams{DecorationKind::kLocation, true}, TestParams{DecorationKind::kLocation, true},
TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false}, TestParams{DecorationKind::kOffset, false},
@ -317,6 +324,32 @@ TEST_F(EntryPointReturnTypeDecorationTest, DuplicateDecoration) {
12:34 note: first decoration declared here)"); 12:34 note: first decoration declared here)");
} }
TEST_F(EntryPointReturnTypeDecorationTest, InvariantWithPosition) {
Func("main", ast::VariableList{}, ty.vec4<f32>(),
ast::StatementList{Return(Construct(ty.vec4<f32>()))},
ast::DecorationList{Stage(ast::PipelineStage::kVertex)},
ast::DecorationList{
Invariant(Source{{12, 34}}),
Builtin(Source{{56, 78}}, ast::Builtin::kPosition),
});
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(EntryPointReturnTypeDecorationTest, InvariantWithoutPosition) {
Func("main", ast::VariableList{}, ty.vec4<f32>(),
ast::StatementList{Return(Construct(ty.vec4<f32>()))},
ast::DecorationList{Stage(ast::PipelineStage::kFragment)},
ast::DecorationList{
Invariant(Source{{12, 34}}),
Location(Source{{56, 78}}, 0),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: invariant attribute must only be applied to a "
"position builtin");
}
using ArrayDecorationTest = TestWithParams; using ArrayDecorationTest = TestWithParams;
TEST_P(ArrayDecorationTest, IsValid) { TEST_P(ArrayDecorationTest, IsValid) {
auto& params = GetParam(); auto& params = GetParam();
@ -347,6 +380,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBuiltin, false}, TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false}, TestParams{DecorationKind::kInterpolate, false},
TestParams{DecorationKind::kInvariant, false},
TestParams{DecorationKind::kLocation, false}, TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false}, TestParams{DecorationKind::kOffset, false},
@ -382,6 +416,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBuiltin, false}, TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false}, TestParams{DecorationKind::kInterpolate, false},
TestParams{DecorationKind::kInvariant, false},
TestParams{DecorationKind::kLocation, false}, TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false}, TestParams{DecorationKind::kOffset, false},
@ -445,6 +480,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBuiltin, true}, TestParams{DecorationKind::kBuiltin, true},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, true}, TestParams{DecorationKind::kInterpolate, true},
TestParams{DecorationKind::kInvariant, true},
TestParams{DecorationKind::kLocation, true}, TestParams{DecorationKind::kLocation, true},
TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, true}, TestParams{DecorationKind::kOffset, true},
@ -507,6 +543,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBuiltin, false}, TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false}, TestParams{DecorationKind::kInterpolate, false},
TestParams{DecorationKind::kInvariant, false},
TestParams{DecorationKind::kLocation, false}, TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false}, TestParams{DecorationKind::kOffset, false},
@ -558,6 +595,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBuiltin, false}, TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false}, TestParams{DecorationKind::kInterpolate, false},
TestParams{DecorationKind::kInvariant, false},
TestParams{DecorationKind::kLocation, false}, TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, true}, TestParams{DecorationKind::kOverride, true},
TestParams{DecorationKind::kOffset, false}, TestParams{DecorationKind::kOffset, false},
@ -607,6 +645,7 @@ INSTANTIATE_TEST_SUITE_P(
TestParams{DecorationKind::kBuiltin, false}, TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false}, TestParams{DecorationKind::kInterpolate, false},
TestParams{DecorationKind::kInvariant, false},
TestParams{DecorationKind::kLocation, false}, TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false}, TestParams{DecorationKind::kOffset, false},

View File

@ -722,7 +722,7 @@ bool Resolver::ValidateGlobalVariable(const VariableInfo* info) {
} else { } else {
bool is_shader_io_decoration = bool is_shader_io_decoration =
deco->IsAnyOf<ast::BuiltinDecoration, ast::InterpolateDecoration, deco->IsAnyOf<ast::BuiltinDecoration, ast::InterpolateDecoration,
ast::LocationDecoration>(); ast::InvariantDecoration, ast::LocationDecoration>();
bool has_io_storage_class = bool has_io_storage_class =
info->storage_class == ast::StorageClass::kInput || info->storage_class == ast::StorageClass::kInput ||
info->storage_class == ast::StorageClass::kOutput; info->storage_class == ast::StorageClass::kOutput;
@ -1194,6 +1194,7 @@ bool Resolver::ValidateFunction(const ast::Function* func,
return false; return false;
} }
} else if (!deco->IsAnyOf<ast::LocationDecoration, ast::BuiltinDecoration, } else if (!deco->IsAnyOf<ast::LocationDecoration, ast::BuiltinDecoration,
ast::InvariantDecoration,
ast::InternalDecoration>() && ast::InternalDecoration>() &&
(IsValidationEnabled( (IsValidationEnabled(
info->declaration->decorations(), info->declaration->decorations(),
@ -1242,6 +1243,7 @@ bool Resolver::ValidateEntryPoint(const ast::Function* func,
// Scan decorations for pipeline IO attributes. // Scan decorations for pipeline IO attributes.
// Check for overlap with attributes that have been seen previously. // Check for overlap with attributes that have been seen previously.
ast::Decoration* pipeline_io_attribute = nullptr; ast::Decoration* pipeline_io_attribute = nullptr;
ast::InvariantDecoration* invariant_attribute = nullptr;
for (auto* deco : decos) { for (auto* deco : decos) {
if (auto* builtin = deco->As<ast::BuiltinDecoration>()) { if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
if (pipeline_io_attribute) { if (pipeline_io_attribute) {
@ -1286,6 +1288,8 @@ bool Resolver::ValidateEntryPoint(const ast::Function* func,
return false; return false;
} }
locations.emplace(location->value()); locations.emplace(location->value());
} else if (auto* invariant = deco->As<ast::InvariantDecoration>()) {
invariant_attribute = invariant;
} }
} }
@ -1312,10 +1316,19 @@ bool Resolver::ValidateEntryPoint(const ast::Function* func,
return false; return false;
} }
auto* builtin = pipeline_io_attribute->As<ast::BuiltinDecoration>();
if (invariant_attribute &&
!(builtin && builtin->value() == ast::Builtin::kPosition)) {
AddError(
"invariant attribute must only be applied to a position builtin",
invariant_attribute->source());
return false;
}
// Check that all user defined attributes are numeric scalars, vectors // Check that all user defined attributes are numeric scalars, vectors
// of numeric scalars. // of numeric scalars.
// Testing for being a struct is handled by the if portion above. // Testing for being a struct is handled by the if portion above.
if (!pipeline_io_attribute->Is<ast::BuiltinDecoration>()) { if (!builtin) {
if (!ty->is_numeric_scalar_or_vector()) { if (!ty->is_numeric_scalar_or_vector()) {
AddError( AddError(
"User defined entry point IO types must be a numeric scalar, " "User defined entry point IO types must be a numeric scalar, "
@ -3558,6 +3571,7 @@ bool Resolver::ValidateStructure(const sem::Struct* str) {
for (auto* deco : member->Declaration()->decorations()) { for (auto* deco : member->Declaration()->decorations()) {
if (!(deco->Is<ast::BuiltinDecoration>() || if (!(deco->Is<ast::BuiltinDecoration>() ||
deco->Is<ast::InterpolateDecoration>() || deco->Is<ast::InterpolateDecoration>() ||
deco->Is<ast::InvariantDecoration>() ||
deco->Is<ast::LocationDecoration>() || deco->Is<ast::LocationDecoration>() ||
deco->Is<ast::StructMemberOffsetDecoration>() || deco->Is<ast::StructMemberOffsetDecoration>() ||
deco->Is<ast::StructMemberSizeDecoration>() || deco->Is<ast::StructMemberSizeDecoration>() ||