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/resolver/resolver.h"
16
17 #include "gmock/gmock.h"
18 #include "src/ast/struct_block_decoration.h"
19 #include "src/resolver/resolver_test_helper.h"
20 #include "src/sem/storage_texture_type.h"
21
22 namespace tint {
23 namespace resolver {
24 namespace {
25
26 using ResolverAssignmentValidationTest = ResolverTest;
27
TEST_F(ResolverAssignmentValidationTest,ReadOnlyBuffer)28 TEST_F(ResolverAssignmentValidationTest, ReadOnlyBuffer) {
29 // [[block]] struct S { m : i32 };
30 // [[group(0), binding(0)]]
31 // var<storage,read> a : S;
32 auto* s = Structure("S", {Member("m", ty.i32())},
33 {create<ast::StructBlockDecoration>()});
34 Global(Source{{12, 34}}, "a", ty.Of(s), ast::StorageClass::kStorage,
35 ast::Access::kRead,
36 ast::DecorationList{
37 create<ast::BindingDecoration>(0),
38 create<ast::GroupDecoration>(0),
39 });
40
41 WrapInFunction(Assign(Source{{56, 78}}, MemberAccessor("a", "m"), 1));
42
43 EXPECT_FALSE(r()->Resolve());
44 EXPECT_EQ(r()->error(),
45 "56:78 error: cannot store into a read-only type 'ref<storage, "
46 "i32, read>'");
47 }
48
TEST_F(ResolverAssignmentValidationTest,AssignIncompatibleTypes)49 TEST_F(ResolverAssignmentValidationTest, AssignIncompatibleTypes) {
50 // {
51 // var a : i32 = 2;
52 // a = 2.3;
53 // }
54
55 auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
56
57 auto* assign = Assign(Source{{12, 34}}, "a", 2.3f);
58 WrapInFunction(var, assign);
59
60 ASSERT_FALSE(r()->Resolve());
61
62 EXPECT_EQ(r()->error(), "12:34 error: cannot assign 'f32' to 'i32'");
63 }
64
TEST_F(ResolverAssignmentValidationTest,AssignArraysWithDifferentSizeExpressions_Pass)65 TEST_F(ResolverAssignmentValidationTest,
66 AssignArraysWithDifferentSizeExpressions_Pass) {
67 // let len = 4u;
68 // {
69 // var a : array<f32, 4>;
70 // var b : array<f32, len>;
71 // a = b;
72 // }
73
74 GlobalConst("len", nullptr, Expr(4u));
75
76 auto* a = Var("a", ty.array(ty.f32(), 4));
77 auto* b = Var("b", ty.array(ty.f32(), "len"));
78
79 auto* assign = Assign(Source{{12, 34}}, "a", "b");
80 WrapInFunction(a, b, assign);
81
82 ASSERT_TRUE(r()->Resolve()) << r()->error();
83 }
84
TEST_F(ResolverAssignmentValidationTest,AssignArraysWithDifferentSizeExpressions_Fail)85 TEST_F(ResolverAssignmentValidationTest,
86 AssignArraysWithDifferentSizeExpressions_Fail) {
87 // let len = 5u;
88 // {
89 // var a : array<f32, 4>;
90 // var b : array<f32, len>;
91 // a = b;
92 // }
93
94 GlobalConst("len", nullptr, Expr(5u));
95
96 auto* a = Var("a", ty.array(ty.f32(), 4));
97 auto* b = Var("b", ty.array(ty.f32(), "len"));
98
99 auto* assign = Assign(Source{{12, 34}}, "a", "b");
100 WrapInFunction(a, b, assign);
101
102 ASSERT_FALSE(r()->Resolve());
103
104 EXPECT_EQ(r()->error(),
105 "12:34 error: cannot assign 'array<f32, 5>' to 'array<f32, 4>'");
106 }
107
TEST_F(ResolverAssignmentValidationTest,AssignCompatibleTypesInBlockStatement_Pass)108 TEST_F(ResolverAssignmentValidationTest,
109 AssignCompatibleTypesInBlockStatement_Pass) {
110 // {
111 // var a : i32 = 2;
112 // a = 2
113 // }
114 auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
115 WrapInFunction(var, Assign("a", 2));
116
117 ASSERT_TRUE(r()->Resolve()) << r()->error();
118 }
119
TEST_F(ResolverAssignmentValidationTest,AssignIncompatibleTypesInBlockStatement_Fail)120 TEST_F(ResolverAssignmentValidationTest,
121 AssignIncompatibleTypesInBlockStatement_Fail) {
122 // {
123 // var a : i32 = 2;
124 // a = 2.3;
125 // }
126
127 auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
128 WrapInFunction(var, Assign(Source{{12, 34}}, "a", 2.3f));
129
130 ASSERT_FALSE(r()->Resolve());
131
132 EXPECT_EQ(r()->error(), "12:34 error: cannot assign 'f32' to 'i32'");
133 }
134
TEST_F(ResolverAssignmentValidationTest,AssignIncompatibleTypesInNestedBlockStatement_Fail)135 TEST_F(ResolverAssignmentValidationTest,
136 AssignIncompatibleTypesInNestedBlockStatement_Fail) {
137 // {
138 // {
139 // var a : i32 = 2;
140 // a = 2.3;
141 // }
142 // }
143
144 auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
145 auto* inner_block = Block(Decl(var), Assign(Source{{12, 34}}, "a", 2.3f));
146 auto* outer_block = Block(inner_block);
147 WrapInFunction(outer_block);
148
149 ASSERT_FALSE(r()->Resolve());
150
151 EXPECT_EQ(r()->error(), "12:34 error: cannot assign 'f32' to 'i32'");
152 }
153
TEST_F(ResolverAssignmentValidationTest,AssignToScalar_Fail)154 TEST_F(ResolverAssignmentValidationTest, AssignToScalar_Fail) {
155 // var my_var : i32 = 2;
156 // 1 = my_var;
157
158 auto* var = Var("my_var", ty.i32(), ast::StorageClass::kNone, Expr(2));
159 WrapInFunction(var, Assign(Expr(Source{{12, 34}}, 1), "my_var"));
160
161 EXPECT_FALSE(r()->Resolve());
162 EXPECT_EQ(r()->error(), "12:34 error: cannot assign to value of type 'i32'");
163 }
164
TEST_F(ResolverAssignmentValidationTest,AssignCompatibleTypes_Pass)165 TEST_F(ResolverAssignmentValidationTest, AssignCompatibleTypes_Pass) {
166 // var a : i32 = 2;
167 // a = 2
168 auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
169 WrapInFunction(var, Assign(Source{{12, 34}}, "a", 2));
170
171 EXPECT_TRUE(r()->Resolve()) << r()->error();
172 }
173
TEST_F(ResolverAssignmentValidationTest,AssignCompatibleTypesThroughAlias_Pass)174 TEST_F(ResolverAssignmentValidationTest,
175 AssignCompatibleTypesThroughAlias_Pass) {
176 // alias myint = i32;
177 // var a : myint = 2;
178 // a = 2
179 auto* myint = Alias("myint", ty.i32());
180 auto* var = Var("a", ty.Of(myint), ast::StorageClass::kNone, Expr(2));
181 WrapInFunction(var, Assign(Source{{12, 34}}, "a", 2));
182
183 EXPECT_TRUE(r()->Resolve()) << r()->error();
184 }
185
TEST_F(ResolverAssignmentValidationTest,AssignCompatibleTypesInferRHSLoad_Pass)186 TEST_F(ResolverAssignmentValidationTest,
187 AssignCompatibleTypesInferRHSLoad_Pass) {
188 // var a : i32 = 2;
189 // var b : i32 = 3;
190 // a = b;
191 auto* var_a = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
192 auto* var_b = Var("b", ty.i32(), ast::StorageClass::kNone, Expr(3));
193 WrapInFunction(var_a, var_b, Assign(Source{{12, 34}}, "a", "b"));
194
195 EXPECT_TRUE(r()->Resolve()) << r()->error();
196 }
197
TEST_F(ResolverAssignmentValidationTest,AssignThroughPointer_Pass)198 TEST_F(ResolverAssignmentValidationTest, AssignThroughPointer_Pass) {
199 // var a : i32;
200 // let b : ptr<function,i32> = &a;
201 // *b = 2;
202 const auto func = ast::StorageClass::kFunction;
203 auto* var_a = Var("a", ty.i32(), func, Expr(2));
204 auto* var_b = Const("b", ty.pointer<int>(func), AddressOf(Expr("a")));
205 WrapInFunction(var_a, var_b, Assign(Source{{12, 34}}, Deref("b"), 2));
206
207 EXPECT_TRUE(r()->Resolve()) << r()->error();
208 }
209
TEST_F(ResolverAssignmentValidationTest,AssignToConstant_Fail)210 TEST_F(ResolverAssignmentValidationTest, AssignToConstant_Fail) {
211 // {
212 // let a : i32 = 2;
213 // a = 2
214 // }
215 auto* var = Const("a", ty.i32(), Expr(2));
216 WrapInFunction(var, Assign(Expr(Source{{12, 34}}, "a"), 2));
217
218 EXPECT_FALSE(r()->Resolve());
219 EXPECT_EQ(r()->error(),
220 "12:34 error: cannot assign to const\nnote: 'a' is declared here:");
221 }
222
TEST_F(ResolverAssignmentValidationTest,AssignNonConstructible_Handle)223 TEST_F(ResolverAssignmentValidationTest, AssignNonConstructible_Handle) {
224 // var a : texture_storage_1d<rgba8unorm, write>;
225 // var b : texture_storage_1d<rgba8unorm, write>;
226 // a = b;
227
228 auto make_type = [&] {
229 return ty.storage_texture(ast::TextureDimension::k1d,
230 ast::ImageFormat::kRgba8Unorm,
231 ast::Access::kWrite);
232 };
233
234 Global("a", make_type(), ast::StorageClass::kNone,
235 ast::DecorationList{
236 create<ast::BindingDecoration>(0),
237 create<ast::GroupDecoration>(0),
238 });
239 Global("b", make_type(), ast::StorageClass::kNone,
240 ast::DecorationList{
241 create<ast::BindingDecoration>(1),
242 create<ast::GroupDecoration>(0),
243 });
244
245 WrapInFunction(Assign(Source{{56, 78}}, "a", "b"));
246
247 EXPECT_FALSE(r()->Resolve());
248 EXPECT_EQ(r()->error(),
249 "56:78 error: storage type of assignment must be constructible");
250 }
251
TEST_F(ResolverAssignmentValidationTest,AssignNonConstructible_Atomic)252 TEST_F(ResolverAssignmentValidationTest, AssignNonConstructible_Atomic) {
253 // [[block]] struct S { a : atomic<i32>; };
254 // [[group(0), binding(0)]] var<storage, read_write> v : S;
255 // v.a = v.a;
256
257 auto* s = Structure("S", {Member("a", ty.atomic(ty.i32()))},
258 {create<ast::StructBlockDecoration>()});
259 Global(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kStorage,
260 ast::Access::kReadWrite,
261 ast::DecorationList{
262 create<ast::BindingDecoration>(0),
263 create<ast::GroupDecoration>(0),
264 });
265
266 WrapInFunction(Assign(Source{{56, 78}}, MemberAccessor("v", "a"),
267 MemberAccessor("v", "a")));
268
269 EXPECT_FALSE(r()->Resolve());
270 EXPECT_EQ(r()->error(),
271 "56:78 error: storage type of assignment must be constructible");
272 }
273
TEST_F(ResolverAssignmentValidationTest,AssignNonConstructible_RuntimeArray)274 TEST_F(ResolverAssignmentValidationTest, AssignNonConstructible_RuntimeArray) {
275 // [[block]] struct S { a : array<f32>; };
276 // [[group(0), binding(0)]] var<storage, read_write> v : S;
277 // v.a = v.a;
278
279 auto* s = Structure("S", {Member("a", ty.array(ty.f32()))},
280 {create<ast::StructBlockDecoration>()});
281 Global(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kStorage,
282 ast::Access::kReadWrite,
283 ast::DecorationList{
284 create<ast::BindingDecoration>(0),
285 create<ast::GroupDecoration>(0),
286 });
287
288 WrapInFunction(Assign(Source{{56, 78}}, MemberAccessor("v", "a"),
289 MemberAccessor("v", "a")));
290
291 EXPECT_FALSE(r()->Resolve());
292 EXPECT_EQ(r()->error(),
293 "56:78 error: storage type of assignment must be constructible");
294 }
295
TEST_F(ResolverAssignmentValidationTest,AssignToPhony_NonConstructibleStruct_Fail)296 TEST_F(ResolverAssignmentValidationTest,
297 AssignToPhony_NonConstructibleStruct_Fail) {
298 // [[block]]
299 // struct S {
300 // arr: array<i32>;
301 // };
302 // [[group(0), binding(0)]] var<storage, read_write> s : S;
303 // fn f() {
304 // _ = s;
305 // }
306 auto* s = Structure("S", {Member("arr", ty.array<i32>())}, {StructBlock()});
307 Global("s", ty.Of(s), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
308
309 WrapInFunction(Assign(Phony(), Expr(Source{{12, 34}}, "s")));
310
311 EXPECT_FALSE(r()->Resolve());
312 EXPECT_EQ(r()->error(),
313 "12:34 error: cannot assign 'S' to '_'. "
314 "'_' can only be assigned a constructible, pointer, texture or "
315 "sampler type");
316 }
317
TEST_F(ResolverAssignmentValidationTest,AssignToPhony_DynamicArray_Fail)318 TEST_F(ResolverAssignmentValidationTest, AssignToPhony_DynamicArray_Fail) {
319 // [[block]]
320 // struct S {
321 // arr: array<i32>;
322 // };
323 // [[group(0), binding(0)]] var<storage, read_write> s : S;
324 // fn f() {
325 // _ = s.arr;
326 // }
327 auto* s = Structure("S", {Member("arr", ty.array<i32>())}, {StructBlock()});
328 Global("s", ty.Of(s), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
329
330 WrapInFunction(Assign(Phony(), MemberAccessor(Source{{12, 34}}, "s", "arr")));
331
332 EXPECT_FALSE(r()->Resolve());
333 EXPECT_EQ(
334 r()->error(),
335 "12:34 error: cannot assign 'array<i32>' to '_'. "
336 "'_' can only be assigned a constructible, pointer, texture or sampler "
337 "type");
338 }
339
TEST_F(ResolverAssignmentValidationTest,AssignToPhony_Pass)340 TEST_F(ResolverAssignmentValidationTest, AssignToPhony_Pass) {
341 // [[block]]
342 // struct S {
343 // i: i32;
344 // arr: array<i32>;
345 // };
346 // [[block]]
347 // struct U {
348 // i: i32;
349 // };
350 // [[group(0), binding(0)]] var tex texture_2d;
351 // [[group(0), binding(1)]] var smp sampler;
352 // [[group(0), binding(2)]] var<uniform> u : U;
353 // [[group(0), binding(3)]] var<storage, read_write> s : S;
354 // var<workgroup> wg : array<f32, 10>
355 // fn f() {
356 // _ = 1;
357 // _ = 2u;
358 // _ = 3.0;
359 // _ = vec2<bool>();
360 // _ = tex;
361 // _ = smp;
362 // _ = &s;
363 // _ = s.i;
364 // _ = &s.arr;
365 // _ = u;
366 // _ = u.i;
367 // _ = wg;
368 // _ = wg[3];
369 // }
370 auto* S = Structure("S",
371 {
372 Member("i", ty.i32()),
373 Member("arr", ty.array<i32>()),
374 },
375 {StructBlock()});
376 auto* U = Structure("U", {Member("i", ty.i32())}, {StructBlock()});
377 Global("tex", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
378 GroupAndBinding(0, 0));
379 Global("smp", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(0, 1));
380 Global("u", ty.Of(U), ast::StorageClass::kUniform, GroupAndBinding(0, 2));
381 Global("s", ty.Of(S), ast::StorageClass::kStorage, GroupAndBinding(0, 3));
382 Global("wg", ty.array<f32, 10>(), ast::StorageClass::kWorkgroup);
383
384 WrapInFunction(Assign(Phony(), 1), //
385 Assign(Phony(), 2), //
386 Assign(Phony(), 3), //
387 Assign(Phony(), vec2<bool>()), //
388 Assign(Phony(), "tex"), //
389 Assign(Phony(), "smp"), //
390 Assign(Phony(), AddressOf("s")), //
391 Assign(Phony(), MemberAccessor("s", "i")), //
392 Assign(Phony(), AddressOf(MemberAccessor("s", "arr"))), //
393 Assign(Phony(), "u"), //
394 Assign(Phony(), MemberAccessor("u", "i")), //
395 Assign(Phony(), "wg"), //
396 Assign(Phony(), IndexAccessor("wg", 3)));
397
398 EXPECT_TRUE(r()->Resolve()) << r()->error();
399 }
400
401 } // namespace
402 } // namespace resolver
403 } // namespace tint
404