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:
Ben Clayton
2021-05-07 14:49:34 +00:00
committed by Commit Bot service account
parent 33d0f6aa08
commit ba6ab5e6bd
86 changed files with 962 additions and 1313 deletions

View File

@@ -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);

View File

@@ -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_(), {},

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"),

View File

@@ -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);

View File

@@ -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);

View File

@@ -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));

View File

@@ -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,