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/discard_statement.h"
16 #include "src/ast/return_statement.h"
17 #include "src/ast/stage_decoration.h"
18 #include "src/resolver/resolver.h"
19 #include "src/resolver/resolver_test_helper.h"
20
21 #include "gmock/gmock.h"
22
23 namespace tint {
24 namespace {
25
26 class ResolverFunctionValidationTest : public resolver::TestHelper,
27 public testing::Test {};
28
TEST_F(ResolverFunctionValidationTest,DuplicateParameterName)29 TEST_F(ResolverFunctionValidationTest, DuplicateParameterName) {
30 // fn func_a(common_name : f32) { }
31 // fn func_b(common_name : f32) { }
32 Func("func_a", {Param("common_name", ty.f32())}, ty.void_(), {});
33 Func("func_b", {Param("common_name", ty.f32())}, ty.void_(), {});
34
35 ASSERT_TRUE(r()->Resolve()) << r()->error();
36 }
37
TEST_F(ResolverFunctionValidationTest,ParameterMayShadowGlobal)38 TEST_F(ResolverFunctionValidationTest, ParameterMayShadowGlobal) {
39 // var<private> common_name : f32;
40 // fn func(common_name : f32) { }
41 Global("common_name", ty.f32(), ast::StorageClass::kPrivate);
42 Func("func", {Param("common_name", ty.f32())}, ty.void_(), {});
43
44 ASSERT_TRUE(r()->Resolve()) << r()->error();
45 }
46
TEST_F(ResolverFunctionValidationTest,LocalConflictsWithParameter)47 TEST_F(ResolverFunctionValidationTest, LocalConflictsWithParameter) {
48 // fn func(common_name : f32) {
49 // let common_name = 1;
50 // }
51 Func("func", {Param(Source{{12, 34}}, "common_name", ty.f32())}, ty.void_(),
52 {Decl(Const(Source{{56, 78}}, "common_name", nullptr, Expr(1)))});
53
54 EXPECT_FALSE(r()->Resolve());
55 EXPECT_EQ(r()->error(), R"(56:78 error: redeclaration of 'common_name'
56 12:34 note: 'common_name' previously declared here)");
57 }
58
TEST_F(ResolverFunctionValidationTest,NestedLocalMayShadowParameter)59 TEST_F(ResolverFunctionValidationTest, NestedLocalMayShadowParameter) {
60 // fn func(common_name : f32) {
61 // {
62 // let common_name = 1;
63 // }
64 // }
65 Func("func", {Param(Source{{12, 34}}, "common_name", ty.f32())}, ty.void_(),
66 {Block(Decl(Const(Source{{56, 78}}, "common_name", nullptr, Expr(1))))});
67
68 ASSERT_TRUE(r()->Resolve()) << r()->error();
69 }
70
TEST_F(ResolverFunctionValidationTest,VoidFunctionEndWithoutReturnStatement_Pass)71 TEST_F(ResolverFunctionValidationTest,
72 VoidFunctionEndWithoutReturnStatement_Pass) {
73 // fn func { var a:i32 = 2; }
74 auto* var = Var("a", ty.i32(), Expr(2));
75
76 Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.void_(),
77 ast::StatementList{
78 Decl(var),
79 });
80
81 ASSERT_TRUE(r()->Resolve()) << r()->error();
82 }
83
TEST_F(ResolverFunctionValidationTest,FunctionUsingSameVariableName_Pass)84 TEST_F(ResolverFunctionValidationTest, FunctionUsingSameVariableName_Pass) {
85 // fn func() -> i32 {
86 // var func:i32 = 0;
87 // return func;
88 // }
89
90 auto* var = Var("func", ty.i32(), Expr(0));
91 Func("func", ast::VariableList{}, ty.i32(),
92 ast::StatementList{
93 Decl(var),
94 Return(Source{{12, 34}}, Expr("func")),
95 },
96 ast::DecorationList{});
97
98 ASSERT_TRUE(r()->Resolve()) << r()->error();
99 }
100
TEST_F(ResolverFunctionValidationTest,FunctionNameSameAsFunctionScopeVariableName_Pass)101 TEST_F(ResolverFunctionValidationTest,
102 FunctionNameSameAsFunctionScopeVariableName_Pass) {
103 // fn a() -> void { var b:i32 = 0; }
104 // fn b() -> i32 { return 2; }
105
106 auto* var = Var("b", ty.i32(), Expr(0));
107 Func("a", ast::VariableList{}, ty.void_(),
108 ast::StatementList{
109 Decl(var),
110 },
111 ast::DecorationList{});
112
113 Func(Source{{12, 34}}, "b", ast::VariableList{}, ty.i32(),
114 ast::StatementList{
115 Return(2),
116 },
117 ast::DecorationList{});
118
119 ASSERT_TRUE(r()->Resolve()) << r()->error();
120 }
121
TEST_F(ResolverFunctionValidationTest,UnreachableCode_return)122 TEST_F(ResolverFunctionValidationTest, UnreachableCode_return) {
123 // fn func() -> {
124 // var a : i32;
125 // return;
126 // a = 2;
127 //}
128
129 auto* decl_a = Decl(Var("a", ty.i32()));
130 auto* ret = Return();
131 auto* assign_a = Assign(Source{{12, 34}}, "a", 2);
132
133 Func("func", ast::VariableList{}, ty.void_(), {decl_a, ret, assign_a});
134
135 ASSERT_TRUE(r()->Resolve());
136
137 EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
138 EXPECT_TRUE(Sem().Get(decl_a)->IsReachable());
139 EXPECT_TRUE(Sem().Get(ret)->IsReachable());
140 EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
141 }
142
TEST_F(ResolverFunctionValidationTest,UnreachableCode_return_InBlocks)143 TEST_F(ResolverFunctionValidationTest, UnreachableCode_return_InBlocks) {
144 // fn func() -> {
145 // var a : i32;
146 // {{{return;}}}
147 // a = 2;
148 //}
149
150 auto* decl_a = Decl(Var("a", ty.i32()));
151 auto* ret = Return();
152 auto* assign_a = Assign(Source{{12, 34}}, "a", 2);
153
154 Func("func", ast::VariableList{}, ty.void_(),
155 {decl_a, Block(Block(Block(ret))), assign_a});
156
157 ASSERT_TRUE(r()->Resolve());
158 EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
159 EXPECT_TRUE(Sem().Get(decl_a)->IsReachable());
160 EXPECT_TRUE(Sem().Get(ret)->IsReachable());
161 EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
162 }
163
TEST_F(ResolverFunctionValidationTest,UnreachableCode_discard)164 TEST_F(ResolverFunctionValidationTest, UnreachableCode_discard) {
165 // fn func() -> {
166 // var a : i32;
167 // discard;
168 // a = 2;
169 //}
170
171 auto* decl_a = Decl(Var("a", ty.i32()));
172 auto* discard = Discard();
173 auto* assign_a = Assign(Source{{12, 34}}, "a", 2);
174
175 Func("func", ast::VariableList{}, ty.void_(), {decl_a, discard, assign_a});
176
177 ASSERT_TRUE(r()->Resolve());
178 EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
179 EXPECT_TRUE(Sem().Get(decl_a)->IsReachable());
180 EXPECT_TRUE(Sem().Get(discard)->IsReachable());
181 EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
182 }
183
TEST_F(ResolverFunctionValidationTest,UnreachableCode_discard_InBlocks)184 TEST_F(ResolverFunctionValidationTest, UnreachableCode_discard_InBlocks) {
185 // fn func() -> {
186 // var a : i32;
187 // {{{discard;}}}
188 // a = 2;
189 //}
190
191 auto* decl_a = Decl(Var("a", ty.i32()));
192 auto* discard = Discard();
193 auto* assign_a = Assign(Source{{12, 34}}, "a", 2);
194
195 Func("func", ast::VariableList{}, ty.void_(),
196 {decl_a, Block(Block(Block(discard))), assign_a});
197
198 ASSERT_TRUE(r()->Resolve());
199 EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
200 EXPECT_TRUE(Sem().Get(decl_a)->IsReachable());
201 EXPECT_TRUE(Sem().Get(discard)->IsReachable());
202 EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
203 }
204
TEST_F(ResolverFunctionValidationTest,FunctionEndWithoutReturnStatement_Fail)205 TEST_F(ResolverFunctionValidationTest, FunctionEndWithoutReturnStatement_Fail) {
206 // fn func() -> int { var a:i32 = 2; }
207
208 auto* var = Var("a", ty.i32(), Expr(2));
209
210 Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.i32(),
211 ast::StatementList{
212 Decl(var),
213 },
214 ast::DecorationList{});
215
216 EXPECT_FALSE(r()->Resolve());
217 EXPECT_EQ(r()->error(), "12:34 error: missing return at end of function");
218 }
219
TEST_F(ResolverFunctionValidationTest,VoidFunctionEndWithoutReturnStatementEmptyBody_Pass)220 TEST_F(ResolverFunctionValidationTest,
221 VoidFunctionEndWithoutReturnStatementEmptyBody_Pass) {
222 // fn func {}
223
224 Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.void_(),
225 ast::StatementList{});
226
227 ASSERT_TRUE(r()->Resolve()) << r()->error();
228 }
229
TEST_F(ResolverFunctionValidationTest,FunctionEndWithoutReturnStatementEmptyBody_Fail)230 TEST_F(ResolverFunctionValidationTest,
231 FunctionEndWithoutReturnStatementEmptyBody_Fail) {
232 // fn func() -> int {}
233
234 Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.i32(),
235 ast::StatementList{}, ast::DecorationList{});
236
237 EXPECT_FALSE(r()->Resolve());
238 EXPECT_EQ(r()->error(), "12:34 error: missing return at end of function");
239 }
240
TEST_F(ResolverFunctionValidationTest,FunctionTypeMustMatchReturnStatementType_Pass)241 TEST_F(ResolverFunctionValidationTest,
242 FunctionTypeMustMatchReturnStatementType_Pass) {
243 // fn func { return; }
244
245 Func("func", ast::VariableList{}, ty.void_(),
246 ast::StatementList{
247 Return(),
248 });
249
250 ASSERT_TRUE(r()->Resolve()) << r()->error();
251 }
252
TEST_F(ResolverFunctionValidationTest,FunctionTypeMustMatchReturnStatementType_fail)253 TEST_F(ResolverFunctionValidationTest,
254 FunctionTypeMustMatchReturnStatementType_fail) {
255 // fn func { return 2; }
256 Func("func", ast::VariableList{}, ty.void_(),
257 ast::StatementList{
258 Return(Source{{12, 34}}, Expr(2)),
259 },
260 ast::DecorationList{});
261
262 EXPECT_FALSE(r()->Resolve());
263 EXPECT_EQ(r()->error(),
264 "12:34 error: return statement type must match its function return "
265 "type, returned 'i32', expected 'void'");
266 }
267
TEST_F(ResolverFunctionValidationTest,FunctionTypeMustMatchReturnStatementType_void_fail)268 TEST_F(ResolverFunctionValidationTest,
269 FunctionTypeMustMatchReturnStatementType_void_fail) {
270 // fn v { return; }
271 // fn func { return v(); }
272 Func("v", {}, ty.void_(), {Return()});
273 Func("func", {}, ty.void_(),
274 {
275 Return(Call(Source{{12, 34}}, "v")),
276 });
277
278 EXPECT_FALSE(r()->Resolve());
279 EXPECT_EQ(r()->error(), "12:34 error: function 'v' does not return a value");
280 }
281
TEST_F(ResolverFunctionValidationTest,FunctionTypeMustMatchReturnStatementTypeMissing_fail)282 TEST_F(ResolverFunctionValidationTest,
283 FunctionTypeMustMatchReturnStatementTypeMissing_fail) {
284 // fn func() -> f32 { return; }
285 Func("func", ast::VariableList{}, ty.f32(),
286 ast::StatementList{
287 Return(Source{{12, 34}}, nullptr),
288 },
289 ast::DecorationList{});
290
291 EXPECT_FALSE(r()->Resolve());
292 EXPECT_EQ(r()->error(),
293 "12:34 error: return statement type must match its function return "
294 "type, returned 'void', expected 'f32'");
295 }
296
TEST_F(ResolverFunctionValidationTest,FunctionTypeMustMatchReturnStatementTypeF32_pass)297 TEST_F(ResolverFunctionValidationTest,
298 FunctionTypeMustMatchReturnStatementTypeF32_pass) {
299 // fn func() -> f32 { return 2.0; }
300 Func("func", ast::VariableList{}, ty.f32(),
301 ast::StatementList{
302 Return(Source{{12, 34}}, Expr(2.f)),
303 },
304 ast::DecorationList{});
305
306 ASSERT_TRUE(r()->Resolve()) << r()->error();
307 }
308
TEST_F(ResolverFunctionValidationTest,FunctionTypeMustMatchReturnStatementTypeF32_fail)309 TEST_F(ResolverFunctionValidationTest,
310 FunctionTypeMustMatchReturnStatementTypeF32_fail) {
311 // fn func() -> f32 { return 2; }
312 Func("func", ast::VariableList{}, ty.f32(),
313 ast::StatementList{
314 Return(Source{{12, 34}}, Expr(2)),
315 },
316 ast::DecorationList{});
317
318 EXPECT_FALSE(r()->Resolve());
319 EXPECT_EQ(r()->error(),
320 "12:34 error: return statement type must match its function return "
321 "type, returned 'i32', expected 'f32'");
322 }
323
TEST_F(ResolverFunctionValidationTest,FunctionTypeMustMatchReturnStatementTypeF32Alias_pass)324 TEST_F(ResolverFunctionValidationTest,
325 FunctionTypeMustMatchReturnStatementTypeF32Alias_pass) {
326 // type myf32 = f32;
327 // fn func() -> myf32 { return 2.0; }
328 auto* myf32 = Alias("myf32", ty.f32());
329 Func("func", ast::VariableList{}, ty.Of(myf32),
330 ast::StatementList{
331 Return(Source{{12, 34}}, Expr(2.f)),
332 },
333 ast::DecorationList{});
334
335 ASSERT_TRUE(r()->Resolve()) << r()->error();
336 }
337
TEST_F(ResolverFunctionValidationTest,FunctionTypeMustMatchReturnStatementTypeF32Alias_fail)338 TEST_F(ResolverFunctionValidationTest,
339 FunctionTypeMustMatchReturnStatementTypeF32Alias_fail) {
340 // type myf32 = f32;
341 // fn func() -> myf32 { return 2; }
342 auto* myf32 = Alias("myf32", ty.f32());
343 Func("func", ast::VariableList{}, ty.Of(myf32),
344 ast::StatementList{
345 Return(Source{{12, 34}}, Expr(2u)),
346 },
347 ast::DecorationList{});
348
349 EXPECT_FALSE(r()->Resolve());
350 EXPECT_EQ(r()->error(),
351 "12:34 error: return statement type must match its function return "
352 "type, returned 'u32', expected 'f32'");
353 }
354
TEST_F(ResolverFunctionValidationTest,CannotCallEntryPoint)355 TEST_F(ResolverFunctionValidationTest, CannotCallEntryPoint) {
356 // [[stage(compute), workgroup_size(1)]] fn entrypoint() {}
357 // fn func() { return entrypoint(); }
358 Func("entrypoint", ast::VariableList{}, ty.void_(), {},
359 {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
360
361 Func("func", ast::VariableList{}, ty.void_(),
362 {
363 CallStmt(Call(Source{{12, 34}}, "entrypoint")),
364 });
365
366 EXPECT_FALSE(r()->Resolve());
367 EXPECT_EQ(
368 r()->error(),
369
370 R"(12:34 error: entry point functions cannot be the target of a function call)");
371 }
372
TEST_F(ResolverFunctionValidationTest,PipelineStage_MustBeUnique_Fail)373 TEST_F(ResolverFunctionValidationTest, PipelineStage_MustBeUnique_Fail) {
374 // [[stage(fragment)]]
375 // [[stage(vertex)]]
376 // fn main() { return; }
377 Func(Source{{12, 34}}, "main", ast::VariableList{}, ty.void_(),
378 ast::StatementList{
379 Return(),
380 },
381 ast::DecorationList{
382 Stage(Source{{12, 34}}, ast::PipelineStage::kVertex),
383 Stage(Source{{56, 78}}, ast::PipelineStage::kFragment),
384 });
385
386 EXPECT_FALSE(r()->Resolve());
387 EXPECT_EQ(r()->error(),
388 R"(56:78 error: duplicate stage decoration
389 12:34 note: first decoration declared here)");
390 }
391
TEST_F(ResolverFunctionValidationTest,NoPipelineEntryPoints)392 TEST_F(ResolverFunctionValidationTest, NoPipelineEntryPoints) {
393 Func("vtx_func", ast::VariableList{}, ty.void_(),
394 ast::StatementList{
395 Return(),
396 },
397 ast::DecorationList{});
398
399 ASSERT_TRUE(r()->Resolve()) << r()->error();
400 }
401
TEST_F(ResolverFunctionValidationTest,FunctionVarInitWithParam)402 TEST_F(ResolverFunctionValidationTest, FunctionVarInitWithParam) {
403 // fn foo(bar : f32){
404 // var baz : f32 = bar;
405 // }
406
407 auto* bar = Param("bar", ty.f32());
408 auto* baz = Var("baz", ty.f32(), Expr("bar"));
409
410 Func("foo", ast::VariableList{bar}, ty.void_(), ast::StatementList{Decl(baz)},
411 ast::DecorationList{});
412
413 ASSERT_TRUE(r()->Resolve()) << r()->error();
414 }
415
TEST_F(ResolverFunctionValidationTest,FunctionConstInitWithParam)416 TEST_F(ResolverFunctionValidationTest, FunctionConstInitWithParam) {
417 // fn foo(bar : f32){
418 // let baz : f32 = bar;
419 // }
420
421 auto* bar = Param("bar", ty.f32());
422 auto* baz = Const("baz", ty.f32(), Expr("bar"));
423
424 Func("foo", ast::VariableList{bar}, ty.void_(), ast::StatementList{Decl(baz)},
425 ast::DecorationList{});
426
427 ASSERT_TRUE(r()->Resolve()) << r()->error();
428 }
429
TEST_F(ResolverFunctionValidationTest,FunctionParamsConst)430 TEST_F(ResolverFunctionValidationTest, FunctionParamsConst) {
431 Func("foo", {Param(Sym("arg"), ty.i32())}, ty.void_(),
432 {Assign(Expr(Source{{12, 34}}, "arg"), Expr(1)), Return()});
433
434 EXPECT_FALSE(r()->Resolve());
435 EXPECT_EQ(r()->error(),
436 "12:34 error: cannot assign to function parameter\nnote: 'arg' is "
437 "declared here:");
438 }
439
TEST_F(ResolverFunctionValidationTest,WorkgroupSize_GoodType_ConstU32)440 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_GoodType_ConstU32) {
441 // let x = 4u;
442 // let x = 8u;
443 // [[stage(compute), workgroup_size(x, y, 16u)]]
444 // fn main() {}
445 auto* x = GlobalConst("x", ty.u32(), Expr(4u));
446 auto* y = GlobalConst("y", ty.u32(), Expr(8u));
447 auto* func = Func("main", {}, ty.void_(), {},
448 {Stage(ast::PipelineStage::kCompute),
449 WorkgroupSize(Expr("x"), Expr("y"), Expr(16u))});
450
451 ASSERT_TRUE(r()->Resolve()) << r()->error();
452
453 auto* sem_func = Sem().Get(func);
454 auto* sem_x = Sem().Get<sem::GlobalVariable>(x);
455 auto* sem_y = Sem().Get<sem::GlobalVariable>(y);
456
457 ASSERT_NE(sem_func, nullptr);
458 ASSERT_NE(sem_x, nullptr);
459 ASSERT_NE(sem_y, nullptr);
460
461 EXPECT_TRUE(sem_func->DirectlyReferencedGlobals().contains(sem_x));
462 EXPECT_TRUE(sem_func->DirectlyReferencedGlobals().contains(sem_y));
463 }
464
TEST_F(ResolverFunctionValidationTest,WorkgroupSize_GoodType_U32)465 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_GoodType_U32) {
466 // [[stage(compute), workgroup_size(1u, 2u, 3u)]
467 // fn main() {}
468
469 Func("main", {}, ty.void_(), {},
470 {Stage(ast::PipelineStage::kCompute),
471 WorkgroupSize(Source{{12, 34}}, Expr(1u), Expr(2u), Expr(3u))});
472
473 ASSERT_TRUE(r()->Resolve()) << r()->error();
474 }
475
TEST_F(ResolverFunctionValidationTest,WorkgroupSize_MismatchTypeU32)476 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_MismatchTypeU32) {
477 // [[stage(compute), workgroup_size(1u, 2u, 3)]
478 // fn main() {}
479
480 Func("main", {}, ty.void_(), {},
481 {Stage(ast::PipelineStage::kCompute),
482 WorkgroupSize(Expr(1u), Expr(2u), Expr(Source{{12, 34}}, 3))});
483
484 EXPECT_FALSE(r()->Resolve());
485 EXPECT_EQ(r()->error(),
486 "12:34 error: workgroup_size arguments must be of the same type, "
487 "either i32 or u32");
488 }
489
TEST_F(ResolverFunctionValidationTest,WorkgroupSize_MismatchTypeI32)490 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_MismatchTypeI32) {
491 // [[stage(compute), workgroup_size(1, 2u, 3)]
492 // fn main() {}
493
494 Func("main", {}, ty.void_(), {},
495 {Stage(ast::PipelineStage::kCompute),
496 WorkgroupSize(Expr(1), Expr(Source{{12, 34}}, 2u), Expr(3))});
497
498 EXPECT_FALSE(r()->Resolve());
499 EXPECT_EQ(r()->error(),
500 "12:34 error: workgroup_size arguments must be of the same type, "
501 "either i32 or u32");
502 }
503
TEST_F(ResolverFunctionValidationTest,WorkgroupSize_Const_TypeMismatch)504 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Const_TypeMismatch) {
505 // let x = 64u;
506 // [[stage(compute), workgroup_size(1, x)]
507 // fn main() {}
508 GlobalConst("x", ty.u32(), Expr(64u));
509 Func("main", {}, ty.void_(), {},
510 {Stage(ast::PipelineStage::kCompute),
511 WorkgroupSize(Expr(1), Expr(Source{{12, 34}}, "x"))});
512
513 EXPECT_FALSE(r()->Resolve());
514 EXPECT_EQ(r()->error(),
515 "12:34 error: workgroup_size arguments must be of the same type, "
516 "either i32 or u32");
517 }
518
TEST_F(ResolverFunctionValidationTest,WorkgroupSize_Const_TypeMismatch2)519 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Const_TypeMismatch2) {
520 // let x = 64u;
521 // let y = 32;
522 // [[stage(compute), workgroup_size(x, y)]
523 // fn main() {}
524 GlobalConst("x", ty.u32(), Expr(64u));
525 GlobalConst("y", ty.i32(), Expr(32));
526 Func("main", {}, ty.void_(), {},
527 {Stage(ast::PipelineStage::kCompute),
528 WorkgroupSize(Expr("x"), Expr(Source{{12, 34}}, "y"))});
529
530 EXPECT_FALSE(r()->Resolve());
531 EXPECT_EQ(r()->error(),
532 "12:34 error: workgroup_size arguments must be of the same type, "
533 "either i32 or u32");
534 }
TEST_F(ResolverFunctionValidationTest,WorkgroupSize_Mismatch_ConstU32)535 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Mismatch_ConstU32) {
536 // let x = 4u;
537 // let x = 8u;
538 // [[stage(compute), workgroup_size(x, y, 16]
539 // fn main() {}
540 GlobalConst("x", ty.u32(), Expr(4u));
541 GlobalConst("y", ty.u32(), Expr(8u));
542 Func("main", {}, ty.void_(), {},
543 {Stage(ast::PipelineStage::kCompute),
544 WorkgroupSize(Expr("x"), Expr("y"), Expr(Source{{12, 34}}, 16))});
545
546 EXPECT_FALSE(r()->Resolve());
547 EXPECT_EQ(r()->error(),
548 "12:34 error: workgroup_size arguments must be of the same type, "
549 "either i32 or u32");
550 }
551
TEST_F(ResolverFunctionValidationTest,WorkgroupSize_Literal_BadType)552 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Literal_BadType) {
553 // [[stage(compute), workgroup_size(64.0)]
554 // fn main() {}
555
556 Func("main", {}, ty.void_(), {},
557 {Stage(ast::PipelineStage::kCompute),
558 WorkgroupSize(Expr(Source{{12, 34}}, 64.f))});
559
560 EXPECT_FALSE(r()->Resolve());
561 EXPECT_EQ(r()->error(),
562 "12:34 error: workgroup_size argument must be either literal or "
563 "module-scope constant of type i32 or u32");
564 }
565
TEST_F(ResolverFunctionValidationTest,WorkgroupSize_Literal_Negative)566 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Literal_Negative) {
567 // [[stage(compute), workgroup_size(-2)]
568 // fn main() {}
569
570 Func("main", {}, ty.void_(), {},
571 {Stage(ast::PipelineStage::kCompute),
572 WorkgroupSize(Expr(Source{{12, 34}}, -2))});
573
574 EXPECT_FALSE(r()->Resolve());
575 EXPECT_EQ(r()->error(),
576 "12:34 error: workgroup_size argument must be at least 1");
577 }
578
TEST_F(ResolverFunctionValidationTest,WorkgroupSize_Literal_Zero)579 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Literal_Zero) {
580 // [[stage(compute), workgroup_size(0)]
581 // fn main() {}
582
583 Func("main", {}, ty.void_(), {},
584 {Stage(ast::PipelineStage::kCompute),
585 WorkgroupSize(Expr(Source{{12, 34}}, 0))});
586
587 EXPECT_FALSE(r()->Resolve());
588 EXPECT_EQ(r()->error(),
589 "12:34 error: workgroup_size argument must be at least 1");
590 }
591
TEST_F(ResolverFunctionValidationTest,WorkgroupSize_Const_BadType)592 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Const_BadType) {
593 // let x = 64.0;
594 // [[stage(compute), workgroup_size(x)]
595 // fn main() {}
596 GlobalConst("x", ty.f32(), Expr(64.f));
597 Func("main", {}, ty.void_(), {},
598 {Stage(ast::PipelineStage::kCompute),
599 WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
600
601 EXPECT_FALSE(r()->Resolve());
602 EXPECT_EQ(r()->error(),
603 "12:34 error: workgroup_size argument must be either literal or "
604 "module-scope constant of type i32 or u32");
605 }
606
TEST_F(ResolverFunctionValidationTest,WorkgroupSize_Const_Negative)607 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Const_Negative) {
608 // let x = -2;
609 // [[stage(compute), workgroup_size(x)]
610 // fn main() {}
611 GlobalConst("x", ty.i32(), Expr(-2));
612 Func("main", {}, ty.void_(), {},
613 {Stage(ast::PipelineStage::kCompute),
614 WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
615
616 EXPECT_FALSE(r()->Resolve());
617 EXPECT_EQ(r()->error(),
618 "12:34 error: workgroup_size argument must be at least 1");
619 }
620
TEST_F(ResolverFunctionValidationTest,WorkgroupSize_Const_Zero)621 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Const_Zero) {
622 // let x = 0;
623 // [[stage(compute), workgroup_size(x)]
624 // fn main() {}
625 GlobalConst("x", ty.i32(), Expr(0));
626 Func("main", {}, ty.void_(), {},
627 {Stage(ast::PipelineStage::kCompute),
628 WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
629
630 EXPECT_FALSE(r()->Resolve());
631 EXPECT_EQ(r()->error(),
632 "12:34 error: workgroup_size argument must be at least 1");
633 }
634
TEST_F(ResolverFunctionValidationTest,WorkgroupSize_Const_NestedZeroValueConstructor)635 TEST_F(ResolverFunctionValidationTest,
636 WorkgroupSize_Const_NestedZeroValueConstructor) {
637 // let x = i32(i32(i32()));
638 // [[stage(compute), workgroup_size(x)]
639 // fn main() {}
640 GlobalConst("x", ty.i32(),
641 Construct(ty.i32(), Construct(ty.i32(), Construct(ty.i32()))));
642 Func("main", {}, ty.void_(), {},
643 {Stage(ast::PipelineStage::kCompute),
644 WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
645
646 EXPECT_FALSE(r()->Resolve());
647 EXPECT_EQ(r()->error(),
648 "12:34 error: workgroup_size argument must be at least 1");
649 }
650
TEST_F(ResolverFunctionValidationTest,WorkgroupSize_NonConst)651 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_NonConst) {
652 // var<private> x = 0;
653 // [[stage(compute), workgroup_size(x)]
654 // fn main() {}
655 Global("x", ty.i32(), ast::StorageClass::kPrivate, Expr(64));
656 Func("main", {}, ty.void_(), {},
657 {Stage(ast::PipelineStage::kCompute),
658 WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
659
660 EXPECT_FALSE(r()->Resolve());
661 EXPECT_EQ(r()->error(),
662 "12:34 error: workgroup_size argument must be either literal or "
663 "module-scope constant of type i32 or u32");
664 }
665
TEST_F(ResolverFunctionValidationTest,WorkgroupSize_InvalidExpr)666 TEST_F(ResolverFunctionValidationTest, WorkgroupSize_InvalidExpr) {
667 // [[stage(compute), workgroup_size(i32(1))]
668 // fn main() {}
669 Func("main", {}, ty.void_(), {},
670 {Stage(ast::PipelineStage::kCompute),
671 WorkgroupSize(Construct(Source{{12, 34}}, ty.i32(), 1))});
672
673 EXPECT_FALSE(r()->Resolve());
674 EXPECT_EQ(r()->error(),
675 "12:34 error: workgroup_size argument must be either a literal or "
676 "a module-scope constant");
677 }
678
TEST_F(ResolverFunctionValidationTest,ReturnIsConstructible_NonPlain)679 TEST_F(ResolverFunctionValidationTest, ReturnIsConstructible_NonPlain) {
680 auto* ret_type =
681 ty.pointer(Source{{12, 34}}, ty.i32(), ast::StorageClass::kFunction);
682 Func("f", {}, ret_type, {});
683
684 EXPECT_FALSE(r()->Resolve());
685 EXPECT_EQ(r()->error(),
686 "12:34 error: function return type must be a constructible type");
687 }
688
TEST_F(ResolverFunctionValidationTest,ReturnIsConstructible_AtomicInt)689 TEST_F(ResolverFunctionValidationTest, ReturnIsConstructible_AtomicInt) {
690 auto* ret_type = ty.atomic(Source{{12, 34}}, ty.i32());
691 Func("f", {}, ret_type, {});
692
693 EXPECT_FALSE(r()->Resolve());
694 EXPECT_EQ(r()->error(),
695 "12:34 error: function return type must be a constructible type");
696 }
697
TEST_F(ResolverFunctionValidationTest,ReturnIsConstructible_ArrayOfAtomic)698 TEST_F(ResolverFunctionValidationTest, ReturnIsConstructible_ArrayOfAtomic) {
699 auto* ret_type = ty.array(Source{{12, 34}}, ty.atomic(ty.i32()), 10);
700 Func("f", {}, ret_type, {});
701
702 EXPECT_FALSE(r()->Resolve());
703 EXPECT_EQ(r()->error(),
704 "12:34 error: function return type must be a constructible type");
705 }
706
TEST_F(ResolverFunctionValidationTest,ReturnIsConstructible_StructOfAtomic)707 TEST_F(ResolverFunctionValidationTest, ReturnIsConstructible_StructOfAtomic) {
708 Structure("S", {Member("m", ty.atomic(ty.i32()))});
709 auto* ret_type = ty.type_name(Source{{12, 34}}, "S");
710 Func("f", {}, ret_type, {});
711
712 EXPECT_FALSE(r()->Resolve());
713 EXPECT_EQ(r()->error(),
714 "12:34 error: function return type must be a constructible type");
715 }
716
TEST_F(ResolverFunctionValidationTest,ReturnIsConstructible_RuntimeArray)717 TEST_F(ResolverFunctionValidationTest, ReturnIsConstructible_RuntimeArray) {
718 auto* ret_type = ty.array(Source{{12, 34}}, ty.i32());
719 Func("f", {}, ret_type, {});
720
721 EXPECT_FALSE(r()->Resolve());
722 EXPECT_EQ(r()->error(),
723 "12:34 error: function return type must be a constructible type");
724 }
725
TEST_F(ResolverFunctionValidationTest,ParameterStoreType_NonAtomicFree)726 TEST_F(ResolverFunctionValidationTest, ParameterStoreType_NonAtomicFree) {
727 Structure("S", {Member("m", ty.atomic(ty.i32()))});
728 auto* ret_type = ty.type_name(Source{{12, 34}}, "S");
729 auto* bar = Param(Source{{12, 34}}, "bar", ret_type);
730 Func("f", ast::VariableList{bar}, ty.void_(), {});
731
732 EXPECT_FALSE(r()->Resolve());
733 EXPECT_EQ(r()->error(),
734 "12:34 error: store type of function parameter must be a "
735 "constructible type");
736 }
737
TEST_F(ResolverFunctionValidationTest,ParameterSotreType_AtomicFree)738 TEST_F(ResolverFunctionValidationTest, ParameterSotreType_AtomicFree) {
739 Structure("S", {Member("m", ty.i32())});
740 auto* ret_type = ty.type_name(Source{{12, 34}}, "S");
741 auto* bar = Param(Source{{12, 34}}, "bar", ret_type);
742 Func("f", ast::VariableList{bar}, ty.void_(), {});
743
744 ASSERT_TRUE(r()->Resolve()) << r()->error();
745 }
746
TEST_F(ResolverFunctionValidationTest,ParametersAtLimit)747 TEST_F(ResolverFunctionValidationTest, ParametersAtLimit) {
748 ast::VariableList params;
749 for (int i = 0; i < 255; i++) {
750 params.emplace_back(Param("param_" + std::to_string(i), ty.i32()));
751 }
752 Func(Source{{12, 34}}, "f", params, ty.void_(), {});
753
754 ASSERT_TRUE(r()->Resolve()) << r()->error();
755 }
756
TEST_F(ResolverFunctionValidationTest,ParametersOverLimit)757 TEST_F(ResolverFunctionValidationTest, ParametersOverLimit) {
758 ast::VariableList params;
759 for (int i = 0; i < 256; i++) {
760 params.emplace_back(Param("param_" + std::to_string(i), ty.i32()));
761 }
762 Func(Source{{12, 34}}, "f", params, ty.void_(), {});
763
764 EXPECT_FALSE(r()->Resolve());
765 EXPECT_EQ(r()->error(),
766 "12:34 error: functions may declare at most 255 parameters");
767 }
768
769 struct TestParams {
770 ast::StorageClass storage_class;
771 bool should_pass;
772 };
773
774 struct TestWithParams : resolver::ResolverTestWithParam<TestParams> {};
775
776 using ResolverFunctionParameterValidationTest = TestWithParams;
TEST_P(ResolverFunctionParameterValidationTest,StorageClass)777 TEST_P(ResolverFunctionParameterValidationTest, StorageClass) {
778 auto& param = GetParam();
779 auto* ptr_type = ty.pointer(Source{{12, 34}}, ty.i32(), param.storage_class);
780 auto* arg = Param(Source{{12, 34}}, "p", ptr_type);
781 Func("f", ast::VariableList{arg}, ty.void_(), {});
782
783 if (param.should_pass) {
784 ASSERT_TRUE(r()->Resolve()) << r()->error();
785 } else {
786 std::stringstream ss;
787 ss << param.storage_class;
788 EXPECT_FALSE(r()->Resolve());
789 EXPECT_EQ(r()->error(),
790 "12:34 error: function parameter of pointer type cannot be in '" +
791 ss.str() + "' storage class");
792 }
793 }
794 INSTANTIATE_TEST_SUITE_P(
795 ResolverTest,
796 ResolverFunctionParameterValidationTest,
797 testing::Values(TestParams{ast::StorageClass::kNone, false},
798 TestParams{ast::StorageClass::kInput, false},
799 TestParams{ast::StorageClass::kOutput, false},
800 TestParams{ast::StorageClass::kUniform, false},
801 TestParams{ast::StorageClass::kWorkgroup, true},
802 TestParams{ast::StorageClass::kUniformConstant, false},
803 TestParams{ast::StorageClass::kStorage, false},
804 TestParams{ast::StorageClass::kImage, false},
805 TestParams{ast::StorageClass::kPrivate, true},
806 TestParams{ast::StorageClass::kFunction, true}));
807
808 } // namespace
809 } // namespace tint
810