mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-17 00:47:13 +00:00
sem: Fold together sem::Struct and sem::StructType
There's now no need to have both. Removes a whole bunch of Sem().Get() smell, and simplifies the resolver. Bug: tint:724 Fixed: tint:761 Change-Id: I756a32680ac52441fd6eebf6fc53dd507ef5e538 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/49961 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Antonio Maiorano <amaiorano@google.com> Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
committed by
Commit Bot service account
parent
33d0f6aa08
commit
ba6ab5e6bd
@@ -466,8 +466,8 @@ namespace {
|
||||
|
||||
using StructBlockTest = ResolverTest;
|
||||
TEST_F(StructBlockTest, StructUsedAsArrayElement) {
|
||||
auto s = Structure("S", {Member("x", ty.i32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* s = Structure("S", {Member("x", ty.i32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto a = ty.array(s, 4);
|
||||
Global("G", a, ast::StorageClass::kPrivate);
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Struct) {
|
||||
// fn main() -> [[location(0)]] Output {
|
||||
// return Output();
|
||||
// }
|
||||
auto output = Structure("Output", {});
|
||||
auto* output = Structure("Output", {});
|
||||
Func(Source{{12, 34}}, "main", {}, output, {Return(Construct(output))},
|
||||
{Stage(ast::PipelineStage::kVertex)}, {Location(Source{{13, 43}}, 0)});
|
||||
|
||||
@@ -105,7 +105,7 @@ TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_Valid) {
|
||||
// fn main() -> Output {
|
||||
// return Output();
|
||||
// }
|
||||
auto output = Structure(
|
||||
auto* output = Structure(
|
||||
"Output", {Member("a", ty.f32(), {Location(0)}),
|
||||
Member("b", ty.f32(), {Builtin(ast::Builtin::kFragDepth)})});
|
||||
Func(Source{{12, 34}}, "main", {}, output, {Return(Construct(output))},
|
||||
@@ -123,7 +123,7 @@ TEST_F(ResolverEntryPointValidationTest,
|
||||
// fn main() -> Output {
|
||||
// return Output();
|
||||
// }
|
||||
auto output = Structure(
|
||||
auto* output = Structure(
|
||||
"Output",
|
||||
{Member("a", ty.f32(),
|
||||
{Location(Source{{13, 43}}, 0),
|
||||
@@ -147,7 +147,7 @@ TEST_F(ResolverEntryPointValidationTest,
|
||||
// fn main() -> Output {
|
||||
// return Output();
|
||||
// }
|
||||
auto output = Structure(
|
||||
auto* output = Structure(
|
||||
"Output", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)}),
|
||||
Member(Source{{14, 52}}, "b", ty.f32(), {})});
|
||||
Func(Source{{12, 34}}, "main", {}, output, {Return(Construct(output))},
|
||||
@@ -170,9 +170,9 @@ TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_NestedStruct) {
|
||||
// fn main() -> Output {
|
||||
// return Output();
|
||||
// }
|
||||
auto inner = Structure(
|
||||
auto* inner = Structure(
|
||||
"Inner", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)})});
|
||||
auto output = Structure(
|
||||
auto* output = Structure(
|
||||
"Output", {Member(Source{{14, 52}}, "a", inner, {Location(0)})});
|
||||
Func(Source{{12, 34}}, "main", {}, output, {Return(Construct(output))},
|
||||
{Stage(ast::PipelineStage::kFragment)});
|
||||
@@ -193,7 +193,7 @@ TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_RuntimeArray) {
|
||||
// fn main() -> Output {
|
||||
// return Output();
|
||||
// }
|
||||
auto output = Structure(
|
||||
auto* output = Structure(
|
||||
"Output",
|
||||
{Member(Source{{13, 43}}, "a", ty.array<float>(), {Location(0)})},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
@@ -216,7 +216,7 @@ TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_DuplicateBuiltins) {
|
||||
// fn main() -> Output {
|
||||
// return Output();
|
||||
// }
|
||||
auto output = Structure(
|
||||
auto* output = Structure(
|
||||
"Output", {Member("a", ty.f32(), {Builtin(ast::Builtin::kFragDepth)}),
|
||||
Member("b", ty.f32(), {Builtin(ast::Builtin::kFragDepth)})});
|
||||
Func(Source{{12, 34}}, "main", {}, output, {Return(Construct(output))},
|
||||
@@ -238,8 +238,8 @@ TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_DuplicateLocation) {
|
||||
// fn main() -> Output {
|
||||
// return Output();
|
||||
// }
|
||||
auto output = Structure("Output", {Member("a", ty.f32(), {Location(1)}),
|
||||
Member("b", ty.f32(), {Location(1)})});
|
||||
auto* output = Structure("Output", {Member("a", ty.f32(), {Location(1)}),
|
||||
Member("b", ty.f32(), {Location(1)})});
|
||||
Func(Source{{12, 34}}, "main", {}, output, {Return(Construct(output))},
|
||||
{Stage(ast::PipelineStage::kFragment)});
|
||||
|
||||
@@ -302,7 +302,7 @@ TEST_F(ResolverEntryPointValidationTest, ParameterAttribute_Struct) {
|
||||
// };
|
||||
// [[stage(fragment)]]
|
||||
// fn main([[location(0)]] param : Input) {}
|
||||
auto input = Structure("Input", {});
|
||||
auto* input = Structure("Input", {});
|
||||
auto* param = Param("param", input, {Location(Source{{13, 43}}, 0)});
|
||||
Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
|
||||
{Stage(ast::PipelineStage::kFragment)});
|
||||
@@ -321,7 +321,7 @@ TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_Valid) {
|
||||
// };
|
||||
// [[stage(fragment)]]
|
||||
// fn main(param : Input) {}
|
||||
auto input = Structure(
|
||||
auto* input = Structure(
|
||||
"Input", {Member("a", ty.f32(), {Location(0)}),
|
||||
Member("b", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)})});
|
||||
auto* param = Param("param", input);
|
||||
@@ -338,7 +338,7 @@ TEST_F(ResolverEntryPointValidationTest,
|
||||
// };
|
||||
// [[stage(fragment)]]
|
||||
// fn main(param : Input) {}
|
||||
auto input = Structure(
|
||||
auto* input = Structure(
|
||||
"Input",
|
||||
{Member("a", ty.f32(),
|
||||
{Location(Source{{13, 43}}, 0),
|
||||
@@ -361,7 +361,7 @@ TEST_F(ResolverEntryPointValidationTest,
|
||||
// };
|
||||
// [[stage(fragment)]]
|
||||
// fn main(param : Input) {}
|
||||
auto input = Structure(
|
||||
auto* input = Structure(
|
||||
"Input", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)}),
|
||||
Member(Source{{14, 52}}, "b", ty.f32(), {})});
|
||||
auto* param = Param("param", input);
|
||||
@@ -382,9 +382,9 @@ TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_NestedStruct) {
|
||||
// };
|
||||
// [[stage(fragment)]]
|
||||
// fn main(param : Input) {}
|
||||
auto inner = Structure(
|
||||
auto* inner = Structure(
|
||||
"Inner", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)})});
|
||||
auto input =
|
||||
auto* input =
|
||||
Structure("Input", {Member(Source{{14, 52}}, "a", inner, {Location(0)})});
|
||||
auto* param = Param("param", input);
|
||||
Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
|
||||
@@ -404,7 +404,7 @@ TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_RuntimeArray) {
|
||||
// };
|
||||
// [[stage(fragment)]]
|
||||
// fn main(param : Input) {}
|
||||
auto input = Structure(
|
||||
auto* input = Structure(
|
||||
"Input",
|
||||
{Member(Source{{13, 43}}, "a", ty.array<float>(), {Location(0)})},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
@@ -446,9 +446,9 @@ TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_DuplicateBuiltins) {
|
||||
// };
|
||||
// [[stage(fragment)]]
|
||||
// fn main(param_a : InputA, param_b : InputB) {}
|
||||
auto input_a = Structure(
|
||||
auto* input_a = Structure(
|
||||
"InputA", {Member("a", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)})});
|
||||
auto input_b = Structure(
|
||||
auto* input_b = Structure(
|
||||
"InputB", {Member("a", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)})});
|
||||
auto* param_a = Param("param_a", input_a);
|
||||
auto* param_b = Param("param_b", input_b);
|
||||
@@ -486,8 +486,8 @@ TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_DuplicateLocation) {
|
||||
// };
|
||||
// [[stage(fragment)]]
|
||||
// fn main(param_a : InputA, param_b : InputB) {}
|
||||
auto input_a = Structure("InputA", {Member("a", ty.f32(), {Location(1)})});
|
||||
auto input_b = Structure("InputB", {Member("a", ty.f32(), {Location(1)})});
|
||||
auto* input_a = Structure("InputA", {Member("a", ty.f32(), {Location(1)})});
|
||||
auto* input_b = Structure("InputB", {Member("a", ty.f32(), {Location(1)})});
|
||||
auto* param_a = Param("param_a", input_a);
|
||||
auto* param_b = Param("param_b", input_b);
|
||||
Func(Source{{12, 34}}, "main", {param_a, param_b}, ty.void_(), {},
|
||||
|
||||
@@ -27,8 +27,8 @@ namespace {
|
||||
using ResolverHostShareableValidationTest = ResolverTest;
|
||||
|
||||
TEST_F(ResolverHostShareableValidationTest, BoolMember) {
|
||||
auto s = Structure("S", {Member(Source{{12, 34}}, "x", ty.bool_())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.bool_())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto a = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage);
|
||||
|
||||
@@ -42,8 +42,8 @@ TEST_F(ResolverHostShareableValidationTest, BoolMember) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverHostShareableValidationTest, BoolVectorMember) {
|
||||
auto s = Structure("S", {Member(Source{{12, 34}}, "x", ty.vec3<bool>())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.vec3<bool>())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto a = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage);
|
||||
|
||||
@@ -59,8 +59,8 @@ TEST_F(ResolverHostShareableValidationTest, BoolVectorMember) {
|
||||
TEST_F(ResolverHostShareableValidationTest, Aliases) {
|
||||
auto a1 = ty.alias("a1", ty.bool_());
|
||||
AST().AddConstructedType(a1);
|
||||
auto s = Structure("S", {Member(Source{{12, 34}}, "x", a1)},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", a1)},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto ac = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
auto a2 = ty.alias("a2", ac);
|
||||
AST().AddConstructedType(a2);
|
||||
@@ -76,12 +76,12 @@ TEST_F(ResolverHostShareableValidationTest, Aliases) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverHostShareableValidationTest, NestedStructures) {
|
||||
auto i1 = Structure("I1", {Member(Source{{1, 2}}, "x", ty.bool_())});
|
||||
auto i2 = Structure("I2", {Member(Source{{3, 4}}, "y", i1)});
|
||||
auto i3 = Structure("I3", {Member(Source{{5, 6}}, "z", i2)});
|
||||
auto* i1 = Structure("I1", {Member(Source{{1, 2}}, "x", ty.bool_())});
|
||||
auto* i2 = Structure("I2", {Member(Source{{3, 4}}, "y", i1)});
|
||||
auto* i3 = Structure("I3", {Member(Source{{5, 6}}, "z", i2)});
|
||||
|
||||
auto s = Structure("S", {Member(Source{{7, 8}}, "m", i3)},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* s = Structure("S", {Member(Source{{7, 8}}, "m", i3)},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto a = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
Global(Source{{9, 10}}, "g", a, ast::StorageClass::kStorage);
|
||||
|
||||
@@ -98,7 +98,7 @@ TEST_F(ResolverHostShareableValidationTest, NestedStructures) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverHostShareableValidationTest, NoError) {
|
||||
auto i1 =
|
||||
auto* i1 =
|
||||
Structure("I1", {
|
||||
Member(Source{{1, 1}}, "x1", ty.f32()),
|
||||
Member(Source{{2, 1}}, "y1", ty.vec3<f32>()),
|
||||
@@ -106,21 +106,21 @@ TEST_F(ResolverHostShareableValidationTest, NoError) {
|
||||
});
|
||||
auto a1 = ty.alias("a1", i1);
|
||||
AST().AddConstructedType(a1);
|
||||
auto i2 = Structure("I2", {
|
||||
Member(Source{{4, 1}}, "x2", ty.mat2x2<f32>()),
|
||||
Member(Source{{5, 1}}, "y2", i1),
|
||||
Member(Source{{6, 1}}, "z2", ty.mat3x2<i32>()),
|
||||
});
|
||||
auto* i2 = Structure("I2", {
|
||||
Member(Source{{4, 1}}, "x2", ty.mat2x2<f32>()),
|
||||
Member(Source{{5, 1}}, "y2", i1),
|
||||
Member(Source{{6, 1}}, "z2", ty.mat3x2<i32>()),
|
||||
});
|
||||
auto a2 = ty.alias("a2", i2);
|
||||
AST().AddConstructedType(a2);
|
||||
auto i3 = Structure("I3", {
|
||||
Member(Source{{4, 1}}, "x3", a1),
|
||||
Member(Source{{5, 1}}, "y3", i2),
|
||||
Member(Source{{6, 1}}, "z3", a2),
|
||||
});
|
||||
auto* i3 = Structure("I3", {
|
||||
Member(Source{{4, 1}}, "x3", a1),
|
||||
Member(Source{{5, 1}}, "y3", i2),
|
||||
Member(Source{{6, 1}}, "z3", a2),
|
||||
});
|
||||
|
||||
auto s = Structure("S", {Member(Source{{7, 8}}, "m", i3)},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* s = Structure("S", {Member(Source{{7, 8}}, "m", i3)},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto a = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
Global(Source{{9, 10}}, "g", a, ast::StorageClass::kStorage);
|
||||
|
||||
|
||||
@@ -757,8 +757,8 @@ using ResolverIntrinsicDataTest = ResolverTest;
|
||||
|
||||
TEST_F(ResolverIntrinsicDataTest, ArrayLength_Vector) {
|
||||
auto ary = ty.array<i32>();
|
||||
auto str = Structure("S", {Member("x", ary)},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* str = Structure("S", {Member("x", ary)},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto ac = ty.access(ast::AccessControl::kReadOnly, str);
|
||||
Global("a", ac, ast::StorageClass::kStorage);
|
||||
|
||||
|
||||
@@ -105,46 +105,7 @@ TEST_F(ResolverIsHostShareable, ArrayUnsizedOfHostShareable) {
|
||||
EXPECT_TRUE(r()->IsHostShareable(ty.array<i32>()));
|
||||
}
|
||||
|
||||
TEST_F(ResolverIsHostShareable, Struct_AllMembersHostShareable) {
|
||||
EXPECT_TRUE(r()->IsHostShareable(Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
})));
|
||||
}
|
||||
|
||||
TEST_F(ResolverIsHostShareable, Struct_SomeMembersNonHostShareable) {
|
||||
auto ptr_ty = ty.pointer<i32>(ast::StorageClass::kPrivate);
|
||||
EXPECT_FALSE(r()->IsHostShareable(Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ptr_ty),
|
||||
})));
|
||||
}
|
||||
|
||||
TEST_F(ResolverIsHostShareable, Struct_NestedHostShareable) {
|
||||
auto host_shareable = Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
EXPECT_TRUE(
|
||||
r()->IsHostShareable(Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", host_shareable),
|
||||
})));
|
||||
}
|
||||
|
||||
TEST_F(ResolverIsHostShareable, Struct_NestedNonHostShareable) {
|
||||
auto ptr_ty = ty.pointer<i32>(ast::StorageClass::kPrivate);
|
||||
auto non_host_shareable =
|
||||
Structure("non_host_shareable", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ptr_ty),
|
||||
});
|
||||
EXPECT_FALSE(
|
||||
r()->IsHostShareable(Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", non_host_shareable),
|
||||
})));
|
||||
}
|
||||
// Note: Structure tests covered in host_shareable_validation_test.cc
|
||||
|
||||
} // namespace
|
||||
} // namespace resolver
|
||||
|
||||
@@ -90,41 +90,55 @@ TEST_F(ResolverIsStorableTest, ArrayUnsizedOfStorable) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverIsStorableTest, Struct_AllMembersStorable) {
|
||||
EXPECT_TRUE(r()->IsStorable(Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
})));
|
||||
Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverIsStorableTest, Struct_SomeMembersNonStorable) {
|
||||
auto ptr_ty = ty.pointer<i32>(ast::StorageClass::kPrivate);
|
||||
EXPECT_FALSE(r()->IsStorable(Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ptr_ty),
|
||||
})));
|
||||
Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ptr_ty),
|
||||
});
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(
|
||||
r()->error(),
|
||||
R"(error: ptr<private, i32> cannot be used as the type of a structure member)");
|
||||
}
|
||||
|
||||
TEST_F(ResolverIsStorableTest, Struct_NestedStorable) {
|
||||
auto storable = Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
EXPECT_TRUE(r()->IsStorable(Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", storable),
|
||||
})));
|
||||
auto* storable = Structure("Storable", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ty.f32()),
|
||||
});
|
||||
Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", storable),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
}
|
||||
|
||||
TEST_F(ResolverIsStorableTest, Struct_NestedNonStorable) {
|
||||
auto ptr_ty = ty.pointer<i32>(ast::StorageClass::kPrivate);
|
||||
auto non_storable = Structure("nonstorable", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ptr_ty),
|
||||
});
|
||||
EXPECT_FALSE(r()->IsStorable(Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", non_storable),
|
||||
})));
|
||||
auto* non_storable = Structure("nonstorable", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", ptr_ty),
|
||||
});
|
||||
Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", non_storable),
|
||||
});
|
||||
|
||||
EXPECT_FALSE(r()->Resolve());
|
||||
EXPECT_EQ(
|
||||
r()->error(),
|
||||
R"(error: ptr<private, i32> cannot be used as the type of a structure member)");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -181,15 +181,13 @@ bool Resolver::IsStorable(const sem::Type* type) {
|
||||
if (auto* arr = type->As<sem::ArrayType>()) {
|
||||
return IsStorable(arr->type());
|
||||
}
|
||||
if (auto* str_ty = type->As<sem::StructType>()) {
|
||||
if (auto* str = Structure(str_ty)) {
|
||||
for (const auto* member : str->members) {
|
||||
if (!IsStorable(member->Type())) {
|
||||
return false;
|
||||
}
|
||||
if (auto* str = type->As<sem::Struct>()) {
|
||||
for (const auto* member : str->Members()) {
|
||||
if (!IsStorable(member->Type())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -209,12 +207,8 @@ bool Resolver::IsHostShareable(const sem::Type* type) {
|
||||
if (auto* arr = type->As<sem::ArrayType>()) {
|
||||
return IsHostShareable(arr->type());
|
||||
}
|
||||
if (auto* str = type->As<sem::StructType>()) {
|
||||
auto* info = Structure(str);
|
||||
if (!info) {
|
||||
return false;
|
||||
}
|
||||
for (auto* member : info->members) {
|
||||
if (auto* str = type->As<sem::Struct>()) {
|
||||
for (auto* member : str->Members()) {
|
||||
if (!IsHostShareable(member->Type())) {
|
||||
return false;
|
||||
}
|
||||
@@ -243,7 +237,7 @@ bool Resolver::IsValidAssignment(const sem::Type* lhs, const sem::Type* rhs) {
|
||||
bool Resolver::ResolveInternal() {
|
||||
Mark(&builder_->AST());
|
||||
|
||||
auto register_named_type = [this](Symbol name, const sem::Type* type,
|
||||
auto register_named_type = [this](Symbol name, sem::Type* type,
|
||||
const Source& source) {
|
||||
auto added = named_types_.emplace(name, type).second;
|
||||
if (!added) {
|
||||
@@ -312,9 +306,9 @@ bool Resolver::ResolveInternal() {
|
||||
return result;
|
||||
}
|
||||
|
||||
const sem::Type* Resolver::Type(const ast::Type* ty) {
|
||||
sem::Type* Resolver::Type(const ast::Type* ty) {
|
||||
Mark(ty);
|
||||
auto* s = [&]() -> const sem::Type* {
|
||||
auto* s = [&]() -> sem::Type* {
|
||||
if (ty->Is<ast::Void>()) {
|
||||
return builder_->create<sem::Void>();
|
||||
}
|
||||
@@ -357,8 +351,11 @@ const sem::Type* Resolver::Type(const ast::Type* ty) {
|
||||
}
|
||||
if (auto* t = ty->As<ast::Array>()) {
|
||||
if (auto* el = Type(t->type())) {
|
||||
return builder_->create<sem::ArrayType>(const_cast<sem::Type*>(el),
|
||||
t->size(), t->decorations());
|
||||
auto* sem = builder_->create<sem::ArrayType>(
|
||||
const_cast<sem::Type*>(el), t->size(), t->decorations());
|
||||
if (Array(sem, ty->source())) {
|
||||
return sem;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -370,7 +367,7 @@ const sem::Type* Resolver::Type(const ast::Type* ty) {
|
||||
return nullptr;
|
||||
}
|
||||
if (auto* t = ty->As<ast::Struct>()) {
|
||||
return builder_->create<sem::StructType>(const_cast<ast::Struct*>(t));
|
||||
return Structure(t);
|
||||
}
|
||||
if (auto* t = ty->As<ast::Sampler>()) {
|
||||
return builder_->create<sem::Sampler>(t->kind());
|
||||
@@ -417,35 +414,15 @@ const sem::Type* Resolver::Type(const ast::Type* ty) {
|
||||
return nullptr;
|
||||
}();
|
||||
|
||||
if (s == nullptr) {
|
||||
return nullptr;
|
||||
if (s) {
|
||||
builder_->Sem().Add(ty, s);
|
||||
}
|
||||
if (!Type(s, ty->source())) {
|
||||
return nullptr;
|
||||
}
|
||||
builder_->Sem().Add(ty, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
// TODO(crbug.com/tint/724): This method should be merged into Type(ast::Type*)
|
||||
bool Resolver::Type(const sem::Type* ty, const Source& source /* = {} */) {
|
||||
ty = ty->UnwrapAliasIfNeeded();
|
||||
if (auto* str = ty->As<sem::StructType>()) {
|
||||
if (!Structure(str)) {
|
||||
return false;
|
||||
}
|
||||
} else if (auto* arr = ty->As<sem::ArrayType>()) {
|
||||
if (!Array(arr, source)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Resolver::VariableInfo* Resolver::Variable(
|
||||
ast::Variable* var,
|
||||
const sem::Type* type, /* = nullptr */
|
||||
std::string type_name /* = "" */) {
|
||||
Resolver::VariableInfo* Resolver::Variable(ast::Variable* var,
|
||||
sem::Type* type, /* = nullptr */
|
||||
std::string type_name /* = "" */) {
|
||||
auto it = variable_to_info_.find(var);
|
||||
if (it != variable_to_info_.end()) {
|
||||
return it->second;
|
||||
@@ -561,7 +538,7 @@ bool Resolver::ValidateGlobalVariable(const VariableInfo* info) {
|
||||
// attribute, satisfying the storage class constraints.
|
||||
|
||||
auto* access = info->type->As<sem::AccessControl>();
|
||||
auto* str = access ? access->type()->As<sem::StructType>() : nullptr;
|
||||
auto* str = access ? access->type()->As<sem::Struct>() : nullptr;
|
||||
if (!str) {
|
||||
diagnostics_.add_error(
|
||||
"variables declared in the <storage> storage class must be of an "
|
||||
@@ -574,7 +551,7 @@ bool Resolver::ValidateGlobalVariable(const VariableInfo* info) {
|
||||
diagnostics_.add_error(
|
||||
"structure used as a storage buffer must be declared with the "
|
||||
"[[block]] decoration",
|
||||
str->impl()->source());
|
||||
str->Declaration()->source());
|
||||
if (info->declaration->source().range.begin.line) {
|
||||
diagnostics_.add_note("structure used as storage buffer here",
|
||||
info->declaration->source());
|
||||
@@ -588,7 +565,7 @@ bool Resolver::ValidateGlobalVariable(const VariableInfo* info) {
|
||||
// A variable in the uniform storage class is a uniform buffer variable.
|
||||
// Its store type must be a host-shareable structure type with block
|
||||
// attribute, satisfying the storage class constraints.
|
||||
auto* str = info->type->As<sem::StructType>();
|
||||
auto* str = info->type->As<sem::Struct>();
|
||||
if (!str) {
|
||||
diagnostics_.add_error(
|
||||
"variables declared in the <uniform> storage class must be of a "
|
||||
@@ -601,7 +578,7 @@ bool Resolver::ValidateGlobalVariable(const VariableInfo* info) {
|
||||
diagnostics_.add_error(
|
||||
"structure used as a uniform buffer must be declared with the "
|
||||
"[[block]] decoration",
|
||||
str->impl()->source());
|
||||
str->Declaration()->source());
|
||||
if (info->declaration->source().range.begin.line) {
|
||||
diagnostics_.add_note("structure used as uniform buffer here",
|
||||
info->declaration->source());
|
||||
@@ -781,7 +758,7 @@ bool Resolver::ValidateEntryPoint(const ast::Function* func,
|
||||
};
|
||||
// Inner lambda that is applied to a type and all of its members.
|
||||
auto validate_entry_point_decorations_inner =
|
||||
[&](const ast::DecorationList& decos, const sem::Type* ty, Source source,
|
||||
[&](const ast::DecorationList& decos, sem::Type* ty, Source source,
|
||||
ParamOrRetType param_or_ret, bool is_struct_member) {
|
||||
// Scan decorations for pipeline IO attributes.
|
||||
// Check for overlap with attributes that have been seen previously.
|
||||
@@ -834,7 +811,7 @@ bool Resolver::ValidateEntryPoint(const ast::Function* func,
|
||||
}
|
||||
|
||||
// Check that we saw a pipeline IO attribute iff we need one.
|
||||
if (Canonical(ty)->Is<sem::StructType>()) {
|
||||
if (Canonical(ty)->Is<sem::Struct>()) {
|
||||
if (pipeline_io_attribute) {
|
||||
diagnostics_.add_error(
|
||||
"entry point IO attributes must not be used on structure " +
|
||||
@@ -862,8 +839,7 @@ bool Resolver::ValidateEntryPoint(const ast::Function* func,
|
||||
|
||||
// Outer lambda for validating the entry point decorations for a type.
|
||||
auto validate_entry_point_decorations = [&](const ast::DecorationList& decos,
|
||||
const sem::Type* ty,
|
||||
Source source,
|
||||
sem::Type* ty, Source source,
|
||||
ParamOrRetType param_or_ret) {
|
||||
// Validate the decorations for the type.
|
||||
if (!validate_entry_point_decorations_inner(decos, ty, source, param_or_ret,
|
||||
@@ -871,12 +847,12 @@ bool Resolver::ValidateEntryPoint(const ast::Function* func,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto* struct_ty = Canonical(ty)->As<sem::StructType>()) {
|
||||
if (auto* str = Canonical(ty)->As<sem::Struct>()) {
|
||||
// Validate the decorations for each struct members, and also check for
|
||||
// invalid member types.
|
||||
for (auto* member : Structure(struct_ty)->members) {
|
||||
for (auto* member : str->Members()) {
|
||||
auto* member_ty = Canonical(member->Type());
|
||||
if (member_ty->Is<sem::StructType>()) {
|
||||
if (member_ty->Is<sem::Struct>()) {
|
||||
diagnostics_.add_error(
|
||||
"entry point IO types cannot contain nested structures",
|
||||
member->Declaration()->source());
|
||||
@@ -988,23 +964,16 @@ bool Resolver::Function(ast::Function* func) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto* str = param_info->type->As<sem::StructType>()) {
|
||||
auto* str_info = Structure(str);
|
||||
if (!str_info) {
|
||||
return false;
|
||||
}
|
||||
if (auto* str = param_info->type->As<sem::Struct>()) {
|
||||
switch (func->pipeline_stage()) {
|
||||
case ast::PipelineStage::kVertex:
|
||||
str_info->pipeline_stage_uses.emplace(
|
||||
sem::PipelineStageUsage::kVertexInput);
|
||||
str->AddUsage(sem::PipelineStageUsage::kVertexInput);
|
||||
break;
|
||||
case ast::PipelineStage::kFragment:
|
||||
str_info->pipeline_stage_uses.emplace(
|
||||
sem::PipelineStageUsage::kFragmentInput);
|
||||
str->AddUsage(sem::PipelineStageUsage::kFragmentInput);
|
||||
break;
|
||||
case ast::PipelineStage::kCompute:
|
||||
str_info->pipeline_stage_uses.emplace(
|
||||
sem::PipelineStageUsage::kComputeInput);
|
||||
str->AddUsage(sem::PipelineStageUsage::kComputeInput);
|
||||
break;
|
||||
case ast::PipelineStage::kNone:
|
||||
break;
|
||||
@@ -1026,7 +995,7 @@ bool Resolver::Function(ast::Function* func) {
|
||||
|
||||
info->return_type = Canonical(info->return_type);
|
||||
|
||||
if (auto* str = info->return_type->As<sem::StructType>()) {
|
||||
if (auto* str = info->return_type->As<sem::Struct>()) {
|
||||
if (!ApplyStorageClassUsageToType(ast::StorageClass::kNone, str,
|
||||
func->source())) {
|
||||
diagnostics_.add_note("while instantiating return type for " +
|
||||
@@ -1035,22 +1004,15 @@ bool Resolver::Function(ast::Function* func) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* str_info = Structure(str);
|
||||
if (!str_info) {
|
||||
return false;
|
||||
}
|
||||
switch (func->pipeline_stage()) {
|
||||
case ast::PipelineStage::kVertex:
|
||||
str_info->pipeline_stage_uses.emplace(
|
||||
sem::PipelineStageUsage::kVertexOutput);
|
||||
str->AddUsage(sem::PipelineStageUsage::kVertexOutput);
|
||||
break;
|
||||
case ast::PipelineStage::kFragment:
|
||||
str_info->pipeline_stage_uses.emplace(
|
||||
sem::PipelineStageUsage::kFragmentOutput);
|
||||
str->AddUsage(sem::PipelineStageUsage::kFragmentOutput);
|
||||
break;
|
||||
case ast::PipelineStage::kCompute:
|
||||
str_info->pipeline_stage_uses.emplace(
|
||||
sem::PipelineStageUsage::kComputeOutput);
|
||||
str->AddUsage(sem::PipelineStageUsage::kComputeOutput);
|
||||
break;
|
||||
case ast::PipelineStage::kNone:
|
||||
break;
|
||||
@@ -1659,13 +1621,12 @@ bool Resolver::MemberAccessor(ast::MemberAccessorExpression* expr) {
|
||||
sem::Type* ret = nullptr;
|
||||
std::vector<uint32_t> swizzle;
|
||||
|
||||
if (auto* ty = data_type->As<sem::StructType>()) {
|
||||
if (auto* str = data_type->As<sem::Struct>()) {
|
||||
Mark(expr->member());
|
||||
auto symbol = expr->member()->symbol();
|
||||
auto* str = Structure(ty);
|
||||
|
||||
const sem::StructMember* member = nullptr;
|
||||
for (auto* m : str->members) {
|
||||
for (auto* m : str->Members()) {
|
||||
if (m->Declaration()->symbol() == symbol) {
|
||||
ret = m->Type();
|
||||
member = m;
|
||||
@@ -1689,11 +1650,11 @@ bool Resolver::MemberAccessor(ast::MemberAccessorExpression* expr) {
|
||||
expr, ret, current_statement_, member));
|
||||
} else if (auto* vec = data_type->As<sem::Vector>()) {
|
||||
Mark(expr->member());
|
||||
std::string str = builder_->Symbols().NameFor(expr->member()->symbol());
|
||||
auto size = str.size();
|
||||
swizzle.reserve(str.size());
|
||||
std::string s = builder_->Symbols().NameFor(expr->member()->symbol());
|
||||
auto size = s.size();
|
||||
swizzle.reserve(s.size());
|
||||
|
||||
for (auto c : str) {
|
||||
for (auto c : s) {
|
||||
switch (c) {
|
||||
case 'x':
|
||||
case 'r':
|
||||
@@ -1732,8 +1693,8 @@ bool Resolver::MemberAccessor(ast::MemberAccessorExpression* expr) {
|
||||
auto is_xyzw = [](char c) {
|
||||
return c == 'x' || c == 'y' || c == 'z' || c == 'w';
|
||||
};
|
||||
if (!std::all_of(str.begin(), str.end(), is_rgba) &&
|
||||
!std::all_of(str.begin(), str.end(), is_xyzw)) {
|
||||
if (!std::all_of(s.begin(), s.end(), is_rgba) &&
|
||||
!std::all_of(s.begin(), s.end(), is_xyzw)) {
|
||||
diagnostics_.add_error(
|
||||
"invalid mixing of vector swizzle characters rgba with xyzw",
|
||||
expr->member()->source());
|
||||
@@ -2032,7 +1993,7 @@ bool Resolver::VariableDeclStatement(const ast::VariableDeclStatement* stmt) {
|
||||
|
||||
// If the variable has a declared type, resolve it.
|
||||
std::string type_name;
|
||||
const sem::Type* type = nullptr;
|
||||
sem::Type* type = nullptr;
|
||||
if (auto* ast_ty = var->type()) {
|
||||
type_name = ast_ty->FriendlyName(builder_->Symbols());
|
||||
type = Type(ast_ty);
|
||||
@@ -2122,7 +2083,7 @@ bool Resolver::VariableDeclStatement(const ast::VariableDeclStatement* stmt) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const sem::Type* Resolver::TypeOf(const ast::Expression* expr) {
|
||||
sem::Type* Resolver::TypeOf(const ast::Expression* expr) {
|
||||
auto it = expr_info_.find(expr);
|
||||
if (it != expr_info_.end()) {
|
||||
return it->second.type;
|
||||
@@ -2138,7 +2099,7 @@ std::string Resolver::TypeNameOf(const ast::Expression* expr) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const sem::Type* Resolver::TypeOf(const ast::Literal* lit) {
|
||||
sem::Type* Resolver::TypeOf(const ast::Literal* lit) {
|
||||
if (lit->Is<ast::SintLiteral>()) {
|
||||
return builder_->create<sem::I32>();
|
||||
}
|
||||
@@ -2273,17 +2234,6 @@ void Resolver::CreateSemanticNodes() const {
|
||||
builder_->create<sem::Expression>(
|
||||
const_cast<ast::Expression*>(expr), info.type, info.statement));
|
||||
}
|
||||
|
||||
// Create semantic nodes for all structs
|
||||
for (auto it : struct_info_) {
|
||||
auto* str = it.first;
|
||||
auto* info = it.second;
|
||||
builder_->Sem().Add(
|
||||
str, builder_->create<sem::Struct>(
|
||||
const_cast<sem::StructType*>(str), std::move(info->members),
|
||||
info->align, info->size, info->size_no_padding,
|
||||
info->storage_class_usage, info->pipeline_stage_uses));
|
||||
}
|
||||
}
|
||||
|
||||
bool Resolver::DefaultAlignAndSize(const sem::Type* ty,
|
||||
@@ -2305,7 +2255,7 @@ bool Resolver::DefaultAlignAndSize(const sem::Type* ty,
|
||||
/*vec4*/ 16,
|
||||
};
|
||||
|
||||
auto* cty = Canonical(ty);
|
||||
auto* cty = Canonical(const_cast<sem::Type*>(ty));
|
||||
if (cty->is_scalar()) {
|
||||
// Note: Also captures booleans, but these are not host-shareable.
|
||||
align = 4;
|
||||
@@ -2330,13 +2280,10 @@ bool Resolver::DefaultAlignAndSize(const sem::Type* ty,
|
||||
align = vector_align[mat->rows()];
|
||||
size = vector_align[mat->rows()] * mat->columns();
|
||||
return true;
|
||||
} else if (auto* s = cty->As<sem::StructType>()) {
|
||||
if (auto* si = Structure(s)) {
|
||||
align = si->align;
|
||||
size = si->size;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else if (auto* s = cty->As<sem::Struct>()) {
|
||||
align = s->Align();
|
||||
size = s->Size();
|
||||
return true;
|
||||
} else if (cty->Is<sem::ArrayType>()) {
|
||||
if (auto* sem =
|
||||
Array(ty->UnwrapAliasIfNeeded()->As<sem::ArrayType>(), source)) {
|
||||
@@ -2416,8 +2363,8 @@ bool Resolver::ValidateArray(const sem::ArrayType* arr, const Source& source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto* el_str = el_ty->As<sem::StructType>()) {
|
||||
if (el_str->impl()->IsBlockDecorated()) {
|
||||
if (auto* el_str = el_ty->As<sem::Struct>()) {
|
||||
if (el_str->IsBlockDecorated()) {
|
||||
// https://gpuweb.github.io/gpuweb/wgsl/#attributes
|
||||
// A structure type with the block attribute must not be:
|
||||
// * the element type of an array type
|
||||
@@ -2454,23 +2401,23 @@ bool Resolver::ValidateArrayStrideDecoration(const ast::StrideDecoration* deco,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Resolver::ValidateStructure(const StructInfo* st) {
|
||||
for (auto* member : st->members) {
|
||||
bool Resolver::ValidateStructure(const sem::Struct* str) {
|
||||
for (auto* member : str->Members()) {
|
||||
if (auto* r = member->Type()->UnwrapAll()->As<sem::ArrayType>()) {
|
||||
if (r->IsRuntimeArray()) {
|
||||
if (member != st->members.back()) {
|
||||
if (member != str->Members().back()) {
|
||||
diagnostics_.add_error(
|
||||
"v-0015",
|
||||
"runtime arrays may only appear as the last member of a struct",
|
||||
member->Declaration()->source());
|
||||
return false;
|
||||
}
|
||||
if (!st->type->impl()->IsBlockDecorated()) {
|
||||
if (!str->IsBlockDecorated()) {
|
||||
diagnostics_.add_error(
|
||||
"v-0015",
|
||||
"a struct containing a runtime-sized array "
|
||||
"requires the [[block]] attribute: '" +
|
||||
builder_->Symbols().NameFor(st->type->impl()->name()) + "'",
|
||||
builder_->Symbols().NameFor(str->Declaration()->name()) + "'",
|
||||
member->Declaration()->source());
|
||||
return false;
|
||||
}
|
||||
@@ -2498,7 +2445,7 @@ bool Resolver::ValidateStructure(const StructInfo* st) {
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* deco : st->type->impl()->decorations()) {
|
||||
for (auto* deco : str->Declaration()->decorations()) {
|
||||
if (!(deco->Is<ast::StructBlockDecoration>())) {
|
||||
diagnostics_.add_error("decoration is not valid for struct declarations",
|
||||
deco->source());
|
||||
@@ -2509,19 +2456,13 @@ bool Resolver::ValidateStructure(const StructInfo* st) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Resolver::StructInfo* Resolver::Structure(const sem::StructType* str) {
|
||||
auto info_it = struct_info_.find(str);
|
||||
if (info_it != struct_info_.end()) {
|
||||
// StructInfo already resolved for this structure type
|
||||
return info_it->second;
|
||||
}
|
||||
|
||||
for (auto* deco : str->impl()->decorations()) {
|
||||
sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
||||
for (auto* deco : str->decorations()) {
|
||||
Mark(deco);
|
||||
}
|
||||
|
||||
sem::StructMemberList sem_members;
|
||||
sem_members.reserve(str->impl()->members().size());
|
||||
sem_members.reserve(str->members().size());
|
||||
|
||||
// Calculate the effective size and alignment of each field, and the overall
|
||||
// size of the structure.
|
||||
@@ -2537,7 +2478,7 @@ Resolver::StructInfo* Resolver::Structure(const sem::StructType* str) {
|
||||
uint32_t struct_size = 0;
|
||||
uint32_t struct_align = 1;
|
||||
|
||||
for (auto* member : str->impl()->members()) {
|
||||
for (auto* member : str->members()) {
|
||||
Mark(member);
|
||||
|
||||
// Resolve member type
|
||||
@@ -2548,7 +2489,7 @@ Resolver::StructInfo* Resolver::Structure(const sem::StructType* str) {
|
||||
|
||||
// Validate member type
|
||||
if (!IsStorable(type)) {
|
||||
builder_->Diagnostics().add_error(
|
||||
diagnostics_.add_error(
|
||||
type->FriendlyName(builder_->Symbols()) +
|
||||
" cannot be used as the type of a structure member");
|
||||
return nullptr;
|
||||
@@ -2620,19 +2561,14 @@ Resolver::StructInfo* Resolver::Structure(const sem::StructType* str) {
|
||||
auto size_no_padding = struct_size;
|
||||
struct_size = utils::RoundUp(struct_align, struct_size);
|
||||
|
||||
auto* info = struct_infos_.Create();
|
||||
info->type = str;
|
||||
info->members = std::move(sem_members);
|
||||
info->align = struct_align;
|
||||
info->size = struct_size;
|
||||
info->size_no_padding = size_no_padding;
|
||||
struct_info_.emplace(str, info);
|
||||
auto* out = builder_->create<sem::Struct>(
|
||||
str, std::move(sem_members), struct_align, struct_size, size_no_padding);
|
||||
|
||||
if (!ValidateStructure(info)) {
|
||||
if (!ValidateStructure(out)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return info;
|
||||
return out;
|
||||
}
|
||||
|
||||
bool Resolver::ValidateReturn(const ast::ReturnStatement* ret) {
|
||||
@@ -2828,20 +2764,18 @@ bool Resolver::Assignment(ast::AssignmentStatement* a) {
|
||||
}
|
||||
|
||||
bool Resolver::ApplyStorageClassUsageToType(ast::StorageClass sc,
|
||||
const sem::Type* ty,
|
||||
sem::Type* ty,
|
||||
const Source& usage) {
|
||||
ty = ty->UnwrapIfNeeded();
|
||||
|
||||
if (auto* str = ty->As<sem::StructType>()) {
|
||||
auto* info = Structure(str);
|
||||
if (!info) {
|
||||
return false;
|
||||
}
|
||||
if (info->storage_class_usage.count(sc)) {
|
||||
if (auto* str = ty->As<sem::Struct>()) {
|
||||
if (str->StorageClassUsage().count(sc)) {
|
||||
return true; // Already applied
|
||||
}
|
||||
info->storage_class_usage.emplace(sc);
|
||||
for (auto* member : info->members) {
|
||||
|
||||
str->AddUsage(sc);
|
||||
|
||||
for (auto* member : str->Members()) {
|
||||
if (!ApplyStorageClassUsageToType(sc, member->Type(), usage)) {
|
||||
std::stringstream err;
|
||||
err << "while analysing structure member "
|
||||
@@ -2887,7 +2821,7 @@ std::string Resolver::VectorPretty(uint32_t size, sem::Type* element_type) {
|
||||
return vec_type.FriendlyName(builder_->Symbols());
|
||||
}
|
||||
|
||||
const sem::Type* Resolver::Canonical(const sem::Type* type) {
|
||||
sem::Type* Resolver::Canonical(sem::Type* type) {
|
||||
using AccessControl = sem::AccessControl;
|
||||
using Alias = sem::Alias;
|
||||
using Matrix = sem::Matrix;
|
||||
@@ -2899,17 +2833,16 @@ const sem::Type* Resolver::Canonical(const sem::Type* type) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::function<const Type*(const Type*)> make_canonical;
|
||||
make_canonical = [&](const Type* t) -> const sem::Type* {
|
||||
std::function<Type*(Type*)> make_canonical;
|
||||
make_canonical = [&](Type* t) -> sem::Type* {
|
||||
// Unwrap alias sequence
|
||||
const Type* ct = t;
|
||||
Type* ct = t;
|
||||
while (auto* p = ct->As<Alias>()) {
|
||||
ct = p->type();
|
||||
}
|
||||
|
||||
if (auto* v = ct->As<Vector>()) {
|
||||
return builder_->create<Vector>(
|
||||
const_cast<sem::Type*>(make_canonical(v->type())), v->size());
|
||||
return builder_->create<Vector>(make_canonical(v->type()), v->size());
|
||||
}
|
||||
if (auto* m = ct->As<Matrix>()) {
|
||||
auto* column_type =
|
||||
@@ -2943,7 +2876,7 @@ void Resolver::Mark(const ast::Node* node) {
|
||||
}
|
||||
|
||||
Resolver::VariableInfo::VariableInfo(const ast::Variable* decl,
|
||||
const sem::Type* ctype,
|
||||
sem::Type* ctype,
|
||||
const std::string& tn)
|
||||
: declaration(decl),
|
||||
type(ctype),
|
||||
@@ -2955,8 +2888,5 @@ Resolver::VariableInfo::~VariableInfo() = default;
|
||||
Resolver::FunctionInfo::FunctionInfo(ast::Function* decl) : declaration(decl) {}
|
||||
Resolver::FunctionInfo::~FunctionInfo() = default;
|
||||
|
||||
Resolver::StructInfo::StructInfo() = default;
|
||||
Resolver::StructInfo::~StructInfo() = default;
|
||||
|
||||
} // namespace resolver
|
||||
} // namespace tint
|
||||
|
||||
@@ -49,9 +49,6 @@ namespace sem {
|
||||
class Array;
|
||||
class Statement;
|
||||
} // namespace sem
|
||||
namespace sem {
|
||||
class StructType;
|
||||
} // namespace sem
|
||||
|
||||
namespace resolver {
|
||||
|
||||
@@ -90,19 +87,19 @@ class Resolver {
|
||||
/// @returns the canonical type for `type`; that is, a type with all aliases
|
||||
/// removed. For example, `Canonical(alias<alias<vec3<alias<f32>>>>)` is
|
||||
/// `vec3<f32>`.
|
||||
const sem::Type* Canonical(const sem::Type* type);
|
||||
sem::Type* Canonical(sem::Type* type);
|
||||
|
||||
private:
|
||||
/// Structure holding semantic information about a variable.
|
||||
/// Used to build the sem::Variable nodes at the end of resolving.
|
||||
struct VariableInfo {
|
||||
VariableInfo(const ast::Variable* decl,
|
||||
const sem::Type* type,
|
||||
sem::Type* type,
|
||||
const std::string& type_name);
|
||||
~VariableInfo();
|
||||
|
||||
ast::Variable const* const declaration;
|
||||
sem::Type const* type;
|
||||
sem::Type* type;
|
||||
std::string const type_name;
|
||||
ast::StorageClass storage_class;
|
||||
std::vector<ast::IdentifierExpression*> users;
|
||||
@@ -119,7 +116,7 @@ class Resolver {
|
||||
UniqueVector<VariableInfo*> referenced_module_vars;
|
||||
UniqueVector<VariableInfo*> local_referenced_module_vars;
|
||||
std::vector<const ast::ReturnStatement*> return_statements;
|
||||
sem::Type const* return_type = nullptr;
|
||||
sem::Type* return_type = nullptr;
|
||||
std::string return_type_name;
|
||||
|
||||
// List of transitive calls this function makes
|
||||
@@ -129,7 +126,7 @@ class Resolver {
|
||||
/// Structure holding semantic information about an expression.
|
||||
/// Used to build the sem::Expression nodes at the end of resolving.
|
||||
struct ExpressionInfo {
|
||||
sem::Type const* type;
|
||||
sem::Type* type;
|
||||
std::string const type_name; // Declared type name
|
||||
sem::Statement* statement;
|
||||
};
|
||||
@@ -142,21 +139,6 @@ class Resolver {
|
||||
sem::Statement* statement;
|
||||
};
|
||||
|
||||
/// Structure holding semantic information about a struct.
|
||||
/// Used to build the sem::Struct nodes at the end of resolving.
|
||||
struct StructInfo {
|
||||
StructInfo();
|
||||
~StructInfo();
|
||||
|
||||
sem::StructType const* type = nullptr;
|
||||
std::vector<const sem::StructMember*> members;
|
||||
uint32_t align = 0;
|
||||
uint32_t size = 0;
|
||||
uint32_t size_no_padding = 0;
|
||||
std::unordered_set<ast::StorageClass> storage_class_usage;
|
||||
std::unordered_set<sem::PipelineStageUsage> pipeline_stage_uses;
|
||||
};
|
||||
|
||||
/// Structure holding semantic information about a block (i.e. scope), such as
|
||||
/// parent block and variables declared in the block.
|
||||
/// Used to validate variable scoping rules.
|
||||
@@ -237,7 +219,6 @@ class Resolver {
|
||||
bool Statement(ast::Statement*);
|
||||
bool Statements(const ast::StatementList&);
|
||||
bool Switch(ast::SwitchStatement* s);
|
||||
bool Type(const sem::Type* ty, const Source& source = {});
|
||||
bool UnaryOp(ast::UnaryOpExpression*);
|
||||
bool VariableDeclStatement(const ast::VariableDeclStatement*);
|
||||
|
||||
@@ -257,7 +238,7 @@ class Resolver {
|
||||
const sem::Matrix* matrix_type);
|
||||
bool ValidateParameter(const ast::Variable* param);
|
||||
bool ValidateReturn(const ast::ReturnStatement* ret);
|
||||
bool ValidateStructure(const StructInfo* st);
|
||||
bool ValidateStructure(const sem::Struct* str);
|
||||
bool ValidateSwitch(const ast::SwitchStatement* s);
|
||||
bool ValidateVariable(const ast::Variable* param);
|
||||
bool ValidateVectorConstructor(const ast::TypeConstructorExpression* ctor,
|
||||
@@ -267,7 +248,7 @@ class Resolver {
|
||||
/// hasn't been constructed already. If an error is raised, nullptr is
|
||||
/// returned.
|
||||
/// @param ty the ast::Type
|
||||
const sem::Type* Type(const ast::Type* ty);
|
||||
sem::Type* Type(const ast::Type* ty);
|
||||
|
||||
/// @returns the semantic information for the array `arr`, building it if it
|
||||
/// hasn't been constructed already. If an error is raised, nullptr is
|
||||
@@ -276,9 +257,9 @@ class Resolver {
|
||||
/// @param source the Source of the ast node with this array as its type
|
||||
const sem::Array* Array(const sem::ArrayType* arr, const Source& source);
|
||||
|
||||
/// @returns the StructInfo for the structure `str`, building it if it hasn't
|
||||
/// been constructed already. If an error is raised, nullptr is returned.
|
||||
StructInfo* Structure(const sem::StructType* str);
|
||||
/// @returns the sem::Struct for the AST structure `str`. If an error is
|
||||
/// raised, nullptr is returned.
|
||||
sem::Struct* Structure(const ast::Struct* str);
|
||||
|
||||
/// @returns the VariableInfo for the variable `var`, building it if it hasn't
|
||||
/// been constructed already. If an error is raised, nullptr is returned.
|
||||
@@ -287,7 +268,7 @@ class Resolver {
|
||||
/// @param type_name optional type name of `var` to use instead of
|
||||
/// `var->type()->FriendlyName()`.
|
||||
VariableInfo* Variable(ast::Variable* var,
|
||||
const sem::Type* type = nullptr,
|
||||
sem::Type* type = nullptr,
|
||||
std::string type_name = "");
|
||||
|
||||
/// Records the storage class usage for the given type, and any transient
|
||||
@@ -299,7 +280,7 @@ class Resolver {
|
||||
/// given type and storage class. Used for generating sensible error messages.
|
||||
/// @returns true on success, false on error
|
||||
bool ApplyStorageClassUsageToType(ast::StorageClass sc,
|
||||
const sem::Type* ty,
|
||||
sem::Type* ty,
|
||||
const Source& usage);
|
||||
|
||||
/// @param align the output default alignment in bytes for the type `ty`
|
||||
@@ -313,7 +294,7 @@ class Resolver {
|
||||
|
||||
/// @returns the resolved type of the ast::Expression `expr`
|
||||
/// @param expr the expression
|
||||
const sem::Type* TypeOf(const ast::Expression* expr);
|
||||
sem::Type* TypeOf(const ast::Expression* expr);
|
||||
|
||||
/// @returns the declared type name of the ast::Expression `expr`
|
||||
/// @param expr the type name
|
||||
@@ -321,7 +302,7 @@ class Resolver {
|
||||
|
||||
/// @returns the semantic type of the AST literal `lit`
|
||||
/// @param lit the literal
|
||||
const sem::Type* TypeOf(const ast::Literal* lit);
|
||||
sem::Type* TypeOf(const ast::Literal* lit);
|
||||
|
||||
/// Creates a sem::Expression node with the resolved type `type`, and
|
||||
/// assigns this semantic node to the expression `expr`.
|
||||
@@ -369,15 +350,13 @@ class Resolver {
|
||||
std::unordered_map<const ast::Variable*, VariableInfo*> variable_to_info_;
|
||||
std::unordered_map<ast::CallExpression*, FunctionCallInfo> function_calls_;
|
||||
std::unordered_map<const ast::Expression*, ExpressionInfo> expr_info_;
|
||||
std::unordered_map<const sem::StructType*, StructInfo*> struct_info_;
|
||||
std::unordered_map<const sem::Type*, const sem::Type*> type_to_canonical_;
|
||||
std::unordered_map<Symbol, const sem::Type*> named_types_;
|
||||
std::unordered_map<sem::Type*, sem::Type*> type_to_canonical_;
|
||||
std::unordered_map<Symbol, sem::Type*> named_types_;
|
||||
std::unordered_set<const ast::Node*> marked_;
|
||||
FunctionInfo* current_function_ = nullptr;
|
||||
sem::Statement* current_statement_ = nullptr;
|
||||
BlockAllocator<VariableInfo> variable_infos_;
|
||||
BlockAllocator<FunctionInfo> function_infos_;
|
||||
BlockAllocator<StructInfo> struct_infos_;
|
||||
};
|
||||
|
||||
} // namespace resolver
|
||||
|
||||
@@ -764,8 +764,8 @@ TEST_F(ResolverTest, Function_Parameters) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, Function_RegisterInputOutputVariables) {
|
||||
auto s = Structure("S", {Member("m", ty.u32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* s = Structure("S", {Member("m", ty.u32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto a = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
|
||||
auto* in_var = Global("in_var", ty.f32(), ast::StorageClass::kInput);
|
||||
@@ -800,8 +800,8 @@ TEST_F(ResolverTest, Function_RegisterInputOutputVariables) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, Function_RegisterInputOutputVariables_SubFunction) {
|
||||
auto s = Structure("S", {Member("m", ty.u32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* s = Structure("S", {Member("m", ty.u32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto a = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
|
||||
auto* in_var = Global("in_var", ty.f32(), ast::StorageClass::kInput);
|
||||
@@ -884,8 +884,8 @@ TEST_F(ResolverTest, Function_ReturnStatements) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, Expr_MemberAccessor_Struct) {
|
||||
auto st = Structure("S", {Member("first_member", ty.i32()),
|
||||
Member("second_member", ty.f32())});
|
||||
auto* st = Structure("S", {Member("first_member", ty.i32()),
|
||||
Member("second_member", ty.f32())});
|
||||
Global("my_struct", st, ast::StorageClass::kInput);
|
||||
|
||||
auto* mem = MemberAccessor("my_struct", "second_member");
|
||||
@@ -906,8 +906,8 @@ TEST_F(ResolverTest, Expr_MemberAccessor_Struct) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, Expr_MemberAccessor_Struct_Alias) {
|
||||
auto st = Structure("S", {Member("first_member", ty.i32()),
|
||||
Member("second_member", ty.f32())});
|
||||
auto* st = Structure("S", {Member("first_member", ty.i32()),
|
||||
Member("second_member", ty.f32())});
|
||||
auto alias = ty.alias("alias", st);
|
||||
AST().AddConstructedType(alias);
|
||||
Global("my_struct", alias, ast::StorageClass::kInput);
|
||||
@@ -987,8 +987,8 @@ TEST_F(ResolverTest, Expr_Accessor_MultiLevel) {
|
||||
// }
|
||||
//
|
||||
|
||||
auto stB = Structure("B", {Member("foo", ty.vec4<f32>())});
|
||||
auto stA = Structure("A", {Member("mem", ty.vec(stB, 3))});
|
||||
auto* stB = Structure("B", {Member("foo", ty.vec4<f32>())});
|
||||
auto* stA = Structure("A", {Member("mem", ty.vec(stB, 3))});
|
||||
Global("c", stA, ast::StorageClass::kInput);
|
||||
|
||||
auto* mem = MemberAccessor(
|
||||
@@ -1006,8 +1006,8 @@ TEST_F(ResolverTest, Expr_Accessor_MultiLevel) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverTest, Expr_MemberAccessor_InBinaryOp) {
|
||||
auto st = Structure("S", {Member("first_member", ty.f32()),
|
||||
Member("second_member", ty.f32())});
|
||||
auto* st = Structure("S", {Member("first_member", ty.f32()),
|
||||
Member("second_member", ty.f32())});
|
||||
Global("my_struct", st, ast::StorageClass::kInput);
|
||||
|
||||
auto* expr = Add(MemberAccessor("my_struct", "first_member"),
|
||||
|
||||
@@ -60,7 +60,7 @@ TEST_F(ResolverStorageClassValidationTest, StorageBufferPointer) {
|
||||
|
||||
TEST_F(ResolverStorageClassValidationTest, StorageBufferArray) {
|
||||
// var<storage> g : [[access(read)]] array<S, 3>;
|
||||
auto s = Structure("S", {Member("a", ty.f32())});
|
||||
auto* s = Structure("S", {Member("a", ty.f32())});
|
||||
auto a = ty.array(s, 3);
|
||||
auto ac = ty.access(ast::AccessControl::kReadOnly, a);
|
||||
Global(Source{{56, 78}}, "g", ac, ast::StorageClass::kStorage);
|
||||
@@ -88,7 +88,7 @@ TEST_F(ResolverStorageClassValidationTest, StorageBufferBoolAlias) {
|
||||
|
||||
TEST_F(ResolverStorageClassValidationTest, StorageBufferNoAccessControl) {
|
||||
// var<storage> g : S;
|
||||
auto s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
|
||||
Global(Source{{56, 78}}, "g", s, ast::StorageClass::kStorage);
|
||||
|
||||
ASSERT_FALSE(r()->Resolve());
|
||||
@@ -101,7 +101,7 @@ TEST_F(ResolverStorageClassValidationTest, StorageBufferNoAccessControl) {
|
||||
TEST_F(ResolverStorageClassValidationTest, StorageBufferNoBlockDecoration) {
|
||||
// struct S { x : i32 };
|
||||
// var<storage> g : [[access(read)]] S;
|
||||
auto s = Structure(Source{{12, 34}}, "S", {Member("x", ty.i32())});
|
||||
auto* s = Structure(Source{{12, 34}}, "S", {Member("x", ty.i32())});
|
||||
auto a = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage);
|
||||
|
||||
@@ -116,8 +116,8 @@ TEST_F(ResolverStorageClassValidationTest, StorageBufferNoBlockDecoration) {
|
||||
TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Basic) {
|
||||
// [[block]] struct S { x : i32 };
|
||||
// var<storage> g : [[access(read)]] S;
|
||||
auto s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto a = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage);
|
||||
|
||||
@@ -129,8 +129,8 @@ TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Aliases) {
|
||||
// type a1 = S;
|
||||
// type a2 = [[access(read)]] a1;
|
||||
// var<storage> g : a2;
|
||||
auto s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto a1 = ty.alias("a1", s);
|
||||
AST().AddConstructedType(a1);
|
||||
auto ac = ty.access(ast::AccessControl::kReadOnly, a1);
|
||||
@@ -168,7 +168,7 @@ TEST_F(ResolverStorageClassValidationTest, UniformBufferPointer) {
|
||||
|
||||
TEST_F(ResolverStorageClassValidationTest, UniformBufferArray) {
|
||||
// var<uniform> g : [[access(read)]] array<S, 3>;
|
||||
auto s = Structure("S", {Member("a", ty.f32())});
|
||||
auto* s = Structure("S", {Member("a", ty.f32())});
|
||||
auto a = ty.array(s, 3);
|
||||
auto ac = ty.access(ast::AccessControl::kReadOnly, a);
|
||||
Global(Source{{56, 78}}, "g", ac, ast::StorageClass::kUniform);
|
||||
@@ -197,7 +197,7 @@ TEST_F(ResolverStorageClassValidationTest, UniformBufferBoolAlias) {
|
||||
TEST_F(ResolverStorageClassValidationTest, UniformBufferNoBlockDecoration) {
|
||||
// struct S { x : i32 };
|
||||
// var<uniform> g : S;
|
||||
auto s = Structure(Source{{12, 34}}, "S", {Member("x", ty.i32())});
|
||||
auto* s = Structure(Source{{12, 34}}, "S", {Member("x", ty.i32())});
|
||||
Global(Source{{56, 78}}, "g", s, ast::StorageClass::kUniform);
|
||||
|
||||
ASSERT_FALSE(r()->Resolve());
|
||||
@@ -211,8 +211,8 @@ TEST_F(ResolverStorageClassValidationTest, UniformBufferNoBlockDecoration) {
|
||||
TEST_F(ResolverStorageClassValidationTest, UniformBufferNoError_Basic) {
|
||||
// [[block]] struct S { x : i32 };
|
||||
// var<uniform> g : S;
|
||||
auto s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
Global(Source{{56, 78}}, "g", s, ast::StorageClass::kUniform);
|
||||
|
||||
ASSERT_TRUE(r()->Resolve());
|
||||
@@ -222,8 +222,8 @@ TEST_F(ResolverStorageClassValidationTest, UniformBufferNoError_Aliases) {
|
||||
// [[block]] struct S { x : i32 };
|
||||
// type a1 = S;
|
||||
// var<uniform> g : a1;
|
||||
auto s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto a1 = ty.alias("a1", s);
|
||||
AST().AddConstructedType(a1);
|
||||
Global(Source{{56, 78}}, "g", a1, ast::StorageClass::kUniform);
|
||||
|
||||
@@ -26,15 +26,15 @@ namespace {
|
||||
using ResolverStructLayoutTest = ResolverTest;
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, Scalars) {
|
||||
auto s = Structure("S", {
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.u32()),
|
||||
Member("c", ty.i32()),
|
||||
});
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.f32()),
|
||||
Member("b", ty.u32()),
|
||||
Member("c", ty.i32()),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 12u);
|
||||
EXPECT_EQ(sem->SizeNoPadding(), 12u);
|
||||
@@ -57,14 +57,14 @@ TEST_F(ResolverStructLayoutTest, Alias) {
|
||||
auto alias_b = ty.alias("b", ty.f32());
|
||||
AST().AddConstructedType(alias_b);
|
||||
|
||||
auto s = Structure("S", {
|
||||
Member("a", alias_a),
|
||||
Member("b", alias_b),
|
||||
});
|
||||
auto* s = Structure("S", {
|
||||
Member("a", alias_a),
|
||||
Member("b", alias_b),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 8u);
|
||||
EXPECT_EQ(sem->SizeNoPadding(), 8u);
|
||||
@@ -79,15 +79,15 @@ TEST_F(ResolverStructLayoutTest, Alias) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayStaticSize) {
|
||||
auto s = Structure("S", {
|
||||
Member("a", ty.array<i32, 3>()),
|
||||
Member("b", ty.array<f32, 5>()),
|
||||
Member("c", ty.array<f32, 1>()),
|
||||
});
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.array<i32, 3>()),
|
||||
Member("b", ty.array<f32, 5>()),
|
||||
Member("c", ty.array<f32, 1>()),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 36u);
|
||||
EXPECT_EQ(sem->SizeNoPadding(), 36u);
|
||||
@@ -105,15 +105,15 @@ TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayStaticSize) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, ExplicitStrideArrayStaticSize) {
|
||||
auto s = Structure("S", {
|
||||
Member("a", ty.array<i32, 3>(/*stride*/ 8)),
|
||||
Member("b", ty.array<f32, 5>(/*stride*/ 16)),
|
||||
Member("c", ty.array<f32, 1>(/*stride*/ 32)),
|
||||
});
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.array<i32, 3>(/*stride*/ 8)),
|
||||
Member("b", ty.array<f32, 5>(/*stride*/ 16)),
|
||||
Member("c", ty.array<f32, 1>(/*stride*/ 32)),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 136u);
|
||||
EXPECT_EQ(sem->SizeNoPadding(), 136u);
|
||||
@@ -131,15 +131,16 @@ TEST_F(ResolverStructLayoutTest, ExplicitStrideArrayStaticSize) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayRuntimeSized) {
|
||||
auto s = Structure("S",
|
||||
{
|
||||
Member("c", ty.array<f32>()),
|
||||
},
|
||||
ast::DecorationList{create<ast::StructBlockDecoration>()});
|
||||
auto* s =
|
||||
Structure("S",
|
||||
{
|
||||
Member("c", ty.array<f32>()),
|
||||
},
|
||||
ast::DecorationList{create<ast::StructBlockDecoration>()});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 4u);
|
||||
EXPECT_EQ(sem->SizeNoPadding(), 4u);
|
||||
@@ -151,15 +152,16 @@ TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayRuntimeSized) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, ExplicitStrideArrayRuntimeSized) {
|
||||
auto s = Structure("S",
|
||||
{
|
||||
Member("c", ty.array<f32>(/*stride*/ 32)),
|
||||
},
|
||||
ast::DecorationList{create<ast::StructBlockDecoration>()});
|
||||
auto* s =
|
||||
Structure("S",
|
||||
{
|
||||
Member("c", ty.array<f32>(/*stride*/ 32)),
|
||||
},
|
||||
ast::DecorationList{create<ast::StructBlockDecoration>()});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 32u);
|
||||
EXPECT_EQ(sem->SizeNoPadding(), 32u);
|
||||
@@ -173,13 +175,13 @@ TEST_F(ResolverStructLayoutTest, ExplicitStrideArrayRuntimeSized) {
|
||||
TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayOfExplicitStrideArray) {
|
||||
auto inner = ty.array<i32, 2>(/*stride*/ 16); // size: 32
|
||||
auto outer = ty.array(inner, 12); // size: 12 * 32
|
||||
auto s = Structure("S", {
|
||||
Member("c", outer),
|
||||
});
|
||||
auto* s = Structure("S", {
|
||||
Member("c", outer),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 384u);
|
||||
EXPECT_EQ(sem->SizeNoPadding(), 384u);
|
||||
@@ -191,19 +193,19 @@ TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayOfExplicitStrideArray) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayOfStructure) {
|
||||
auto inner = Structure("Inner", {
|
||||
Member("a", ty.vec2<i32>()),
|
||||
Member("b", ty.vec3<i32>()),
|
||||
Member("c", ty.vec4<i32>()),
|
||||
}); // size: 48
|
||||
auto outer = ty.array(inner, 12); // size: 12 * 48
|
||||
auto s = Structure("S", {
|
||||
Member("c", outer),
|
||||
});
|
||||
auto* inner = Structure("Inner", {
|
||||
Member("a", ty.vec2<i32>()),
|
||||
Member("b", ty.vec3<i32>()),
|
||||
Member("c", ty.vec4<i32>()),
|
||||
}); // size: 48
|
||||
auto outer = ty.array(inner, 12); // size: 12 * 48
|
||||
auto* s = Structure("S", {
|
||||
Member("c", outer),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 576u);
|
||||
EXPECT_EQ(sem->SizeNoPadding(), 576u);
|
||||
@@ -215,15 +217,15 @@ TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayOfStructure) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, Vector) {
|
||||
auto s = Structure("S", {
|
||||
Member("a", ty.vec2<i32>()),
|
||||
Member("b", ty.vec3<i32>()),
|
||||
Member("c", ty.vec4<i32>()),
|
||||
});
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.vec2<i32>()),
|
||||
Member("b", ty.vec3<i32>()),
|
||||
Member("c", ty.vec4<i32>()),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 48u);
|
||||
EXPECT_EQ(sem->SizeNoPadding(), 48u);
|
||||
@@ -241,21 +243,21 @@ TEST_F(ResolverStructLayoutTest, Vector) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, Matrix) {
|
||||
auto s = Structure("S", {
|
||||
Member("a", ty.mat2x2<i32>()),
|
||||
Member("b", ty.mat2x3<i32>()),
|
||||
Member("c", ty.mat2x4<i32>()),
|
||||
Member("d", ty.mat3x2<i32>()),
|
||||
Member("e", ty.mat3x3<i32>()),
|
||||
Member("f", ty.mat3x4<i32>()),
|
||||
Member("g", ty.mat4x2<i32>()),
|
||||
Member("h", ty.mat4x3<i32>()),
|
||||
Member("i", ty.mat4x4<i32>()),
|
||||
});
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.mat2x2<i32>()),
|
||||
Member("b", ty.mat2x3<i32>()),
|
||||
Member("c", ty.mat2x4<i32>()),
|
||||
Member("d", ty.mat3x2<i32>()),
|
||||
Member("e", ty.mat3x3<i32>()),
|
||||
Member("f", ty.mat3x4<i32>()),
|
||||
Member("g", ty.mat4x2<i32>()),
|
||||
Member("h", ty.mat4x3<i32>()),
|
||||
Member("i", ty.mat4x4<i32>()),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 368u);
|
||||
EXPECT_EQ(sem->SizeNoPadding(), 368u);
|
||||
@@ -291,18 +293,18 @@ TEST_F(ResolverStructLayoutTest, Matrix) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, NestedStruct) {
|
||||
auto inner = Structure("Inner", {
|
||||
Member("a", ty.mat3x3<i32>()),
|
||||
});
|
||||
auto s = Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", inner),
|
||||
Member("c", ty.i32()),
|
||||
});
|
||||
auto* inner = Structure("Inner", {
|
||||
Member("a", ty.mat3x3<i32>()),
|
||||
});
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.i32()),
|
||||
Member("b", inner),
|
||||
Member("c", ty.i32()),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 80u);
|
||||
EXPECT_EQ(sem->SizeNoPadding(), 68u);
|
||||
@@ -320,21 +322,21 @@ TEST_F(ResolverStructLayoutTest, NestedStruct) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, SizeDecorations) {
|
||||
auto inner = Structure("Inner", {
|
||||
Member("a", ty.f32(), {MemberSize(8)}),
|
||||
Member("b", ty.f32(), {MemberSize(16)}),
|
||||
Member("c", ty.f32(), {MemberSize(8)}),
|
||||
});
|
||||
auto s = Structure("S", {
|
||||
Member("a", ty.f32(), {MemberSize(4)}),
|
||||
Member("b", ty.u32(), {MemberSize(8)}),
|
||||
Member("c", inner),
|
||||
Member("d", ty.i32(), {MemberSize(32)}),
|
||||
});
|
||||
auto* inner = Structure("Inner", {
|
||||
Member("a", ty.f32(), {MemberSize(8)}),
|
||||
Member("b", ty.f32(), {MemberSize(16)}),
|
||||
Member("c", ty.f32(), {MemberSize(8)}),
|
||||
});
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.f32(), {MemberSize(4)}),
|
||||
Member("b", ty.u32(), {MemberSize(8)}),
|
||||
Member("c", inner),
|
||||
Member("d", ty.i32(), {MemberSize(32)}),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 76u);
|
||||
EXPECT_EQ(sem->SizeNoPadding(), 76u);
|
||||
@@ -355,21 +357,21 @@ TEST_F(ResolverStructLayoutTest, SizeDecorations) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, AlignDecorations) {
|
||||
auto inner = Structure("Inner", {
|
||||
Member("a", ty.f32(), {MemberAlign(8)}),
|
||||
Member("b", ty.f32(), {MemberAlign(16)}),
|
||||
Member("c", ty.f32(), {MemberAlign(4)}),
|
||||
});
|
||||
auto s = Structure("S", {
|
||||
Member("a", ty.f32(), {MemberAlign(4)}),
|
||||
Member("b", ty.u32(), {MemberAlign(8)}),
|
||||
Member("c", inner),
|
||||
Member("d", ty.i32(), {MemberAlign(32)}),
|
||||
});
|
||||
auto* inner = Structure("Inner", {
|
||||
Member("a", ty.f32(), {MemberAlign(8)}),
|
||||
Member("b", ty.f32(), {MemberAlign(16)}),
|
||||
Member("c", ty.f32(), {MemberAlign(4)}),
|
||||
});
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.f32(), {MemberAlign(4)}),
|
||||
Member("b", ty.u32(), {MemberAlign(8)}),
|
||||
Member("c", inner),
|
||||
Member("d", ty.i32(), {MemberAlign(32)}),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 96u);
|
||||
EXPECT_EQ(sem->SizeNoPadding(), 68u);
|
||||
@@ -390,13 +392,13 @@ TEST_F(ResolverStructLayoutTest, AlignDecorations) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverStructLayoutTest, StructWithLotsOfPadding) {
|
||||
auto s = Structure("S", {
|
||||
Member("a", ty.i32(), {MemberAlign(1024)}),
|
||||
});
|
||||
auto* s = Structure("S", {
|
||||
Member("a", ty.i32(), {MemberAlign(1024)}),
|
||||
});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_EQ(sem->Size(), 1024u);
|
||||
EXPECT_EQ(sem->SizeNoPadding(), 4u);
|
||||
|
||||
@@ -28,41 +28,41 @@ namespace {
|
||||
using ResolverPipelineStageUseTest = ResolverTest;
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, UnusedStruct) {
|
||||
auto s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
|
||||
auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_TRUE(sem->PipelineStageUses().empty());
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedAsNonEntryPointParam) {
|
||||
auto s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
|
||||
auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
|
||||
|
||||
Func("foo", {Param("param", s)}, ty.void_(), {}, {});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_TRUE(sem->PipelineStageUses().empty());
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedAsNonEntryPointReturnType) {
|
||||
auto s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
|
||||
auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
|
||||
|
||||
Func("foo", {}, s, {Return(Construct(s, Expr(0.f)))}, {});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_TRUE(sem->PipelineStageUses().empty());
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedAsVertexShaderParam) {
|
||||
auto s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
|
||||
auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
|
||||
|
||||
Func("main", {Param("param", s)}, ty.vec4<f32>(),
|
||||
{Return(Construct(ty.vec4<f32>()))},
|
||||
@@ -71,14 +71,14 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsVertexShaderParam) {
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->PipelineStageUses(),
|
||||
UnorderedElementsAre(sem::PipelineStageUsage::kVertexInput));
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedAsVertexShaderReturnType) {
|
||||
auto s = Structure(
|
||||
auto* s = Structure(
|
||||
"S", {Member("a", ty.f32(), {Builtin(ast::Builtin::kPosition)})});
|
||||
|
||||
Func("main", {}, s, {Return(Construct(s, Expr(0.f)))},
|
||||
@@ -86,42 +86,42 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsVertexShaderReturnType) {
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->PipelineStageUses(),
|
||||
UnorderedElementsAre(sem::PipelineStageUsage::kVertexOutput));
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedAsFragmentShaderParam) {
|
||||
auto s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
|
||||
auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
|
||||
|
||||
Func("main", {Param("param", s)}, ty.void_(), {},
|
||||
{Stage(ast::PipelineStage::kFragment)});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->PipelineStageUses(),
|
||||
UnorderedElementsAre(sem::PipelineStageUsage::kFragmentInput));
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedAsFragmentShaderReturnType) {
|
||||
auto s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
|
||||
auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
|
||||
|
||||
Func("main", {}, s, {Return(Construct(s, Expr(0.f)))},
|
||||
{Stage(ast::PipelineStage::kFragment)});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->PipelineStageUses(),
|
||||
UnorderedElementsAre(sem::PipelineStageUsage::kFragmentOutput));
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedAsComputeShaderParam) {
|
||||
auto s = Structure(
|
||||
auto* s = Structure(
|
||||
"S",
|
||||
{Member("a", ty.u32(), {Builtin(ast::Builtin::kLocalInvocationIndex)})});
|
||||
|
||||
@@ -130,14 +130,14 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsComputeShaderParam) {
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->PipelineStageUses(),
|
||||
UnorderedElementsAre(sem::PipelineStageUsage::kComputeInput));
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedMultipleStages) {
|
||||
auto s = Structure(
|
||||
auto* s = Structure(
|
||||
"S", {Member("a", ty.f32(), {Builtin(ast::Builtin::kPosition)})});
|
||||
|
||||
Func("vert_main", {Param("param", s)}, s, {Return(Construct(s, Expr(0.f)))},
|
||||
@@ -148,7 +148,7 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedMultipleStages) {
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->PipelineStageUses(),
|
||||
UnorderedElementsAre(sem::PipelineStageUsage::kVertexInput,
|
||||
@@ -157,7 +157,7 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedMultipleStages) {
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderParamViaAlias) {
|
||||
auto s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
|
||||
auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
|
||||
auto s_alias = ty.alias("S_alias", s);
|
||||
AST().AddConstructedType(s_alias);
|
||||
|
||||
@@ -166,14 +166,14 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderParamViaAlias) {
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->PipelineStageUses(),
|
||||
UnorderedElementsAre(sem::PipelineStageUsage::kFragmentInput));
|
||||
}
|
||||
|
||||
TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderReturnTypeViaAlias) {
|
||||
auto s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
|
||||
auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
|
||||
auto s_alias = ty.alias("S_alias", s);
|
||||
AST().AddConstructedType(s_alias);
|
||||
|
||||
@@ -182,7 +182,7 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderReturnTypeViaAlias) {
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->PipelineStageUses(),
|
||||
UnorderedElementsAre(sem::PipelineStageUsage::kFragmentOutput));
|
||||
|
||||
@@ -28,150 +28,150 @@ namespace {
|
||||
using ResolverStorageClassUseTest = ResolverTest;
|
||||
|
||||
TEST_F(ResolverStorageClassUseTest, UnreachableStruct) {
|
||||
auto s = Structure("S", {Member("a", ty.f32())});
|
||||
auto* s = Structure("S", {Member("a", ty.f32())});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_TRUE(sem->StorageClassUsage().empty());
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassUseTest, StructReachableFromParameter) {
|
||||
auto s = Structure("S", {Member("a", ty.f32())});
|
||||
auto* s = Structure("S", {Member("a", ty.f32())});
|
||||
|
||||
Func("f", {Param("param", s)}, ty.void_(), {}, {});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->StorageClassUsage(),
|
||||
UnorderedElementsAre(ast::StorageClass::kNone));
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassUseTest, StructReachableFromReturnType) {
|
||||
auto s = Structure("S", {Member("a", ty.f32())});
|
||||
auto* s = Structure("S", {Member("a", ty.f32())});
|
||||
|
||||
Func("f", {}, s, {Return(Construct(s))}, {});
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->StorageClassUsage(),
|
||||
UnorderedElementsAre(ast::StorageClass::kNone));
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassUseTest, StructReachableFromGlobal) {
|
||||
auto s = Structure("S", {Member("a", ty.f32())});
|
||||
auto* s = Structure("S", {Member("a", ty.f32())});
|
||||
|
||||
Global("g", s, ast::StorageClass::kPrivate);
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->StorageClassUsage(),
|
||||
UnorderedElementsAre(ast::StorageClass::kPrivate));
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalAlias) {
|
||||
auto s = Structure("S", {Member("a", ty.f32())});
|
||||
auto* s = Structure("S", {Member("a", ty.f32())});
|
||||
auto a = ty.alias("A", s);
|
||||
AST().AddConstructedType(a);
|
||||
Global("g", a, ast::StorageClass::kPrivate);
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->StorageClassUsage(),
|
||||
UnorderedElementsAre(ast::StorageClass::kPrivate));
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalStruct) {
|
||||
auto s = Structure("S", {Member("a", ty.f32())});
|
||||
auto o = Structure("O", {Member("a", s)});
|
||||
auto* s = Structure("S", {Member("a", ty.f32())});
|
||||
auto* o = Structure("O", {Member("a", s)});
|
||||
Global("g", o, ast::StorageClass::kPrivate);
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->StorageClassUsage(),
|
||||
UnorderedElementsAre(ast::StorageClass::kPrivate));
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalArray) {
|
||||
auto s = Structure("S", {Member("a", ty.f32())});
|
||||
auto* s = Structure("S", {Member("a", ty.f32())});
|
||||
auto a = ty.array(s, 3);
|
||||
Global("g", a, ast::StorageClass::kPrivate);
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->StorageClassUsage(),
|
||||
UnorderedElementsAre(ast::StorageClass::kPrivate));
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassUseTest, StructReachableFromLocal) {
|
||||
auto s = Structure("S", {Member("a", ty.f32())});
|
||||
auto* s = Structure("S", {Member("a", ty.f32())});
|
||||
|
||||
WrapInFunction(Var("g", s, ast::StorageClass::kFunction));
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->StorageClassUsage(),
|
||||
UnorderedElementsAre(ast::StorageClass::kFunction));
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassUseTest, StructReachableViaLocalAlias) {
|
||||
auto s = Structure("S", {Member("a", ty.f32())});
|
||||
auto* s = Structure("S", {Member("a", ty.f32())});
|
||||
auto a = ty.alias("A", s);
|
||||
AST().AddConstructedType(a);
|
||||
WrapInFunction(Var("g", a, ast::StorageClass::kFunction));
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->StorageClassUsage(),
|
||||
UnorderedElementsAre(ast::StorageClass::kFunction));
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassUseTest, StructReachableViaLocalStruct) {
|
||||
auto s = Structure("S", {Member("a", ty.f32())});
|
||||
auto o = Structure("O", {Member("a", s)});
|
||||
auto* s = Structure("S", {Member("a", ty.f32())});
|
||||
auto* o = Structure("O", {Member("a", s)});
|
||||
WrapInFunction(Var("g", o, ast::StorageClass::kFunction));
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->StorageClassUsage(),
|
||||
UnorderedElementsAre(ast::StorageClass::kFunction));
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassUseTest, StructReachableViaLocalArray) {
|
||||
auto s = Structure("S", {Member("a", ty.f32())});
|
||||
auto* s = Structure("S", {Member("a", ty.f32())});
|
||||
auto a = ty.array(s, 3);
|
||||
WrapInFunction(Var("g", a, ast::StorageClass::kFunction));
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->StorageClassUsage(),
|
||||
UnorderedElementsAre(ast::StorageClass::kFunction));
|
||||
}
|
||||
|
||||
TEST_F(ResolverStorageClassUseTest, StructMultipleStorageClassUses) {
|
||||
auto s = Structure("S", {Member("a", ty.f32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto* s = Structure("S", {Member("a", ty.f32())},
|
||||
{create<ast::StructBlockDecoration>()});
|
||||
auto ac = ty.access(ast::AccessControl::kReadOnly, s);
|
||||
Global("x", s, ast::StorageClass::kUniform);
|
||||
Global("y", ac, ast::StorageClass::kStorage);
|
||||
@@ -179,7 +179,7 @@ TEST_F(ResolverStorageClassUseTest, StructMultipleStorageClassUses) {
|
||||
|
||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||
|
||||
auto* sem = Sem().Get(s.sem);
|
||||
auto* sem = TypeOf(s)->As<sem::Struct>();
|
||||
ASSERT_NE(sem, nullptr);
|
||||
EXPECT_THAT(sem->StorageClassUsage(),
|
||||
UnorderedElementsAre(ast::StorageClass::kUniform,
|
||||
|
||||
Reference in New Issue
Block a user