blob: 859d614851fe73aff556053b4df793e77840f36b [file] [log] [blame]
// Copyright 2021 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/lang/core/type/atomic.h"
#include "src/tint/lang/core/type/reference.h"
#include "src/tint/lang/wgsl/resolver/resolver.h"
#include "src/tint/lang/wgsl/resolver/resolver_test_helper.h"
#include "gmock/gmock.h"
using namespace tint::number_suffixes; // NOLINT
namespace tint::resolver {
namespace {
struct ResolverAtomicValidationTest : public resolver::TestHelper, public testing::Test {};
TEST_F(ResolverAtomicValidationTest, AddressSpace_WorkGroup) {
GlobalVar("a", ty.atomic(Source{{12, 34}}, ty.i32()), builtin::AddressSpace::kWorkgroup);
EXPECT_TRUE(r()->Resolve());
}
TEST_F(ResolverAtomicValidationTest, AddressSpace_Storage) {
GlobalVar("g", ty.atomic(Source{{12, 34}}, ty.i32()), builtin::AddressSpace::kStorage,
builtin::Access::kReadWrite, Group(0_a), Binding(0_a));
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverAtomicValidationTest, AddressSpace_Storage_Struct) {
auto* s = Structure("s", Vector{Member(Source{{12, 34}}, "a", ty.atomic(ty.i32()))});
GlobalVar("g", ty.Of(s), builtin::AddressSpace::kStorage, builtin::Access::kReadWrite,
Group(0_a), Binding(0_a));
EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverAtomicValidationTest, InvalidType) {
GlobalVar("a", ty.atomic(ty.f32(Source{{12, 34}})), builtin::AddressSpace::kWorkgroup);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: atomic only supports i32 or u32 types");
}
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Simple) {
GlobalVar(Source{{12, 34}}, "a", ty.atomic(ty.i32()), builtin::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: atomic variables must have <storage> or <workgroup> address space");
}
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Array) {
GlobalVar(Source{{12, 34}}, "a", ty.atomic(ty.i32()), builtin::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: atomic variables must have <storage> or <workgroup> address space");
}
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Struct) {
auto* s = Structure("s", Vector{Member("a", ty.atomic(ty.i32()))});
GlobalVar(Source{{56, 78}}, "g", ty.Of(s), builtin::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"56:78 error: atomic variables must have <storage> or <workgroup> address space\n"
"note: atomic sub-type of 's' is declared here");
}
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_StructOfStruct) {
// struct Inner { m : atomic<i32>; };
// struct Outer { m : array<Inner, 4>; };
// var<private> g : Outer;
auto* Inner = Structure("Inner", Vector{Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
auto* Outer = Structure("Outer", Vector{Member("m", ty.Of(Inner))});
GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), builtin::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"56:78 error: atomic variables must have <storage> or <workgroup> address space\n"
"note: atomic sub-type of 'Outer' is declared here");
}
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_StructOfStructOfArray) {
// struct Inner { m : array<atomic<i32>, 4>; };
// struct Outer { m : array<Inner, 4>; };
// var<private> g : Outer;
auto* Inner = Structure("Inner", Vector{Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
auto* Outer = Structure("Outer", Vector{Member("m", ty.Of(Inner))});
GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), builtin::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(56:78 error: atomic variables must have <storage> or <workgroup> address space
12:34 note: atomic sub-type of 'Outer' is declared here)");
}
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_ArrayOfArray) {
// type AtomicArray = array<atomic<i32>, 5>;
// var<private> v: array<s, 5>;
auto* atomic_array =
Alias(Source{{12, 34}}, "AtomicArray", ty.atomic(Source{{12, 34}}, ty.i32()));
GlobalVar(Source{{56, 78}}, "v", ty.Of(atomic_array), builtin::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"56:78 error: atomic variables must have <storage> or <workgroup> address space");
}
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_ArrayOfStruct) {
// struct S{
// m: atomic<u32>;
// };
// var<private> v: array<S, 5u>;
auto* s = Structure("S", Vector{Member(Source{{12, 34}}, "m", ty.atomic<u32>())});
GlobalVar(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5_u), builtin::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(56:78 error: atomic variables must have <storage> or <workgroup> address space
12:34 note: atomic sub-type of 'array<S, 5>' is declared here)");
}
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_ArrayOfStructOfArray) {
// type AtomicArray = array<atomic<i32>, 5u>;
// struct S{
// m: AtomicArray;
// };
// var<private> v: array<S, 5u>;
auto* atomic_array = Alias("AtomicArray", ty.atomic(ty.i32()));
auto* s = Structure("S", Vector{Member(Source{{12, 34}}, "m", ty.Of(atomic_array))});
GlobalVar(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5_u), builtin::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(56:78 error: atomic variables must have <storage> or <workgroup> address space
12:34 note: atomic sub-type of 'array<S, 5>' is declared here)");
}
TEST_F(ResolverAtomicValidationTest, InvalidAddressSpace_Complex) {
// type AtomicArray = array<atomic<i32>, 5u>;
// struct S6 { x: array<i32, 4>; };
// struct S5 { x: S6;
// y: AtomicArray;
// z: array<atomic<u32>, 8u>; };
// struct S4 { x: S6;
// y: S5;
// z: array<atomic<i32>, 4u>; };
// struct S3 { x: S4; };
// struct S2 { x: S3; };
// struct S1 { x: S2; };
// struct S0 { x: S1; };
// var<private> g : S0;
auto* atomic_array = Alias("AtomicArray", ty.atomic(ty.i32()));
auto array_i32_4 = ty.array<i32, 4>();
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* s6 = Structure("S6", Vector{Member("x", array_i32_4)});
auto* s5 = Structure("S5", Vector{Member("x", ty.Of(s6)), //
Member(Source{{12, 34}}, "y", ty.Of(atomic_array)), //
Member("z", array_atomic_u32_8)}); //
auto* s4 = Structure("S4", Vector{Member("x", ty.Of(s6)), //
Member("y", ty.Of(s5)), //
Member("z", array_atomic_i32_4)}); //
auto* s3 = Structure("S3", Vector{Member("x", ty.Of(s4))});
auto* s2 = Structure("S2", Vector{Member("x", ty.Of(s3))});
auto* s1 = Structure("S1", Vector{Member("x", ty.Of(s2))});
auto* s0 = Structure("S0", Vector{Member("x", ty.Of(s1))});
GlobalVar(Source{{56, 78}}, "g", ty.Of(s0), builtin::AddressSpace::kPrivate);
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
R"(56:78 error: atomic variables must have <storage> or <workgroup> address space
12:34 note: atomic sub-type of 'S0' is declared here)");
}
TEST_F(ResolverAtomicValidationTest, Struct_AccessMode_Read) {
auto* s = Structure("s", Vector{Member(Source{{12, 34}}, "a", ty.atomic(ty.i32()))});
GlobalVar(Source{{56, 78}}, "g", ty.Of(s), builtin::AddressSpace::kStorage,
builtin::Access::kRead, Group(0_a), Binding(0_a));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(56:78 error: atomic variables in <storage> address space must have read_write access mode
12:34 note: atomic sub-type of 's' is declared here)");
}
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_Struct) {
auto* s = Structure("s", Vector{Member(Source{{12, 34}}, "a", ty.atomic(ty.i32()))});
GlobalVar(Source{{56, 78}}, "g", ty.Of(s), builtin::AddressSpace::kStorage,
builtin::Access::kRead, Group(0_a), Binding(0_a));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(56:78 error: atomic variables in <storage> address space must have read_write access mode
12:34 note: atomic sub-type of 's' is declared here)");
}
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_StructOfStruct) {
// struct Inner { m : atomic<i32>; };
// struct Outer { m : array<Inner, 4>; };
// var<storage, read> g : Outer;
auto* Inner = Structure("Inner", Vector{Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
auto* Outer = Structure("Outer", Vector{Member("m", ty.Of(Inner))});
GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), builtin::AddressSpace::kStorage,
builtin::Access::kRead, Group(0_a), Binding(0_a));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
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)");
}
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_StructOfStructOfArray) {
// struct Inner { m : array<atomic<i32>, 4>; };
// struct Outer { m : array<Inner, 4>; };
// var<storage, read> g : Outer;
auto* Inner = Structure("Inner", Vector{Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
auto* Outer = Structure("Outer", Vector{Member("m", ty.Of(Inner))});
GlobalVar(Source{{56, 78}}, "g", ty.Of(Outer), builtin::AddressSpace::kStorage,
builtin::Access::kRead, Group(0_a), Binding(0_a));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
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)");
}
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_Complex) {
// type AtomicArray = array<atomic<i32>, 5>;
// struct S6 { x: array<i32, 4u>; };
// struct S5 { x: S6;
// y: AtomicArray;
// z: array<atomic<u32>, 8u>; };
// struct S4 { x: S6;
// y: S5;
// z: array<atomic<i32>, 4u>; };
// struct S3 { x: S4; };
// struct S2 { x: S3; };
// struct S1 { x: S2; };
// struct S0 { x: S1; };
// var<storage, read> g : S0;
auto* atomic_array = Alias("AtomicArray", ty.atomic(ty.i32()));
auto array_i32_4 = ty.array<i32, 4>();
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* s6 = Structure("S6", Vector{Member("x", array_i32_4)});
auto* s5 = Structure("S5", Vector{Member("x", ty.Of(s6)), //
Member(Source{{56, 78}}, "y", ty.Of(atomic_array)), //
Member("z", array_atomic_u32_8)}); //
auto* s4 = Structure("S4", Vector{Member("x", ty.Of(s6)), //
Member("y", ty.Of(s5)), //
Member("z", array_atomic_i32_4)}); //
auto* s3 = Structure("S3", Vector{Member("x", ty.Of(s4))});
auto* s2 = Structure("S2", Vector{Member("x", ty.Of(s3))});
auto* s1 = Structure("S1", Vector{Member("x", ty.Of(s2))});
auto* s0 = Structure("S0", Vector{Member("x", ty.Of(s1))});
GlobalVar(Source{{12, 34}}, "g", ty.Of(s0), builtin::AddressSpace::kStorage,
builtin::Access::kRead, Group(0_a), Binding(0_a));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(12:34 error: atomic variables in <storage> address space must have read_write access mode
56:78 note: atomic sub-type of 'S0' is declared here)");
}
TEST_F(ResolverAtomicValidationTest, Local) {
WrapInFunction(Var("a", ty.atomic(Source{{12, 34}}, ty.i32())));
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: function-scope 'var' must have a constructible type");
}
} // namespace
} // namespace tint::resolver