Ben Clayton | 313e618 | 2021-06-17 19:56:14 +0000 | [diff] [blame] | 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 | #include "src/resolver/resolver_test_helper.h" |
| 17 | #include "src/sem/atomic_type.h" |
| 18 | #include "src/sem/reference_type.h" |
| 19 | |
| 20 | #include "gmock/gmock.h" |
| 21 | |
| 22 | namespace tint { |
| 23 | namespace resolver { |
| 24 | namespace { |
| 25 | |
| 26 | struct ResolverAtomicValidationTest : public resolver::TestHelper, |
| 27 | public testing::Test {}; |
| 28 | |
Sarah | 4038fa7 | 2021-08-05 15:18:29 +0000 | [diff] [blame] | 29 | TEST_F(ResolverAtomicValidationTest, StorageClass_WorkGroup) { |
| 30 | Global("a", ty.atomic(Source{{12, 34}}, ty.i32()), |
| 31 | ast::StorageClass::kWorkgroup); |
| 32 | |
| 33 | EXPECT_TRUE(r()->Resolve()); |
| 34 | } |
| 35 | |
| 36 | TEST_F(ResolverAtomicValidationTest, StorageClass_Storage) { |
| 37 | auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))}, |
| 38 | {StructBlock()}); |
| 39 | Global("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite, |
| 40 | GroupAndBinding(0, 0)); |
| 41 | |
| 42 | EXPECT_TRUE(r()->Resolve()) << r()->error(); |
| 43 | } |
| 44 | |
| 45 | TEST_F(ResolverAtomicValidationTest, InvalidType) { |
Ben Clayton | 313e618 | 2021-06-17 19:56:14 +0000 | [diff] [blame] | 46 | Global("a", ty.atomic(ty.f32(Source{{12, 34}})), |
| 47 | ast::StorageClass::kWorkgroup); |
| 48 | |
| 49 | EXPECT_FALSE(r()->Resolve()); |
| 50 | EXPECT_EQ(r()->error(), "12:34 error: atomic only supports i32 or u32 types"); |
| 51 | } |
| 52 | |
Sarah | 4038fa7 | 2021-08-05 15:18:29 +0000 | [diff] [blame] | 53 | TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_Simple) { |
Ben Clayton | 313e618 | 2021-06-17 19:56:14 +0000 | [diff] [blame] | 54 | Global("a", ty.atomic(Source{{12, 34}}, ty.i32()), |
| 55 | ast::StorageClass::kPrivate); |
| 56 | |
| 57 | EXPECT_FALSE(r()->Resolve()); |
Sarah | 4038fa7 | 2021-08-05 15:18:29 +0000 | [diff] [blame] | 58 | EXPECT_EQ(r()->error(), |
| 59 | "12:34 error: atomic variables must have <storage> or <workgroup> " |
| 60 | "storage class"); |
Ben Clayton | 313e618 | 2021-06-17 19:56:14 +0000 | [diff] [blame] | 61 | } |
| 62 | |
Sarah | 4038fa7 | 2021-08-05 15:18:29 +0000 | [diff] [blame] | 63 | TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_Array) { |
| 64 | Global("a", ty.atomic(Source{{12, 34}}, ty.i32()), |
| 65 | ast::StorageClass::kPrivate); |
| 66 | |
| 67 | EXPECT_FALSE(r()->Resolve()); |
| 68 | EXPECT_EQ(r()->error(), |
| 69 | "12:34 error: atomic variables must have <storage> or <workgroup> " |
| 70 | "storage class"); |
| 71 | } |
| 72 | |
| 73 | TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_Struct) { |
Ben Clayton | 313e618 | 2021-06-17 19:56:14 +0000 | [diff] [blame] | 74 | auto* s = |
| 75 | Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))}); |
| 76 | Global("g", ty.Of(s), ast::StorageClass::kPrivate); |
| 77 | |
| 78 | EXPECT_FALSE(r()->Resolve()); |
| 79 | EXPECT_EQ(r()->error(), |
Sarah | 4038fa7 | 2021-08-05 15:18:29 +0000 | [diff] [blame] | 80 | "error: atomic variables must have <storage> or <workgroup> " |
| 81 | "storage class\n" |
| 82 | "note: atomic sub-type of 's' is declared here"); |
Ben Clayton | 313e618 | 2021-06-17 19:56:14 +0000 | [diff] [blame] | 83 | } |
| 84 | |
Sarah | 4038fa7 | 2021-08-05 15:18:29 +0000 | [diff] [blame] | 85 | TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_StructOfStruct) { |
| 86 | // struct Inner { m : atomic<i32>; }; |
| 87 | // struct Outer { m : array<Inner, 4>; }; |
| 88 | // var<private> g : Outer; |
| 89 | |
| 90 | auto* Inner = |
| 91 | Structure("Inner", {Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))}); |
| 92 | auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))}); |
| 93 | Global("g", ty.Of(Outer), ast::StorageClass::kPrivate); |
| 94 | |
| 95 | EXPECT_FALSE(r()->Resolve()); |
| 96 | EXPECT_EQ(r()->error(), |
| 97 | "error: atomic variables must have <storage> or <workgroup> " |
| 98 | "storage class\n" |
| 99 | "note: atomic sub-type of 'Outer' is declared here"); |
| 100 | } |
| 101 | |
| 102 | TEST_F(ResolverAtomicValidationTest, |
| 103 | InvalidStorageClass_StructOfStructOfArray) { |
| 104 | // struct Inner { m : array<atomic<i32>, 4>; }; |
| 105 | // struct Outer { m : array<Inner, 4>; }; |
| 106 | // var<private> g : Outer; |
| 107 | |
| 108 | auto* Inner = |
| 109 | Structure("Inner", {Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))}); |
| 110 | auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))}); |
| 111 | Global("g", ty.Of(Outer), ast::StorageClass::kPrivate); |
| 112 | |
| 113 | EXPECT_FALSE(r()->Resolve()); |
| 114 | EXPECT_EQ(r()->error(), |
| 115 | "error: atomic variables must have <storage> or <workgroup> " |
| 116 | "storage class\n" |
| 117 | "12:34 note: atomic sub-type of 'Outer' is declared here"); |
| 118 | } |
| 119 | |
| 120 | TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_ArrayOfArray) { |
| 121 | // type AtomicArray = array<atomic<i32>, 5>; |
| 122 | // var<private> v: array<s, 5>; |
| 123 | |
| 124 | auto* atomic_array = Alias(Source{{12, 34}}, "AtomicArray", |
| 125 | ty.atomic(Source{{12, 34}}, ty.i32())); |
| 126 | Global(Source{{56, 78}}, "v", ty.Of(atomic_array), |
| 127 | ast::StorageClass::kPrivate); |
| 128 | |
| 129 | EXPECT_FALSE(r()->Resolve()); |
| 130 | EXPECT_EQ(r()->error(), |
| 131 | "error: atomic variables must have <storage> or <workgroup> " |
| 132 | "storage class"); |
| 133 | } |
| 134 | |
| 135 | TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_ArrayOfStruct) { |
| 136 | // struct S{ |
| 137 | // m: atomic<u32>; |
| 138 | // }; |
| 139 | // var<private> v: array<S, 5>; |
| 140 | |
| 141 | auto* s = Structure("S", {Member("m", ty.atomic<u32>())}); |
| 142 | Global(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5), |
| 143 | ast::StorageClass::kPrivate); |
| 144 | |
| 145 | EXPECT_FALSE(r()->Resolve()); |
| 146 | EXPECT_EQ(r()->error(), |
| 147 | "error: atomic variables must have <storage> or <workgroup> " |
| 148 | "storage class\n" |
| 149 | "note: atomic sub-type of 'array<S, 5>' is declared here"); |
| 150 | } |
| 151 | |
| 152 | TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_ArrayOfStructOfArray) { |
| 153 | // type AtomicArray = array<atomic<i32>, 5>; |
| 154 | // struct S{ |
| 155 | // m: AtomicArray; |
| 156 | // }; |
| 157 | // var<private> v: array<S, 5>; |
| 158 | |
| 159 | auto* atomic_array = Alias(Source{{12, 34}}, "AtomicArray", |
| 160 | ty.atomic(Source{{12, 34}}, ty.i32())); |
| 161 | auto* s = Structure("S", {Member("m", ty.Of(atomic_array))}); |
| 162 | Global(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5), |
| 163 | ast::StorageClass::kPrivate); |
| 164 | |
| 165 | EXPECT_FALSE(r()->Resolve()); |
| 166 | EXPECT_EQ(r()->error(), |
| 167 | "error: atomic variables must have <storage> or <workgroup> " |
| 168 | "storage class\n" |
| 169 | "note: atomic sub-type of 'array<S, 5>' is declared here"); |
| 170 | } |
| 171 | |
| 172 | TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_Complex) { |
| 173 | // type AtomicArray = array<atomic<i32>, 5>; |
| 174 | // struct S6 { x: array<i32, 4>; }; |
| 175 | // struct S5 { x: S6; |
| 176 | // y: AtomicArray; |
| 177 | // z: array<atomic<u32>, 8>; }; |
| 178 | // struct S4 { x: S6; |
| 179 | // y: S5; |
| 180 | // z: array<atomic<i32>, 4>; }; |
| 181 | // struct S3 { x: S4; }; |
| 182 | // struct S2 { x: S3; }; |
| 183 | // struct S1 { x: S2; }; |
| 184 | // struct S0 { x: S1; }; |
| 185 | // var<private> g : S0; |
| 186 | |
| 187 | auto* atomic_array = Alias(Source{{12, 34}}, "AtomicArray", |
| 188 | ty.atomic(Source{{12, 34}}, ty.i32())); |
| 189 | auto* array_i32_4 = ty.array(ty.i32(), 4); |
| 190 | auto* array_atomic_u32_8 = ty.array(ty.atomic(ty.u32()), 8); |
| 191 | auto* array_atomic_i32_4 = ty.array(ty.atomic(ty.i32()), 4); |
| 192 | |
| 193 | auto* s6 = Structure("S6", {Member("x", array_i32_4)}); |
| 194 | auto* s5 = Structure("S5", {Member("x", ty.Of(s6)), // |
| 195 | Member("y", ty.Of(atomic_array)), // |
| 196 | Member("z", array_atomic_u32_8)}); // |
| 197 | auto* s4 = Structure("S4", {Member("x", ty.Of(s6)), // |
| 198 | Member("y", ty.Of(s5)), // |
| 199 | Member("z", array_atomic_i32_4)}); // |
| 200 | auto* s3 = Structure("S3", {Member("x", ty.Of(s4))}); |
| 201 | auto* s2 = Structure("S2", {Member("x", ty.Of(s3))}); |
| 202 | auto* s1 = Structure("S1", {Member("x", ty.Of(s2))}); |
| 203 | auto* s0 = Structure("S0", {Member("x", ty.Of(s1))}); |
| 204 | Global(Source{{56, 78}}, "g", ty.Of(s0), ast::StorageClass::kPrivate); |
| 205 | |
| 206 | EXPECT_FALSE(r()->Resolve()); |
| 207 | EXPECT_EQ(r()->error(), |
| 208 | "error: atomic variables must have <storage> or <workgroup> " |
| 209 | "storage class\n" |
| 210 | "note: atomic sub-type of 'S0' is declared here"); |
| 211 | } |
| 212 | |
| 213 | TEST_F(ResolverAtomicValidationTest, Struct_AccessMode_Read) { |
| 214 | auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))}, |
| 215 | {StructBlock()}); |
| 216 | Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, |
| 217 | ast::Access::kRead, GroupAndBinding(0, 0)); |
| 218 | |
| 219 | EXPECT_FALSE(r()->Resolve()); |
| 220 | EXPECT_EQ( |
| 221 | r()->error(), |
| 222 | "error: atomic variables in <storage> storage class must have read_write " |
| 223 | "access mode\n" |
| 224 | "note: atomic sub-type of 's' is declared here"); |
| 225 | } |
| 226 | |
| 227 | TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_Struct) { |
| 228 | auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))}, |
| 229 | {StructBlock()}); |
| 230 | Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, |
| 231 | ast::Access::kRead, GroupAndBinding(0, 0)); |
| 232 | |
| 233 | EXPECT_FALSE(r()->Resolve()); |
| 234 | EXPECT_EQ( |
| 235 | r()->error(), |
| 236 | "error: atomic variables in <storage> storage class must have read_write " |
| 237 | "access mode\n" |
| 238 | "note: atomic sub-type of 's' is declared here"); |
| 239 | } |
| 240 | |
| 241 | TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_StructOfStruct) { |
| 242 | // struct Inner { m : atomic<i32>; }; |
| 243 | // struct Outer { m : array<Inner, 4>; }; |
| 244 | // var<storage, read> g : Outer; |
| 245 | |
| 246 | auto* Inner = |
| 247 | Structure("Inner", {Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))}); |
| 248 | auto* Outer = |
| 249 | Structure("Outer", {Member("m", ty.Of(Inner))}, {StructBlock()}); |
| 250 | Global(Source{{56, 78}}, "g", ty.Of(Outer), ast::StorageClass::kStorage, |
| 251 | ast::Access::kRead, GroupAndBinding(0, 0)); |
| 252 | |
| 253 | EXPECT_FALSE(r()->Resolve()); |
| 254 | EXPECT_EQ( |
| 255 | r()->error(), |
| 256 | "error: atomic variables in <storage> storage class must have read_write " |
| 257 | "access mode\n" |
| 258 | "note: atomic sub-type of 'Outer' is declared here"); |
| 259 | } |
| 260 | |
| 261 | TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_StructOfStructOfArray) { |
| 262 | // struct Inner { m : array<atomic<i32>, 4>; }; |
| 263 | // struct Outer { m : array<Inner, 4>; }; |
| 264 | // var<storage, read> g : Outer; |
| 265 | |
| 266 | auto* Inner = |
| 267 | Structure("Inner", {Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))}); |
| 268 | auto* Outer = |
| 269 | Structure("Outer", {Member("m", ty.Of(Inner))}, {StructBlock()}); |
| 270 | Global(Source{{56, 78}}, "g", ty.Of(Outer), ast::StorageClass::kStorage, |
| 271 | ast::Access::kRead, GroupAndBinding(0, 0)); |
| 272 | |
| 273 | EXPECT_FALSE(r()->Resolve()); |
| 274 | EXPECT_EQ(r()->error(), |
| 275 | "error: atomic variables in <storage> storage class must have " |
| 276 | "read_write access mode\n" |
| 277 | "12:34 note: atomic sub-type of 'Outer' is declared here"); |
| 278 | } |
| 279 | |
| 280 | TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_Complex) { |
| 281 | // type AtomicArray = array<atomic<i32>, 5>; |
| 282 | // struct S6 { x: array<i32, 4>; }; |
| 283 | // struct S5 { x: S6; |
| 284 | // y: AtomicArray; |
| 285 | // z: array<atomic<u32>, 8>; }; |
| 286 | // struct S4 { x: S6; |
| 287 | // y: S5; |
| 288 | // z: array<atomic<i32>, 4>; }; |
| 289 | // struct S3 { x: S4; }; |
| 290 | // struct S2 { x: S3; }; |
| 291 | // struct S1 { x: S2; }; |
| 292 | // struct S0 { x: S1; }; |
| 293 | // var<storage, read> g : S0; |
| 294 | |
| 295 | auto* atomic_array = Alias(Source{{12, 34}}, "AtomicArray", |
| 296 | ty.atomic(Source{{12, 34}}, ty.i32())); |
| 297 | auto* array_i32_4 = ty.array(ty.i32(), 4); |
| 298 | auto* array_atomic_u32_8 = ty.array(ty.atomic(ty.u32()), 8); |
| 299 | auto* array_atomic_i32_4 = ty.array(ty.atomic(ty.i32()), 4); |
| 300 | |
| 301 | auto* s6 = Structure("S6", {Member("x", array_i32_4)}); |
| 302 | auto* s5 = Structure("S5", {Member("x", ty.Of(s6)), // |
| 303 | Member("y", ty.Of(atomic_array)), // |
| 304 | Member("z", array_atomic_u32_8)}); // |
| 305 | auto* s4 = Structure("S4", {Member("x", ty.Of(s6)), // |
| 306 | Member("y", ty.Of(s5)), // |
| 307 | Member("z", array_atomic_i32_4)}); // |
| 308 | auto* s3 = Structure("S3", {Member("x", ty.Of(s4))}); |
| 309 | auto* s2 = Structure("S2", {Member("x", ty.Of(s3))}); |
| 310 | auto* s1 = Structure("S1", {Member("x", ty.Of(s2))}); |
| 311 | auto* s0 = Structure("S0", {Member("x", ty.Of(s1))}, {StructBlock()}); |
| 312 | Global(Source{{56, 78}}, "g", ty.Of(s0), ast::StorageClass::kStorage, |
| 313 | ast::Access::kRead, GroupAndBinding(0, 0)); |
| 314 | |
| 315 | EXPECT_FALSE(r()->Resolve()); |
| 316 | EXPECT_EQ(r()->error(), |
| 317 | "error: atomic variables in <storage> storage class must have " |
| 318 | "read_write access mode\n" |
| 319 | "note: atomic sub-type of 'S0' is declared here"); |
| 320 | } |
Ben Clayton | 313e618 | 2021-06-17 19:56:14 +0000 | [diff] [blame] | 321 | |
| 322 | TEST_F(ResolverAtomicValidationTest, Local) { |
| 323 | WrapInFunction(Var("a", ty.atomic(Source{{12, 34}}, ty.i32()))); |
| 324 | |
| 325 | EXPECT_FALSE(r()->Resolve()); |
Sarah | 7249404 | 2021-07-28 22:43:36 +0000 | [diff] [blame] | 326 | EXPECT_EQ(r()->error(), |
| 327 | "12:34 error: function variable must have a constructible type"); |
Ben Clayton | 313e618 | 2021-06-17 19:56:14 +0000 | [diff] [blame] | 328 | } |
| 329 | |
Ben Clayton | 313e618 | 2021-06-17 19:56:14 +0000 | [diff] [blame] | 330 | } // namespace |
| 331 | } // namespace resolver |
| 332 | } // namespace tint |