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/builtin_decoration.h"
16 #include "src/ast/location_decoration.h"
17 #include "src/ast/return_statement.h"
18 #include "src/ast/stage_decoration.h"
19 #include "src/ast/struct_block_decoration.h"
20 #include "src/resolver/resolver.h"
21 #include "src/resolver/resolver_test_helper.h"
22
23 #include "gmock/gmock.h"
24
25 namespace tint {
26 namespace resolver {
27 namespace {
28
29 // Helpers and typedefs
30 template <typename T>
31 using DataType = builder::DataType<T>;
32 template <typename T>
33 using vec2 = builder::vec2<T>;
34 template <typename T>
35 using vec3 = builder::vec3<T>;
36 template <typename T>
37 using vec4 = builder::vec4<T>;
38 template <typename T>
39 using mat2x2 = builder::mat2x2<T>;
40 template <typename T>
41 using mat3x3 = builder::mat3x3<T>;
42 template <typename T>
43 using mat4x4 = builder::mat4x4<T>;
44 template <typename T>
45 using alias = builder::alias<T>;
46 using f32 = builder::f32;
47 using i32 = builder::i32;
48 using u32 = builder::u32;
49
50 class ResolverEntryPointValidationTest : public TestHelper,
51 public testing::Test {};
52
TEST_F(ResolverEntryPointValidationTest,ReturnTypeAttribute_Location)53 TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Location) {
54 // [[stage(fragment)]]
55 // fn main() -> [[location(0)]] f32 { return 1.0; }
56 Func(Source{{12, 34}}, "main", {}, ty.f32(), {Return(1.0f)},
57 {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
58
59 EXPECT_TRUE(r()->Resolve()) << r()->error();
60 }
61
TEST_F(ResolverEntryPointValidationTest,ReturnTypeAttribute_Builtin)62 TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Builtin) {
63 // [[stage(vertex)]]
64 // fn main() -> [[builtin(position)]] vec4<f32> { return vec4<f32>(); }
65 Func(Source{{12, 34}}, "main", {}, ty.vec4<f32>(),
66 {Return(Construct(ty.vec4<f32>()))},
67 {Stage(ast::PipelineStage::kVertex)},
68 {Builtin(ast::Builtin::kPosition)});
69
70 EXPECT_TRUE(r()->Resolve()) << r()->error();
71 }
72
TEST_F(ResolverEntryPointValidationTest,ReturnTypeAttribute_Missing)73 TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Missing) {
74 // [[stage(vertex)]]
75 // fn main() -> f32 {
76 // return 1.0;
77 // }
78 Func(Source{{12, 34}}, "main", {}, ty.vec4<f32>(),
79 {Return(Construct(ty.vec4<f32>()))},
80 {Stage(ast::PipelineStage::kVertex)});
81
82 EXPECT_FALSE(r()->Resolve());
83 EXPECT_EQ(r()->error(),
84 "12:34 error: missing entry point IO attribute on return type");
85 }
86
TEST_F(ResolverEntryPointValidationTest,ReturnTypeAttribute_Multiple)87 TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Multiple) {
88 // [[stage(vertex)]]
89 // fn main() -> [[location(0)]] [[builtin(position)]] vec4<f32> {
90 // return vec4<f32>();
91 // }
92 Func(Source{{12, 34}}, "main", {}, ty.vec4<f32>(),
93 {Return(Construct(ty.vec4<f32>()))},
94 {Stage(ast::PipelineStage::kVertex)},
95 {Location(Source{{13, 43}}, 0),
96 Builtin(Source{{14, 52}}, ast::Builtin::kPosition)});
97
98 EXPECT_FALSE(r()->Resolve());
99 EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
100 13:43 note: previously consumed location(0))");
101 }
102
TEST_F(ResolverEntryPointValidationTest,ReturnType_Struct_Valid)103 TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_Valid) {
104 // struct Output {
105 // [[location(0)]] a : f32;
106 // [[builtin(frag_depth)]] b : f32;
107 // };
108 // [[stage(fragment)]]
109 // fn main() -> Output {
110 // return Output();
111 // }
112 auto* output = Structure(
113 "Output", {Member("a", ty.f32(), {Location(0)}),
114 Member("b", ty.f32(), {Builtin(ast::Builtin::kFragDepth)})});
115 Func(Source{{12, 34}}, "main", {}, ty.Of(output),
116 {Return(Construct(ty.Of(output)))},
117 {Stage(ast::PipelineStage::kFragment)});
118
119 EXPECT_TRUE(r()->Resolve()) << r()->error();
120 }
121
TEST_F(ResolverEntryPointValidationTest,ReturnType_Struct_MemberMultipleAttributes)122 TEST_F(ResolverEntryPointValidationTest,
123 ReturnType_Struct_MemberMultipleAttributes) {
124 // struct Output {
125 // [[location(0)]] [[builtin(frag_depth)]] a : f32;
126 // };
127 // [[stage(fragment)]]
128 // fn main() -> Output {
129 // return Output();
130 // }
131 auto* output = Structure(
132 "Output",
133 {Member("a", ty.f32(),
134 {Location(Source{{13, 43}}, 0),
135 Builtin(Source{{14, 52}}, ast::Builtin::kFragDepth)})});
136 Func(Source{{12, 34}}, "main", {}, ty.Of(output),
137 {Return(Construct(ty.Of(output)))},
138 {Stage(ast::PipelineStage::kFragment)});
139
140 EXPECT_FALSE(r()->Resolve());
141 EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
142 13:43 note: previously consumed location(0)
143 12:34 note: while analysing entry point 'main')");
144 }
145
TEST_F(ResolverEntryPointValidationTest,ReturnType_Struct_MemberMissingAttribute)146 TEST_F(ResolverEntryPointValidationTest,
147 ReturnType_Struct_MemberMissingAttribute) {
148 // struct Output {
149 // [[location(0)]] a : f32;
150 // b : f32;
151 // };
152 // [[stage(fragment)]]
153 // fn main() -> Output {
154 // return Output();
155 // }
156 auto* output = Structure(
157 "Output", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)}),
158 Member(Source{{14, 52}}, "b", ty.f32(), {})});
159 Func(Source{{12, 34}}, "main", {}, ty.Of(output),
160 {Return(Construct(ty.Of(output)))},
161 {Stage(ast::PipelineStage::kFragment)});
162
163 EXPECT_FALSE(r()->Resolve());
164 EXPECT_EQ(r()->error(),
165 R"(14:52 error: missing entry point IO attribute
166 12:34 note: while analysing entry point 'main')");
167 }
168
TEST_F(ResolverEntryPointValidationTest,ReturnType_Struct_DuplicateBuiltins)169 TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_DuplicateBuiltins) {
170 // struct Output {
171 // [[builtin(frag_depth)]] a : f32;
172 // [[builtin(frag_depth)]] b : f32;
173 // };
174 // [[stage(fragment)]]
175 // fn main() -> Output {
176 // return Output();
177 // }
178 auto* output = Structure(
179 "Output", {Member("a", ty.f32(), {Builtin(ast::Builtin::kFragDepth)}),
180 Member("b", ty.f32(), {Builtin(ast::Builtin::kFragDepth)})});
181 Func(Source{{12, 34}}, "main", {}, ty.Of(output),
182 {Return(Construct(ty.Of(output)))},
183 {Stage(ast::PipelineStage::kFragment)});
184
185 EXPECT_FALSE(r()->Resolve());
186 EXPECT_EQ(
187 r()->error(),
188 R"(12:34 error: builtin(frag_depth) attribute appears multiple times as pipeline output
189 12:34 note: while analysing entry point 'main')");
190 }
191
TEST_F(ResolverEntryPointValidationTest,ParameterAttribute_Location)192 TEST_F(ResolverEntryPointValidationTest, ParameterAttribute_Location) {
193 // [[stage(fragment)]]
194 // fn main([[location(0)]] param : f32) {}
195 auto* param = Param("param", ty.f32(), {Location(0)});
196 Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
197 {Stage(ast::PipelineStage::kFragment)});
198
199 EXPECT_TRUE(r()->Resolve()) << r()->error();
200 }
201
TEST_F(ResolverEntryPointValidationTest,ParameterAttribute_Missing)202 TEST_F(ResolverEntryPointValidationTest, ParameterAttribute_Missing) {
203 // [[stage(fragment)]]
204 // fn main(param : f32) {}
205 auto* param = Param(Source{{13, 43}}, "param", ty.vec4<f32>());
206 Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
207 {Stage(ast::PipelineStage::kFragment)});
208
209 EXPECT_FALSE(r()->Resolve());
210 EXPECT_EQ(r()->error(),
211 "13:43 error: missing entry point IO attribute on parameter");
212 }
213
TEST_F(ResolverEntryPointValidationTest,ParameterAttribute_Multiple)214 TEST_F(ResolverEntryPointValidationTest, ParameterAttribute_Multiple) {
215 // [[stage(fragment)]]
216 // fn main([[location(0)]] [[builtin(sample_index)]] param : u32) {}
217 auto* param = Param("param", ty.u32(),
218 {Location(Source{{13, 43}}, 0),
219 Builtin(Source{{14, 52}}, ast::Builtin::kSampleIndex)});
220 Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
221 {Stage(ast::PipelineStage::kFragment)});
222
223 EXPECT_FALSE(r()->Resolve());
224 EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
225 13:43 note: previously consumed location(0))");
226 }
227
TEST_F(ResolverEntryPointValidationTest,Parameter_Struct_Valid)228 TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_Valid) {
229 // struct Input {
230 // [[location(0)]] a : f32;
231 // [[builtin(sample_index)]] b : u32;
232 // };
233 // [[stage(fragment)]]
234 // fn main(param : Input) {}
235 auto* input = Structure(
236 "Input", {Member("a", ty.f32(), {Location(0)}),
237 Member("b", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)})});
238 auto* param = Param("param", ty.Of(input));
239 Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
240 {Stage(ast::PipelineStage::kFragment)});
241
242 EXPECT_TRUE(r()->Resolve()) << r()->error();
243 }
244
TEST_F(ResolverEntryPointValidationTest,Parameter_Struct_MemberMultipleAttributes)245 TEST_F(ResolverEntryPointValidationTest,
246 Parameter_Struct_MemberMultipleAttributes) {
247 // struct Input {
248 // [[location(0)]] [[builtin(sample_index)]] a : u32;
249 // };
250 // [[stage(fragment)]]
251 // fn main(param : Input) {}
252 auto* input = Structure(
253 "Input",
254 {Member("a", ty.u32(),
255 {Location(Source{{13, 43}}, 0),
256 Builtin(Source{{14, 52}}, ast::Builtin::kSampleIndex)})});
257 auto* param = Param("param", ty.Of(input));
258 Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
259 {Stage(ast::PipelineStage::kFragment)});
260
261 EXPECT_FALSE(r()->Resolve());
262 EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
263 13:43 note: previously consumed location(0)
264 12:34 note: while analysing entry point 'main')");
265 }
266
TEST_F(ResolverEntryPointValidationTest,Parameter_Struct_MemberMissingAttribute)267 TEST_F(ResolverEntryPointValidationTest,
268 Parameter_Struct_MemberMissingAttribute) {
269 // struct Input {
270 // [[location(0)]] a : f32;
271 // b : f32;
272 // };
273 // [[stage(fragment)]]
274 // fn main(param : Input) {}
275 auto* input = Structure(
276 "Input", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)}),
277 Member(Source{{14, 52}}, "b", ty.f32(), {})});
278 auto* param = Param("param", ty.Of(input));
279 Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
280 {Stage(ast::PipelineStage::kFragment)});
281
282 EXPECT_FALSE(r()->Resolve());
283 EXPECT_EQ(r()->error(), R"(14:52 error: missing entry point IO attribute
284 12:34 note: while analysing entry point 'main')");
285 }
286
TEST_F(ResolverEntryPointValidationTest,Parameter_DuplicateBuiltins)287 TEST_F(ResolverEntryPointValidationTest, Parameter_DuplicateBuiltins) {
288 // [[stage(fragment)]]
289 // fn main([[builtin(sample_index)]] param_a : u32,
290 // [[builtin(sample_index)]] param_b : u32) {}
291 auto* param_a =
292 Param("param_a", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)});
293 auto* param_b =
294 Param("param_b", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)});
295 Func(Source{{12, 34}}, "main", {param_a, param_b}, ty.void_(), {},
296 {Stage(ast::PipelineStage::kFragment)});
297
298 EXPECT_FALSE(r()->Resolve());
299 EXPECT_EQ(
300 r()->error(),
301 "12:34 error: builtin(sample_index) attribute appears multiple times as "
302 "pipeline input");
303 }
304
TEST_F(ResolverEntryPointValidationTest,Parameter_Struct_DuplicateBuiltins)305 TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_DuplicateBuiltins) {
306 // struct InputA {
307 // [[builtin(sample_index)]] a : u32;
308 // };
309 // struct InputB {
310 // [[builtin(sample_index)]] a : u32;
311 // };
312 // [[stage(fragment)]]
313 // fn main(param_a : InputA, param_b : InputB) {}
314 auto* input_a = Structure(
315 "InputA", {Member("a", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)})});
316 auto* input_b = Structure(
317 "InputB", {Member("a", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)})});
318 auto* param_a = Param("param_a", ty.Of(input_a));
319 auto* param_b = Param("param_b", ty.Of(input_b));
320 Func(Source{{12, 34}}, "main", {param_a, param_b}, ty.void_(), {},
321 {Stage(ast::PipelineStage::kFragment)});
322
323 EXPECT_FALSE(r()->Resolve());
324 EXPECT_EQ(
325 r()->error(),
326 R"(12:34 error: builtin(sample_index) attribute appears multiple times as pipeline input
327 12:34 note: while analysing entry point 'main')");
328 }
329
TEST_F(ResolverEntryPointValidationTest,VertexShaderMustReturnPosition)330 TEST_F(ResolverEntryPointValidationTest, VertexShaderMustReturnPosition) {
331 // [[stage(vertex)]]
332 // fn main() {}
333 Func(Source{{12, 34}}, "main", {}, ty.void_(), {},
334 {Stage(ast::PipelineStage::kVertex)});
335
336 EXPECT_FALSE(r()->Resolve());
337 EXPECT_EQ(r()->error(),
338 "12:34 error: a vertex shader must include the 'position' builtin "
339 "in its return type");
340 }
341
342 namespace TypeValidationTests {
343 struct Params {
344 builder::ast_type_func_ptr create_ast_type;
345 bool is_valid;
346 };
347
348 template <typename T>
ParamsFor(bool is_valid)349 constexpr Params ParamsFor(bool is_valid) {
350 return Params{DataType<T>::AST, is_valid};
351 }
352
353 using TypeValidationTest = resolver::ResolverTestWithParam<Params>;
354
355 static constexpr Params cases[] = {
356 ParamsFor<f32>(true), //
357 ParamsFor<i32>(true), //
358 ParamsFor<u32>(true), //
359 ParamsFor<bool>(false), //
360 ParamsFor<vec2<f32>>(true), //
361 ParamsFor<vec3<f32>>(true), //
362 ParamsFor<vec4<f32>>(true), //
363 ParamsFor<mat2x2<f32>>(false), //
364 ParamsFor<mat3x3<f32>>(false), //
365 ParamsFor<mat4x4<f32>>(false), //
366 ParamsFor<alias<f32>>(true), //
367 ParamsFor<alias<i32>>(true), //
368 ParamsFor<alias<u32>>(true), //
369 ParamsFor<alias<bool>>(false), //
370 };
371
TEST_P(TypeValidationTest,BareInputs)372 TEST_P(TypeValidationTest, BareInputs) {
373 // [[stage(fragment)]]
374 // fn main([[location(0)]] a : *) {}
375 auto params = GetParam();
376 auto* a = Param("a", params.create_ast_type(*this), {Location(0)});
377 Func(Source{{12, 34}}, "main", {a}, ty.void_(), {},
378 {Stage(ast::PipelineStage::kFragment)});
379
380 if (params.is_valid) {
381 EXPECT_TRUE(r()->Resolve()) << r()->error();
382 } else {
383 EXPECT_FALSE(r()->Resolve());
384 }
385 }
386
TEST_P(TypeValidationTest,StructInputs)387 TEST_P(TypeValidationTest, StructInputs) {
388 // struct Input {
389 // [[location(0)]] a : *;
390 // };
391 // [[stage(fragment)]]
392 // fn main(a : Input) {}
393 auto params = GetParam();
394 auto* input = Structure(
395 "Input", {Member("a", params.create_ast_type(*this), {Location(0)})});
396 auto* a = Param("a", ty.Of(input), {});
397 Func(Source{{12, 34}}, "main", {a}, ty.void_(), {},
398 {Stage(ast::PipelineStage::kFragment)});
399
400 if (params.is_valid) {
401 EXPECT_TRUE(r()->Resolve()) << r()->error();
402 } else {
403 EXPECT_FALSE(r()->Resolve());
404 }
405 }
406
TEST_P(TypeValidationTest,BareOutputs)407 TEST_P(TypeValidationTest, BareOutputs) {
408 // [[stage(fragment)]]
409 // fn main() -> [[location(0)]] * {
410 // return *();
411 // }
412 auto params = GetParam();
413 Func(Source{{12, 34}}, "main", {}, params.create_ast_type(*this),
414 {Return(Construct(params.create_ast_type(*this)))},
415 {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
416
417 if (params.is_valid) {
418 EXPECT_TRUE(r()->Resolve()) << r()->error();
419 } else {
420 EXPECT_FALSE(r()->Resolve());
421 }
422 }
423
TEST_P(TypeValidationTest,StructOutputs)424 TEST_P(TypeValidationTest, StructOutputs) {
425 // struct Output {
426 // [[location(0)]] a : *;
427 // };
428 // [[stage(fragment)]]
429 // fn main() -> Output {
430 // return Output();
431 // }
432 auto params = GetParam();
433 auto* output = Structure(
434 "Output", {Member("a", params.create_ast_type(*this), {Location(0)})});
435 Func(Source{{12, 34}}, "main", {}, ty.Of(output),
436 {Return(Construct(ty.Of(output)))},
437 {Stage(ast::PipelineStage::kFragment)});
438
439 if (params.is_valid) {
440 EXPECT_TRUE(r()->Resolve()) << r()->error();
441 } else {
442 EXPECT_FALSE(r()->Resolve());
443 }
444 }
445 INSTANTIATE_TEST_SUITE_P(ResolverEntryPointValidationTest,
446 TypeValidationTest,
447 testing::ValuesIn(cases));
448
449 } // namespace TypeValidationTests
450
451 namespace LocationDecorationTests {
452 namespace {
453 using LocationDecorationTests = ResolverTest;
454
TEST_F(LocationDecorationTests,Pass)455 TEST_F(LocationDecorationTests, Pass) {
456 // [[stage(fragment)]]
457 // fn frag_main([[location(0)]] a: i32) {}
458
459 auto* p = Param(Source{{12, 34}}, "a", ty.i32(), {Location(0)});
460 Func("frag_main", {p}, ty.void_(), {},
461 {Stage(ast::PipelineStage::kFragment)});
462
463 EXPECT_TRUE(r()->Resolve()) << r()->error();
464 }
465
TEST_F(LocationDecorationTests,BadType_Input_bool)466 TEST_F(LocationDecorationTests, BadType_Input_bool) {
467 // [[stage(fragment)]]
468 // fn frag_main([[location(0)]] a: bool) {}
469
470 auto* p =
471 Param(Source{{12, 34}}, "a", ty.bool_(), {Location(Source{{34, 56}}, 0)});
472 Func("frag_main", {p}, ty.void_(), {},
473 {Stage(ast::PipelineStage::kFragment)});
474
475 EXPECT_FALSE(r()->Resolve());
476 EXPECT_EQ(r()->error(),
477 "12:34 error: cannot apply 'location' attribute to declaration of "
478 "type 'bool'\n"
479 "34:56 note: 'location' attribute must only be applied to "
480 "declarations of numeric scalar or numeric vector type");
481 }
482
TEST_F(LocationDecorationTests,BadType_Output_Array)483 TEST_F(LocationDecorationTests, BadType_Output_Array) {
484 // [[stage(fragment)]]
485 // fn frag_main()->[[location(0)]] array<f32, 2> { return array<f32, 2>(); }
486
487 Func(Source{{12, 34}}, "frag_main", {}, ty.array<f32, 2>(),
488 {Return(Construct(ty.array<f32, 2>()))},
489 {Stage(ast::PipelineStage::kFragment)}, {Location(Source{{34, 56}}, 0)});
490
491 EXPECT_FALSE(r()->Resolve());
492 EXPECT_EQ(r()->error(),
493 "12:34 error: cannot apply 'location' attribute to declaration of "
494 "type 'array<f32, 2>'\n"
495 "34:56 note: 'location' attribute must only be applied to "
496 "declarations of numeric scalar or numeric vector type");
497 }
498
TEST_F(LocationDecorationTests,BadType_Input_Struct)499 TEST_F(LocationDecorationTests, BadType_Input_Struct) {
500 // struct Input {
501 // a : f32;
502 // };
503 // [[stage(fragment)]]
504 // fn main([[location(0)]] param : Input) {}
505 auto* input = Structure("Input", {Member("a", ty.f32())});
506 auto* param = Param(Source{{12, 34}}, "param", ty.Of(input),
507 {Location(Source{{13, 43}}, 0)});
508 Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
509 {Stage(ast::PipelineStage::kFragment)});
510
511 EXPECT_FALSE(r()->Resolve());
512 EXPECT_EQ(r()->error(),
513 "12:34 error: cannot apply 'location' attribute to declaration of "
514 "type 'Input'\n"
515 "13:43 note: 'location' attribute must only be applied to "
516 "declarations of numeric scalar or numeric vector type");
517 }
518
TEST_F(LocationDecorationTests,BadType_Input_Struct_NestedStruct)519 TEST_F(LocationDecorationTests, BadType_Input_Struct_NestedStruct) {
520 // struct Inner {
521 // [[location(0)]] b : f32;
522 // };
523 // struct Input {
524 // a : Inner;
525 // };
526 // [[stage(fragment)]]
527 // fn main(param : Input) {}
528 auto* inner = Structure(
529 "Inner", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)})});
530 auto* input =
531 Structure("Input", {Member(Source{{14, 52}}, "a", ty.Of(inner))});
532 auto* param = Param("param", ty.Of(input));
533 Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
534 {Stage(ast::PipelineStage::kFragment)});
535
536 EXPECT_FALSE(r()->Resolve());
537 EXPECT_EQ(r()->error(),
538 "14:52 error: nested structures cannot be used for entry point IO\n"
539 "12:34 note: while analysing entry point 'main'");
540 }
541
TEST_F(LocationDecorationTests,BadType_Input_Struct_RuntimeArray)542 TEST_F(LocationDecorationTests, BadType_Input_Struct_RuntimeArray) {
543 // [[block]]
544 // struct Input {
545 // [[location(0)]] a : array<f32>;
546 // };
547 // [[stage(fragment)]]
548 // fn main(param : Input) {}
549 auto* input = Structure(
550 "Input",
551 {Member(Source{{13, 43}}, "a", ty.array<float>(), {Location(0)})},
552 {create<ast::StructBlockDecoration>()});
553 auto* param = Param("param", ty.Of(input));
554 Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
555 {Stage(ast::PipelineStage::kFragment)});
556
557 EXPECT_FALSE(r()->Resolve());
558 EXPECT_EQ(r()->error(),
559 "13:43 error: cannot apply 'location' attribute to declaration of "
560 "type 'array<f32>'\n"
561 "note: 'location' attribute must only be applied to declarations "
562 "of numeric scalar or numeric vector type");
563 }
564
TEST_F(LocationDecorationTests,BadMemberType_Input)565 TEST_F(LocationDecorationTests, BadMemberType_Input) {
566 // [[block]]
567 // struct S { [[location(0)]] m: array<i32>; };
568 // [[stage(fragment)]]
569 // fn frag_main( a: S) {}
570
571 auto* m = Member(Source{{34, 56}}, "m", ty.array<i32>(),
572 ast::DecorationList{Location(Source{{12, 34}}, 0u)});
573 auto* s = Structure("S", {m}, ast::DecorationList{StructBlock()});
574 auto* p = Param("a", ty.Of(s));
575
576 Func("frag_main", {p}, ty.void_(), {},
577 {Stage(ast::PipelineStage::kFragment)});
578
579 EXPECT_FALSE(r()->Resolve());
580 EXPECT_EQ(r()->error(),
581 "34:56 error: cannot apply 'location' attribute to declaration of "
582 "type 'array<i32>'\n"
583 "12:34 note: 'location' attribute must only be applied to "
584 "declarations of numeric scalar or numeric vector type");
585 }
586
TEST_F(LocationDecorationTests,BadMemberType_Output)587 TEST_F(LocationDecorationTests, BadMemberType_Output) {
588 // struct S { [[location(0)]] m: atomic<i32>; };
589 // [[stage(fragment)]]
590 // fn frag_main() -> S {}
591 auto* m = Member(Source{{34, 56}}, "m", ty.atomic<i32>(),
592 ast::DecorationList{Location(Source{{12, 34}}, 0u)});
593 auto* s = Structure("S", {m});
594
595 Func("frag_main", {}, ty.Of(s), {Return(Construct(ty.Of(s)))},
596 {Stage(ast::PipelineStage::kFragment)}, {});
597
598 EXPECT_FALSE(r()->Resolve());
599 EXPECT_EQ(r()->error(),
600 "34:56 error: cannot apply 'location' attribute to declaration of "
601 "type 'atomic<i32>'\n"
602 "12:34 note: 'location' attribute must only be applied to "
603 "declarations of numeric scalar or numeric vector type");
604 }
605
TEST_F(LocationDecorationTests,BadMemberType_Unused)606 TEST_F(LocationDecorationTests, BadMemberType_Unused) {
607 // struct S { [[location(0)]] m: mat3x2<f32>; };
608
609 auto* m = Member(Source{{34, 56}}, "m", ty.mat3x2<f32>(),
610 ast::DecorationList{Location(Source{{12, 34}}, 0u)});
611 Structure("S", {m});
612
613 EXPECT_FALSE(r()->Resolve());
614 EXPECT_EQ(r()->error(),
615 "34:56 error: cannot apply 'location' attribute to declaration of "
616 "type 'mat3x2<f32>'\n"
617 "12:34 note: 'location' attribute must only be applied to "
618 "declarations of numeric scalar or numeric vector type");
619 }
620
TEST_F(LocationDecorationTests,ReturnType_Struct_Valid)621 TEST_F(LocationDecorationTests, ReturnType_Struct_Valid) {
622 // struct Output {
623 // [[location(0)]] a : f32;
624 // [[builtin(frag_depth)]] b : f32;
625 // };
626 // [[stage(fragment)]]
627 // fn main() -> Output {
628 // return Output();
629 // }
630 auto* output = Structure(
631 "Output", {Member("a", ty.f32(), {Location(0)}),
632 Member("b", ty.f32(), {Builtin(ast::Builtin::kFragDepth)})});
633 Func(Source{{12, 34}}, "main", {}, ty.Of(output),
634 {Return(Construct(ty.Of(output)))},
635 {Stage(ast::PipelineStage::kFragment)});
636
637 EXPECT_TRUE(r()->Resolve()) << r()->error();
638 }
639
TEST_F(LocationDecorationTests,ReturnType_Struct)640 TEST_F(LocationDecorationTests, ReturnType_Struct) {
641 // struct Output {
642 // a : f32;
643 // };
644 // [[stage(vertex)]]
645 // fn main() -> [[location(0)]] Output {
646 // return Output();
647 // }
648 auto* output = Structure("Output", {Member("a", ty.f32())});
649 Func(Source{{12, 34}}, "main", {}, ty.Of(output),
650 {Return(Construct(ty.Of(output)))}, {Stage(ast::PipelineStage::kVertex)},
651 {Location(Source{{13, 43}}, 0)});
652
653 EXPECT_FALSE(r()->Resolve());
654 EXPECT_EQ(r()->error(),
655 "12:34 error: cannot apply 'location' attribute to declaration of "
656 "type 'Output'\n"
657 "13:43 note: 'location' attribute must only be applied to "
658 "declarations of numeric scalar or numeric vector type");
659 }
660
TEST_F(LocationDecorationTests,ReturnType_Struct_NestedStruct)661 TEST_F(LocationDecorationTests, ReturnType_Struct_NestedStruct) {
662 // struct Inner {
663 // [[location(0)]] b : f32;
664 // };
665 // struct Output {
666 // a : Inner;
667 // };
668 // [[stage(fragment)]]
669 // fn main() -> Output { return Output(); }
670 auto* inner = Structure(
671 "Inner", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)})});
672 auto* output =
673 Structure("Output", {Member(Source{{14, 52}}, "a", ty.Of(inner))});
674 Func(Source{{12, 34}}, "main", {}, ty.Of(output),
675 {Return(Construct(ty.Of(output)))},
676 {Stage(ast::PipelineStage::kFragment)});
677
678 EXPECT_FALSE(r()->Resolve());
679 EXPECT_EQ(r()->error(),
680 "14:52 error: nested structures cannot be used for entry point IO\n"
681 "12:34 note: while analysing entry point 'main'");
682 }
683
TEST_F(LocationDecorationTests,ReturnType_Struct_RuntimeArray)684 TEST_F(LocationDecorationTests, ReturnType_Struct_RuntimeArray) {
685 // [[block]]
686 // struct Output {
687 // [[location(0)]] a : array<f32>;
688 // };
689 // [[stage(fragment)]]
690 // fn main() -> Output {
691 // return Output();
692 // }
693 auto* output = Structure("Output",
694 {Member(Source{{13, 43}}, "a", ty.array<float>(),
695 {Location(Source{{12, 34}}, 0)})},
696 {create<ast::StructBlockDecoration>()});
697 Func(Source{{12, 34}}, "main", {}, ty.Of(output),
698 {Return(Construct(ty.Of(output)))},
699 {Stage(ast::PipelineStage::kFragment)});
700
701 EXPECT_FALSE(r()->Resolve());
702 EXPECT_EQ(r()->error(),
703 "13:43 error: cannot apply 'location' attribute to declaration of "
704 "type 'array<f32>'\n"
705 "12:34 note: 'location' attribute must only be applied to "
706 "declarations of numeric scalar or numeric vector type");
707 }
708
TEST_F(LocationDecorationTests,ComputeShaderLocation_Input)709 TEST_F(LocationDecorationTests, ComputeShaderLocation_Input) {
710 Func("main", {}, ty.i32(), {Return(Expr(1))},
711 {Stage(ast::PipelineStage::kCompute),
712 create<ast::WorkgroupDecoration>(Source{{12, 34}}, Expr(1))},
713 ast::DecorationList{Location(Source{{12, 34}}, 1)});
714
715 EXPECT_FALSE(r()->Resolve());
716 EXPECT_EQ(r()->error(),
717 "12:34 error: decoration is not valid for compute shader output");
718 }
719
TEST_F(LocationDecorationTests,ComputeShaderLocation_Output)720 TEST_F(LocationDecorationTests, ComputeShaderLocation_Output) {
721 auto* input = Param("input", ty.i32(),
722 ast::DecorationList{Location(Source{{12, 34}}, 0u)});
723 Func("main", {input}, ty.void_(), {},
724 {Stage(ast::PipelineStage::kCompute),
725 create<ast::WorkgroupDecoration>(Source{{12, 34}}, Expr(1))});
726
727 EXPECT_FALSE(r()->Resolve());
728 EXPECT_EQ(r()->error(),
729 "12:34 error: decoration is not valid for compute shader inputs");
730 }
731
TEST_F(LocationDecorationTests,ComputeShaderLocationStructMember_Output)732 TEST_F(LocationDecorationTests, ComputeShaderLocationStructMember_Output) {
733 auto* m = Member("m", ty.i32(),
734 ast::DecorationList{Location(Source{{12, 34}}, 0u)});
735 auto* s = Structure("S", {m});
736 Func(Source{{56, 78}}, "main", {}, ty.Of(s),
737 ast::StatementList{Return(Expr(Construct(ty.Of(s))))},
738 {Stage(ast::PipelineStage::kCompute),
739 create<ast::WorkgroupDecoration>(Source{{12, 34}}, Expr(1))});
740
741 EXPECT_FALSE(r()->Resolve());
742 EXPECT_EQ(r()->error(),
743 "12:34 error: decoration is not valid for compute shader output\n"
744 "56:78 note: while analysing entry point 'main'");
745 }
746
TEST_F(LocationDecorationTests,ComputeShaderLocationStructMember_Input)747 TEST_F(LocationDecorationTests, ComputeShaderLocationStructMember_Input) {
748 auto* m = Member("m", ty.i32(),
749 ast::DecorationList{Location(Source{{12, 34}}, 0u)});
750 auto* s = Structure("S", {m});
751 auto* input = Param("input", ty.Of(s));
752 Func(Source{{56, 78}}, "main", {input}, ty.void_(), {},
753 {Stage(ast::PipelineStage::kCompute),
754 create<ast::WorkgroupDecoration>(Source{{12, 34}}, Expr(1))});
755
756 EXPECT_FALSE(r()->Resolve());
757 EXPECT_EQ(r()->error(),
758 "12:34 error: decoration is not valid for compute shader inputs\n"
759 "56:78 note: while analysing entry point 'main'");
760 }
761
TEST_F(LocationDecorationTests,Duplicate_input)762 TEST_F(LocationDecorationTests, Duplicate_input) {
763 // [[stage(fragment)]]
764 // fn main([[location(1)]] param_a : f32,
765 // [[location(1)]] param_b : f32) {}
766 auto* param_a = Param("param_a", ty.f32(), {Location(1)});
767 auto* param_b = Param("param_b", ty.f32(), {Location(Source{{12, 34}}, 1)});
768 Func(Source{{12, 34}}, "main", {param_a, param_b}, ty.void_(), {},
769 {Stage(ast::PipelineStage::kFragment)});
770
771 EXPECT_FALSE(r()->Resolve());
772 EXPECT_EQ(r()->error(),
773 "12:34 error: location(1) attribute appears multiple times");
774 }
775
TEST_F(LocationDecorationTests,Duplicate_struct)776 TEST_F(LocationDecorationTests, Duplicate_struct) {
777 // struct InputA {
778 // [[location(1)]] a : f32;
779 // };
780 // struct InputB {
781 // [[location(1)]] a : f32;
782 // };
783 // [[stage(fragment)]]
784 // fn main(param_a : InputA, param_b : InputB) {}
785 auto* input_a = Structure("InputA", {Member("a", ty.f32(), {Location(1)})});
786 auto* input_b = Structure(
787 "InputB", {Member("a", ty.f32(), {Location(Source{{34, 56}}, 1)})});
788 auto* param_a = Param("param_a", ty.Of(input_a));
789 auto* param_b = Param("param_b", ty.Of(input_b));
790 Func(Source{{12, 34}}, "main", {param_a, param_b}, ty.void_(), {},
791 {Stage(ast::PipelineStage::kFragment)});
792
793 EXPECT_FALSE(r()->Resolve());
794 EXPECT_EQ(r()->error(),
795 "34:56 error: location(1) attribute appears multiple times\n"
796 "12:34 note: while analysing entry point 'main'");
797 }
798
799 } // namespace
800 } // namespace LocationDecorationTests
801
802 } // namespace
803 } // namespace resolver
804 } // namespace tint
805