| // Copyright 2021 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "src/tint/lang/wgsl/resolver/resolver.h" |
| |
| #include "gmock/gmock.h" |
| #include "src/tint/lang/wgsl/resolver/resolver_helper_test.h" |
| #include "src/tint/lang/wgsl/sem/struct.h" |
| |
| namespace tint::resolver { |
| namespace { |
| |
| using namespace tint::core::fluent_types; // NOLINT |
| using namespace tint::core::number_suffixes; // NOLINT |
| |
| using ::testing::HasSubstr; |
| |
| using ResolverAddressSpaceValidationTest = ResolverTest; |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_NoAddressSpace_Fail) { |
| // var g : f32; |
| GlobalVar(Source{{12, 34}}, "g", ty.f32()); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ( |
| r()->error(), |
| R"(12:34 error: module-scope 'var' declarations that are not of texture or sampler types must provide an address space)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_NoAddressSpace_Fail) { |
| // type g = ptr<f32>; |
| Alias("g", ty(Source{{12, 34}}, "ptr", ty.f32())); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), R"(12:34 error: 'ptr' requires at least 2 template arguments)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_FunctionAddressSpace_Fail) { |
| // var<private> g : f32; |
| GlobalVar(Source{{12, 34}}, "g", ty.f32(), core::AddressSpace::kFunction); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| "12:34 error: module-scope 'var' must not use address space 'function'"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Private_RuntimeArray) { |
| // var<private> v : array<i32>; |
| GlobalVar(Source{{56, 78}}, "v", ty.array(Source{{12, 34}}, ty.i32()), |
| core::AddressSpace::kPrivate); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space |
| 56:78 note: while instantiating 'var' v)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Private_RuntimeArray) { |
| // type t : ptr<private, array<i32>>; |
| Alias("t", ty.ptr<private_>(Source{{56, 78}}, ty.array(Source{{12, 34}}, ty.i32()))); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space |
| 56:78 note: while instantiating ptr<private, array<i32>, read_write>)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Private_RuntimeArrayInStruct) { |
| // struct S { m : array<i32> }; |
| // var<private> v : S; |
| Structure("S", Vector{Member(Source{{12, 34}}, "m", ty.array(ty.i32()))}); |
| GlobalVar(Source{{56, 78}}, "v", ty("S"), core::AddressSpace::kPrivate); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| R"(error: runtime-sized arrays can only be used in the <storage> address space |
| 12:34 note: while analyzing structure member S.m |
| 56:78 note: while instantiating 'var' v)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Private_RuntimeArrayInStruct) { |
| // struct S { m : array<i32> }; |
| // type t = ptr<private, S>; |
| Structure("S", Vector{Member(Source{{12, 34}}, "m", ty.array(ty.i32()))}); |
| Alias("t", ty.ptr<private_>(ty("S"))); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| R"(error: runtime-sized arrays can only be used in the <storage> address space |
| 12:34 note: while analyzing structure member S.m |
| note: while instantiating ptr<private, S, read_write>)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Workgroup_RuntimeArray) { |
| // var<workgroup> v : array<i32>; |
| GlobalVar(Source{{56, 78}}, "v", ty.array(Source{{12, 34}}, ty.i32()), |
| core::AddressSpace::kWorkgroup); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space |
| 56:78 note: while instantiating 'var' v)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Workgroup_RuntimeArray) { |
| // type t = ptr<workgroup, array<i32>>; |
| Alias("t", ty.ptr<workgroup>(ty.array(Source{{12, 34}}, ty.i32()))); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space |
| note: while instantiating ptr<workgroup, array<i32>, read_write>)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Workgroup_RuntimeArrayInStruct) { |
| // struct S { m : array<i32> }; |
| // var<workgroup> v : S; |
| Structure("S", Vector{Member(Source{{12, 34}}, "m", ty.array(ty.i32()))}); |
| GlobalVar(Source{{56, 78}}, "v", ty("S"), core::AddressSpace::kWorkgroup); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| R"(error: runtime-sized arrays can only be used in the <storage> address space |
| 12:34 note: while analyzing structure member S.m |
| 56:78 note: while instantiating 'var' v)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Workgroup_RuntimeArrayInStruct) { |
| // struct S { m : array<i32> }; |
| // type t = ptr<workgroup, S>; |
| Structure("S", Vector{Member(Source{{12, 34}}, "m", ty.array(ty.i32()))}); |
| Alias(Source{{56, 78}}, "t", ty.ptr<workgroup>(ty("S"))); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| R"(error: runtime-sized arrays can only be used in the <storage> address space |
| 12:34 note: while analyzing structure member S.m |
| note: while instantiating ptr<workgroup, S, read_write>)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_Bool) { |
| // var<storage> g : bool; |
| GlobalVar(Source{{56, 78}}, "g", ty.bool_(Source{{12, 34}}), core::AddressSpace::kStorage, |
| Binding(0_a), Group(0_a)); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| |
| EXPECT_EQ( |
| r()->error(), |
| R"(12:34 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable |
| 56:78 note: while instantiating 'var' g)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_Bool) { |
| // type t = ptr<storage, bool>; |
| Alias(Source{{56, 78}}, "t", ty.ptr<storage>(ty.bool_(Source{{12, 34}}))); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| |
| EXPECT_EQ( |
| r()->error(), |
| R"(12:34 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable |
| note: while instantiating ptr<storage, bool, read>)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_BoolAlias) { |
| // type a = bool; |
| // @binding(0) @group(0) var<storage, read> g : a; |
| Alias("a", ty.bool_()); |
| GlobalVar(Source{{56, 78}}, "g", ty(Source{{12, 34}}, "a"), core::AddressSpace::kStorage, |
| Binding(0_a), Group(0_a)); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| |
| EXPECT_EQ( |
| r()->error(), |
| R"(12:34 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable |
| 56:78 note: while instantiating 'var' g)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_BoolAlias) { |
| // type a = bool; |
| // type t = ptr<storage, a>; |
| Alias("a", ty.bool_()); |
| Alias(Source{{56, 78}}, "t", ty.ptr<storage>(ty(Source{{12, 34}}, "a"))); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| |
| EXPECT_EQ( |
| r()->error(), |
| R"(12:34 error: Type 'bool' cannot be used in address space 'storage' as it is non-host-shareable |
| note: while instantiating ptr<storage, bool, read>)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_Pointer) { |
| // var<storage> g : ptr<private, f32>; |
| GlobalVar(Source{{56, 78}}, "g", ty.ptr<private_, f32>(Source{{12, 34}}), |
| core::AddressSpace::kStorage, Binding(0_a), Group(0_a)); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| |
| EXPECT_EQ( |
| r()->error(), |
| R"(12:34 error: Type 'ptr<private, f32, read_write>' cannot be used in address space 'storage' as it is non-host-shareable |
| 56:78 note: while instantiating 'var' g)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_IntScalar) { |
| // var<storage> g : i32; |
| GlobalVar("g", ty.i32(), core::AddressSpace::kStorage, Binding(0_a), Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_IntScalar) { |
| // type t = ptr<storage, i32; |
| Alias("t", ty.ptr<storage, i32>()); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_F16) { |
| // enable f16; |
| // var<storage> g : f16; |
| Enable(wgsl::Extension::kF16); |
| |
| GlobalVar("g", ty.f16(), core::AddressSpace::kStorage, Binding(0_a), Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_F16) { |
| // enable f16; |
| // type t = ptr<storage, f16>; |
| Enable(wgsl::Extension::kF16); |
| |
| Alias("t", ty.ptr<storage, f16>()); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_F16Alias) { |
| // enable f16; |
| // type a = f16; |
| // var<storage, read> g : a; |
| Enable(wgsl::Extension::kF16); |
| |
| Alias("a", ty.f16()); |
| GlobalVar("g", ty("a"), core::AddressSpace::kStorage, Binding(0_a), Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_F16Alias) { |
| // enable f16; |
| // type a = f16; |
| // type t = ptr<storage, a>; |
| Enable(wgsl::Extension::kF16); |
| |
| Alias("a", ty.f16()); |
| Alias("t", ty.ptr<storage>(ty("a"))); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_VectorF32) { |
| // var<storage> g : vec4<f32>; |
| GlobalVar("g", ty.vec4<f32>(), core::AddressSpace::kStorage, Binding(0_a), Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_VectorF32) { |
| // type t = ptr<storage, vec4<f32>>; |
| Alias("t", ty.ptr<storage, vec4<f32>>()); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_VectorF16) { |
| // var<storage> g : vec4<f16>; |
| Enable(wgsl::Extension::kF16); |
| GlobalVar("g", ty.vec(ty.f16(), 4u), core::AddressSpace::kStorage, Binding(0_a), Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_VectorF16) { |
| // type t = ptr<storage, vec4<f16>>; |
| Enable(wgsl::Extension::kF16); |
| Alias("t", ty.ptr<storage, vec4<f32>>()); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_ArrayF32) { |
| // struct S{ a : f32 }; |
| // var<storage, read> g : array<S, 3u>; |
| Structure("S", Vector{Member("a", ty.f32())}); |
| GlobalVar("g", ty.array(ty("S"), 3_u), core::AddressSpace::kStorage, core::Access::kRead, |
| Binding(0_a), Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_ArrayF32) { |
| // struct S{ a : f32 }; |
| // type t = ptr<storage, array<S, 3u>>; |
| Structure("S", Vector{Member("a", ty.f32())}); |
| Alias("t", ty.ptr<storage>(ty.array(ty("S"), 3_u))); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_ArrayF16) { |
| // enable f16; |
| // struct S{ a : f16 }; |
| // var<storage, read> g : array<S, 3u>; |
| Enable(wgsl::Extension::kF16); |
| |
| Structure("S", Vector{Member("a", ty.f16())}); |
| GlobalVar("g", ty.array(ty("S"), 3_u), core::AddressSpace::kStorage, core::Access::kRead, |
| Binding(0_a), Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_ArrayF16) { |
| // enable f16; |
| // struct S{ a : f16 }; |
| // type t = ptr<storage, array<S, 3u>, read>; |
| Enable(wgsl::Extension::kF16); |
| |
| Structure("S", Vector{Member("a", ty.f16())}); |
| Alias("t", ty.ptr<storage, read>(ty.array(ty("S"), 3_u))); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_StructI32) { |
| // struct S { x : i32 }; |
| // var<storage, read> g : S; |
| Structure("S", Vector{Member("x", ty.i32())}); |
| GlobalVar("g", ty("S"), core::AddressSpace::kStorage, core::Access::kRead, Binding(0_a), |
| Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_StructI32) { |
| // struct S { x : i32 }; |
| // type t = ptr<storage, S, read>; |
| Structure("S", Vector{Member("x", ty.i32())}); |
| Alias("t", ty.ptr<storage, read>(ty("S"))); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_StructI32Aliases) { |
| // struct S { x : i32 }; |
| // type a1 = S; |
| // var<storage, read> g : a1; |
| Structure("S", Vector{Member("x", ty.i32())}); |
| Alias("a1", ty("S")); |
| Alias("a2", ty("a1")); |
| GlobalVar("g", ty("a2"), core::AddressSpace::kStorage, core::Access::kRead, Binding(0_a), |
| Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_StructI32Aliases) { |
| // struct S { x : i32 }; |
| // type a1 = S; |
| // type t = ptr<storage, a1, read>; |
| Structure("S", Vector{Member("x", ty.i32())}); |
| Alias("a1", ty("S")); |
| Alias("a2", ty("a1")); |
| Alias("t", ty.ptr<storage, read>(ty("a2"))); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_StructF16) { |
| // struct S { x : f16 }; |
| // var<storage, read> g : S; |
| Enable(wgsl::Extension::kF16); |
| |
| Structure("S", Vector{Member("x", ty.f16())}); |
| GlobalVar("g", ty("S"), core::AddressSpace::kStorage, core::Access::kRead, Binding(0_a), |
| Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_StructF16) { |
| // struct S { x : f16 }; |
| // type t = ptr<storage, S, read>; |
| Enable(wgsl::Extension::kF16); |
| |
| Structure("S", Vector{Member("x", ty.f16())}); |
| Alias("t", ty.ptr<storage, read>(ty("S"))); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_StructF16Aliases) { |
| // struct S { x : f16 }; |
| // type a1 = S; |
| // var<storage, read> g : a1; |
| Enable(wgsl::Extension::kF16); |
| |
| Structure("S", Vector{Member("x", ty.f16())}); |
| Alias("a1", ty("S")); |
| Alias("a2", ty("a1")); |
| GlobalVar("g", ty("a2"), core::AddressSpace::kStorage, core::Access::kRead, Binding(0_a), |
| Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_StructF16Aliases) { |
| // struct S { x : f16 }; |
| // type a1 = S; |
| // type t = ptr<storage, a1, read>; |
| Enable(wgsl::Extension::kF16); |
| |
| Structure("S", Vector{Member("x", ty.f16())}); |
| Alias("a1", ty("S")); |
| Alias("a2", ty("a1")); |
| Alias("g", ty.ptr<storage, read>(ty("a2"))); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_NotStorage_AccessMode) { |
| // var<private, read> g : a; |
| GlobalVar(Source{{12, 34}}, "g", ty.i32(), core::AddressSpace::kPrivate, core::Access::kRead); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| |
| EXPECT_EQ( |
| r()->error(), |
| R"(12:34 error: only variables in <storage> address space may specify an access mode)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_NotStorage_AccessMode) { |
| // type t = ptr<private, i32, read>; |
| Alias("t", ty.ptr<private_, i32, read>(Source{{12, 34}})); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| |
| EXPECT_EQ( |
| r()->error(), |
| R"(12:34 error: only pointers in <storage> address space may specify an access mode)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_ReadAccessMode) { |
| // @group(0) @binding(0) var<storage, read> a : i32; |
| GlobalVar("a", ty.i32(), core::AddressSpace::kStorage, core::Access::kRead, Group(0_a), |
| Binding(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_ReadAccessMode) { |
| // type t = ptr<storage, i32, read>; |
| Alias("t", ty.ptr<storage, i32, read>()); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_ReadWriteAccessMode) { |
| // @group(0) @binding(0) var<storage, read_write> a : i32; |
| GlobalVar("a", ty.i32(), core::AddressSpace::kStorage, core::Access::kReadWrite, Group(0_a), |
| Binding(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_ReadWriteAccessMode) { |
| // type t = ptr<storage, i32, read_write>; |
| Alias("t", ty.ptr<storage, i32, read_write>()); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_Storage_WriteAccessMode) { |
| // @group(0) @binding(0) var<storage, read_write> a : i32; |
| GlobalVar(Source{{12, 34}}, "a", ty.i32(), core::AddressSpace::kStorage, core::Access::kWrite, |
| Group(0_a), Binding(0_a)); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| |
| EXPECT_EQ(r()->error(), |
| R"(12:34 error: access mode 'write' is not valid for the 'storage' address space)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_Storage_WriteAccessMode) { |
| // type t = ptr<storage, i32, write>; |
| Alias("t", ty.ptr<storage, i32, write>(Source{{12, 34}})); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| |
| EXPECT_EQ(r()->error(), |
| R"(12:34 error: access mode 'write' is not valid for the 'storage' address space)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBuffer_Struct_Runtime) { |
| // struct S { m: array<f32>; }; |
| // @group(0) @binding(0) var<uniform> svar : S; |
| |
| Structure("S", Vector{Member(Source{{56, 78}}, "m", ty.array(Source{{12, 34}}, ty.i32()))}); |
| |
| GlobalVar(Source{{90, 12}}, "svar", ty("S"), core::AddressSpace::kUniform, Binding(0_a), |
| Group(0_a)); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| R"(12:34 error: runtime-sized arrays can only be used in the <storage> address space |
| 56:78 note: while analyzing structure member S.m |
| 90:12 note: while instantiating 'var' svar)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBuffer_Struct_Runtime) { |
| // struct S { m: array<f32>; }; |
| // type t = ptr<uniform, S>; |
| |
| Structure("S", Vector{Member(Source{{56, 78}}, "m", ty.array(Source{{12, 34}}, ty.i32()))}); |
| |
| Alias("t", ty.ptr<uniform>(Source{{90, 12}}, ty("S"))); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| EXPECT_EQ( |
| r()->error(), |
| R"(12:34 error: uniform storage requires that array elements are aligned to 16 bytes, but array element of type 'i32' has a stride of 4 bytes. Consider using a vector or struct as the element type instead. |
| note: see layout of struct: |
| /* align(4) size(4) */ struct S { |
| /* offset(0) align(4) size(4) */ m : array<i32>; |
| /* */ }; |
| 90:12 note: 'S' used in address space 'uniform' here)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferBool) { |
| // var<uniform> g : bool; |
| GlobalVar(Source{{56, 78}}, "g", ty.bool_(Source{{12, 34}}), core::AddressSpace::kUniform, |
| Binding(0_a), Group(0_a)); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| |
| EXPECT_EQ( |
| r()->error(), |
| R"(12:34 error: Type 'bool' cannot be used in address space 'uniform' as it is non-host-shareable |
| 56:78 note: while instantiating 'var' g)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferBool) { |
| // type t = ptr<uniform, bool>; |
| Alias("t", ty.ptr<uniform>(Source{{56, 78}}, ty.bool_(Source{{12, 34}}))); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| |
| EXPECT_EQ( |
| r()->error(), |
| R"(12:34 error: Type 'bool' cannot be used in address space 'uniform' as it is non-host-shareable |
| 56:78 note: while instantiating ptr<uniform, bool, read>)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferBoolAlias) { |
| // type a = bool; |
| // var<uniform> g : a; |
| Alias("a", ty.bool_()); |
| GlobalVar(Source{{56, 78}}, "g", ty(Source{{12, 34}}, "a"), core::AddressSpace::kUniform, |
| Binding(0_a), Group(0_a)); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| |
| EXPECT_EQ( |
| r()->error(), |
| R"(12:34 error: Type 'bool' cannot be used in address space 'uniform' as it is non-host-shareable |
| 56:78 note: while instantiating 'var' g)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferBoolAlias) { |
| // type a = bool; |
| // type t = ptr<uniform, a>; |
| Alias("a", ty.bool_()); |
| Alias("t", ty.ptr<uniform>(Source{{56, 78}}, ty(Source{{12, 34}}, "a"))); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| |
| EXPECT_EQ( |
| r()->error(), |
| R"(12:34 error: Type 'bool' cannot be used in address space 'uniform' as it is non-host-shareable |
| 56:78 note: while instantiating ptr<uniform, bool, read>)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformPointer) { |
| // var<uniform> g : ptr<private, f32>; |
| GlobalVar(Source{{56, 78}}, "g", ty.ptr<private_, f32>(Source{{12, 34}}), |
| core::AddressSpace::kUniform, Binding(0_a), Group(0_a)); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| |
| EXPECT_EQ( |
| r()->error(), |
| R"(12:34 error: Type 'ptr<private, f32, read_write>' cannot be used in address space 'uniform' as it is non-host-shareable |
| 56:78 note: while instantiating 'var' g)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferIntScalar) { |
| // var<uniform> g : i32; |
| GlobalVar(Source{{56, 78}}, "g", ty.i32(), core::AddressSpace::kUniform, Binding(0_a), |
| Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferIntScalar) { |
| // type t = ptr<uniform, i32>; |
| Alias("t", ty.ptr<uniform, i32>()); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferF16) { |
| // enable f16; |
| // var<uniform> g : f16; |
| Enable(wgsl::Extension::kF16); |
| |
| GlobalVar("g", ty.f16(), core::AddressSpace::kUniform, Binding(0_a), Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferF16) { |
| // enable f16; |
| // type t = ptr<uniform, f16>; |
| Enable(wgsl::Extension::kF16); |
| |
| Alias("t", ty.ptr<uniform, f16>()); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferVectorF32) { |
| // var<uniform> g : vec4<f32>; |
| GlobalVar("g", ty.vec4<f32>(), core::AddressSpace::kUniform, Binding(0_a), Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferVectorF32) { |
| // type t = ptr<uniform, vec4<f32>>; |
| Alias("t", ty.ptr<uniform, vec4<f32>>()); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferVectorF16) { |
| // enable f16; |
| // var<uniform> g : vec4<f16>; |
| Enable(wgsl::Extension::kF16); |
| |
| GlobalVar("g", ty.vec4<f16>(), core::AddressSpace::kUniform, Binding(0_a), Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferVectorF16) { |
| // enable f16; |
| // type t = ptr<uniform, vec4<f16>>; |
| Enable(wgsl::Extension::kF16); |
| |
| Alias("t", ty.ptr<uniform, f16>()); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferArrayF32) { |
| // struct S { |
| // @size(16) f : f32; |
| // } |
| // var<uniform> g : array<S, 3u>; |
| Structure("S", Vector{Member("a", ty.f32(), Vector{MemberSize(16_a)})}); |
| GlobalVar("g", ty.array(ty("S"), 3_u), core::AddressSpace::kUniform, Binding(0_a), Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferArrayF32) { |
| // struct S { |
| // @size(16) f : f32; |
| // } |
| // type t = ptr<uniform, array<S, 3u>>; |
| Structure("S", Vector{Member("a", ty.f32(), Vector{MemberSize(16_a)})}); |
| Alias("t", ty.ptr<uniform>(ty.array(ty("S"), 3_u))); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferArrayF16) { |
| // enable f16; |
| // struct S { |
| // @size(16) f : f16; |
| // } |
| // var<uniform> g : array<S, 3u>; |
| Enable(wgsl::Extension::kF16); |
| |
| Structure("S", Vector{Member("a", ty.f16(), Vector{MemberSize(16_a)})}); |
| GlobalVar("g", ty.array(ty("S"), 3_u), core::AddressSpace::kUniform, Binding(0_a), Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferArrayF16) { |
| // enable f16; |
| // struct S { |
| // @size(16) f : f16; |
| // } |
| // type t = ptr<uniform, array<S, 3u>>; |
| Enable(wgsl::Extension::kF16); |
| |
| Structure("S", Vector{Member("a", ty.f16(), Vector{MemberSize(16_a)})}); |
| Alias("t", ty.ptr<uniform>(ty.array(ty("S"), 3_u))); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferStructI32) { |
| // struct S { x : i32 }; |
| // var<uniform> g : S; |
| Structure("S", Vector{Member("x", ty.i32())}); |
| GlobalVar("g", ty("S"), core::AddressSpace::kUniform, Binding(0_a), Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferStructI32) { |
| // struct S { x : i32 }; |
| // type t = ptr<uniform, S>; |
| Structure("S", Vector{Member("x", ty.i32())}); |
| Alias("t", ty.ptr<uniform>(ty("S"))); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferStructI32Aliases) { |
| // struct S { x : i32 }; |
| // type a1 = S; |
| // var<uniform> g : a1; |
| Structure("S", Vector{Member("x", ty.i32())}); |
| Alias("a1", ty("S")); |
| GlobalVar("g", ty("a1"), core::AddressSpace::kUniform, Binding(0_a), Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferStructI32Aliases) { |
| // struct S { x : i32 }; |
| // type a1 = S; |
| // type t = ptr<uniform, a1>; |
| Structure("S", Vector{Member("x", ty.i32())}); |
| Alias("a1", ty("S")); |
| Alias("t", ty.ptr<uniform>(ty("a1"))); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferStructF16) { |
| // enable f16; |
| // struct S { x : f16 }; |
| // var<uniform> g : S; |
| Enable(wgsl::Extension::kF16); |
| |
| Structure("S", Vector{Member("x", ty.f16())}); |
| GlobalVar("g", ty("S"), core::AddressSpace::kUniform, Binding(0_a), Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferStructF16) { |
| // enable f16; |
| // struct S { x : f16 }; |
| // type t = ptr<uniform, S>; |
| Enable(wgsl::Extension::kF16); |
| |
| Structure("S", Vector{Member("x", ty.f16())}); |
| Alias("t", ty.ptr<uniform>(ty("S"))); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_UniformBufferStructF16Aliases) { |
| // enable f16; |
| // struct S { x : f16 }; |
| // type a1 = S; |
| // var<uniform> g : a1; |
| Enable(wgsl::Extension::kF16); |
| |
| Structure("S", Vector{Member("x", ty.f16())}); |
| Alias("a1", ty("S")); |
| GlobalVar("g", ty("a1"), core::AddressSpace::kUniform, Binding(0_a), Group(0_a)); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_UniformBufferStructF16Aliases) { |
| // enable f16; |
| // struct S { x : f16 }; |
| // type a1 = S; |
| // type t = ptr<uniform, a1>; |
| Enable(wgsl::Extension::kF16); |
| |
| Structure("S", Vector{Member("x", ty.f16())}); |
| Alias("a1", ty("S")); |
| Alias("t", ty.ptr<uniform>(ty("a1"))); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_PushConstantBool) { |
| // enable chromium_experimental_push_constant; |
| // var<push_constant> g : bool; |
| Enable(wgsl::Extension::kChromiumExperimentalPushConstant); |
| GlobalVar(Source{{56, 78}}, "g", ty.bool_(Source{{12, 34}}), core::AddressSpace::kPushConstant); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| EXPECT_EQ( |
| r()->error(), |
| R"(12:34 error: Type 'bool' cannot be used in address space 'push_constant' as it is non-host-shareable |
| 56:78 note: while instantiating 'var' g)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_PushConstantBool) { |
| // enable chromium_experimental_push_constant; |
| // type t = ptr<push_constant, bool>; |
| Enable(wgsl::Extension::kChromiumExperimentalPushConstant); |
| Alias(Source{{56, 78}}, "t", ty.ptr<push_constant>(ty.bool_(Source{{12, 34}}))); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| EXPECT_EQ( |
| r()->error(), |
| R"(12:34 error: Type 'bool' cannot be used in address space 'push_constant' as it is non-host-shareable |
| note: while instantiating ptr<push_constant, bool, read_write>)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_PushConstantF16) { |
| // enable f16; |
| // enable chromium_experimental_push_constant; |
| // var<push_constant> g : f16; |
| Enable(wgsl::Extension::kF16); |
| Enable(wgsl::Extension::kChromiumExperimentalPushConstant); |
| GlobalVar("g", ty.f16(Source{{56, 78}}), core::AddressSpace::kPushConstant); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| "error: using f16 types in 'push_constant' address space is not implemented yet"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_PushConstantF16) { |
| // enable f16; |
| // enable chromium_experimental_push_constant; |
| // type t = ptr<push_constant, f16>; |
| Enable(wgsl::Extension::kF16); |
| Enable(wgsl::Extension::kChromiumExperimentalPushConstant); |
| Alias("t", ty.ptr<push_constant>(ty.f16(Source{{56, 78}}))); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| "error: using f16 types in 'push_constant' address space is not implemented yet"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_PushConstantPointer) { |
| // enable chromium_experimental_push_constant; |
| // var<push_constant> g : ptr<private, f32>; |
| Enable(wgsl::Extension::kChromiumExperimentalPushConstant); |
| GlobalVar(Source{{56, 78}}, "g", ty.ptr<private_, f32>(Source{{12, 34}}), |
| core::AddressSpace::kPushConstant); |
| |
| ASSERT_FALSE(r()->Resolve()); |
| EXPECT_EQ( |
| r()->error(), |
| R"(12:34 error: Type 'ptr<private, f32, read_write>' cannot be used in address space 'push_constant' as it is non-host-shareable |
| 56:78 note: while instantiating 'var' g)"); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_PushConstantIntScalar) { |
| // enable chromium_experimental_push_constant; |
| // var<push_constant> g : i32; |
| Enable(wgsl::Extension::kChromiumExperimentalPushConstant); |
| GlobalVar("g", ty.i32(), core::AddressSpace::kPushConstant); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_PushConstantIntScalar) { |
| // enable chromium_experimental_push_constant; |
| // type t = ptr<push_constant, i32>; |
| Enable(wgsl::Extension::kChromiumExperimentalPushConstant); |
| Alias("t", ty.ptr<push_constant, i32>()); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_PushConstantVectorF32) { |
| // enable chromium_experimental_push_constant; |
| // var<push_constant> g : vec4<f32>; |
| Enable(wgsl::Extension::kChromiumExperimentalPushConstant); |
| GlobalVar("g", ty.vec4<f32>(), core::AddressSpace::kPushConstant); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_PushConstantVectorF32) { |
| // enable chromium_experimental_push_constant; |
| // var<push_constant> g : vec4<f32>; |
| Enable(wgsl::Extension::kChromiumExperimentalPushConstant); |
| Alias("t", ty.ptr<push_constant, vec4<f32>>()); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, GlobalVariable_PushConstantArrayF32) { |
| // enable chromium_experimental_push_constant; |
| // struct S { a : f32} |
| // var<push_constant> g : array<S, 3u>; |
| Enable(wgsl::Extension::kChromiumExperimentalPushConstant); |
| Structure("S", Vector{Member("a", ty.f32())}); |
| GlobalVar("g", ty.array(ty("S"), 3_u), core::AddressSpace::kPushConstant); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| TEST_F(ResolverAddressSpaceValidationTest, PointerAlias_PushConstantArrayF32) { |
| // enable chromium_experimental_push_constant; |
| // struct S { a : f32} |
| // type t = ptr<push_constant, array<S, 3u>>; |
| Enable(wgsl::Extension::kChromiumExperimentalPushConstant); |
| Structure("S", Vector{Member("a", ty.f32())}); |
| Alias("t", ty.ptr<push_constant>(ty.array(ty("S"), 3_u))); |
| |
| ASSERT_TRUE(r()->Resolve()) << r()->error(); |
| } |
| |
| } // namespace |
| } // namespace tint::resolver |