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>
diff --git a/src/program_builder.h b/src/program_builder.h
index dcf311c..f0b0e05 100644
--- a/src/program_builder.h
+++ b/src/program_builder.h
@@ -39,6 +39,7 @@
#include "src/ast/i32.h"
#include "src/ast/if_statement.h"
#include "src/ast/interpolate_decoration.h"
+#include "src/ast/invariant_decoration.h"
#include "src/ast/loop_statement.h"
#include "src/ast/matrix.h"
#include "src/ast/member_accessor_expression.h"
@@ -2042,6 +2043,19 @@
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
/// @param source the source information
/// @param location the location value
diff --git a/src/resolver/decoration_validation_test.cc b/src/resolver/decoration_validation_test.cc
index 61f8d36..6b893ac 100644
--- a/src/resolver/decoration_validation_test.cc
+++ b/src/resolver/decoration_validation_test.cc
@@ -62,6 +62,7 @@
kBuiltin,
kGroup,
kInterpolate,
+ kInvariant,
kLocation,
kOverride,
kOffset,
@@ -107,6 +108,8 @@
return {builder.Interpolate(source, ast::InterpolationType::kLinear,
ast::InterpolationSampling::kCenter),
builder.Location(0)};
+ case DecorationKind::kInvariant:
+ return {builder.Invariant(source)};
case DecorationKind::kLocation:
return {builder.Location(source, 1)};
case DecorationKind::kOverride:
@@ -157,6 +160,7 @@
TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false},
+ TestParams{DecorationKind::kInvariant, false},
TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false},
@@ -193,6 +197,7 @@
TestParams{DecorationKind::kBuiltin, true},
TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, true},
+ TestParams{DecorationKind::kInvariant, false},
TestParams{DecorationKind::kLocation, true},
TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false},
@@ -257,6 +262,7 @@
TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false},
+ TestParams{DecorationKind::kInvariant, false},
TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false},
@@ -293,6 +299,7 @@
TestParams{DecorationKind::kBuiltin, true},
TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, true},
+ // kInvariant tested separately (requires position builtin)
TestParams{DecorationKind::kLocation, true},
TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false},
@@ -317,6 +324,32 @@
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;
TEST_P(ArrayDecorationTest, IsValid) {
auto& params = GetParam();
@@ -347,6 +380,7 @@
TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false},
+ TestParams{DecorationKind::kInvariant, false},
TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false},
@@ -382,6 +416,7 @@
TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false},
+ TestParams{DecorationKind::kInvariant, false},
TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false},
@@ -445,6 +480,7 @@
TestParams{DecorationKind::kBuiltin, true},
TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, true},
+ TestParams{DecorationKind::kInvariant, true},
TestParams{DecorationKind::kLocation, true},
TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, true},
@@ -507,6 +543,7 @@
TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false},
+ TestParams{DecorationKind::kInvariant, false},
TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false},
@@ -558,6 +595,7 @@
TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false},
+ TestParams{DecorationKind::kInvariant, false},
TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, true},
TestParams{DecorationKind::kOffset, false},
@@ -607,6 +645,7 @@
TestParams{DecorationKind::kBuiltin, false},
TestParams{DecorationKind::kGroup, false},
TestParams{DecorationKind::kInterpolate, false},
+ TestParams{DecorationKind::kInvariant, false},
TestParams{DecorationKind::kLocation, false},
TestParams{DecorationKind::kOverride, false},
TestParams{DecorationKind::kOffset, false},
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index 0c48f04..fe559f3 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -722,7 +722,7 @@
} else {
bool is_shader_io_decoration =
deco->IsAnyOf<ast::BuiltinDecoration, ast::InterpolateDecoration,
- ast::LocationDecoration>();
+ ast::InvariantDecoration, ast::LocationDecoration>();
bool has_io_storage_class =
info->storage_class == ast::StorageClass::kInput ||
info->storage_class == ast::StorageClass::kOutput;
@@ -1194,6 +1194,7 @@
return false;
}
} else if (!deco->IsAnyOf<ast::LocationDecoration, ast::BuiltinDecoration,
+ ast::InvariantDecoration,
ast::InternalDecoration>() &&
(IsValidationEnabled(
info->declaration->decorations(),
@@ -1242,6 +1243,7 @@
// Scan decorations for pipeline IO attributes.
// Check for overlap with attributes that have been seen previously.
ast::Decoration* pipeline_io_attribute = nullptr;
+ ast::InvariantDecoration* invariant_attribute = nullptr;
for (auto* deco : decos) {
if (auto* builtin = deco->As<ast::BuiltinDecoration>()) {
if (pipeline_io_attribute) {
@@ -1286,6 +1288,8 @@
return false;
}
locations.emplace(location->value());
+ } else if (auto* invariant = deco->As<ast::InvariantDecoration>()) {
+ invariant_attribute = invariant;
}
}
@@ -1312,10 +1316,19 @@
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
// of numeric scalars.
// 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()) {
AddError(
"User defined entry point IO types must be a numeric scalar, "
@@ -3558,6 +3571,7 @@
for (auto* deco : member->Declaration()->decorations()) {
if (!(deco->Is<ast::BuiltinDecoration>() ||
deco->Is<ast::InterpolateDecoration>() ||
+ deco->Is<ast::InvariantDecoration>() ||
deco->Is<ast::LocationDecoration>() ||
deco->Is<ast::StructMemberOffsetDecoration>() ||
deco->Is<ast::StructMemberSizeDecoration>() ||