diff --git a/src/resolver/decoration_validation_test.cc b/src/resolver/decoration_validation_test.cc index 6e9aa0a752..48b6c8c84a 100644 --- a/src/resolver/decoration_validation_test.cc +++ b/src/resolver/decoration_validation_test.cc @@ -197,7 +197,6 @@ INSTANTIATE_TEST_SUITE_P( TestParams{DecorationKind::kBuiltin, true}, TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kInterpolate, true}, - // TODO(crbug.com/tint/1008) // kInvariant tested separately (requires position builtin) TestParams{DecorationKind::kLocation, true}, TestParams{DecorationKind::kOverride, false}, @@ -252,6 +251,34 @@ TEST_F(EntryPointParameterDecorationTest, ComputeShaderLocation) { "parameters"); } +TEST_F(EntryPointParameterDecorationTest, InvariantWithPosition) { + auto* param = Param("p", ty.vec4(), + {Invariant(Source{{12, 34}}), + Builtin(Source{{56, 78}}, ast::Builtin::kPosition)}); + Func("main", ast::VariableList{param}, ty.vec4(), + ast::StatementList{Return(Construct(ty.vec4()))}, + ast::DecorationList{Stage(ast::PipelineStage::kFragment)}, + ast::DecorationList{ + Location(0), + }); + EXPECT_TRUE(r()->Resolve()) << r()->error(); +} + +TEST_F(EntryPointParameterDecorationTest, InvariantWithoutPosition) { + auto* param = + Param("p", ty.vec4(), {Invariant(Source{{12, 34}}), Location(0)}); + Func("main", ast::VariableList{param}, ty.vec4(), + ast::StatementList{Return(Construct(ty.vec4()))}, + ast::DecorationList{Stage(ast::PipelineStage::kFragment)}, + ast::DecorationList{ + Location(0), + }); + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: invariant attribute must only be applied to a " + "position builtin"); +} + using FunctionReturnTypeDecorationTest = TestWithParams; TEST_P(FunctionReturnTypeDecorationTest, IsValid) { auto& params = GetParam(); @@ -359,7 +386,7 @@ TEST_F(EntryPointReturnTypeDecorationTest, InvariantWithPosition) { TEST_F(EntryPointReturnTypeDecorationTest, InvariantWithoutPosition) { Func("main", ast::VariableList{}, ty.vec4(), ast::StatementList{Return(Construct(ty.vec4()))}, - ast::DecorationList{Stage(ast::PipelineStage::kFragment)}, + ast::DecorationList{Stage(ast::PipelineStage::kVertex)}, ast::DecorationList{ Invariant(Source{{12, 34}}), Location(Source{{56, 78}}, 0), @@ -501,7 +528,7 @@ INSTANTIATE_TEST_SUITE_P( TestParams{DecorationKind::kBuiltin, true}, TestParams{DecorationKind::kGroup, false}, TestParams{DecorationKind::kInterpolate, true}, - TestParams{DecorationKind::kInvariant, true}, + // kInvariant tested separately (requires position builtin) TestParams{DecorationKind::kLocation, true}, TestParams{DecorationKind::kOverride, false}, TestParams{DecorationKind::kOffset, true}, @@ -531,6 +558,34 @@ TEST_F(StructMemberDecorationTest, DuplicateDecoration) { 12:34 note: first decoration declared here)"); } +TEST_F(StructMemberDecorationTest, InvariantDecorationWithPosition) { + Structure("mystruct", { + Member("a", ty.vec4(), + { + Invariant(), + Builtin(ast::Builtin::kPosition), + }), + }); + + WrapInFunction(); + EXPECT_TRUE(r()->Resolve()) << r()->error(); +} + +TEST_F(StructMemberDecorationTest, InvariantDecorationWithoutPosition) { + Structure("mystruct", { + Member("a", ty.vec4(), + { + Invariant(Source{{12, 34}}), + }), + }); + + WrapInFunction(); + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), + "12:34 error: invariant attribute must only be applied to a " + "position builtin"); +} + using VariableDecorationTest = TestWithParams; TEST_P(VariableDecorationTest, IsValid) { auto& params = GetParam(); diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc index 844d44445c..b41b1bcf69 100644 --- a/src/resolver/resolver.cc +++ b/src/resolver/resolver.cc @@ -1149,7 +1149,7 @@ bool Resolver::ValidateFunctionParameter(const ast::Function* func, deco->source()); return false; } - } else if (!deco->IsAnyOfIsAnyOf() && (IsValidationEnabled( info->declaration->decorations(), @@ -3896,6 +3896,8 @@ bool Resolver::ValidateStructure(const sem::Struct* str) { } } + auto has_position = false; + ast::InvariantDecoration* invariant_attribute = nullptr; for (auto* deco : member->Declaration()->decorations()) { if (!(deco->Is() || deco->Is() || @@ -3908,10 +3910,16 @@ bool Resolver::ValidateStructure(const sem::Struct* str) { deco->source()); return false; } + if (auto* invariant = deco->As()) { + invariant_attribute = invariant; + } if (auto* builtin = deco->As()) { if (!ValidateBuiltinDecoration(builtin, member->Type())) { return false; } + if (builtin->value() == ast::Builtin::kPosition) { + has_position = true; + } } else if (auto* interpolate = deco->As()) { if (!ValidateInterpolateDecoration(interpolate, member->Type())) { return false; @@ -3919,6 +3927,12 @@ bool Resolver::ValidateStructure(const sem::Struct* str) { } } + if (invariant_attribute && !has_position) { + AddError("invariant attribute must only be applied to a position builtin", + invariant_attribute->source()); + return false; + } + if (auto* member_struct_type = member->Type()->As()) { if (auto* member_struct_type_block_decoration = ast::GetDecoration(