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