• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/ast/disable_validation_decoration.h"
16 #include "src/resolver/resolver.h"
17 #include "src/resolver/resolver_test_helper.h"
18 
19 #include "gmock/gmock.h"
20 
21 namespace tint {
22 namespace resolver {
23 
24 // Helpers and typedefs
25 template <typename T>
26 using DataType = builder::DataType<T>;
27 template <typename T>
28 using vec2 = builder::vec2<T>;
29 template <typename T>
30 using vec3 = builder::vec3<T>;
31 template <typename T>
32 using vec4 = builder::vec4<T>;
33 template <typename T>
34 using mat2x2 = builder::mat2x2<T>;
35 template <typename T>
36 using mat3x3 = builder::mat3x3<T>;
37 template <typename T>
38 using mat4x4 = builder::mat4x4<T>;
39 template <typename T, int ID = 0>
40 using alias = builder::alias<T, ID>;
41 template <typename T>
42 using alias1 = builder::alias1<T>;
43 template <typename T>
44 using alias2 = builder::alias2<T>;
45 template <typename T>
46 using alias3 = builder::alias3<T>;
47 using f32 = builder::f32;
48 using i32 = builder::i32;
49 using u32 = builder::u32;
50 
51 namespace DecorationTests {
52 namespace {
53 enum class DecorationKind {
54   kAlign,
55   kBinding,
56   kBuiltin,
57   kGroup,
58   kInterpolate,
59   kInvariant,
60   kLocation,
61   kOverride,
62   kOffset,
63   kSize,
64   kStage,
65   kStride,
66   kStructBlock,
67   kWorkgroup,
68 
69   kBindingAndGroup,
70 };
71 
IsBindingDecoration(DecorationKind kind)72 static bool IsBindingDecoration(DecorationKind kind) {
73   switch (kind) {
74     case DecorationKind::kBinding:
75     case DecorationKind::kGroup:
76     case DecorationKind::kBindingAndGroup:
77       return true;
78     default:
79       return false;
80   }
81 }
82 
83 struct TestParams {
84   DecorationKind kind;
85   bool should_pass;
86 };
87 struct TestWithParams : ResolverTestWithParam<TestParams> {};
88 
createDecorations(const Source & source,ProgramBuilder & builder,DecorationKind kind)89 static ast::DecorationList createDecorations(const Source& source,
90                                              ProgramBuilder& builder,
91                                              DecorationKind kind) {
92   switch (kind) {
93     case DecorationKind::kAlign:
94       return {builder.create<ast::StructMemberAlignDecoration>(source, 4u)};
95     case DecorationKind::kBinding:
96       return {builder.create<ast::BindingDecoration>(source, 1u)};
97     case DecorationKind::kBuiltin:
98       return {builder.Builtin(source, ast::Builtin::kPosition)};
99     case DecorationKind::kGroup:
100       return {builder.create<ast::GroupDecoration>(source, 1u)};
101     case DecorationKind::kInterpolate:
102       return {builder.Interpolate(source, ast::InterpolationType::kLinear,
103                                   ast::InterpolationSampling::kCenter)};
104     case DecorationKind::kInvariant:
105       return {builder.Invariant(source)};
106     case DecorationKind::kLocation:
107       return {builder.Location(source, 1)};
108     case DecorationKind::kOverride:
109       return {builder.create<ast::OverrideDecoration>(source, 0u)};
110     case DecorationKind::kOffset:
111       return {builder.create<ast::StructMemberOffsetDecoration>(source, 4u)};
112     case DecorationKind::kSize:
113       return {builder.create<ast::StructMemberSizeDecoration>(source, 16u)};
114     case DecorationKind::kStage:
115       return {builder.Stage(source, ast::PipelineStage::kCompute)};
116     case DecorationKind::kStride:
117       return {builder.create<ast::StrideDecoration>(source, 4u)};
118     case DecorationKind::kStructBlock:
119       return {builder.create<ast::StructBlockDecoration>(source)};
120     case DecorationKind::kWorkgroup:
121       return {
122           builder.create<ast::WorkgroupDecoration>(source, builder.Expr(1))};
123     case DecorationKind::kBindingAndGroup:
124       return {builder.create<ast::BindingDecoration>(source, 1u),
125               builder.create<ast::GroupDecoration>(source, 1u)};
126   }
127   return {};
128 }
129 
130 namespace FunctionInputAndOutputTests {
131 using FunctionParameterDecorationTest = TestWithParams;
TEST_P(FunctionParameterDecorationTest,IsValid)132 TEST_P(FunctionParameterDecorationTest, IsValid) {
133   auto& params = GetParam();
134 
135   Func("main",
136        ast::VariableList{Param("a", ty.vec4<f32>(),
137                                createDecorations({}, *this, params.kind))},
138        ty.void_(), {});
139 
140   if (params.should_pass) {
141     EXPECT_TRUE(r()->Resolve()) << r()->error();
142   } else {
143     EXPECT_FALSE(r()->Resolve());
144     EXPECT_EQ(r()->error(),
145               "error: decoration is not valid for non-entry point function "
146               "parameters");
147   }
148 }
149 INSTANTIATE_TEST_SUITE_P(
150     ResolverDecorationValidationTest,
151     FunctionParameterDecorationTest,
152     testing::Values(TestParams{DecorationKind::kAlign, false},
153                     TestParams{DecorationKind::kBinding, false},
154                     TestParams{DecorationKind::kBuiltin, false},
155                     TestParams{DecorationKind::kGroup, false},
156                     TestParams{DecorationKind::kInterpolate, false},
157                     TestParams{DecorationKind::kInvariant, false},
158                     TestParams{DecorationKind::kLocation, false},
159                     TestParams{DecorationKind::kOverride, false},
160                     TestParams{DecorationKind::kOffset, false},
161                     TestParams{DecorationKind::kSize, false},
162                     TestParams{DecorationKind::kStage, false},
163                     TestParams{DecorationKind::kStride, false},
164                     TestParams{DecorationKind::kStructBlock, false},
165                     TestParams{DecorationKind::kWorkgroup, false},
166                     TestParams{DecorationKind::kBindingAndGroup, false}));
167 
168 using FunctionReturnTypeDecorationTest = TestWithParams;
TEST_P(FunctionReturnTypeDecorationTest,IsValid)169 TEST_P(FunctionReturnTypeDecorationTest, IsValid) {
170   auto& params = GetParam();
171 
172   Func("main", ast::VariableList{}, ty.f32(), ast::StatementList{Return(1.f)},
173        {}, createDecorations({}, *this, params.kind));
174 
175   if (params.should_pass) {
176     EXPECT_TRUE(r()->Resolve()) << r()->error();
177   } else {
178     EXPECT_FALSE(r()->Resolve());
179     EXPECT_EQ(r()->error(),
180               "error: decoration is not valid for non-entry point function "
181               "return types");
182   }
183 }
184 INSTANTIATE_TEST_SUITE_P(
185     ResolverDecorationValidationTest,
186     FunctionReturnTypeDecorationTest,
187     testing::Values(TestParams{DecorationKind::kAlign, false},
188                     TestParams{DecorationKind::kBinding, false},
189                     TestParams{DecorationKind::kBuiltin, false},
190                     TestParams{DecorationKind::kGroup, false},
191                     TestParams{DecorationKind::kInterpolate, false},
192                     TestParams{DecorationKind::kInvariant, false},
193                     TestParams{DecorationKind::kLocation, false},
194                     TestParams{DecorationKind::kOverride, false},
195                     TestParams{DecorationKind::kOffset, false},
196                     TestParams{DecorationKind::kSize, false},
197                     TestParams{DecorationKind::kStage, false},
198                     TestParams{DecorationKind::kStride, false},
199                     TestParams{DecorationKind::kStructBlock, false},
200                     TestParams{DecorationKind::kWorkgroup, false},
201                     TestParams{DecorationKind::kBindingAndGroup, false}));
202 }  // namespace FunctionInputAndOutputTests
203 
204 namespace EntryPointInputAndOutputTests {
205 using ComputeShaderParameterDecorationTest = TestWithParams;
TEST_P(ComputeShaderParameterDecorationTest,IsValid)206 TEST_P(ComputeShaderParameterDecorationTest, IsValid) {
207   auto& params = GetParam();
208   auto* p = Param("a", ty.vec4<f32>(),
209                   createDecorations(Source{{12, 34}}, *this, params.kind));
210   Func("main", ast::VariableList{p}, ty.void_(), {},
211        {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
212 
213   if (params.should_pass) {
214     EXPECT_TRUE(r()->Resolve()) << r()->error();
215   } else {
216     EXPECT_FALSE(r()->Resolve());
217     if (params.kind == DecorationKind::kBuiltin) {
218       EXPECT_EQ(r()->error(),
219                 "12:34 error: builtin(position) cannot be used in input of "
220                 "compute pipeline stage");
221     } else if (params.kind == DecorationKind::kInterpolate ||
222                params.kind == DecorationKind::kLocation ||
223                params.kind == DecorationKind::kInvariant) {
224       EXPECT_EQ(
225           r()->error(),
226           "12:34 error: decoration is not valid for compute shader inputs");
227     } else {
228       EXPECT_EQ(r()->error(),
229                 "12:34 error: decoration is not valid for function parameters");
230     }
231   }
232 }
233 INSTANTIATE_TEST_SUITE_P(
234     ResolverDecorationValidationTest,
235     ComputeShaderParameterDecorationTest,
236     testing::Values(TestParams{DecorationKind::kAlign, false},
237                     TestParams{DecorationKind::kBinding, false},
238                     TestParams{DecorationKind::kBuiltin, false},
239                     TestParams{DecorationKind::kGroup, false},
240                     TestParams{DecorationKind::kInterpolate, false},
241                     TestParams{DecorationKind::kInvariant, false},
242                     TestParams{DecorationKind::kLocation, false},
243                     TestParams{DecorationKind::kOverride, false},
244                     TestParams{DecorationKind::kOffset, false},
245                     TestParams{DecorationKind::kSize, false},
246                     TestParams{DecorationKind::kStage, false},
247                     TestParams{DecorationKind::kStride, false},
248                     TestParams{DecorationKind::kStructBlock, false},
249                     TestParams{DecorationKind::kWorkgroup, false},
250                     TestParams{DecorationKind::kBindingAndGroup, false}));
251 
252 using FragmentShaderParameterDecorationTest = TestWithParams;
TEST_P(FragmentShaderParameterDecorationTest,IsValid)253 TEST_P(FragmentShaderParameterDecorationTest, IsValid) {
254   auto& params = GetParam();
255   auto decos = createDecorations(Source{{12, 34}}, *this, params.kind);
256   if (params.kind != DecorationKind::kBuiltin &&
257       params.kind != DecorationKind::kLocation) {
258     decos.push_back(Builtin(Source{{34, 56}}, ast::Builtin::kPosition));
259   }
260   auto* p = Param("a", ty.vec4<f32>(), decos);
261   Func("frag_main", {p}, ty.void_(), {},
262        {Stage(ast::PipelineStage::kFragment)});
263 
264   if (params.should_pass) {
265     EXPECT_TRUE(r()->Resolve()) << r()->error();
266   } else {
267     EXPECT_FALSE(r()->Resolve());
268     EXPECT_EQ(r()->error(),
269               "12:34 error: decoration is not valid for function parameters");
270   }
271 }
272 INSTANTIATE_TEST_SUITE_P(
273     ResolverDecorationValidationTest,
274     FragmentShaderParameterDecorationTest,
275     testing::Values(TestParams{DecorationKind::kAlign, false},
276                     TestParams{DecorationKind::kBinding, false},
277                     TestParams{DecorationKind::kBuiltin, true},
278                     TestParams{DecorationKind::kGroup, false},
279                     // kInterpolate tested separately (requires [[location]])
280                     TestParams{DecorationKind::kInvariant, true},
281                     TestParams{DecorationKind::kLocation, true},
282                     TestParams{DecorationKind::kOverride, false},
283                     TestParams{DecorationKind::kOffset, false},
284                     TestParams{DecorationKind::kSize, false},
285                     TestParams{DecorationKind::kStage, false},
286                     TestParams{DecorationKind::kStride, false},
287                     TestParams{DecorationKind::kStructBlock, false},
288                     TestParams{DecorationKind::kWorkgroup, false},
289                     TestParams{DecorationKind::kBindingAndGroup, false}));
290 
291 using VertexShaderParameterDecorationTest = TestWithParams;
TEST_P(VertexShaderParameterDecorationTest,IsValid)292 TEST_P(VertexShaderParameterDecorationTest, IsValid) {
293   auto& params = GetParam();
294   auto decos = createDecorations(Source{{12, 34}}, *this, params.kind);
295   if (params.kind != DecorationKind::kLocation) {
296     decos.push_back(Location(Source{{34, 56}}, 2));
297   }
298   auto* p = Param("a", ty.vec4<f32>(), decos);
299   Func("vertex_main", ast::VariableList{p}, ty.vec4<f32>(),
300        {Return(Construct(ty.vec4<f32>()))},
301        {Stage(ast::PipelineStage::kVertex)},
302        {Builtin(ast::Builtin::kPosition)});
303 
304   if (params.should_pass) {
305     EXPECT_TRUE(r()->Resolve()) << r()->error();
306   } else {
307     EXPECT_FALSE(r()->Resolve());
308     if (params.kind == DecorationKind::kBuiltin) {
309       EXPECT_EQ(r()->error(),
310                 "12:34 error: builtin(position) cannot be used in input of "
311                 "vertex pipeline stage");
312     } else if (params.kind == DecorationKind::kInvariant) {
313       EXPECT_EQ(r()->error(),
314                 "12:34 error: invariant attribute must only be applied to a "
315                 "position builtin");
316     } else {
317       EXPECT_EQ(r()->error(),
318                 "12:34 error: decoration is not valid for function parameters");
319     }
320   }
321 }
322 INSTANTIATE_TEST_SUITE_P(
323     ResolverDecorationValidationTest,
324     VertexShaderParameterDecorationTest,
325     testing::Values(TestParams{DecorationKind::kAlign, false},
326                     TestParams{DecorationKind::kBinding, false},
327                     TestParams{DecorationKind::kBuiltin, false},
328                     TestParams{DecorationKind::kGroup, false},
329                     TestParams{DecorationKind::kInterpolate, true},
330                     TestParams{DecorationKind::kInvariant, false},
331                     TestParams{DecorationKind::kLocation, true},
332                     TestParams{DecorationKind::kOverride, false},
333                     TestParams{DecorationKind::kOffset, false},
334                     TestParams{DecorationKind::kSize, false},
335                     TestParams{DecorationKind::kStage, false},
336                     TestParams{DecorationKind::kStride, false},
337                     TestParams{DecorationKind::kStructBlock, false},
338                     TestParams{DecorationKind::kWorkgroup, false},
339                     TestParams{DecorationKind::kBindingAndGroup, false}));
340 
341 using ComputeShaderReturnTypeDecorationTest = TestWithParams;
TEST_P(ComputeShaderReturnTypeDecorationTest,IsValid)342 TEST_P(ComputeShaderReturnTypeDecorationTest, IsValid) {
343   auto& params = GetParam();
344   Func("main", ast::VariableList{}, ty.vec4<f32>(),
345        {Return(Construct(ty.vec4<f32>(), 1.f))},
346        {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)},
347        createDecorations(Source{{12, 34}}, *this, params.kind));
348 
349   if (params.should_pass) {
350     EXPECT_TRUE(r()->Resolve()) << r()->error();
351   } else {
352     EXPECT_FALSE(r()->Resolve());
353     if (params.kind == DecorationKind::kBuiltin) {
354       EXPECT_EQ(r()->error(),
355                 "12:34 error: builtin(position) cannot be used in output of "
356                 "compute pipeline stage");
357     } else if (params.kind == DecorationKind::kInterpolate ||
358                params.kind == DecorationKind::kLocation ||
359                params.kind == DecorationKind::kInvariant) {
360       EXPECT_EQ(
361           r()->error(),
362           "12:34 error: decoration is not valid for compute shader output");
363     } else {
364       EXPECT_EQ(r()->error(),
365                 "12:34 error: decoration is not valid for entry point return "
366                 "types");
367     }
368   }
369 }
370 INSTANTIATE_TEST_SUITE_P(
371     ResolverDecorationValidationTest,
372     ComputeShaderReturnTypeDecorationTest,
373     testing::Values(TestParams{DecorationKind::kAlign, false},
374                     TestParams{DecorationKind::kBinding, false},
375                     TestParams{DecorationKind::kBuiltin, false},
376                     TestParams{DecorationKind::kGroup, false},
377                     TestParams{DecorationKind::kInterpolate, false},
378                     TestParams{DecorationKind::kInvariant, false},
379                     TestParams{DecorationKind::kLocation, false},
380                     TestParams{DecorationKind::kOverride, false},
381                     TestParams{DecorationKind::kOffset, false},
382                     TestParams{DecorationKind::kSize, false},
383                     TestParams{DecorationKind::kStage, false},
384                     TestParams{DecorationKind::kStride, false},
385                     TestParams{DecorationKind::kStructBlock, false},
386                     TestParams{DecorationKind::kWorkgroup, false},
387                     TestParams{DecorationKind::kBindingAndGroup, false}));
388 
389 using FragmentShaderReturnTypeDecorationTest = TestWithParams;
TEST_P(FragmentShaderReturnTypeDecorationTest,IsValid)390 TEST_P(FragmentShaderReturnTypeDecorationTest, IsValid) {
391   auto& params = GetParam();
392   auto decos = createDecorations(Source{{12, 34}}, *this, params.kind);
393   decos.push_back(Location(Source{{34, 56}}, 2));
394   Func("frag_main", {}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
395        {Stage(ast::PipelineStage::kFragment)}, decos);
396 
397   if (params.should_pass) {
398     EXPECT_TRUE(r()->Resolve()) << r()->error();
399   } else {
400     EXPECT_FALSE(r()->Resolve());
401     if (params.kind == DecorationKind::kBuiltin) {
402       EXPECT_EQ(r()->error(),
403                 "12:34 error: builtin(position) cannot be used in output of "
404                 "fragment pipeline stage");
405     } else if (params.kind == DecorationKind::kInvariant) {
406       EXPECT_EQ(r()->error(),
407                 "12:34 error: invariant attribute must only be applied to a "
408                 "position builtin");
409     } else if (params.kind == DecorationKind::kLocation) {
410       EXPECT_EQ(r()->error(),
411                 "34:56 error: duplicate location decoration\n"
412                 "12:34 note: first decoration declared here");
413     } else {
414       EXPECT_EQ(r()->error(),
415                 "12:34 error: decoration is not valid for entry point return "
416                 "types");
417     }
418   }
419 }
420 INSTANTIATE_TEST_SUITE_P(
421     ResolverDecorationValidationTest,
422     FragmentShaderReturnTypeDecorationTest,
423     testing::Values(TestParams{DecorationKind::kAlign, false},
424                     TestParams{DecorationKind::kBinding, false},
425                     TestParams{DecorationKind::kBuiltin, false},
426                     TestParams{DecorationKind::kGroup, false},
427                     TestParams{DecorationKind::kInterpolate, true},
428                     TestParams{DecorationKind::kInvariant, false},
429                     TestParams{DecorationKind::kLocation, false},
430                     TestParams{DecorationKind::kOverride, false},
431                     TestParams{DecorationKind::kOffset, false},
432                     TestParams{DecorationKind::kSize, false},
433                     TestParams{DecorationKind::kStage, false},
434                     TestParams{DecorationKind::kStride, false},
435                     TestParams{DecorationKind::kStructBlock, false},
436                     TestParams{DecorationKind::kWorkgroup, false},
437                     TestParams{DecorationKind::kBindingAndGroup, false}));
438 
439 using VertexShaderReturnTypeDecorationTest = TestWithParams;
TEST_P(VertexShaderReturnTypeDecorationTest,IsValid)440 TEST_P(VertexShaderReturnTypeDecorationTest, IsValid) {
441   auto& params = GetParam();
442   auto decos = createDecorations(Source{{12, 34}}, *this, params.kind);
443   // a vertex shader must include the 'position' builtin in its return type
444   if (params.kind != DecorationKind::kBuiltin) {
445     decos.push_back(Builtin(Source{{34, 56}}, ast::Builtin::kPosition));
446   }
447   Func("vertex_main", ast::VariableList{}, ty.vec4<f32>(),
448        {Return(Construct(ty.vec4<f32>()))},
449        {Stage(ast::PipelineStage::kVertex)}, decos);
450 
451   if (params.should_pass) {
452     EXPECT_TRUE(r()->Resolve()) << r()->error();
453   } else {
454     EXPECT_FALSE(r()->Resolve());
455     if (params.kind == DecorationKind::kLocation) {
456       EXPECT_EQ(r()->error(),
457                 "34:56 error: multiple entry point IO attributes\n"
458                 "12:34 note: previously consumed location(1)");
459     } else {
460       EXPECT_EQ(r()->error(),
461                 "12:34 error: decoration is not valid for entry point return "
462                 "types");
463     }
464   }
465 }
466 INSTANTIATE_TEST_SUITE_P(
467     ResolverDecorationValidationTest,
468     VertexShaderReturnTypeDecorationTest,
469     testing::Values(TestParams{DecorationKind::kAlign, false},
470                     TestParams{DecorationKind::kBinding, false},
471                     TestParams{DecorationKind::kBuiltin, true},
472                     TestParams{DecorationKind::kGroup, false},
473                     // kInterpolate tested separately (requires [[location]])
474                     TestParams{DecorationKind::kInvariant, true},
475                     TestParams{DecorationKind::kLocation, false},
476                     TestParams{DecorationKind::kOverride, false},
477                     TestParams{DecorationKind::kOffset, false},
478                     TestParams{DecorationKind::kSize, false},
479                     TestParams{DecorationKind::kStage, false},
480                     TestParams{DecorationKind::kStride, false},
481                     TestParams{DecorationKind::kStructBlock, false},
482                     TestParams{DecorationKind::kWorkgroup, false},
483                     TestParams{DecorationKind::kBindingAndGroup, false}));
484 
485 using EntryPointParameterDecorationTest = TestWithParams;
TEST_F(EntryPointParameterDecorationTest,DuplicateDecoration)486 TEST_F(EntryPointParameterDecorationTest, DuplicateDecoration) {
487   Func("main", ast::VariableList{}, ty.f32(), ast::StatementList{Return(1.f)},
488        {Stage(ast::PipelineStage::kFragment)},
489        {
490            Location(Source{{12, 34}}, 2),
491            Location(Source{{56, 78}}, 3),
492        });
493 
494   EXPECT_FALSE(r()->Resolve());
495   EXPECT_EQ(r()->error(),
496             R"(56:78 error: duplicate location decoration
497 12:34 note: first decoration declared here)");
498 }
499 
TEST_F(EntryPointParameterDecorationTest,DuplicateInternalDecoration)500 TEST_F(EntryPointParameterDecorationTest, DuplicateInternalDecoration) {
501   auto* s = Param("s", ty.sampler(ast::SamplerKind::kSampler),
502                   ast::DecorationList{
503                       create<ast::BindingDecoration>(0),
504                       create<ast::GroupDecoration>(0),
505                       Disable(ast::DisabledValidation::kBindingPointCollision),
506                       Disable(ast::DisabledValidation::kEntryPointParameter),
507                   });
508   Func("f", {s}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
509 
510   EXPECT_TRUE(r()->Resolve()) << r()->error();
511 }
512 
513 using EntryPointReturnTypeDecorationTest = ResolverTest;
TEST_F(EntryPointReturnTypeDecorationTest,DuplicateDecoration)514 TEST_F(EntryPointReturnTypeDecorationTest, DuplicateDecoration) {
515   Func("main", ast::VariableList{}, ty.f32(), ast::StatementList{Return(1.f)},
516        ast::DecorationList{Stage(ast::PipelineStage::kFragment)},
517        ast::DecorationList{
518            Location(Source{{12, 34}}, 2),
519            Location(Source{{56, 78}}, 3),
520        });
521 
522   EXPECT_FALSE(r()->Resolve());
523   EXPECT_EQ(r()->error(),
524             R"(56:78 error: duplicate location decoration
525 12:34 note: first decoration declared here)");
526 }
527 
TEST_F(EntryPointReturnTypeDecorationTest,DuplicateInternalDecoration)528 TEST_F(EntryPointReturnTypeDecorationTest, DuplicateInternalDecoration) {
529   Func("f", {}, ty.i32(), {Return(1)}, {Stage(ast::PipelineStage::kFragment)},
530        ast::DecorationList{
531            Disable(ast::DisabledValidation::kBindingPointCollision),
532            Disable(ast::DisabledValidation::kEntryPointParameter),
533        });
534 
535   EXPECT_TRUE(r()->Resolve()) << r()->error();
536 }
537 }  // namespace EntryPointInputAndOutputTests
538 
539 namespace StructAndStructMemberTests {
540 using StructDecorationTest = TestWithParams;
TEST_P(StructDecorationTest,IsValid)541 TEST_P(StructDecorationTest, IsValid) {
542   auto& params = GetParam();
543 
544   Structure("mystruct", {Member("a", ty.f32())},
545             createDecorations(Source{{12, 34}}, *this, params.kind));
546 
547   WrapInFunction();
548 
549   if (params.should_pass) {
550     EXPECT_TRUE(r()->Resolve()) << r()->error();
551   } else {
552     EXPECT_FALSE(r()->Resolve());
553     EXPECT_EQ(r()->error(),
554               "12:34 error: decoration is not valid for struct declarations");
555   }
556 }
557 INSTANTIATE_TEST_SUITE_P(
558     ResolverDecorationValidationTest,
559     StructDecorationTest,
560     testing::Values(TestParams{DecorationKind::kAlign, false},
561                     TestParams{DecorationKind::kBinding, false},
562                     TestParams{DecorationKind::kBuiltin, false},
563                     TestParams{DecorationKind::kGroup, false},
564                     TestParams{DecorationKind::kInterpolate, false},
565                     TestParams{DecorationKind::kInvariant, false},
566                     TestParams{DecorationKind::kLocation, false},
567                     TestParams{DecorationKind::kOverride, false},
568                     TestParams{DecorationKind::kOffset, false},
569                     TestParams{DecorationKind::kSize, false},
570                     TestParams{DecorationKind::kStage, false},
571                     TestParams{DecorationKind::kStride, false},
572                     TestParams{DecorationKind::kStructBlock, true},
573                     TestParams{DecorationKind::kWorkgroup, false},
574                     TestParams{DecorationKind::kBindingAndGroup, false}));
575 
TEST_F(StructDecorationTest,DuplicateDecoration)576 TEST_F(StructDecorationTest, DuplicateDecoration) {
577   Structure("mystruct",
578             {
579                 Member("a", ty.i32()),
580             },
581             {
582                 create<ast::StructBlockDecoration>(Source{{12, 34}}),
583                 create<ast::StructBlockDecoration>(Source{{56, 78}}),
584             });
585   WrapInFunction();
586   EXPECT_FALSE(r()->Resolve());
587   EXPECT_EQ(r()->error(),
588             R"(56:78 error: duplicate block decoration
589 12:34 note: first decoration declared here)");
590 }
591 using StructMemberDecorationTest = TestWithParams;
TEST_P(StructMemberDecorationTest,IsValid)592 TEST_P(StructMemberDecorationTest, IsValid) {
593   auto& params = GetParam();
594   ast::StructMemberList members;
595   if (params.kind == DecorationKind::kBuiltin) {
596     members.push_back(
597         {Member("a", ty.vec4<f32>(),
598                 createDecorations(Source{{12, 34}}, *this, params.kind))});
599   } else {
600     members.push_back(
601         {Member("a", ty.f32(),
602                 createDecorations(Source{{12, 34}}, *this, params.kind))});
603   }
604   Structure("mystruct", members);
605   WrapInFunction();
606   if (params.should_pass) {
607     EXPECT_TRUE(r()->Resolve()) << r()->error();
608   } else {
609     EXPECT_FALSE(r()->Resolve());
610     EXPECT_EQ(r()->error(),
611               "12:34 error: decoration is not valid for structure members");
612   }
613 }
614 INSTANTIATE_TEST_SUITE_P(
615     ResolverDecorationValidationTest,
616     StructMemberDecorationTest,
617     testing::Values(TestParams{DecorationKind::kAlign, true},
618                     TestParams{DecorationKind::kBinding, false},
619                     TestParams{DecorationKind::kBuiltin, true},
620                     TestParams{DecorationKind::kGroup, false},
621                     // kInterpolate tested separately (requires [[location]])
622                     // kInvariant tested separately (requires position builtin)
623                     TestParams{DecorationKind::kLocation, true},
624                     TestParams{DecorationKind::kOverride, false},
625                     TestParams{DecorationKind::kOffset, true},
626                     TestParams{DecorationKind::kSize, true},
627                     TestParams{DecorationKind::kStage, false},
628                     TestParams{DecorationKind::kStride, false},
629                     TestParams{DecorationKind::kStructBlock, false},
630                     TestParams{DecorationKind::kWorkgroup, false},
631                     TestParams{DecorationKind::kBindingAndGroup, false}));
TEST_F(StructMemberDecorationTest,DuplicateDecoration)632 TEST_F(StructMemberDecorationTest, DuplicateDecoration) {
633   Structure("mystruct", {
634                             Member("a", ty.i32(),
635                                    {
636                                        create<ast::StructMemberAlignDecoration>(
637                                            Source{{12, 34}}, 4u),
638                                        create<ast::StructMemberAlignDecoration>(
639                                            Source{{56, 78}}, 8u),
640                                    }),
641                         });
642   WrapInFunction();
643   EXPECT_FALSE(r()->Resolve());
644   EXPECT_EQ(r()->error(),
645             R"(56:78 error: duplicate align decoration
646 12:34 note: first decoration declared here)");
647 }
TEST_F(StructMemberDecorationTest,InvariantDecorationWithPosition)648 TEST_F(StructMemberDecorationTest, InvariantDecorationWithPosition) {
649   Structure("mystruct", {
650                             Member("a", ty.vec4<f32>(),
651                                    {
652                                        Invariant(),
653                                        Builtin(ast::Builtin::kPosition),
654                                    }),
655                         });
656   WrapInFunction();
657   EXPECT_TRUE(r()->Resolve()) << r()->error();
658 }
TEST_F(StructMemberDecorationTest,InvariantDecorationWithoutPosition)659 TEST_F(StructMemberDecorationTest, InvariantDecorationWithoutPosition) {
660   Structure("mystruct", {
661                             Member("a", ty.vec4<f32>(),
662                                    {
663                                        Invariant(Source{{12, 34}}),
664                                    }),
665                         });
666   WrapInFunction();
667   EXPECT_FALSE(r()->Resolve());
668   EXPECT_EQ(r()->error(),
669             "12:34 error: invariant attribute must only be applied to a "
670             "position builtin");
671 }
672 
673 }  // namespace StructAndStructMemberTests
674 
675 using ArrayDecorationTest = TestWithParams;
TEST_P(ArrayDecorationTest,IsValid)676 TEST_P(ArrayDecorationTest, IsValid) {
677   auto& params = GetParam();
678 
679   auto* arr = ty.array(ty.f32(), nullptr,
680                        createDecorations(Source{{12, 34}}, *this, params.kind));
681   Structure("mystruct",
682             {
683                 Member("a", arr),
684             },
685             {create<ast::StructBlockDecoration>()});
686 
687   WrapInFunction();
688 
689   if (params.should_pass) {
690     EXPECT_TRUE(r()->Resolve()) << r()->error();
691   } else {
692     EXPECT_FALSE(r()->Resolve());
693     EXPECT_EQ(r()->error(),
694               "12:34 error: decoration is not valid for array types");
695   }
696 }
697 INSTANTIATE_TEST_SUITE_P(
698     ResolverDecorationValidationTest,
699     ArrayDecorationTest,
700     testing::Values(TestParams{DecorationKind::kAlign, false},
701                     TestParams{DecorationKind::kBinding, false},
702                     TestParams{DecorationKind::kBuiltin, false},
703                     TestParams{DecorationKind::kGroup, false},
704                     TestParams{DecorationKind::kInterpolate, false},
705                     TestParams{DecorationKind::kInvariant, false},
706                     TestParams{DecorationKind::kLocation, false},
707                     TestParams{DecorationKind::kOverride, false},
708                     TestParams{DecorationKind::kOffset, false},
709                     TestParams{DecorationKind::kSize, false},
710                     TestParams{DecorationKind::kStage, false},
711                     TestParams{DecorationKind::kStride, true},
712                     TestParams{DecorationKind::kStructBlock, false},
713                     TestParams{DecorationKind::kWorkgroup, false},
714                     TestParams{DecorationKind::kBindingAndGroup, false}));
715 
716 using VariableDecorationTest = TestWithParams;
TEST_P(VariableDecorationTest,IsValid)717 TEST_P(VariableDecorationTest, IsValid) {
718   auto& params = GetParam();
719 
720   if (IsBindingDecoration(params.kind)) {
721     Global("a", ty.sampler(ast::SamplerKind::kSampler),
722            ast::StorageClass::kNone, nullptr,
723            createDecorations(Source{{12, 34}}, *this, params.kind));
724   } else {
725     Global("a", ty.f32(), ast::StorageClass::kPrivate, nullptr,
726            createDecorations(Source{{12, 34}}, *this, params.kind));
727   }
728 
729   WrapInFunction();
730 
731   if (params.should_pass) {
732     EXPECT_TRUE(r()->Resolve()) << r()->error();
733   } else {
734     EXPECT_FALSE(r()->Resolve());
735     if (!IsBindingDecoration(params.kind)) {
736       EXPECT_EQ(r()->error(),
737                 "12:34 error: decoration is not valid for variables");
738     }
739   }
740 }
741 INSTANTIATE_TEST_SUITE_P(
742     ResolverDecorationValidationTest,
743     VariableDecorationTest,
744     testing::Values(TestParams{DecorationKind::kAlign, false},
745                     TestParams{DecorationKind::kBinding, false},
746                     TestParams{DecorationKind::kBuiltin, false},
747                     TestParams{DecorationKind::kGroup, false},
748                     TestParams{DecorationKind::kInterpolate, false},
749                     TestParams{DecorationKind::kInvariant, false},
750                     TestParams{DecorationKind::kLocation, false},
751                     TestParams{DecorationKind::kOverride, false},
752                     TestParams{DecorationKind::kOffset, false},
753                     TestParams{DecorationKind::kSize, false},
754                     TestParams{DecorationKind::kStage, false},
755                     TestParams{DecorationKind::kStride, false},
756                     TestParams{DecorationKind::kStructBlock, false},
757                     TestParams{DecorationKind::kWorkgroup, false},
758                     TestParams{DecorationKind::kBindingAndGroup, true}));
759 
TEST_F(VariableDecorationTest,DuplicateDecoration)760 TEST_F(VariableDecorationTest, DuplicateDecoration) {
761   Global("a", ty.sampler(ast::SamplerKind::kSampler),
762          ast::DecorationList{
763              create<ast::BindingDecoration>(Source{{12, 34}}, 2),
764              create<ast::GroupDecoration>(2),
765              create<ast::BindingDecoration>(Source{{56, 78}}, 3),
766          });
767 
768   WrapInFunction();
769 
770   EXPECT_FALSE(r()->Resolve());
771   EXPECT_EQ(r()->error(),
772             R"(56:78 error: duplicate binding decoration
773 12:34 note: first decoration declared here)");
774 }
775 
TEST_F(VariableDecorationTest,LocalVariable)776 TEST_F(VariableDecorationTest, LocalVariable) {
777   auto* v = Var("a", ty.f32(),
778                 ast::DecorationList{
779                     create<ast::BindingDecoration>(Source{{12, 34}}, 2),
780                 });
781 
782   WrapInFunction(v);
783 
784   EXPECT_FALSE(r()->Resolve());
785   EXPECT_EQ(r()->error(),
786             "12:34 error: decorations are not valid on local variables");
787 }
788 
789 using ConstantDecorationTest = TestWithParams;
TEST_P(ConstantDecorationTest,IsValid)790 TEST_P(ConstantDecorationTest, IsValid) {
791   auto& params = GetParam();
792 
793   GlobalConst("a", ty.f32(), Expr(1.23f),
794               createDecorations(Source{{12, 34}}, *this, params.kind));
795 
796   WrapInFunction();
797 
798   if (params.should_pass) {
799     EXPECT_TRUE(r()->Resolve()) << r()->error();
800   } else {
801     EXPECT_FALSE(r()->Resolve());
802     EXPECT_EQ(r()->error(),
803               "12:34 error: decoration is not valid for constants");
804   }
805 }
806 INSTANTIATE_TEST_SUITE_P(
807     ResolverDecorationValidationTest,
808     ConstantDecorationTest,
809     testing::Values(TestParams{DecorationKind::kAlign, false},
810                     TestParams{DecorationKind::kBinding, false},
811                     TestParams{DecorationKind::kBuiltin, false},
812                     TestParams{DecorationKind::kGroup, false},
813                     TestParams{DecorationKind::kInterpolate, false},
814                     TestParams{DecorationKind::kInvariant, false},
815                     TestParams{DecorationKind::kLocation, false},
816                     TestParams{DecorationKind::kOverride, true},
817                     TestParams{DecorationKind::kOffset, false},
818                     TestParams{DecorationKind::kSize, false},
819                     TestParams{DecorationKind::kStage, false},
820                     TestParams{DecorationKind::kStride, false},
821                     TestParams{DecorationKind::kStructBlock, false},
822                     TestParams{DecorationKind::kWorkgroup, false},
823                     TestParams{DecorationKind::kBindingAndGroup, false}));
824 
TEST_F(ConstantDecorationTest,DuplicateDecoration)825 TEST_F(ConstantDecorationTest, DuplicateDecoration) {
826   GlobalConst("a", ty.f32(), Expr(1.23f),
827               ast::DecorationList{
828                   create<ast::OverrideDecoration>(Source{{12, 34}}),
829                   create<ast::OverrideDecoration>(Source{{56, 78}}, 1),
830               });
831 
832   WrapInFunction();
833 
834   EXPECT_FALSE(r()->Resolve());
835   EXPECT_EQ(r()->error(),
836             R"(56:78 error: duplicate override decoration
837 12:34 note: first decoration declared here)");
838 }
839 
840 }  // namespace
841 }  // namespace DecorationTests
842 
843 namespace ArrayStrideTests {
844 namespace {
845 
846 struct Params {
847   builder::ast_type_func_ptr create_el_type;
848   uint32_t stride;
849   bool should_pass;
850 };
851 
852 template <typename T>
ParamsFor(uint32_t stride,bool should_pass)853 constexpr Params ParamsFor(uint32_t stride, bool should_pass) {
854   return Params{DataType<T>::AST, stride, should_pass};
855 }
856 
857 struct TestWithParams : ResolverTestWithParam<Params> {};
858 
859 using ArrayStrideTest = TestWithParams;
TEST_P(ArrayStrideTest,All)860 TEST_P(ArrayStrideTest, All) {
861   auto& params = GetParam();
862   auto* el_ty = params.create_el_type(*this);
863 
864   std::stringstream ss;
865   ss << "el_ty: " << FriendlyName(el_ty) << ", stride: " << params.stride
866      << ", should_pass: " << params.should_pass;
867   SCOPED_TRACE(ss.str());
868 
869   auto* arr = ty.array(Source{{12, 34}}, el_ty, 4, params.stride);
870 
871   Global("myarray", arr, ast::StorageClass::kPrivate);
872 
873   if (params.should_pass) {
874     EXPECT_TRUE(r()->Resolve()) << r()->error();
875   } else {
876     EXPECT_FALSE(r()->Resolve());
877     EXPECT_EQ(r()->error(),
878               "12:34 error: arrays decorated with the stride attribute must "
879               "have a stride that is at least the size of the element type, "
880               "and be a multiple of the element type's alignment value.");
881   }
882 }
883 
884 struct SizeAndAlignment {
885   uint32_t size;
886   uint32_t align;
887 };
888 constexpr SizeAndAlignment default_u32 = {4, 4};
889 constexpr SizeAndAlignment default_i32 = {4, 4};
890 constexpr SizeAndAlignment default_f32 = {4, 4};
891 constexpr SizeAndAlignment default_vec2 = {8, 8};
892 constexpr SizeAndAlignment default_vec3 = {12, 16};
893 constexpr SizeAndAlignment default_vec4 = {16, 16};
894 constexpr SizeAndAlignment default_mat2x2 = {16, 8};
895 constexpr SizeAndAlignment default_mat3x3 = {48, 16};
896 constexpr SizeAndAlignment default_mat4x4 = {64, 16};
897 
898 INSTANTIATE_TEST_SUITE_P(
899     ResolverDecorationValidationTest,
900     ArrayStrideTest,
901     testing::Values(
902         // Succeed because stride >= element size (while being multiple of
903         // element alignment)
904         ParamsFor<u32>(default_u32.size, true),
905         ParamsFor<i32>(default_i32.size, true),
906         ParamsFor<f32>(default_f32.size, true),
907         ParamsFor<vec2<f32>>(default_vec2.size, true),
908         // vec3's default size is not a multiple of its alignment
909         // ParamsFor<vec3<f32>, default_vec3.size, true},
910         ParamsFor<vec4<f32>>(default_vec4.size, true),
911         ParamsFor<mat2x2<f32>>(default_mat2x2.size, true),
912         ParamsFor<mat3x3<f32>>(default_mat3x3.size, true),
913         ParamsFor<mat4x4<f32>>(default_mat4x4.size, true),
914 
915         // Fail because stride is < element size
916         ParamsFor<u32>(default_u32.size - 1, false),
917         ParamsFor<i32>(default_i32.size - 1, false),
918         ParamsFor<f32>(default_f32.size - 1, false),
919         ParamsFor<vec2<f32>>(default_vec2.size - 1, false),
920         ParamsFor<vec3<f32>>(default_vec3.size - 1, false),
921         ParamsFor<vec4<f32>>(default_vec4.size - 1, false),
922         ParamsFor<mat2x2<f32>>(default_mat2x2.size - 1, false),
923         ParamsFor<mat3x3<f32>>(default_mat3x3.size - 1, false),
924         ParamsFor<mat4x4<f32>>(default_mat4x4.size - 1, false),
925 
926         // Succeed because stride equals multiple of element alignment
927         ParamsFor<u32>(default_u32.align * 7, true),
928         ParamsFor<i32>(default_i32.align * 7, true),
929         ParamsFor<f32>(default_f32.align * 7, true),
930         ParamsFor<vec2<f32>>(default_vec2.align * 7, true),
931         ParamsFor<vec3<f32>>(default_vec3.align * 7, true),
932         ParamsFor<vec4<f32>>(default_vec4.align * 7, true),
933         ParamsFor<mat2x2<f32>>(default_mat2x2.align * 7, true),
934         ParamsFor<mat3x3<f32>>(default_mat3x3.align * 7, true),
935         ParamsFor<mat4x4<f32>>(default_mat4x4.align * 7, true),
936 
937         // Fail because stride is not multiple of element alignment
938         ParamsFor<u32>((default_u32.align - 1) * 7, false),
939         ParamsFor<i32>((default_i32.align - 1) * 7, false),
940         ParamsFor<f32>((default_f32.align - 1) * 7, false),
941         ParamsFor<vec2<f32>>((default_vec2.align - 1) * 7, false),
942         ParamsFor<vec3<f32>>((default_vec3.align - 1) * 7, false),
943         ParamsFor<vec4<f32>>((default_vec4.align - 1) * 7, false),
944         ParamsFor<mat2x2<f32>>((default_mat2x2.align - 1) * 7, false),
945         ParamsFor<mat3x3<f32>>((default_mat3x3.align - 1) * 7, false),
946         ParamsFor<mat4x4<f32>>((default_mat4x4.align - 1) * 7, false)));
947 
TEST_F(ArrayStrideTest,DuplicateDecoration)948 TEST_F(ArrayStrideTest, DuplicateDecoration) {
949   auto* arr = ty.array(Source{{12, 34}}, ty.i32(), 4,
950                        {
951                            create<ast::StrideDecoration>(Source{{12, 34}}, 4),
952                            create<ast::StrideDecoration>(Source{{56, 78}}, 4),
953                        });
954 
955   Global("myarray", arr, ast::StorageClass::kPrivate);
956 
957   EXPECT_FALSE(r()->Resolve());
958   EXPECT_EQ(r()->error(),
959             R"(56:78 error: duplicate stride decoration
960 12:34 note: first decoration declared here)");
961 }
962 
963 }  // namespace
964 }  // namespace ArrayStrideTests
965 
966 namespace StructBlockTests {
967 namespace {
968 
969 using StructBlockTest = ResolverTest;
TEST_F(StructBlockTest,StructUsedAsArrayElement)970 TEST_F(StructBlockTest, StructUsedAsArrayElement) {
971   auto* s = Structure("S", {Member("x", ty.i32())},
972                       {create<ast::StructBlockDecoration>()});
973   auto* a = ty.array(ty.Of(s), 4);
974   Global("G", a, ast::StorageClass::kPrivate);
975 
976   EXPECT_FALSE(r()->Resolve());
977   EXPECT_EQ(r()->error(),
978             "error: A structure type with a [[block]] decoration cannot be "
979             "used as an element of an array");
980 }
981 
TEST_F(StructBlockTest,StructWithNestedBlockMember_Invalid)982 TEST_F(StructBlockTest, StructWithNestedBlockMember_Invalid) {
983   auto* inner =
984       Structure("Inner", {Member("x", ty.i32())},
985                 {create<ast::StructBlockDecoration>(Source{{56, 78}})});
986 
987   auto* outer =
988       Structure("Outer", {Member(Source{{12, 34}}, "y", ty.Of(inner))});
989 
990   Global("G", ty.Of(outer), ast::StorageClass::kPrivate);
991 
992   EXPECT_FALSE(r()->Resolve());
993   EXPECT_EQ(
994       r()->error(),
995       R"(12:34 error: structs must not contain [[block]] decorated struct members
996 56:78 note: see member's struct decoration here)");
997 }
998 }  // namespace
999 }  // namespace StructBlockTests
1000 
1001 namespace ResourceTests {
1002 namespace {
1003 
1004 using ResourceDecorationTest = ResolverTest;
TEST_F(ResourceDecorationTest,UniformBufferMissingBinding)1005 TEST_F(ResourceDecorationTest, UniformBufferMissingBinding) {
1006   auto* s = Structure("S", {Member("x", ty.i32())},
1007                       {create<ast::StructBlockDecoration>()});
1008   Global(Source{{12, 34}}, "G", ty.Of(s), ast::StorageClass::kUniform);
1009 
1010   EXPECT_FALSE(r()->Resolve());
1011   EXPECT_EQ(r()->error(),
1012             "12:34 error: resource variables require [[group]] and [[binding]] "
1013             "decorations");
1014 }
1015 
TEST_F(ResourceDecorationTest,StorageBufferMissingBinding)1016 TEST_F(ResourceDecorationTest, StorageBufferMissingBinding) {
1017   auto* s = Structure("S", {Member("x", ty.i32())},
1018                       {create<ast::StructBlockDecoration>()});
1019   Global(Source{{12, 34}}, "G", ty.Of(s), ast::StorageClass::kStorage,
1020          ast::Access::kRead);
1021 
1022   EXPECT_FALSE(r()->Resolve());
1023   EXPECT_EQ(r()->error(),
1024             "12:34 error: resource variables require [[group]] and [[binding]] "
1025             "decorations");
1026 }
1027 
TEST_F(ResourceDecorationTest,TextureMissingBinding)1028 TEST_F(ResourceDecorationTest, TextureMissingBinding) {
1029   Global(Source{{12, 34}}, "G", ty.depth_texture(ast::TextureDimension::k2d),
1030          ast::StorageClass::kNone);
1031 
1032   EXPECT_FALSE(r()->Resolve());
1033   EXPECT_EQ(r()->error(),
1034             "12:34 error: resource variables require [[group]] and [[binding]] "
1035             "decorations");
1036 }
1037 
TEST_F(ResourceDecorationTest,SamplerMissingBinding)1038 TEST_F(ResourceDecorationTest, SamplerMissingBinding) {
1039   Global(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler),
1040          ast::StorageClass::kNone);
1041 
1042   EXPECT_FALSE(r()->Resolve());
1043   EXPECT_EQ(r()->error(),
1044             "12:34 error: resource variables require [[group]] and [[binding]] "
1045             "decorations");
1046 }
1047 
TEST_F(ResourceDecorationTest,BindingPairMissingBinding)1048 TEST_F(ResourceDecorationTest, BindingPairMissingBinding) {
1049   Global(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler),
1050          ast::StorageClass::kNone,
1051          ast::DecorationList{
1052              create<ast::GroupDecoration>(1),
1053          });
1054 
1055   EXPECT_FALSE(r()->Resolve());
1056   EXPECT_EQ(r()->error(),
1057             "12:34 error: resource variables require [[group]] and [[binding]] "
1058             "decorations");
1059 }
1060 
TEST_F(ResourceDecorationTest,BindingPairMissingGroup)1061 TEST_F(ResourceDecorationTest, BindingPairMissingGroup) {
1062   Global(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler),
1063          ast::StorageClass::kNone,
1064          ast::DecorationList{
1065              create<ast::BindingDecoration>(1),
1066          });
1067 
1068   EXPECT_FALSE(r()->Resolve());
1069   EXPECT_EQ(r()->error(),
1070             "12:34 error: resource variables require [[group]] and [[binding]] "
1071             "decorations");
1072 }
1073 
TEST_F(ResourceDecorationTest,BindingPointUsedTwiceByEntryPoint)1074 TEST_F(ResourceDecorationTest, BindingPointUsedTwiceByEntryPoint) {
1075   Global(Source{{12, 34}}, "A",
1076          ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
1077          ast::StorageClass::kNone,
1078          ast::DecorationList{
1079              create<ast::BindingDecoration>(1),
1080              create<ast::GroupDecoration>(2),
1081          });
1082   Global(Source{{56, 78}}, "B",
1083          ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
1084          ast::StorageClass::kNone,
1085          ast::DecorationList{
1086              create<ast::BindingDecoration>(1),
1087              create<ast::GroupDecoration>(2),
1088          });
1089 
1090   Func("F", {}, ty.void_(),
1091        {
1092            Decl(Var("a", ty.vec4<f32>(), ast::StorageClass::kNone,
1093                     Call("textureLoad", "A", vec2<i32>(1, 2), 0))),
1094            Decl(Var("b", ty.vec4<f32>(), ast::StorageClass::kNone,
1095                     Call("textureLoad", "B", vec2<i32>(1, 2), 0))),
1096        },
1097        {Stage(ast::PipelineStage::kFragment)});
1098 
1099   EXPECT_FALSE(r()->Resolve());
1100   EXPECT_EQ(
1101       r()->error(),
1102       R"(56:78 error: entry point 'F' references multiple variables that use the same resource binding [[group(2), binding(1)]]
1103 12:34 note: first resource binding usage declared here)");
1104 }
1105 
TEST_F(ResourceDecorationTest,BindingPointUsedTwiceByDifferentEntryPoints)1106 TEST_F(ResourceDecorationTest, BindingPointUsedTwiceByDifferentEntryPoints) {
1107   Global(Source{{12, 34}}, "A",
1108          ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
1109          ast::StorageClass::kNone,
1110          ast::DecorationList{
1111              create<ast::BindingDecoration>(1),
1112              create<ast::GroupDecoration>(2),
1113          });
1114   Global(Source{{56, 78}}, "B",
1115          ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
1116          ast::StorageClass::kNone,
1117          ast::DecorationList{
1118              create<ast::BindingDecoration>(1),
1119              create<ast::GroupDecoration>(2),
1120          });
1121 
1122   Func("F_A", {}, ty.void_(),
1123        {
1124            Decl(Var("a", ty.vec4<f32>(), ast::StorageClass::kNone,
1125                     Call("textureLoad", "A", vec2<i32>(1, 2), 0))),
1126        },
1127        {Stage(ast::PipelineStage::kFragment)});
1128   Func("F_B", {}, ty.void_(),
1129        {
1130            Decl(Var("b", ty.vec4<f32>(), ast::StorageClass::kNone,
1131                     Call("textureLoad", "B", vec2<i32>(1, 2), 0))),
1132        },
1133        {Stage(ast::PipelineStage::kFragment)});
1134 
1135   EXPECT_TRUE(r()->Resolve()) << r()->error();
1136 }
1137 
TEST_F(ResourceDecorationTest,BindingPointOnNonResource)1138 TEST_F(ResourceDecorationTest, BindingPointOnNonResource) {
1139   Global(Source{{12, 34}}, "G", ty.f32(), ast::StorageClass::kPrivate,
1140          ast::DecorationList{
1141              create<ast::BindingDecoration>(1),
1142              create<ast::GroupDecoration>(2),
1143          });
1144 
1145   EXPECT_FALSE(r()->Resolve());
1146   EXPECT_EQ(r()->error(),
1147             "12:34 error: non-resource variables must not have [[group]] or "
1148             "[[binding]] decorations");
1149 }
1150 
1151 }  // namespace
1152 }  // namespace ResourceTests
1153 
1154 namespace InvariantDecorationTests {
1155 namespace {
1156 using InvariantDecorationTests = ResolverTest;
TEST_F(InvariantDecorationTests,InvariantWithPosition)1157 TEST_F(InvariantDecorationTests, InvariantWithPosition) {
1158   auto* param = Param("p", ty.vec4<f32>(),
1159                       {Invariant(Source{{12, 34}}),
1160                        Builtin(Source{{56, 78}}, ast::Builtin::kPosition)});
1161   Func("main", ast::VariableList{param}, ty.vec4<f32>(),
1162        ast::StatementList{Return(Construct(ty.vec4<f32>()))},
1163        ast::DecorationList{Stage(ast::PipelineStage::kFragment)},
1164        ast::DecorationList{
1165            Location(0),
1166        });
1167   EXPECT_TRUE(r()->Resolve()) << r()->error();
1168 }
1169 
TEST_F(InvariantDecorationTests,InvariantWithoutPosition)1170 TEST_F(InvariantDecorationTests, InvariantWithoutPosition) {
1171   auto* param =
1172       Param("p", ty.vec4<f32>(), {Invariant(Source{{12, 34}}), Location(0)});
1173   Func("main", ast::VariableList{param}, ty.vec4<f32>(),
1174        ast::StatementList{Return(Construct(ty.vec4<f32>()))},
1175        ast::DecorationList{Stage(ast::PipelineStage::kFragment)},
1176        ast::DecorationList{
1177            Location(0),
1178        });
1179   EXPECT_FALSE(r()->Resolve());
1180   EXPECT_EQ(r()->error(),
1181             "12:34 error: invariant attribute must only be applied to a "
1182             "position builtin");
1183 }
1184 }  // namespace
1185 }  // namespace InvariantDecorationTests
1186 
1187 namespace WorkgroupDecorationTests {
1188 namespace {
1189 
1190 using WorkgroupDecoration = ResolverTest;
TEST_F(WorkgroupDecoration,ComputeShaderPass)1191 TEST_F(WorkgroupDecoration, ComputeShaderPass) {
1192   Func("main", {}, ty.void_(), {},
1193        {Stage(ast::PipelineStage::kCompute),
1194         create<ast::WorkgroupDecoration>(Source{{12, 34}}, Expr(1))});
1195 
1196   EXPECT_TRUE(r()->Resolve()) << r()->error();
1197 }
1198 
TEST_F(WorkgroupDecoration,Missing)1199 TEST_F(WorkgroupDecoration, Missing) {
1200   Func(Source{{12, 34}}, "main", {}, ty.void_(), {},
1201        {Stage(ast::PipelineStage::kCompute)});
1202 
1203   EXPECT_FALSE(r()->Resolve());
1204   EXPECT_EQ(
1205       r()->error(),
1206       "12:34 error: a compute shader must include 'workgroup_size' in its "
1207       "attributes");
1208 }
1209 
TEST_F(WorkgroupDecoration,NotAnEntryPoint)1210 TEST_F(WorkgroupDecoration, NotAnEntryPoint) {
1211   Func("main", {}, ty.void_(), {},
1212        {create<ast::WorkgroupDecoration>(Source{{12, 34}}, Expr(1))});
1213 
1214   EXPECT_FALSE(r()->Resolve());
1215   EXPECT_EQ(r()->error(),
1216             "12:34 error: the workgroup_size attribute is only valid for "
1217             "compute stages");
1218 }
1219 
TEST_F(WorkgroupDecoration,NotAComputeShader)1220 TEST_F(WorkgroupDecoration, NotAComputeShader) {
1221   Func("main", {}, ty.void_(), {},
1222        {Stage(ast::PipelineStage::kFragment),
1223         create<ast::WorkgroupDecoration>(Source{{12, 34}}, Expr(1))});
1224 
1225   EXPECT_FALSE(r()->Resolve());
1226   EXPECT_EQ(r()->error(),
1227             "12:34 error: the workgroup_size attribute is only valid for "
1228             "compute stages");
1229 }
1230 
TEST_F(WorkgroupDecoration,DuplicateDecoration)1231 TEST_F(WorkgroupDecoration, DuplicateDecoration) {
1232   Func(Source{{12, 34}}, "main", {}, ty.void_(), {},
1233        {
1234            Stage(ast::PipelineStage::kCompute),
1235            WorkgroupSize(Source{{12, 34}}, 1, nullptr, nullptr),
1236            WorkgroupSize(Source{{56, 78}}, 2, nullptr, nullptr),
1237        });
1238 
1239   EXPECT_FALSE(r()->Resolve());
1240   EXPECT_EQ(r()->error(),
1241             R"(56:78 error: duplicate workgroup_size decoration
1242 12:34 note: first decoration declared here)");
1243 }
1244 
1245 }  // namespace
1246 }  // namespace WorkgroupDecorationTests
1247 
1248 namespace InterpolateTests {
1249 namespace {
1250 
1251 using InterpolateTest = ResolverTest;
1252 
1253 struct Params {
1254   ast::InterpolationType type;
1255   ast::InterpolationSampling sampling;
1256   bool should_pass;
1257 };
1258 
1259 struct TestWithParams : ResolverTestWithParam<Params> {};
1260 
1261 using InterpolateParameterTest = TestWithParams;
TEST_P(InterpolateParameterTest,All)1262 TEST_P(InterpolateParameterTest, All) {
1263   auto& params = GetParam();
1264 
1265   Func("main",
1266        ast::VariableList{Param(
1267            "a", ty.f32(),
1268            {Location(0),
1269             Interpolate(Source{{12, 34}}, params.type, params.sampling)})},
1270        ty.void_(), {},
1271        ast::DecorationList{Stage(ast::PipelineStage::kFragment)});
1272 
1273   if (params.should_pass) {
1274     EXPECT_TRUE(r()->Resolve()) << r()->error();
1275   } else {
1276     EXPECT_FALSE(r()->Resolve());
1277     EXPECT_EQ(r()->error(),
1278               "12:34 error: flat interpolation attribute must not have a "
1279               "sampling parameter");
1280   }
1281 }
1282 
TEST_P(InterpolateParameterTest,IntegerScalar)1283 TEST_P(InterpolateParameterTest, IntegerScalar) {
1284   auto& params = GetParam();
1285 
1286   Func("main",
1287        ast::VariableList{Param(
1288            "a", ty.i32(),
1289            {Location(0),
1290             Interpolate(Source{{12, 34}}, params.type, params.sampling)})},
1291        ty.void_(), {},
1292        ast::DecorationList{Stage(ast::PipelineStage::kFragment)});
1293 
1294   if (params.type != ast::InterpolationType::kFlat) {
1295     EXPECT_FALSE(r()->Resolve());
1296     EXPECT_EQ(r()->error(),
1297               "12:34 error: interpolation type must be 'flat' for integral "
1298               "user-defined IO types");
1299   } else if (params.should_pass) {
1300     EXPECT_TRUE(r()->Resolve()) << r()->error();
1301   } else {
1302     EXPECT_FALSE(r()->Resolve());
1303     EXPECT_EQ(r()->error(),
1304               "12:34 error: flat interpolation attribute must not have a "
1305               "sampling parameter");
1306   }
1307 }
1308 
TEST_P(InterpolateParameterTest,IntegerVector)1309 TEST_P(InterpolateParameterTest, IntegerVector) {
1310   auto& params = GetParam();
1311 
1312   Func("main",
1313        ast::VariableList{Param(
1314            "a", ty.vec4<u32>(),
1315            {Location(0),
1316             Interpolate(Source{{12, 34}}, params.type, params.sampling)})},
1317        ty.void_(), {},
1318        ast::DecorationList{Stage(ast::PipelineStage::kFragment)});
1319 
1320   if (params.type != ast::InterpolationType::kFlat) {
1321     EXPECT_FALSE(r()->Resolve());
1322     EXPECT_EQ(r()->error(),
1323               "12:34 error: interpolation type must be 'flat' for integral "
1324               "user-defined IO types");
1325   } else if (params.should_pass) {
1326     EXPECT_TRUE(r()->Resolve()) << r()->error();
1327   } else {
1328     EXPECT_FALSE(r()->Resolve());
1329     EXPECT_EQ(r()->error(),
1330               "12:34 error: flat interpolation attribute must not have a "
1331               "sampling parameter");
1332   }
1333 }
1334 
1335 INSTANTIATE_TEST_SUITE_P(
1336     ResolverDecorationValidationTest,
1337     InterpolateParameterTest,
1338     testing::Values(Params{ast::InterpolationType::kPerspective,
1339                            ast::InterpolationSampling::kNone, true},
1340                     Params{ast::InterpolationType::kPerspective,
1341                            ast::InterpolationSampling::kCenter, true},
1342                     Params{ast::InterpolationType::kPerspective,
1343                            ast::InterpolationSampling::kCentroid, true},
1344                     Params{ast::InterpolationType::kPerspective,
1345                            ast::InterpolationSampling::kSample, true},
1346                     Params{ast::InterpolationType::kLinear,
1347                            ast::InterpolationSampling::kNone, true},
1348                     Params{ast::InterpolationType::kLinear,
1349                            ast::InterpolationSampling::kCenter, true},
1350                     Params{ast::InterpolationType::kLinear,
1351                            ast::InterpolationSampling::kCentroid, true},
1352                     Params{ast::InterpolationType::kLinear,
1353                            ast::InterpolationSampling::kSample, true},
1354                     // flat interpolation must not have a sampling type
1355                     Params{ast::InterpolationType::kFlat,
1356                            ast::InterpolationSampling::kNone, true},
1357                     Params{ast::InterpolationType::kFlat,
1358                            ast::InterpolationSampling::kCenter, false},
1359                     Params{ast::InterpolationType::kFlat,
1360                            ast::InterpolationSampling::kCentroid, false},
1361                     Params{ast::InterpolationType::kFlat,
1362                            ast::InterpolationSampling::kSample, false}));
1363 
TEST_F(InterpolateTest,FragmentInput_Integer_MissingFlatInterpolation)1364 TEST_F(InterpolateTest, FragmentInput_Integer_MissingFlatInterpolation) {
1365   Func("main",
1366        ast::VariableList{Param(Source{{12, 34}}, "a", ty.i32(), {Location(0)})},
1367        ty.void_(), {},
1368        ast::DecorationList{Stage(ast::PipelineStage::kFragment)});
1369 
1370   // TODO(crbug.com/tint/1224): Make this an error.
1371   EXPECT_TRUE(r()->Resolve());
1372   EXPECT_EQ(r()->error(),
1373             "12:34 warning: integral user-defined fragment inputs must have a "
1374             "flat interpolation attribute");
1375 }
1376 
TEST_F(InterpolateTest,VertexOutput_Integer_MissingFlatInterpolation)1377 TEST_F(InterpolateTest, VertexOutput_Integer_MissingFlatInterpolation) {
1378   auto* s = Structure(
1379       "S",
1380       {
1381           Member("pos", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)}),
1382           Member(Source{{12, 34}}, "u", ty.u32(), {Location(0)}),
1383       },
1384       {});
1385   Func("main", {}, ty.Of(s), {Return(Construct(ty.Of(s)))},
1386        ast::DecorationList{Stage(ast::PipelineStage::kVertex)});
1387 
1388   // TODO(crbug.com/tint/1224): Make this an error.
1389   EXPECT_TRUE(r()->Resolve());
1390   EXPECT_EQ(r()->error(),
1391             "12:34 warning: integral user-defined vertex outputs must have a "
1392             "flat interpolation attribute");
1393 }
1394 
TEST_F(InterpolateTest,MissingLocationAttribute_Parameter)1395 TEST_F(InterpolateTest, MissingLocationAttribute_Parameter) {
1396   Func("main",
1397        ast::VariableList{
1398            Param("a", ty.vec4<f32>(),
1399                  {Builtin(ast::Builtin::kPosition),
1400                   Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
1401                               ast::InterpolationSampling::kNone)})},
1402        ty.void_(), {},
1403        ast::DecorationList{Stage(ast::PipelineStage::kFragment)});
1404 
1405   EXPECT_FALSE(r()->Resolve());
1406   EXPECT_EQ(r()->error(),
1407             "12:34 error: interpolate attribute must only be used with "
1408             "[[location]]");
1409 }
1410 
TEST_F(InterpolateTest,MissingLocationAttribute_ReturnType)1411 TEST_F(InterpolateTest, MissingLocationAttribute_ReturnType) {
1412   Func("main", {}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
1413        ast::DecorationList{Stage(ast::PipelineStage::kVertex)},
1414        {Builtin(ast::Builtin::kPosition),
1415         Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
1416                     ast::InterpolationSampling::kNone)});
1417 
1418   EXPECT_FALSE(r()->Resolve());
1419   EXPECT_EQ(r()->error(),
1420             "12:34 error: interpolate attribute must only be used with "
1421             "[[location]]");
1422 }
1423 
TEST_F(InterpolateTest,MissingLocationAttribute_Struct)1424 TEST_F(InterpolateTest, MissingLocationAttribute_Struct) {
1425   Structure(
1426       "S", {Member("a", ty.f32(),
1427                    {Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
1428                                 ast::InterpolationSampling::kNone)})});
1429 
1430   EXPECT_FALSE(r()->Resolve());
1431   EXPECT_EQ(r()->error(),
1432             "12:34 error: interpolate attribute must only be used with "
1433             "[[location]]");
1434 }
1435 
1436 }  // namespace
1437 }  // namespace InterpolateTests
1438 
1439 }  // namespace resolver
1440 }  // namespace tint
1441