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