tint/resolver: Clean up variable validation
Move the validation of usage and address space to helper. Improve diagnostics. Fix / clean up tests. This is in preparation for tint:1553 Change-Id: I2cbc8b851ecf02f214341f8cba6bd52413c42911 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/111880 Commit-Queue: Ben Clayton <bclayton@google.com> Auto-Submit: Ben Clayton <bclayton@google.com> Reviewed-by: David Neto <dneto@google.com> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
67d52eb4f2
commit
34afd9b6c6
|
@ -52,7 +52,7 @@ TEST_F(ResolverAddressSpaceLayoutValidationTest, StorageBuffer_UnalignedMember)
|
||||||
/* offset(5) align(1) size( 4) */ b : f32;
|
/* offset(5) align(1) size( 4) */ b : f32;
|
||||||
/* offset(9) align(1) size( 3) */ // -- implicit struct size padding --;
|
/* offset(9) align(1) size( 3) */ // -- implicit struct size padding --;
|
||||||
/* */ };
|
/* */ };
|
||||||
78:90 note: see declaration of variable)");
|
78:90 note: 'S' used in address space 'storage' here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAddressSpaceLayoutValidationTest, StorageBuffer_UnalignedMember_SuggestedFix) {
|
TEST_F(ResolverAddressSpaceLayoutValidationTest, StorageBuffer_UnalignedMember_SuggestedFix) {
|
||||||
|
@ -116,7 +116,7 @@ TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_UnalignedMember_S
|
||||||
/* align(4) size(4) */ struct Inner {
|
/* align(4) size(4) */ struct Inner {
|
||||||
/* offset(0) align(4) size(4) */ scalar : i32;
|
/* offset(0) align(4) size(4) */ scalar : i32;
|
||||||
/* */ };
|
/* */ };
|
||||||
78:90 note: see declaration of variable)");
|
78:90 note: 'Outer' used in address space 'uniform' here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAddressSpaceLayoutValidationTest,
|
TEST_F(ResolverAddressSpaceLayoutValidationTest,
|
||||||
|
@ -182,7 +182,7 @@ TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_UnalignedMember_A
|
||||||
/* offset( 0) align(4) size( 4) */ scalar : f32;
|
/* offset( 0) align(4) size( 4) */ scalar : f32;
|
||||||
/* offset( 4) align(4) size(160) */ inner : @stride(16) array<f32, 10>;
|
/* offset( 4) align(4) size(160) */ inner : @stride(16) array<f32, 10>;
|
||||||
/* */ };
|
/* */ };
|
||||||
78:90 note: see declaration of variable)");
|
78:90 note: 'Outer' used in address space 'uniform' here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_UnalignedMember_Array_SuggestedFix) {
|
TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_UnalignedMember_Array_SuggestedFix) {
|
||||||
|
@ -253,7 +253,7 @@ TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_MembersOffsetNotM
|
||||||
/* align(1) size(5) */ struct Inner {
|
/* align(1) size(5) */ struct Inner {
|
||||||
/* offset(0) align(1) size(5) */ scalar : i32;
|
/* offset(0) align(1) size(5) */ scalar : i32;
|
||||||
/* */ };
|
/* */ };
|
||||||
22:24 note: see declaration of variable)");
|
22:24 note: 'Outer' used in address space 'uniform' here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
// See https://crbug.com/tint/1344
|
// See https://crbug.com/tint/1344
|
||||||
|
@ -308,7 +308,7 @@ TEST_F(ResolverAddressSpaceLayoutValidationTest,
|
||||||
/* offset(12) align(1) size( 5) */ scalar : i32;
|
/* offset(12) align(1) size( 5) */ scalar : i32;
|
||||||
/* offset(17) align(1) size( 3) */ // -- implicit struct size padding --;
|
/* offset(17) align(1) size( 3) */ // -- implicit struct size padding --;
|
||||||
/* */ };
|
/* */ };
|
||||||
22:24 note: see declaration of variable)");
|
22:24 note: 'Outer' used in address space 'uniform' here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAddressSpaceLayoutValidationTest,
|
TEST_F(ResolverAddressSpaceLayoutValidationTest,
|
||||||
|
@ -418,7 +418,7 @@ TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStrid
|
||||||
/* offset( 0) align(4) size(40) */ inner : array<f32, 10>;
|
/* offset( 0) align(4) size(40) */ inner : array<f32, 10>;
|
||||||
/* offset(40) align(4) size( 4) */ scalar : i32;
|
/* offset(40) align(4) size( 4) */ scalar : i32;
|
||||||
/* */ };
|
/* */ };
|
||||||
78:90 note: see declaration of variable)");
|
78:90 note: 'Outer' used in address space 'uniform' here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_Vector) {
|
TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_Vector) {
|
||||||
|
@ -453,7 +453,7 @@ TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStrid
|
||||||
/* offset(80) align(4) size( 4) */ scalar : i32;
|
/* offset(80) align(4) size( 4) */ scalar : i32;
|
||||||
/* offset(84) align(1) size( 4) */ // -- implicit struct size padding --;
|
/* offset(84) align(1) size( 4) */ // -- implicit struct size padding --;
|
||||||
/* */ };
|
/* */ };
|
||||||
78:90 note: see declaration of variable)");
|
78:90 note: 'Outer' used in address space 'uniform' here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_Struct) {
|
TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_Struct) {
|
||||||
|
@ -495,7 +495,7 @@ TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStrid
|
||||||
/* offset( 0) align(4) size(80) */ inner : array<ArrayElem, 10>;
|
/* offset( 0) align(4) size(80) */ inner : array<ArrayElem, 10>;
|
||||||
/* offset(80) align(4) size( 4) */ scalar : i32;
|
/* offset(80) align(4) size( 4) */ scalar : i32;
|
||||||
/* */ };
|
/* */ };
|
||||||
78:90 note: see declaration of variable)");
|
78:90 note: 'Outer' used in address space 'uniform' here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_TopLevelArray) {
|
TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_TopLevelArray) {
|
||||||
|
@ -507,7 +507,7 @@ TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStrid
|
||||||
ASSERT_FALSE(r()->Resolve());
|
ASSERT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
r()->error(),
|
r()->error(),
|
||||||
R"(34:56 error: uniform storage requires that array elements be aligned to 16 bytes, but array element alignment is currently 4. Consider using a vector or struct as the element type instead.)");
|
R"(78:90 error: uniform storage requires that array elements be aligned to 16 bytes, but array element alignment is currently 4. Consider using a vector or struct as the element type instead.)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_NestedArray) {
|
TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_NestedArray) {
|
||||||
|
@ -534,7 +534,7 @@ TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStrid
|
||||||
/* align(4) size(64) */ struct Outer {
|
/* align(4) size(64) */ struct Outer {
|
||||||
/* offset( 0) align(4) size(64) */ inner : array<array<f32, 4>, 4>;
|
/* offset( 0) align(4) size(64) */ inner : array<array<f32, 4>, 4>;
|
||||||
/* */ };
|
/* */ };
|
||||||
78:90 note: see declaration of variable)");
|
78:90 note: 'Outer' used in address space 'uniform' here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_SuggestedFix) {
|
TEST_F(ResolverAddressSpaceLayoutValidationTest, UniformBuffer_InvalidArrayStride_SuggestedFix) {
|
||||||
|
@ -587,7 +587,7 @@ TEST_F(ResolverAddressSpaceLayoutValidationTest, PushConstant_UnalignedMember) {
|
||||||
/* offset(5) align(1) size( 4) */ b : f32;
|
/* offset(5) align(1) size( 4) */ b : f32;
|
||||||
/* offset(9) align(1) size( 3) */ // -- implicit struct size padding --;
|
/* offset(9) align(1) size( 3) */ // -- implicit struct size padding --;
|
||||||
/* */ };
|
/* */ };
|
||||||
78:90 note: see declaration of variable)");
|
78:90 note: 'S' used in address space 'push_constant' here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAddressSpaceLayoutValidationTest, PushConstant_Aligned) {
|
TEST_F(ResolverAddressSpaceLayoutValidationTest, PushConstant_Aligned) {
|
||||||
|
|
|
@ -472,8 +472,7 @@ TEST_F(ResolverAddressSpaceValidationTest, PushConstantF16) {
|
||||||
|
|
||||||
ASSERT_FALSE(r()->Resolve());
|
ASSERT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"56:78 error: using f16 types in 'push_constant' address space is not "
|
"error: using f16 types in 'push_constant' address space is not implemented yet");
|
||||||
"implemented yet");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAddressSpaceValidationTest, PushConstantPointer) {
|
TEST_F(ResolverAddressSpaceValidationTest, PushConstantPointer) {
|
||||||
|
|
|
@ -40,7 +40,7 @@ TEST_F(ResolverAtomicValidationTest, AddressSpace_Storage) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAtomicValidationTest, AddressSpace_Storage_Struct) {
|
TEST_F(ResolverAtomicValidationTest, AddressSpace_Storage_Struct) {
|
||||||
auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
|
auto* s = Structure("s", utils::Vector{Member(Source{{12, 34}}, "a", ty.atomic(ty.i32()))});
|
||||||
GlobalVar("g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Group(0_a),
|
GlobalVar("g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kReadWrite, Group(0_a),
|
||||||
Binding(0_a));
|
Binding(0_a));
|
||||||
|
|
||||||
|
@ -55,31 +55,28 @@ TEST_F(ResolverAtomicValidationTest, InvalidType) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Simple) {
|
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Simple) {
|
||||||
GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::AddressSpace::kPrivate);
|
GlobalVar(Source{{12, 34}}, "a", ty.atomic(ty.i32()), ast::AddressSpace::kPrivate);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: atomic variables must have <storage> or <workgroup> "
|
"12:34 error: atomic variables must have <storage> or <workgroup> address space");
|
||||||
"address space");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Array) {
|
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Array) {
|
||||||
GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::AddressSpace::kPrivate);
|
GlobalVar(Source{{12, 34}}, "a", ty.atomic(ty.i32()), ast::AddressSpace::kPrivate);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:34 error: atomic variables must have <storage> or <workgroup> "
|
"12:34 error: atomic variables must have <storage> or <workgroup> address space");
|
||||||
"address space");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Struct) {
|
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Struct) {
|
||||||
auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
|
auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(ty.i32()))});
|
||||||
GlobalVar("g", ty.Of(s), ast::AddressSpace::kPrivate);
|
GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kPrivate);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"error: atomic variables must have <storage> or <workgroup> "
|
"56:78 error: atomic variables must have <storage> or <workgroup> address space\n"
|
||||||
"address space\n"
|
|
||||||
"note: atomic sub-type of 's' is declared here");
|
"note: atomic sub-type of 's' is declared here");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,12 +88,11 @@ TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_StructOfStruct) {
|
||||||
auto* Inner =
|
auto* Inner =
|
||||||
Structure("Inner", utils::Vector{Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
|
Structure("Inner", utils::Vector{Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
|
||||||
auto* Outer = Structure("Outer", utils::Vector{Member("m", ty.Of(Inner))});
|
auto* Outer = Structure("Outer", utils::Vector{Member("m", ty.Of(Inner))});
|
||||||
GlobalVar("g", ty.Of(Outer), ast::AddressSpace::kPrivate);
|
GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), ast::AddressSpace::kPrivate);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"error: atomic variables must have <storage> or <workgroup> "
|
"56:78 error: atomic variables must have <storage> or <workgroup> address space\n"
|
||||||
"address space\n"
|
|
||||||
"note: atomic sub-type of 'Outer' is declared here");
|
"note: atomic sub-type of 'Outer' is declared here");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,13 +104,12 @@ TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_StructOfStructOfArray)
|
||||||
auto* Inner =
|
auto* Inner =
|
||||||
Structure("Inner", utils::Vector{Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
|
Structure("Inner", utils::Vector{Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
|
||||||
auto* Outer = Structure("Outer", utils::Vector{Member("m", ty.Of(Inner))});
|
auto* Outer = Structure("Outer", utils::Vector{Member("m", ty.Of(Inner))});
|
||||||
GlobalVar("g", ty.Of(Outer), ast::AddressSpace::kPrivate);
|
GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), ast::AddressSpace::kPrivate);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"error: atomic variables must have <storage> or <workgroup> "
|
R"(56:78 error: atomic variables must have <storage> or <workgroup> address space
|
||||||
"address space\n"
|
12:34 note: atomic sub-type of 'Outer' is declared here)");
|
||||||
"12:34 note: atomic sub-type of 'Outer' is declared here");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_ArrayOfArray) {
|
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_ArrayOfArray) {
|
||||||
|
@ -127,8 +122,7 @@ TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_ArrayOfArray) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"error: atomic variables must have <storage> or <workgroup> "
|
"56:78 error: atomic variables must have <storage> or <workgroup> address space");
|
||||||
"address space");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_ArrayOfStruct) {
|
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_ArrayOfStruct) {
|
||||||
|
@ -137,14 +131,13 @@ TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_ArrayOfStruct) {
|
||||||
// };
|
// };
|
||||||
// var<private> v: array<S, 5u>;
|
// var<private> v: array<S, 5u>;
|
||||||
|
|
||||||
auto* s = Structure("S", utils::Vector{Member("m", ty.atomic<u32>())});
|
auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "m", ty.atomic<u32>())});
|
||||||
GlobalVar(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5_u), ast::AddressSpace::kPrivate);
|
GlobalVar(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5_u), ast::AddressSpace::kPrivate);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"error: atomic variables must have <storage> or <workgroup> "
|
R"(56:78 error: atomic variables must have <storage> or <workgroup> address space
|
||||||
"address space\n"
|
12:34 note: atomic sub-type of 'array<S, 5>' is declared here)");
|
||||||
"note: atomic sub-type of 'array<S, 5>' is declared here");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_ArrayOfStructOfArray) {
|
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_ArrayOfStructOfArray) {
|
||||||
|
@ -154,16 +147,14 @@ TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_ArrayOfStructOfArray) {
|
||||||
// };
|
// };
|
||||||
// var<private> v: array<S, 5u>;
|
// var<private> v: array<S, 5u>;
|
||||||
|
|
||||||
auto* atomic_array =
|
auto* atomic_array = Alias("AtomicArray", ty.atomic(ty.i32()));
|
||||||
Alias(Source{{12, 34}}, "AtomicArray", ty.atomic(Source{{12, 34}}, ty.i32()));
|
auto* s = Structure("S", utils::Vector{Member(Source{{12, 34}}, "m", ty.Of(atomic_array))});
|
||||||
auto* s = Structure("S", utils::Vector{Member("m", ty.Of(atomic_array))});
|
|
||||||
GlobalVar(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5_u), ast::AddressSpace::kPrivate);
|
GlobalVar(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5_u), ast::AddressSpace::kPrivate);
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"error: atomic variables must have <storage> or <workgroup> "
|
R"(56:78 error: atomic variables must have <storage> or <workgroup> address space
|
||||||
"address space\n"
|
12:34 note: atomic sub-type of 'array<S, 5>' is declared here)");
|
||||||
"note: atomic sub-type of 'array<S, 5>' is declared here");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Complex) {
|
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Complex) {
|
||||||
|
@ -181,15 +172,14 @@ TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Complex) {
|
||||||
// struct S0 { x: S1; };
|
// struct S0 { x: S1; };
|
||||||
// var<private> g : S0;
|
// var<private> g : S0;
|
||||||
|
|
||||||
auto* atomic_array =
|
auto* atomic_array = Alias("AtomicArray", ty.atomic(ty.i32()));
|
||||||
Alias(Source{{12, 34}}, "AtomicArray", ty.atomic(Source{{12, 34}}, ty.i32()));
|
|
||||||
auto* array_i32_4 = ty.array(ty.i32(), 4_u);
|
auto* array_i32_4 = ty.array(ty.i32(), 4_u);
|
||||||
auto* array_atomic_u32_8 = ty.array(ty.atomic(ty.u32()), 8_u);
|
auto* array_atomic_u32_8 = ty.array(ty.atomic(ty.u32()), 8_u);
|
||||||
auto* array_atomic_i32_4 = ty.array(ty.atomic(ty.i32()), 4_u);
|
auto* array_atomic_i32_4 = ty.array(ty.atomic(ty.i32()), 4_u);
|
||||||
|
|
||||||
auto* s6 = Structure("S6", utils::Vector{Member("x", array_i32_4)});
|
auto* s6 = Structure("S6", utils::Vector{Member("x", array_i32_4)});
|
||||||
auto* s5 = Structure("S5", utils::Vector{Member("x", ty.Of(s6)), //
|
auto* s5 = Structure("S5", utils::Vector{Member("x", ty.Of(s6)), //
|
||||||
Member("y", ty.Of(atomic_array)), //
|
Member(Source{{12, 34}}, "y", ty.Of(atomic_array)), //
|
||||||
Member("z", array_atomic_u32_8)}); //
|
Member("z", array_atomic_u32_8)}); //
|
||||||
auto* s4 = Structure("S4", utils::Vector{Member("x", ty.Of(s6)), //
|
auto* s4 = Structure("S4", utils::Vector{Member("x", ty.Of(s6)), //
|
||||||
Member("y", ty.Of(s5)), //
|
Member("y", ty.Of(s5)), //
|
||||||
|
@ -202,33 +192,32 @@ TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Complex) {
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"error: atomic variables must have <storage> or <workgroup> "
|
R"(56:78 error: atomic variables must have <storage> or <workgroup> address space
|
||||||
"address space\n"
|
12:34 note: atomic sub-type of 'S0' is declared here)");
|
||||||
"note: atomic sub-type of 'S0' is declared here");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAtomicValidationTest, Struct_AccessMode_Read) {
|
TEST_F(ResolverAtomicValidationTest, Struct_AccessMode_Read) {
|
||||||
auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
|
auto* s = Structure("s", utils::Vector{Member(Source{{12, 34}}, "a", ty.atomic(ty.i32()))});
|
||||||
GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
|
GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
|
||||||
Group(0_a), Binding(0_a));
|
Group(0_a), Binding(0_a));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(
|
||||||
"error: atomic variables in <storage> address space must have read_write "
|
r()->error(),
|
||||||
"access mode\n"
|
R"(56:78 error: atomic variables in <storage> address space must have read_write access mode
|
||||||
"note: atomic sub-type of 's' is declared here");
|
12:34 note: atomic sub-type of 's' is declared here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_Struct) {
|
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_Struct) {
|
||||||
auto* s = Structure("s", utils::Vector{Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
|
auto* s = Structure("s", utils::Vector{Member(Source{{12, 34}}, "a", ty.atomic(ty.i32()))});
|
||||||
GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
|
GlobalVar(Source{{56, 78}}, "g", ty.Of(s), ast::AddressSpace::kStorage, ast::Access::kRead,
|
||||||
Group(0_a), Binding(0_a));
|
Group(0_a), Binding(0_a));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(
|
||||||
"error: atomic variables in <storage> address space must have read_write "
|
r()->error(),
|
||||||
"access mode\n"
|
R"(56:78 error: atomic variables in <storage> address space must have read_write access mode
|
||||||
"note: atomic sub-type of 's' is declared here");
|
12:34 note: atomic sub-type of 's' is declared here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_StructOfStruct) {
|
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_StructOfStruct) {
|
||||||
|
@ -237,16 +226,16 @@ TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_StructOfStruct) {
|
||||||
// var<storage, read> g : Outer;
|
// var<storage, read> g : Outer;
|
||||||
|
|
||||||
auto* Inner =
|
auto* Inner =
|
||||||
Structure("Inner", utils::Vector{Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
|
Structure("Inner", utils::Vector{Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
|
||||||
auto* Outer = Structure("Outer", utils::Vector{Member("m", ty.Of(Inner))});
|
auto* Outer = Structure("Outer", utils::Vector{Member("m", ty.Of(Inner))});
|
||||||
GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), ast::AddressSpace::kStorage, ast::Access::kRead,
|
GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), ast::AddressSpace::kStorage, ast::Access::kRead,
|
||||||
Group(0_a), Binding(0_a));
|
Group(0_a), Binding(0_a));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(
|
||||||
"error: atomic variables in <storage> address space must have read_write "
|
r()->error(),
|
||||||
"access mode\n"
|
R"(56:78 error: atomic variables in <storage> address space must have read_write access mode
|
||||||
"note: atomic sub-type of 'Outer' is declared here");
|
12:34 note: atomic sub-type of 'Outer' is declared here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_StructOfStructOfArray) {
|
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_StructOfStructOfArray) {
|
||||||
|
@ -261,10 +250,10 @@ TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_StructOfStructOfArray) {
|
||||||
Group(0_a), Binding(0_a));
|
Group(0_a), Binding(0_a));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(
|
||||||
"error: atomic variables in <storage> address space must have "
|
r()->error(),
|
||||||
"read_write access mode\n"
|
R"(56:78 error: atomic variables in <storage> address space must have read_write access mode
|
||||||
"12:34 note: atomic sub-type of 'Outer' is declared here");
|
12:34 note: atomic sub-type of 'Outer' is declared here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_Complex) {
|
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_Complex) {
|
||||||
|
@ -282,15 +271,14 @@ TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_Complex) {
|
||||||
// struct S0 { x: S1; };
|
// struct S0 { x: S1; };
|
||||||
// var<storage, read> g : S0;
|
// var<storage, read> g : S0;
|
||||||
|
|
||||||
auto* atomic_array =
|
auto* atomic_array = Alias("AtomicArray", ty.atomic(ty.i32()));
|
||||||
Alias(Source{{12, 34}}, "AtomicArray", ty.atomic(Source{{12, 34}}, ty.i32()));
|
|
||||||
auto* array_i32_4 = ty.array(ty.i32(), 4_u);
|
auto* array_i32_4 = ty.array(ty.i32(), 4_u);
|
||||||
auto* array_atomic_u32_8 = ty.array(ty.atomic(ty.u32()), 8_u);
|
auto* array_atomic_u32_8 = ty.array(ty.atomic(ty.u32()), 8_u);
|
||||||
auto* array_atomic_i32_4 = ty.array(ty.atomic(ty.i32()), 4_u);
|
auto* array_atomic_i32_4 = ty.array(ty.atomic(ty.i32()), 4_u);
|
||||||
|
|
||||||
auto* s6 = Structure("S6", utils::Vector{Member("x", array_i32_4)});
|
auto* s6 = Structure("S6", utils::Vector{Member("x", array_i32_4)});
|
||||||
auto* s5 = Structure("S5", utils::Vector{Member("x", ty.Of(s6)), //
|
auto* s5 = Structure("S5", utils::Vector{Member("x", ty.Of(s6)), //
|
||||||
Member("y", ty.Of(atomic_array)), //
|
Member(Source{{56, 78}}, "y", ty.Of(atomic_array)), //
|
||||||
Member("z", array_atomic_u32_8)}); //
|
Member("z", array_atomic_u32_8)}); //
|
||||||
auto* s4 = Structure("S4", utils::Vector{Member("x", ty.Of(s6)), //
|
auto* s4 = Structure("S4", utils::Vector{Member("x", ty.Of(s6)), //
|
||||||
Member("y", ty.Of(s5)), //
|
Member("y", ty.Of(s5)), //
|
||||||
|
@ -299,14 +287,14 @@ TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_Complex) {
|
||||||
auto* s2 = Structure("S2", utils::Vector{Member("x", ty.Of(s3))});
|
auto* s2 = Structure("S2", utils::Vector{Member("x", ty.Of(s3))});
|
||||||
auto* s1 = Structure("S1", utils::Vector{Member("x", ty.Of(s2))});
|
auto* s1 = Structure("S1", utils::Vector{Member("x", ty.Of(s2))});
|
||||||
auto* s0 = Structure("S0", utils::Vector{Member("x", ty.Of(s1))});
|
auto* s0 = Structure("S0", utils::Vector{Member("x", ty.Of(s1))});
|
||||||
GlobalVar(Source{{56, 78}}, "g", ty.Of(s0), ast::AddressSpace::kStorage, ast::Access::kRead,
|
GlobalVar(Source{{12, 34}}, "g", ty.Of(s0), ast::AddressSpace::kStorage, ast::Access::kRead,
|
||||||
Group(0_a), Binding(0_a));
|
Group(0_a), Binding(0_a));
|
||||||
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(
|
||||||
"error: atomic variables in <storage> address space must have "
|
r()->error(),
|
||||||
"read_write access mode\n"
|
R"(12:34 error: atomic variables in <storage> address space must have read_write access mode
|
||||||
"note: atomic sub-type of 'S0' is declared here");
|
56:78 note: atomic sub-type of 'S0' is declared here)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverAtomicValidationTest, Local) {
|
TEST_F(ResolverAtomicValidationTest, Local) {
|
||||||
|
|
|
@ -103,7 +103,11 @@ Resolver::Resolver(ProgramBuilder* builder)
|
||||||
const_eval_(*builder),
|
const_eval_(*builder),
|
||||||
intrinsic_table_(IntrinsicTable::Create(*builder)),
|
intrinsic_table_(IntrinsicTable::Create(*builder)),
|
||||||
sem_(builder, dependencies_),
|
sem_(builder, dependencies_),
|
||||||
validator_(builder, sem_) {}
|
validator_(builder,
|
||||||
|
sem_,
|
||||||
|
enabled_extensions_,
|
||||||
|
atomic_composite_info_,
|
||||||
|
valid_type_storage_layouts_) {}
|
||||||
|
|
||||||
Resolver::~Resolver() = default;
|
Resolver::~Resolver() = default;
|
||||||
|
|
||||||
|
@ -891,13 +895,7 @@ sem::GlobalVariable* Resolver::GlobalVariable(const ast::Variable* v) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validator_.GlobalVariable(sem, override_ids_, atomic_composite_info_)) {
|
if (!validator_.GlobalVariable(sem, override_ids_)) {
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(bclayton): Call this at the end of resolve on all uniform and storage
|
|
||||||
// referenced structs
|
|
||||||
if (!validator_.AddressSpaceLayout(sem, enabled_extensions_, valid_type_storage_layouts_)) {
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2300,7 +2298,7 @@ sem::Call* Resolver::BuiltinCall(const ast::CallExpression* expr,
|
||||||
current_function_->AddDirectCall(call);
|
current_function_->AddDirectCall(call);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validator_.RequiredExtensionForBuiltinFunction(call, enabled_extensions_)) {
|
if (!validator_.RequiredExtensionForBuiltinFunction(call)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -111,8 +111,6 @@ class Resolver {
|
||||||
const Validator* GetValidatorForTesting() const { return &validator_; }
|
const Validator* GetValidatorForTesting() const { return &validator_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Validator::ValidTypeStorageLayouts valid_type_storage_layouts_;
|
|
||||||
|
|
||||||
/// Resolves the program, without creating final the semantic nodes.
|
/// Resolves the program, without creating final the semantic nodes.
|
||||||
/// @returns true on success, false on error
|
/// @returns true on success, false on error
|
||||||
bool ResolveInternal();
|
bool ResolveInternal();
|
||||||
|
@ -471,6 +469,7 @@ class Resolver {
|
||||||
sem::CompoundStatement* current_compound_statement_ = nullptr;
|
sem::CompoundStatement* current_compound_statement_ = nullptr;
|
||||||
uint32_t current_scoping_depth_ = 0;
|
uint32_t current_scoping_depth_ = 0;
|
||||||
utils::UniqueVector<const sem::GlobalVariable*, 4>* resolved_overrides_ = nullptr;
|
utils::UniqueVector<const sem::GlobalVariable*, 4>* resolved_overrides_ = nullptr;
|
||||||
|
utils::Hashset<TypeAndAddressSpace, 8> valid_type_storage_layouts_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tint::resolver
|
} // namespace tint::resolver
|
||||||
|
|
|
@ -153,8 +153,18 @@ void TraverseCallChain(diag::List& diagnostics,
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Validator::Validator(ProgramBuilder* builder, SemHelper& sem)
|
Validator::Validator(
|
||||||
: symbols_(builder->Symbols()), diagnostics_(builder->Diagnostics()), sem_(sem) {}
|
ProgramBuilder* builder,
|
||||||
|
SemHelper& sem,
|
||||||
|
const ast::Extensions& enabled_extensions,
|
||||||
|
const utils::Hashmap<const sem::Type*, const Source*, 8>& atomic_composite_info,
|
||||||
|
utils::Hashset<TypeAndAddressSpace, 8>& valid_type_storage_layouts)
|
||||||
|
: symbols_(builder->Symbols()),
|
||||||
|
diagnostics_(builder->Diagnostics()),
|
||||||
|
sem_(sem),
|
||||||
|
enabled_extensions_(enabled_extensions),
|
||||||
|
atomic_composite_info_(atomic_composite_info),
|
||||||
|
valid_type_storage_layouts_(valid_type_storage_layouts) {}
|
||||||
|
|
||||||
Validator::~Validator() = default;
|
Validator::~Validator() = default;
|
||||||
|
|
||||||
|
@ -360,8 +370,7 @@ bool Validator::VariableInitializer(const ast::Variable* v,
|
||||||
|
|
||||||
bool Validator::AddressSpaceLayout(const sem::Type* store_ty,
|
bool Validator::AddressSpaceLayout(const sem::Type* store_ty,
|
||||||
ast::AddressSpace address_space,
|
ast::AddressSpace address_space,
|
||||||
Source source,
|
Source source) const {
|
||||||
ValidTypeStorageLayouts& layouts) const {
|
|
||||||
// https://gpuweb.github.io/gpuweb/wgsl/#storage-class-layout-constraints
|
// https://gpuweb.github.io/gpuweb/wgsl/#storage-class-layout-constraints
|
||||||
|
|
||||||
auto is_uniform_struct_or_array = [address_space](const sem::Type* ty) {
|
auto is_uniform_struct_or_array = [address_space](const sem::Type* ty) {
|
||||||
|
@ -386,8 +395,8 @@ bool Validator::AddressSpaceLayout(const sem::Type* store_ty,
|
||||||
return symbols_.NameFor(sm->Declaration()->symbol);
|
return symbols_.NameFor(sm->Declaration()->symbol);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cache result of type + address space pair.
|
// Only validate the [type + address space] once
|
||||||
if (!layouts.emplace(store_ty, address_space).second) {
|
if (!valid_type_storage_layouts_.Add(TypeAndAddressSpace{store_ty, address_space})) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,6 +404,12 @@ bool Validator::AddressSpaceLayout(const sem::Type* store_ty,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto note_usage = [&] {
|
||||||
|
AddNote("'" + store_ty->FriendlyName(symbols_) + "' used in address space '" +
|
||||||
|
utils::ToString(address_space) + "' here",
|
||||||
|
source);
|
||||||
|
};
|
||||||
|
|
||||||
// Among three host-shareable address spaces, f16 is supported in "uniform" and
|
// Among three host-shareable address spaces, f16 is supported in "uniform" and
|
||||||
// "storage" address space, but not "push_constant" address space yet.
|
// "storage" address space, but not "push_constant" address space yet.
|
||||||
if (Is<sem::F16>(sem::Type::DeepestElementOf(store_ty)) &&
|
if (Is<sem::F16>(sem::Type::DeepestElementOf(store_ty)) &&
|
||||||
|
@ -409,10 +424,10 @@ bool Validator::AddressSpaceLayout(const sem::Type* store_ty,
|
||||||
uint32_t required_align = required_alignment_of(m->Type());
|
uint32_t required_align = required_alignment_of(m->Type());
|
||||||
|
|
||||||
// Recurse into the member type.
|
// Recurse into the member type.
|
||||||
if (!AddressSpaceLayout(m->Type(), address_space, m->Declaration()->type->source,
|
if (!AddressSpaceLayout(m->Type(), address_space, m->Declaration()->type->source)) {
|
||||||
layouts)) {
|
|
||||||
AddNote("see layout of struct:\n" + str->Layout(symbols_),
|
AddNote("see layout of struct:\n" + str->Layout(symbols_),
|
||||||
str->Declaration()->source);
|
str->Declaration()->source);
|
||||||
|
note_usage();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,6 +450,7 @@ bool Validator::AddressSpaceLayout(const sem::Type* store_ty,
|
||||||
member_str->Declaration()->source);
|
member_str->Declaration()->source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
note_usage();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,6 +476,7 @@ bool Validator::AddressSpaceLayout(const sem::Type* store_ty,
|
||||||
AddNote("and layout of previous member struct:\n" +
|
AddNote("and layout of previous member struct:\n" +
|
||||||
prev_member_str->Layout(symbols_),
|
prev_member_str->Layout(symbols_),
|
||||||
prev_member_str->Declaration()->source);
|
prev_member_str->Declaration()->source);
|
||||||
|
note_usage();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -472,7 +489,7 @@ bool Validator::AddressSpaceLayout(const sem::Type* store_ty,
|
||||||
// TODO(crbug.com/tint/1388): Ideally we'd pass the source for nested element type here, but
|
// TODO(crbug.com/tint/1388): Ideally we'd pass the source for nested element type here, but
|
||||||
// we can't easily get that from the semantic node. We should consider recursing through the
|
// we can't easily get that from the semantic node. We should consider recursing through the
|
||||||
// AST type nodes instead.
|
// AST type nodes instead.
|
||||||
if (!AddressSpaceLayout(arr->ElemType(), address_space, source, layouts)) {
|
if (!AddressSpaceLayout(arr->ElemType(), address_space, source)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,21 +501,16 @@ bool Validator::AddressSpaceLayout(const sem::Type* store_ty,
|
||||||
// shader author can resolve the issue.
|
// shader author can resolve the issue.
|
||||||
std::string hint;
|
std::string hint;
|
||||||
if (arr->ElemType()->is_scalar()) {
|
if (arr->ElemType()->is_scalar()) {
|
||||||
hint =
|
hint = "Consider using a vector or struct as the element type instead.";
|
||||||
"Consider using a vector or struct as the element type "
|
|
||||||
"instead.";
|
|
||||||
} else if (auto* vec = arr->ElemType()->As<sem::Vector>();
|
} else if (auto* vec = arr->ElemType()->As<sem::Vector>();
|
||||||
vec && vec->type()->Size() == 4) {
|
vec && vec->type()->Size() == 4) {
|
||||||
hint = "Consider using a vec4 instead.";
|
hint = "Consider using a vec4 instead.";
|
||||||
} else if (arr->ElemType()->Is<sem::Struct>()) {
|
} else if (arr->ElemType()->Is<sem::Struct>()) {
|
||||||
hint =
|
hint = "Consider using the @size attribute on the last struct member.";
|
||||||
"Consider using the @size attribute on the last struct "
|
|
||||||
"member.";
|
|
||||||
} else {
|
} else {
|
||||||
hint =
|
hint =
|
||||||
"Consider wrapping the element type in a struct and using "
|
"Consider wrapping the element type in a struct and using the @size "
|
||||||
"the "
|
"attribute.";
|
||||||
"@size attribute.";
|
|
||||||
}
|
}
|
||||||
AddError(
|
AddError(
|
||||||
"uniform storage requires that array elements be aligned to 16 "
|
"uniform storage requires that array elements be aligned to 16 "
|
||||||
|
@ -513,42 +525,6 @@ bool Validator::AddressSpaceLayout(const sem::Type* store_ty,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Validator::AddressSpaceLayout(const sem::Variable* var,
|
|
||||||
const ast::Extensions& enabled_extensions,
|
|
||||||
ValidTypeStorageLayouts& layouts) const {
|
|
||||||
if (var->AddressSpace() == ast::AddressSpace::kPushConstant &&
|
|
||||||
!enabled_extensions.Contains(ast::Extension::kChromiumExperimentalPushConstant) &&
|
|
||||||
IsValidationEnabled(var->Declaration()->attributes,
|
|
||||||
ast::DisabledValidation::kIgnoreAddressSpace)) {
|
|
||||||
AddError(
|
|
||||||
"use of variable address space 'push_constant' requires enabling extension "
|
|
||||||
"'chromium_experimental_push_constant'",
|
|
||||||
var->Declaration()->source);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto* str = var->Type()->UnwrapRef()->As<sem::Struct>()) {
|
|
||||||
// Check the structure has a declaration. Builtins like modf() and frexp() return untypeable
|
|
||||||
// structures, and so they have no declaration. Just skip validation for these.
|
|
||||||
if (auto* str_decl = str->Declaration()) {
|
|
||||||
if (!AddressSpaceLayout(str, var->AddressSpace(), str_decl->source, layouts)) {
|
|
||||||
AddNote("see declaration of variable", var->Declaration()->source);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Source source = var->Declaration()->source;
|
|
||||||
if (var->Declaration()->type) {
|
|
||||||
source = var->Declaration()->type->source;
|
|
||||||
}
|
|
||||||
if (!AddressSpaceLayout(var->Type()->UnwrapRef(), var->AddressSpace(), source, layouts)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Validator::LocalVariable(const sem::Variable* local) const {
|
bool Validator::LocalVariable(const sem::Variable* local) const {
|
||||||
auto* decl = local->Declaration();
|
auto* decl = local->Declaration();
|
||||||
if (IsArrayWithOverrideCount(local->Type())) {
|
if (IsArrayWithOverrideCount(local->Type())) {
|
||||||
|
@ -581,8 +557,7 @@ bool Validator::LocalVariable(const sem::Variable* local) const {
|
||||||
|
|
||||||
bool Validator::GlobalVariable(
|
bool Validator::GlobalVariable(
|
||||||
const sem::GlobalVariable* global,
|
const sem::GlobalVariable* global,
|
||||||
const utils::Hashmap<OverrideId, const sem::Variable*, 8>& override_ids,
|
const utils::Hashmap<OverrideId, const sem::Variable*, 8>& override_ids) const {
|
||||||
const utils::Hashmap<const sem::Type*, const Source*, 8>& atomic_composite_info) const {
|
|
||||||
auto* decl = global->Declaration();
|
auto* decl = global->Declaration();
|
||||||
if (global->AddressSpace() != ast::AddressSpace::kWorkgroup &&
|
if (global->AddressSpace() != ast::AddressSpace::kWorkgroup &&
|
||||||
IsArrayWithOverrideCount(global->Type())) {
|
IsArrayWithOverrideCount(global->Type())) {
|
||||||
|
@ -592,7 +567,7 @@ bool Validator::GlobalVariable(
|
||||||
}
|
}
|
||||||
bool ok = Switch(
|
bool ok = Switch(
|
||||||
decl, //
|
decl, //
|
||||||
[&](const ast::Var* var) {
|
[&](const ast::Var*) {
|
||||||
if (auto* init = global->Initializer();
|
if (auto* init = global->Initializer();
|
||||||
init && init->Stage() > sem::EvaluationStage::kOverride) {
|
init && init->Stage() > sem::EvaluationStage::kOverride) {
|
||||||
AddError("module-scope 'var' initializer must be a constant or override-expression",
|
AddError("module-scope 'var' initializer must be a constant or override-expression",
|
||||||
|
@ -620,29 +595,6 @@ bool Validator::GlobalVariable(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://gpuweb.github.io/gpuweb/wgsl/#variable-declaration
|
|
||||||
// The access mode always has a default, and except for variables in the storage address
|
|
||||||
// space, must not be written.
|
|
||||||
if (var->declared_access != ast::Access::kUndefined) {
|
|
||||||
if (global->AddressSpace() == ast::AddressSpace::kStorage) {
|
|
||||||
// The access mode for the storage address space can only be 'read' or
|
|
||||||
// 'read_write'.
|
|
||||||
if (var->declared_access == ast::Access::kWrite) {
|
|
||||||
AddError("access mode 'write' is not valid for the 'storage' address space",
|
|
||||||
decl->source);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
AddError("only variables in <storage> address space may declare an access mode",
|
|
||||||
decl->source);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!AtomicVariable(global, atomic_composite_info)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Var(global);
|
return Var(global);
|
||||||
},
|
},
|
||||||
[&](const ast::Override*) { return Override(global, override_ids); },
|
[&](const ast::Override*) { return Override(global, override_ids); },
|
||||||
|
@ -698,67 +650,42 @@ bool Validator::GlobalVariable(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://gpuweb.github.io/gpuweb/wgsl/#atomic-types
|
|
||||||
// Atomic types may only be instantiated by variables in the workgroup storage class or by storage
|
|
||||||
// buffer variables with a read_write access mode.
|
|
||||||
bool Validator::AtomicVariable(
|
|
||||||
const sem::Variable* var,
|
|
||||||
const utils::Hashmap<const sem::Type*, const Source*, 8>& atomic_composite_info) const {
|
|
||||||
auto address_space = var->AddressSpace();
|
|
||||||
auto* decl = var->Declaration();
|
|
||||||
auto access = var->Access();
|
|
||||||
auto* type = var->Type()->UnwrapRef();
|
|
||||||
auto source = decl->type ? decl->type->source : decl->source;
|
|
||||||
|
|
||||||
if (type->Is<sem::Atomic>()) {
|
|
||||||
if (address_space != ast::AddressSpace::kWorkgroup &&
|
|
||||||
address_space != ast::AddressSpace::kStorage) {
|
|
||||||
AddError("atomic variables must have <storage> or <workgroup> address space", source);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (type->IsAnyOf<sem::Struct, sem::Array>()) {
|
|
||||||
if (auto found = atomic_composite_info.Find(type)) {
|
|
||||||
if (address_space != ast::AddressSpace::kStorage &&
|
|
||||||
address_space != ast::AddressSpace::kWorkgroup) {
|
|
||||||
AddError("atomic variables must have <storage> or <workgroup> address space",
|
|
||||||
source);
|
|
||||||
AddNote("atomic sub-type of '" + sem_.TypeNameOf(type) + "' is declared here",
|
|
||||||
**found);
|
|
||||||
return false;
|
|
||||||
} else if (address_space == ast::AddressSpace::kStorage &&
|
|
||||||
access != ast::Access::kReadWrite) {
|
|
||||||
AddError(
|
|
||||||
"atomic variables in <storage> address space must have read_write "
|
|
||||||
"access mode",
|
|
||||||
source);
|
|
||||||
AddNote("atomic sub-type of '" + sem_.TypeNameOf(type) + "' is declared here",
|
|
||||||
**found);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Validator::Var(const sem::Variable* v) const {
|
bool Validator::Var(const sem::Variable* v) const {
|
||||||
auto* var = v->Declaration()->As<ast::Var>();
|
auto* var = v->Declaration()->As<ast::Var>();
|
||||||
auto* storage_ty = v->Type()->UnwrapRef();
|
auto* store_ty = v->Type()->UnwrapRef();
|
||||||
|
|
||||||
if (!IsStorable(storage_ty)) {
|
if (!IsStorable(store_ty)) {
|
||||||
AddError(sem_.TypeNameOf(storage_ty) + " cannot be used as the type of a var", var->source);
|
AddError(sem_.TypeNameOf(store_ty) + " cannot be used as the type of a var", var->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (storage_ty->is_handle() && var->declared_address_space != ast::AddressSpace::kNone) {
|
if (store_ty->is_handle()) {
|
||||||
|
if (var->declared_address_space != ast::AddressSpace::kNone) {
|
||||||
// https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables
|
// https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables
|
||||||
// If the store type is a texture type or a sampler type, then the variable declaration must
|
// If the store type is a texture type or a sampler type, then the variable declaration
|
||||||
// not have a address space attribute. The address space will always be handle.
|
// must not have a address space attribute. The address space will always be handle.
|
||||||
AddError(
|
AddError("variables of type '" + sem_.TypeNameOf(store_ty) +
|
||||||
"variables of type '" + sem_.TypeNameOf(storage_ty) + "' must not have a address space",
|
"' must not have a address space",
|
||||||
var->source);
|
var->source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var->declared_access != ast::Access::kUndefined) {
|
||||||
|
// https://gpuweb.github.io/gpuweb/wgsl/#variable-declaration
|
||||||
|
// The access mode always has a default, and except for variables in the storage address
|
||||||
|
// space, must not be written.
|
||||||
|
if (var->declared_address_space != ast::AddressSpace::kStorage) {
|
||||||
|
AddError("only variables in <storage> address space may declare an access mode",
|
||||||
|
var->source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CheckTypeAccessAddressSpace(v->Type()->UnwrapRef(), v->Access(), v->AddressSpace(),
|
||||||
|
var->attributes, var->source)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (IsValidationEnabled(var->attributes, ast::DisabledValidation::kIgnoreAddressSpace) &&
|
if (IsValidationEnabled(var->attributes, ast::DisabledValidation::kIgnoreAddressSpace) &&
|
||||||
(var->declared_address_space == ast::AddressSpace::kIn ||
|
(var->declared_address_space == ast::AddressSpace::kIn ||
|
||||||
|
@ -1642,9 +1569,7 @@ bool Validator::TextureBuiltinFunction(const sem::Call* call) const {
|
||||||
check_arg_is_constexpr(sem::ParameterUsage::kComponent, 0, 3);
|
check_arg_is_constexpr(sem::ParameterUsage::kComponent, 0, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Validator::RequiredExtensionForBuiltinFunction(
|
bool Validator::RequiredExtensionForBuiltinFunction(const sem::Call* call) const {
|
||||||
const sem::Call* call,
|
|
||||||
const ast::Extensions& enabled_extensions) const {
|
|
||||||
const auto* builtin = call->Target()->As<sem::Builtin>();
|
const auto* builtin = call->Target()->As<sem::Builtin>();
|
||||||
if (!builtin) {
|
if (!builtin) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -1655,7 +1580,7 @@ bool Validator::RequiredExtensionForBuiltinFunction(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!enabled_extensions.Contains(extension)) {
|
if (!enabled_extensions_.Contains(extension)) {
|
||||||
AddError("cannot call built-in function '" + std::string(builtin->str()) +
|
AddError("cannot call built-in function '" + std::string(builtin->str()) +
|
||||||
"' without extension " + utils::ToString(extension),
|
"' without extension " + utils::ToString(extension),
|
||||||
call->Declaration()->source);
|
call->Declaration()->source);
|
||||||
|
@ -2451,4 +2376,69 @@ std::string Validator::VectorPretty(uint32_t size, const sem::Type* element_type
|
||||||
return vec_type.FriendlyName(symbols_);
|
return vec_type.FriendlyName(symbols_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Validator::CheckTypeAccessAddressSpace(
|
||||||
|
const sem::Type* store_ty,
|
||||||
|
ast::Access access,
|
||||||
|
ast::AddressSpace address_space,
|
||||||
|
const utils::VectorRef<const tint::ast::Attribute*> attributes,
|
||||||
|
const Source& source) const {
|
||||||
|
if (!AddressSpaceLayout(store_ty, address_space, source)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address_space == ast::AddressSpace::kPushConstant &&
|
||||||
|
!enabled_extensions_.Contains(ast::Extension::kChromiumExperimentalPushConstant) &&
|
||||||
|
IsValidationEnabled(attributes, ast::DisabledValidation::kIgnoreAddressSpace)) {
|
||||||
|
AddError(
|
||||||
|
"use of variable address space 'push_constant' requires enabling extension "
|
||||||
|
"'chromium_experimental_push_constant'",
|
||||||
|
source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address_space == ast::AddressSpace::kStorage && access == ast::Access::kWrite) {
|
||||||
|
// The access mode for the storage address space can only be 'read' or
|
||||||
|
// 'read_write'.
|
||||||
|
AddError("access mode 'write' is not valid for the 'storage' address space", source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto atomic_error = [&]() -> const char* {
|
||||||
|
if (address_space != ast::AddressSpace::kStorage &&
|
||||||
|
address_space != ast::AddressSpace::kWorkgroup) {
|
||||||
|
return "atomic variables must have <storage> or <workgroup> address space";
|
||||||
|
}
|
||||||
|
if (address_space == ast::AddressSpace::kStorage && access != ast::Access::kReadWrite) {
|
||||||
|
return "atomic variables in <storage> address space must have read_write access "
|
||||||
|
"mode";
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto check_sub_atomics = [&] {
|
||||||
|
if (auto atomic_use = atomic_composite_info_.Get(store_ty)) {
|
||||||
|
if (auto* err = atomic_error()) {
|
||||||
|
AddError(err, source);
|
||||||
|
AddNote("atomic sub-type of '" + sem_.TypeNameOf(store_ty) + "' is declared here",
|
||||||
|
**atomic_use);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return Switch(
|
||||||
|
store_ty, //
|
||||||
|
[&](const sem::Atomic*) {
|
||||||
|
if (auto* err = atomic_error()) {
|
||||||
|
AddError(err, source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
[&](const sem::Struct*) { return check_sub_atomics(); }, //
|
||||||
|
[&](const sem::Array*) { return check_sub_atomics(); }, //
|
||||||
|
[&](Default) { return true; });
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace tint::resolver
|
} // namespace tint::resolver
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "src/tint/resolver/sem_helper.h"
|
#include "src/tint/resolver/sem_helper.h"
|
||||||
#include "src/tint/sem/evaluation_stage.h"
|
#include "src/tint/sem/evaluation_stage.h"
|
||||||
#include "src/tint/source.h"
|
#include "src/tint/source.h"
|
||||||
|
#include "src/tint/utils/hash.h"
|
||||||
#include "src/tint/utils/hashmap.h"
|
#include "src/tint/utils/hashmap.h"
|
||||||
#include "src/tint/utils/vector.h"
|
#include "src/tint/utils/vector.h"
|
||||||
|
|
||||||
|
@ -66,19 +67,38 @@ class WhileStatement;
|
||||||
|
|
||||||
namespace tint::resolver {
|
namespace tint::resolver {
|
||||||
|
|
||||||
|
/// TypeAndAddressSpace is a pair of type and address space
|
||||||
|
struct TypeAndAddressSpace {
|
||||||
|
/// The type
|
||||||
|
const sem::Type* type;
|
||||||
|
/// The address space
|
||||||
|
ast::AddressSpace address_space;
|
||||||
|
|
||||||
|
/// Equality operator
|
||||||
|
/// @param other the other TypeAndAddressSpace to compare this TypeAndAddressSpace to
|
||||||
|
/// @returns true if the type and address space of this TypeAndAddressSpace is equal to @p other
|
||||||
|
bool operator==(const TypeAndAddressSpace& other) const {
|
||||||
|
return type == other.type && address_space == other.address_space;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// Validation logic for various ast nodes. The validations in general should
|
/// Validation logic for various ast nodes. The validations in general should
|
||||||
/// be shallow and depend on the resolver to call on children. The validations
|
/// be shallow and depend on the resolver to call on children. The validations
|
||||||
/// also assume that sem changes have already been made. The validation checks
|
/// also assume that sem changes have already been made. The validation checks
|
||||||
/// should not alter the AST or SEM trees.
|
/// should not alter the AST or SEM trees.
|
||||||
class Validator {
|
class Validator {
|
||||||
public:
|
public:
|
||||||
/// The valid type storage layouts typedef
|
|
||||||
using ValidTypeStorageLayouts = std::set<std::pair<const sem::Type*, ast::AddressSpace>>;
|
|
||||||
|
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// @param builder the program builder
|
/// @param builder the program builder
|
||||||
/// @param helper the SEM helper to validate with
|
/// @param helper the SEM helper to validate with
|
||||||
Validator(ProgramBuilder* builder, SemHelper& helper);
|
/// @param enabled_extensions all the extensions declared in current module
|
||||||
|
/// @param atomic_composite_info atomic composite info of the module
|
||||||
|
/// @param valid_type_storage_layouts a set of validated type layouts by address space
|
||||||
|
Validator(ProgramBuilder* builder,
|
||||||
|
SemHelper& helper,
|
||||||
|
const ast::Extensions& enabled_extensions,
|
||||||
|
const utils::Hashmap<const sem::Type*, const Source*, 8>& atomic_composite_info,
|
||||||
|
utils::Hashset<TypeAndAddressSpace, 8>& valid_type_storage_layouts);
|
||||||
~Validator();
|
~Validator();
|
||||||
|
|
||||||
/// Adds the given error message to the diagnostics
|
/// Adds the given error message to the diagnostics
|
||||||
|
@ -143,20 +163,12 @@ class Validator {
|
||||||
uint32_t el_size,
|
uint32_t el_size,
|
||||||
uint32_t el_align) const;
|
uint32_t el_align) const;
|
||||||
|
|
||||||
/// Validates an atomic
|
/// Validates an atomic type
|
||||||
/// @param a the atomic ast node to validate
|
/// @param a the atomic ast node
|
||||||
/// @param s the atomic sem node
|
/// @param s the atomic sem node
|
||||||
/// @returns true on success, false otherwise.
|
/// @returns true on success, false otherwise.
|
||||||
bool Atomic(const ast::Atomic* a, const sem::Atomic* s) const;
|
bool Atomic(const ast::Atomic* a, const sem::Atomic* s) const;
|
||||||
|
|
||||||
/// Validates an atoic variable
|
|
||||||
/// @param var the variable to validate
|
|
||||||
/// @param atomic_composite_info store atomic information
|
|
||||||
/// @returns true on success, false otherwise.
|
|
||||||
bool AtomicVariable(
|
|
||||||
const sem::Variable* var,
|
|
||||||
const utils::Hashmap<const sem::Type*, const Source*, 8>& atomic_composite_info) const;
|
|
||||||
|
|
||||||
/// Validates an assignment
|
/// Validates an assignment
|
||||||
/// @param a the assignment statement
|
/// @param a the assignment statement
|
||||||
/// @param rhs_ty the type of the right hand side
|
/// @param rhs_ty the type of the right hand side
|
||||||
|
@ -238,12 +250,10 @@ class Validator {
|
||||||
/// Validates a global variable
|
/// Validates a global variable
|
||||||
/// @param var the global variable to validate
|
/// @param var the global variable to validate
|
||||||
/// @param override_id the set of override ids in the module
|
/// @param override_id the set of override ids in the module
|
||||||
/// @param atomic_composite_info atomic composite info in the module
|
|
||||||
/// @returns true on success, false otherwise
|
/// @returns true on success, false otherwise
|
||||||
bool GlobalVariable(
|
bool GlobalVariable(
|
||||||
const sem::GlobalVariable* var,
|
const sem::GlobalVariable* var,
|
||||||
const utils::Hashmap<OverrideId, const sem::Variable*, 8>& override_id,
|
const utils::Hashmap<OverrideId, const sem::Variable*, 8>& override_id) const;
|
||||||
const utils::Hashmap<const sem::Type*, const Source*, 8>& atomic_composite_info) const;
|
|
||||||
|
|
||||||
/// Validates a break-if statement
|
/// Validates a break-if statement
|
||||||
/// @param stmt the statement to validate
|
/// @param stmt the statement to validate
|
||||||
|
@ -423,10 +433,8 @@ class Validator {
|
||||||
|
|
||||||
/// Validates an optional builtin function and its required extension.
|
/// Validates an optional builtin function and its required extension.
|
||||||
/// @param call the builtin call to validate
|
/// @param call the builtin call to validate
|
||||||
/// @param enabled_extensions all the extensions declared in current module
|
|
||||||
/// @returns true on success, false otherwise
|
/// @returns true on success, false otherwise
|
||||||
bool RequiredExtensionForBuiltinFunction(const sem::Call* call,
|
bool RequiredExtensionForBuiltinFunction(const sem::Call* call) const;
|
||||||
const ast::Extensions& enabled_extensions) const;
|
|
||||||
|
|
||||||
/// Validates there are no duplicate attributes
|
/// Validates there are no duplicate attributes
|
||||||
/// @param attributes the list of attributes to validate
|
/// @param attributes the list of attributes to validate
|
||||||
|
@ -437,21 +445,8 @@ class Validator {
|
||||||
/// @param type the type to validate
|
/// @param type the type to validate
|
||||||
/// @param sc the address space
|
/// @param sc the address space
|
||||||
/// @param source the source of the type
|
/// @param source the source of the type
|
||||||
/// @param layouts previously validated storage layouts
|
|
||||||
/// @returns true on success, false otherwise
|
/// @returns true on success, false otherwise
|
||||||
bool AddressSpaceLayout(const sem::Type* type,
|
bool AddressSpaceLayout(const sem::Type* type, ast::AddressSpace sc, Source source) const;
|
||||||
ast::AddressSpace sc,
|
|
||||||
Source source,
|
|
||||||
ValidTypeStorageLayouts& layouts) const;
|
|
||||||
|
|
||||||
/// Validates a address space layout
|
|
||||||
/// @param var the variable to validate
|
|
||||||
/// @param layouts previously validated storage layouts
|
|
||||||
/// @param enabled_extensions all the extensions declared in current module
|
|
||||||
/// @returns true on success, false otherwise.
|
|
||||||
bool AddressSpaceLayout(const sem::Variable* var,
|
|
||||||
const ast::Extensions& enabled_extensions,
|
|
||||||
ValidTypeStorageLayouts& layouts) const;
|
|
||||||
|
|
||||||
/// @returns true if the attribute list contains a
|
/// @returns true if the attribute list contains a
|
||||||
/// ast::DisableValidationAttribute with the validation mode equal to
|
/// ast::DisableValidationAttribute with the validation mode equal to
|
||||||
|
@ -497,11 +492,41 @@ class Validator {
|
||||||
/// @return pretty string representation
|
/// @return pretty string representation
|
||||||
std::string VectorPretty(uint32_t size, const sem::Type* element_type) const;
|
std::string VectorPretty(uint32_t size, const sem::Type* element_type) const;
|
||||||
|
|
||||||
|
/// Raises an error if combination of @p store_ty, @p access and @p address_space are not valid
|
||||||
|
/// for a `var` or `ptr` declaration.
|
||||||
|
/// @param store_ty the store type of the var or pointer
|
||||||
|
/// @param access the var or pointer access
|
||||||
|
/// @param address_space the var or pointer address space
|
||||||
|
/// @param source the source for the error
|
||||||
|
/// @returns true on success, false if an error was raised.
|
||||||
|
bool CheckTypeAccessAddressSpace(const sem::Type* store_ty,
|
||||||
|
ast::Access access,
|
||||||
|
ast::AddressSpace address_space,
|
||||||
|
const utils::VectorRef<const tint::ast::Attribute*> attributes,
|
||||||
|
const Source& source) const;
|
||||||
SymbolTable& symbols_;
|
SymbolTable& symbols_;
|
||||||
diag::List& diagnostics_;
|
diag::List& diagnostics_;
|
||||||
SemHelper& sem_;
|
SemHelper& sem_;
|
||||||
|
const ast::Extensions& enabled_extensions_;
|
||||||
|
const utils::Hashmap<const sem::Type*, const Source*, 8>& atomic_composite_info_;
|
||||||
|
utils::Hashset<TypeAndAddressSpace, 8>& valid_type_storage_layouts_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tint::resolver
|
} // namespace tint::resolver
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
/// Custom std::hash specialization for tint::resolver::TypeAndAddressSpace.
|
||||||
|
template <>
|
||||||
|
class hash<tint::resolver::TypeAndAddressSpace> {
|
||||||
|
public:
|
||||||
|
/// @param tas the TypeAndAddressSpace
|
||||||
|
/// @return the hash value
|
||||||
|
inline std::size_t operator()(const tint::resolver::TypeAndAddressSpace& tas) const {
|
||||||
|
return tint::utils::Hash(tas.type, tas.address_space);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
#endif // SRC_TINT_RESOLVER_VALIDATOR_H_
|
#endif // SRC_TINT_RESOLVER_VALIDATOR_H_
|
||||||
|
|
Loading…
Reference in New Issue