• 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/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