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/struct.h"
21
22 namespace tint {
23 namespace resolver {
24 namespace {
25
26 using ResolverStorageClassValidationTest = ResolverTest;
27
TEST_F(ResolverStorageClassValidationTest,GlobalVariableNoStorageClass_Fail)28 TEST_F(ResolverStorageClassValidationTest, GlobalVariableNoStorageClass_Fail) {
29 // var g : f32;
30 Global(Source{{12, 34}}, "g", ty.f32(), ast::StorageClass::kNone);
31
32 EXPECT_FALSE(r()->Resolve());
33 EXPECT_EQ(r()->error(),
34 "12:34 error: global variables must have a storage class");
35 }
36
TEST_F(ResolverStorageClassValidationTest,StorageBufferBool)37 TEST_F(ResolverStorageClassValidationTest, StorageBufferBool) {
38 // var<storage> g : i32;
39 Global(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kStorage,
40 ast::DecorationList{
41 create<ast::BindingDecoration>(0),
42 create<ast::GroupDecoration>(0),
43 });
44
45 ASSERT_FALSE(r()->Resolve());
46
47 EXPECT_EQ(
48 r()->error(),
49 R"(56:78 error: variables declared in the <storage> storage class must be of a structure type)");
50 }
51
TEST_F(ResolverStorageClassValidationTest,StorageBufferPointer)52 TEST_F(ResolverStorageClassValidationTest, StorageBufferPointer) {
53 // var<storage> g : vec4<f32>;
54 Global(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::StorageClass::kStorage,
55 ast::DecorationList{
56 create<ast::BindingDecoration>(0),
57 create<ast::GroupDecoration>(0),
58 });
59
60 ASSERT_FALSE(r()->Resolve());
61
62 EXPECT_EQ(
63 r()->error(),
64 R"(56:78 error: variables declared in the <storage> storage class must be of a structure type)");
65 }
66
TEST_F(ResolverStorageClassValidationTest,StorageBufferArray)67 TEST_F(ResolverStorageClassValidationTest, StorageBufferArray) {
68 // var<storage, read> g : array<S, 3>;
69 auto* s = Structure("S", {Member("a", ty.f32())});
70 auto* a = ty.array(ty.Of(s), 3);
71 Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage,
72 ast::Access::kRead,
73 ast::DecorationList{
74 create<ast::BindingDecoration>(0),
75 create<ast::GroupDecoration>(0),
76 });
77
78 ASSERT_FALSE(r()->Resolve());
79
80 EXPECT_EQ(
81 r()->error(),
82 R"(56:78 error: variables declared in the <storage> storage class must be of a structure type)");
83 }
84
TEST_F(ResolverStorageClassValidationTest,StorageBufferBoolAlias)85 TEST_F(ResolverStorageClassValidationTest, StorageBufferBoolAlias) {
86 // type a = bool;
87 // var<storage, read> g : a;
88 auto* a = Alias("a", ty.bool_());
89 Global(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kStorage,
90 ast::DecorationList{
91 create<ast::BindingDecoration>(0),
92 create<ast::GroupDecoration>(0),
93 });
94
95 ASSERT_FALSE(r()->Resolve());
96
97 EXPECT_EQ(
98 r()->error(),
99 R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
100 56:78 note: while instantiating variable g)");
101 }
102
TEST_F(ResolverStorageClassValidationTest,NotStorage_AccessMode)103 TEST_F(ResolverStorageClassValidationTest, NotStorage_AccessMode) {
104 // var<private, read> g : a;
105 Global(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kPrivate,
106 ast::Access::kRead);
107
108 ASSERT_FALSE(r()->Resolve());
109
110 EXPECT_EQ(
111 r()->error(),
112 R"(56:78 error: only variables in <storage> storage class may declare an access mode)");
113 }
114
TEST_F(ResolverStorageClassValidationTest,StorageBufferNoBlockDecoration)115 TEST_F(ResolverStorageClassValidationTest, StorageBufferNoBlockDecoration) {
116 // struct S { x : i32 };
117 // var<storage, read> g : S;
118 auto* s = Structure(Source{{12, 34}}, "S", {Member("x", ty.i32())});
119 Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
120 ast::Access::kRead,
121 ast::DecorationList{
122 create<ast::BindingDecoration>(0),
123 create<ast::GroupDecoration>(0),
124 });
125
126 ASSERT_FALSE(r()->Resolve());
127
128 EXPECT_EQ(
129 r()->error(),
130 R"(12:34 error: structure used as a storage buffer must be declared with the [[block]] decoration
131 56:78 note: structure used as storage buffer here)");
132 }
133
TEST_F(ResolverStorageClassValidationTest,StorageBufferNoError_Basic)134 TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Basic) {
135 // [[block]] struct S { x : i32 };
136 // var<storage, read> g : S;
137 auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
138 {create<ast::StructBlockDecoration>()});
139 Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
140 ast::Access::kRead,
141 ast::DecorationList{
142 create<ast::BindingDecoration>(0),
143 create<ast::GroupDecoration>(0),
144 });
145
146 ASSERT_TRUE(r()->Resolve());
147 }
148
TEST_F(ResolverStorageClassValidationTest,StorageBufferNoError_Aliases)149 TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Aliases) {
150 // [[block]] struct S { x : i32 };
151 // type a1 = S;
152 // var<storage, read> g : a1;
153 auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
154 {create<ast::StructBlockDecoration>()});
155 auto* a1 = Alias("a1", ty.Of(s));
156 auto* a2 = Alias("a2", ty.Of(a1));
157 Global(Source{{56, 78}}, "g", ty.Of(a2), ast::StorageClass::kStorage,
158 ast::Access::kRead,
159 ast::DecorationList{
160 create<ast::BindingDecoration>(0),
161 create<ast::GroupDecoration>(0),
162 });
163
164 ASSERT_TRUE(r()->Resolve());
165 }
166
TEST_F(ResolverStorageClassValidationTest,UniformBuffer_Struct_Runtime)167 TEST_F(ResolverStorageClassValidationTest, UniformBuffer_Struct_Runtime) {
168 // [[block]] struct S { m: array<f32>; };
169 // [[group(0), binding(0)]] var<uniform, > svar : S;
170
171 auto* s = Structure(Source{{12, 34}}, "S", {Member("m", ty.array<i32>())},
172 {create<ast::StructBlockDecoration>()});
173
174 Global(Source{{56, 78}}, "svar", ty.Of(s), ast::StorageClass::kUniform,
175 ast::DecorationList{
176 create<ast::BindingDecoration>(0),
177 create<ast::GroupDecoration>(0),
178 });
179
180 ASSERT_FALSE(r()->Resolve());
181 EXPECT_EQ(r()->error(),
182 "56:78 error: structure containing a runtime sized array cannot be "
183 "used as a uniform buffer\n12:34 note: structure is declared here");
184 }
185
TEST_F(ResolverStorageClassValidationTest,UniformBufferBool)186 TEST_F(ResolverStorageClassValidationTest, UniformBufferBool) {
187 // var<uniform> g : bool;
188 Global(Source{{56, 78}}, "g", ty.bool_(), ast::StorageClass::kUniform,
189 ast::DecorationList{
190 create<ast::BindingDecoration>(0),
191 create<ast::GroupDecoration>(0),
192 });
193
194 ASSERT_FALSE(r()->Resolve());
195
196 EXPECT_EQ(
197 r()->error(),
198 R"(56:78 error: Type 'bool' cannot be used in storage class 'uniform' as it is non-host-shareable
199 56:78 note: while instantiating variable g)");
200 }
201
TEST_F(ResolverStorageClassValidationTest,UniformBufferPointer)202 TEST_F(ResolverStorageClassValidationTest, UniformBufferPointer) {
203 // var<uniform> g : vec4<f32>;
204 Global(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::StorageClass::kUniform,
205 ast::DecorationList{
206 create<ast::BindingDecoration>(0),
207 create<ast::GroupDecoration>(0),
208 });
209
210 ASSERT_FALSE(r()->Resolve());
211
212 EXPECT_EQ(
213 r()->error(),
214 R"(56:78 error: variables declared in the <uniform> storage class must be of a structure type)");
215 }
216
TEST_F(ResolverStorageClassValidationTest,UniformBufferArray)217 TEST_F(ResolverStorageClassValidationTest, UniformBufferArray) {
218 // var<uniform> g : array<S, 3>;
219 auto* s = Structure("S", {Member("a", ty.f32())});
220 auto* a = ty.array(ty.Of(s), 3);
221 Global(Source{{56, 78}}, "g", a, ast::StorageClass::kUniform,
222 ast::DecorationList{
223 create<ast::BindingDecoration>(0),
224 create<ast::GroupDecoration>(0),
225 });
226
227 ASSERT_FALSE(r()->Resolve());
228
229 EXPECT_EQ(
230 r()->error(),
231 R"(56:78 error: variables declared in the <uniform> storage class must be of a structure type)");
232 }
233
TEST_F(ResolverStorageClassValidationTest,UniformBufferBoolAlias)234 TEST_F(ResolverStorageClassValidationTest, UniformBufferBoolAlias) {
235 // type a = bool;
236 // var<uniform> g : a;
237 auto* a = Alias("a", ty.bool_());
238 Global(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kUniform,
239 ast::DecorationList{
240 create<ast::BindingDecoration>(0),
241 create<ast::GroupDecoration>(0),
242 });
243
244 ASSERT_FALSE(r()->Resolve());
245
246 EXPECT_EQ(
247 r()->error(),
248 R"(56:78 error: Type 'bool' cannot be used in storage class 'uniform' as it is non-host-shareable
249 56:78 note: while instantiating variable g)");
250 }
251
TEST_F(ResolverStorageClassValidationTest,UniformBufferNoBlockDecoration)252 TEST_F(ResolverStorageClassValidationTest, UniformBufferNoBlockDecoration) {
253 // struct S { x : i32 };
254 // var<uniform> g : S;
255 auto* s = Structure(Source{{12, 34}}, "S", {Member("x", ty.i32())});
256 Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kUniform,
257 ast::DecorationList{
258 create<ast::BindingDecoration>(0),
259 create<ast::GroupDecoration>(0),
260 });
261
262 ASSERT_FALSE(r()->Resolve());
263
264 EXPECT_EQ(
265 r()->error(),
266 R"(12:34 error: structure used as a uniform buffer must be declared with the [[block]] decoration
267 56:78 note: structure used as uniform buffer here)");
268 }
269
TEST_F(ResolverStorageClassValidationTest,UniformBufferNoError_Basic)270 TEST_F(ResolverStorageClassValidationTest, UniformBufferNoError_Basic) {
271 // [[block]] struct S { x : i32 };
272 // var<uniform> g : S;
273 auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
274 {create<ast::StructBlockDecoration>()});
275 Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kUniform,
276 ast::DecorationList{
277 create<ast::BindingDecoration>(0),
278 create<ast::GroupDecoration>(0),
279 });
280
281 ASSERT_TRUE(r()->Resolve()) << r()->error();
282 }
283
TEST_F(ResolverStorageClassValidationTest,UniformBufferNoError_Aliases)284 TEST_F(ResolverStorageClassValidationTest, UniformBufferNoError_Aliases) {
285 // [[block]] struct S { x : i32 };
286 // type a1 = S;
287 // var<uniform> g : a1;
288 auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
289 {create<ast::StructBlockDecoration>()});
290 auto* a1 = Alias("a1", ty.Of(s));
291 Global(Source{{56, 78}}, "g", ty.Of(a1), ast::StorageClass::kUniform,
292 ast::DecorationList{
293 create<ast::BindingDecoration>(0),
294 create<ast::GroupDecoration>(0),
295 });
296
297 ASSERT_TRUE(r()->Resolve()) << r()->error();
298 }
299
300 } // namespace
301 } // namespace resolver
302 } // namespace tint
303