[wgsl-reader] Add parsing of named structs.
This CL adds the parsing of structs with names. The parsing of type aliased structs remains to allow for migration to the new system. The named struct format is always emitted. Bug: tint:175 Change-Id: Ic0579dedbd2dd0edc7dfd30bc2ec02972091e718 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/30341 Commit-Queue: dan sinclair <dsinclair@chromium.org> Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
parent
c9a3e47396
commit
7156d3e314
|
@ -52,12 +52,25 @@ bool Module::IsValid() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto* const alias : alias_types_) {
|
for (auto* const ty : constructed_types_) {
|
||||||
if (alias == nullptr) {
|
if (ty == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (alias->type()->IsStruct() &&
|
if (ty->IsAlias()) {
|
||||||
alias->type()->AsStruct()->name().empty()) {
|
auto* alias = ty->AsAlias();
|
||||||
|
if (alias->type() == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (alias->type()->IsStruct() &&
|
||||||
|
alias->type()->AsStruct()->name().empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (ty->IsStruct()) {
|
||||||
|
auto* str = ty->AsStruct();
|
||||||
|
if (str->name().empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,13 +87,20 @@ std::string Module::to_str() const {
|
||||||
|
|
||||||
out << "Module{" << std::endl;
|
out << "Module{" << std::endl;
|
||||||
const auto indent = 2;
|
const auto indent = 2;
|
||||||
for (auto* const alias : alias_types_) {
|
for (auto* const ty : constructed_types_) {
|
||||||
for (size_t i = 0; i < indent; ++i) {
|
for (size_t i = 0; i < indent; ++i) {
|
||||||
out << " ";
|
out << " ";
|
||||||
}
|
}
|
||||||
out << alias->name() << " -> " << alias->type()->type_name() << std::endl;
|
if (ty->IsAlias()) {
|
||||||
if (alias->type()->IsStruct()) {
|
auto* alias = ty->AsAlias();
|
||||||
alias->type()->AsStruct()->impl()->to_str(out, indent);
|
out << alias->name() << " -> " << alias->type()->type_name() << std::endl;
|
||||||
|
if (alias->type()->IsStruct()) {
|
||||||
|
alias->type()->AsStruct()->impl()->to_str(out, indent);
|
||||||
|
}
|
||||||
|
} else if (ty->IsStruct()) {
|
||||||
|
auto* str = ty->AsStruct();
|
||||||
|
out << str->name() << " ";
|
||||||
|
str->impl()->to_str(out, indent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const auto& var : global_variables_) {
|
for (const auto& var : global_variables_) {
|
||||||
|
|
|
@ -46,12 +46,15 @@ class Module {
|
||||||
/// @returns the global variables for the module
|
/// @returns the global variables for the module
|
||||||
VariableList& global_variables() { return global_variables_; }
|
VariableList& global_variables() { return global_variables_; }
|
||||||
|
|
||||||
/// Adds a type alias to the module
|
/// Adds a constructed type to the module.
|
||||||
/// @param type the alias to add
|
/// The type must be an alias or a struct.
|
||||||
void AddAliasType(type::AliasType* type) { alias_types_.push_back(type); }
|
/// @param type the constructed type to add
|
||||||
/// @returns the alias types in the module
|
void AddConstructedType(type::Type* type) {
|
||||||
const std::vector<type::AliasType*>& alias_types() const {
|
constructed_types_.push_back(type);
|
||||||
return alias_types_;
|
}
|
||||||
|
/// @returns the constructed types in the module
|
||||||
|
const std::vector<type::Type*>& constructed_types() const {
|
||||||
|
return constructed_types_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a function to the module
|
/// Adds a function to the module
|
||||||
|
@ -82,8 +85,8 @@ class Module {
|
||||||
Module(const Module&) = delete;
|
Module(const Module&) = delete;
|
||||||
|
|
||||||
VariableList global_variables_;
|
VariableList global_variables_;
|
||||||
// The alias types are owned by the type manager
|
// The constructed types are owned by the type manager
|
||||||
std::vector<type::AliasType*> alias_types_;
|
std::vector<type::Type*> constructed_types_;
|
||||||
FunctionList functions_;
|
FunctionList functions_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -91,13 +91,13 @@ TEST_F(ModuleTest, IsValid_Alias) {
|
||||||
type::AliasType alias("alias", &f32);
|
type::AliasType alias("alias", &f32);
|
||||||
|
|
||||||
Module m;
|
Module m;
|
||||||
m.AddAliasType(&alias);
|
m.AddConstructedType(&alias);
|
||||||
EXPECT_TRUE(m.IsValid());
|
EXPECT_TRUE(m.IsValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ModuleTest, IsValid_Null_Alias) {
|
TEST_F(ModuleTest, IsValid_Null_Alias) {
|
||||||
Module m;
|
Module m;
|
||||||
m.AddAliasType(nullptr);
|
m.AddConstructedType(nullptr);
|
||||||
EXPECT_FALSE(m.IsValid());
|
EXPECT_FALSE(m.IsValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ TEST_F(ModuleTest, IsValid_Struct) {
|
||||||
type::AliasType alias("name", &st);
|
type::AliasType alias("name", &st);
|
||||||
|
|
||||||
Module m;
|
Module m;
|
||||||
m.AddAliasType(&alias);
|
m.AddConstructedType(&alias);
|
||||||
EXPECT_TRUE(m.IsValid());
|
EXPECT_TRUE(m.IsValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ TEST_F(ModuleTest, IsValid_Struct_EmptyName) {
|
||||||
type::AliasType alias("name", &st);
|
type::AliasType alias("name", &st);
|
||||||
|
|
||||||
Module m;
|
Module m;
|
||||||
m.AddAliasType(&alias);
|
m.AddConstructedType(&alias);
|
||||||
EXPECT_FALSE(m.IsValid());
|
EXPECT_FALSE(m.IsValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,6 @@ bool Struct::IsValid() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Struct::to_str(std::ostream& out, size_t indent) const {
|
void Struct::to_str(std::ostream& out, size_t indent) const {
|
||||||
make_indent(out, indent);
|
|
||||||
out << "Struct{" << std::endl;
|
out << "Struct{" << std::endl;
|
||||||
for (auto deco : decorations_) {
|
for (auto deco : decorations_) {
|
||||||
make_indent(out, indent + 2);
|
make_indent(out, indent + 2);
|
||||||
|
|
|
@ -134,7 +134,7 @@ TEST_F(StructTest, ToStr) {
|
||||||
|
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
s.to_str(out, 2);
|
s.to_str(out, 2);
|
||||||
EXPECT_EQ(out.str(), R"( Struct{
|
EXPECT_EQ(out.str(), R"(Struct{
|
||||||
[[block]]
|
[[block]]
|
||||||
StructMember{a: __i32}
|
StructMember{a: __i32}
|
||||||
}
|
}
|
||||||
|
|
|
@ -218,10 +218,10 @@ TEST_F(SpvParserTest_Composite_Construct, Struct) {
|
||||||
Variable{
|
Variable{
|
||||||
x_1
|
x_1
|
||||||
none
|
none
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
{
|
{
|
||||||
TypeConstructor{
|
TypeConstructor{
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
TypeConstructor{
|
TypeConstructor{
|
||||||
__vec_2__f32
|
__vec_2__f32
|
||||||
ScalarConstructor{50.000000}
|
ScalarConstructor{50.000000}
|
||||||
|
|
|
@ -800,8 +800,7 @@ TEST_F(SpvParserTest, RemapStorageBuffer_TypesAndVarDeclarations) {
|
||||||
const auto module_str = p->module().to_str();
|
const auto module_str = p->module().to_str();
|
||||||
EXPECT_THAT(module_str, HasSubstr(R"(
|
EXPECT_THAT(module_str, HasSubstr(R"(
|
||||||
RTArr -> __array__u32_stride_4
|
RTArr -> __array__u32_stride_4
|
||||||
S -> __struct_S
|
S Struct{
|
||||||
Struct{
|
|
||||||
[[block]]
|
[[block]]
|
||||||
StructMember{[[ offset 0 ]] field0: __u32}
|
StructMember{[[ offset 0 ]] field0: __u32}
|
||||||
StructMember{[[ offset 4 ]] field1: __alias_RTArr__array__u32_stride_4}
|
StructMember{[[ offset 4 ]] field1: __alias_RTArr__array__u32_stride_4}
|
||||||
|
@ -809,7 +808,7 @@ TEST_F(SpvParserTest, RemapStorageBuffer_TypesAndVarDeclarations) {
|
||||||
Variable{
|
Variable{
|
||||||
myvar
|
myvar
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
})"));
|
})"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -264,10 +264,10 @@ TEST_F(SpvParserTestMiscInstruction, OpUndef_InFunction_Struct) {
|
||||||
Variable{
|
Variable{
|
||||||
x_11
|
x_11
|
||||||
none
|
none
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
{
|
{
|
||||||
TypeConstructor{
|
TypeConstructor{
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
ScalarConstructor{false}
|
ScalarConstructor{false}
|
||||||
ScalarConstructor{0}
|
ScalarConstructor{0}
|
||||||
ScalarConstructor{0}
|
ScalarConstructor{0}
|
||||||
|
|
|
@ -563,10 +563,10 @@ TEST_F(SpvParserTest, EmitFunctionVariables_StructInitializer) {
|
||||||
Variable{
|
Variable{
|
||||||
x_200
|
x_200
|
||||||
function
|
function
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
{
|
{
|
||||||
TypeConstructor{
|
TypeConstructor{
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
ScalarConstructor{1}
|
ScalarConstructor{1}
|
||||||
ScalarConstructor{1.500000}
|
ScalarConstructor{1.500000}
|
||||||
TypeConstructor{
|
TypeConstructor{
|
||||||
|
@ -602,10 +602,10 @@ TEST_F(SpvParserTest, EmitFunctionVariables_StructInitializer_Null) {
|
||||||
Variable{
|
Variable{
|
||||||
x_200
|
x_200
|
||||||
function
|
function
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
{
|
{
|
||||||
TypeConstructor{
|
TypeConstructor{
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
ScalarConstructor{0}
|
ScalarConstructor{0}
|
||||||
ScalarConstructor{0.000000}
|
ScalarConstructor{0.000000}
|
||||||
TypeConstructor{
|
TypeConstructor{
|
||||||
|
|
|
@ -870,6 +870,8 @@ ast::type::Type* ParserImpl::ConvertType(
|
||||||
namer_.GetName(type_id), std::move(ast_struct));
|
namer_.GetName(type_id), std::move(ast_struct));
|
||||||
|
|
||||||
auto* result = ctx_.type_mgr().Get(std::move(ast_struct_type));
|
auto* result = ctx_.type_mgr().Get(std::move(ast_struct_type));
|
||||||
|
id_to_type_[type_id] = result;
|
||||||
|
ast_module_.AddConstructedType(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -934,11 +936,8 @@ void ParserImpl::MaybeGenerateAlias(uint32_t type_id,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only care about struct, arrays, and runtime arrays.
|
// We only care about arrays, and runtime arrays.
|
||||||
switch (type->kind()) {
|
switch (type->kind()) {
|
||||||
case spvtools::opt::analysis::Type::kStruct:
|
|
||||||
// The struct already got a name when the type was first registered.
|
|
||||||
break;
|
|
||||||
case spvtools::opt::analysis::Type::kRuntimeArray:
|
case spvtools::opt::analysis::Type::kRuntimeArray:
|
||||||
// Runtime arrays are always decorated with ArrayStride so always get a
|
// Runtime arrays are always decorated with ArrayStride so always get a
|
||||||
// type alias.
|
// type alias.
|
||||||
|
@ -967,7 +966,7 @@ void ParserImpl::MaybeGenerateAlias(uint32_t type_id,
|
||||||
->AsAlias();
|
->AsAlias();
|
||||||
// Record this new alias as the AST type for this SPIR-V ID.
|
// Record this new alias as the AST type for this SPIR-V ID.
|
||||||
id_to_type_[type_id] = ast_alias_type;
|
id_to_type_[type_id] = ast_alias_type;
|
||||||
ast_module_.AddAliasType(ast_alias_type);
|
ast_module_.AddConstructedType(ast_alias_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ParserImpl::EmitModuleScopeVariables() {
|
bool ParserImpl::EmitModuleScopeVariables() {
|
||||||
|
|
|
@ -1058,10 +1058,10 @@ TEST_F(SpvParserTest, ModuleScopeVar_StructInitializer) {
|
||||||
EXPECT_THAT(module_str, HasSubstr(R"(Variable{
|
EXPECT_THAT(module_str, HasSubstr(R"(Variable{
|
||||||
x_200
|
x_200
|
||||||
private
|
private
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
{
|
{
|
||||||
TypeConstructor{
|
TypeConstructor{
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
ScalarConstructor{1}
|
ScalarConstructor{1}
|
||||||
ScalarConstructor{1.500000}
|
ScalarConstructor{1.500000}
|
||||||
TypeConstructor{
|
TypeConstructor{
|
||||||
|
@ -1087,10 +1087,10 @@ TEST_F(SpvParserTest, ModuleScopeVar_StructNullInitializer) {
|
||||||
EXPECT_THAT(module_str, HasSubstr(R"(Variable{
|
EXPECT_THAT(module_str, HasSubstr(R"(Variable{
|
||||||
x_200
|
x_200
|
||||||
private
|
private
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
{
|
{
|
||||||
TypeConstructor{
|
TypeConstructor{
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
ScalarConstructor{0}
|
ScalarConstructor{0}
|
||||||
ScalarConstructor{0.000000}
|
ScalarConstructor{0.000000}
|
||||||
TypeConstructor{
|
TypeConstructor{
|
||||||
|
@ -1116,10 +1116,10 @@ TEST_F(SpvParserTest, ModuleScopeVar_StructUndefInitializer) {
|
||||||
EXPECT_THAT(module_str, HasSubstr(R"(Variable{
|
EXPECT_THAT(module_str, HasSubstr(R"(Variable{
|
||||||
x_200
|
x_200
|
||||||
private
|
private
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
{
|
{
|
||||||
TypeConstructor{
|
TypeConstructor{
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
ScalarConstructor{0}
|
ScalarConstructor{0}
|
||||||
ScalarConstructor{0.000000}
|
ScalarConstructor{0.000000}
|
||||||
TypeConstructor{
|
TypeConstructor{
|
||||||
|
@ -1203,7 +1203,7 @@ TEST_F(SpvParserTest, ModuleScopeVar_DescriptorSetDecoration_Valid) {
|
||||||
}
|
}
|
||||||
myvar
|
myvar
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
})"))
|
})"))
|
||||||
<< module_str;
|
<< module_str;
|
||||||
}
|
}
|
||||||
|
@ -1257,7 +1257,7 @@ TEST_F(SpvParserTest, ModuleScopeVar_BindingDecoration_Valid) {
|
||||||
}
|
}
|
||||||
myvar
|
myvar
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
})"))
|
})"))
|
||||||
<< module_str;
|
<< module_str;
|
||||||
}
|
}
|
||||||
|
@ -1305,8 +1305,7 @@ TEST_F(SpvParserTest, ModuleScopeVar_NonReadableDecoration_DroppedForNow) {
|
||||||
EXPECT_TRUE(p->error().empty());
|
EXPECT_TRUE(p->error().empty());
|
||||||
const auto module_str = p->module().to_str();
|
const auto module_str = p->module().to_str();
|
||||||
EXPECT_THAT(module_str, HasSubstr(R"(
|
EXPECT_THAT(module_str, HasSubstr(R"(
|
||||||
S -> __struct_S
|
S Struct{
|
||||||
Struct{
|
|
||||||
[[block]]
|
[[block]]
|
||||||
StructMember{field0: __u32}
|
StructMember{field0: __u32}
|
||||||
StructMember{field1: __f32}
|
StructMember{field1: __f32}
|
||||||
|
@ -1315,7 +1314,7 @@ TEST_F(SpvParserTest, ModuleScopeVar_NonReadableDecoration_DroppedForNow) {
|
||||||
Variable{
|
Variable{
|
||||||
myvar
|
myvar
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
}
|
}
|
||||||
})")) << module_str;
|
})")) << module_str;
|
||||||
}
|
}
|
||||||
|
@ -1333,8 +1332,7 @@ TEST_F(SpvParserTest, ModuleScopeVar_NonWritableDecoration_DroppedForNow) {
|
||||||
EXPECT_TRUE(p->error().empty());
|
EXPECT_TRUE(p->error().empty());
|
||||||
const auto module_str = p->module().to_str();
|
const auto module_str = p->module().to_str();
|
||||||
EXPECT_THAT(module_str, HasSubstr(R"(
|
EXPECT_THAT(module_str, HasSubstr(R"(
|
||||||
S -> __struct_S
|
S Struct{
|
||||||
Struct{
|
|
||||||
[[block]]
|
[[block]]
|
||||||
StructMember{field0: __u32}
|
StructMember{field0: __u32}
|
||||||
StructMember{field1: __f32}
|
StructMember{field1: __f32}
|
||||||
|
@ -1343,7 +1341,7 @@ TEST_F(SpvParserTest, ModuleScopeVar_NonWritableDecoration_DroppedForNow) {
|
||||||
Variable{
|
Variable{
|
||||||
myvar
|
myvar
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
}
|
}
|
||||||
})")) << module_str;
|
})")) << module_str;
|
||||||
}
|
}
|
||||||
|
@ -1365,15 +1363,14 @@ TEST_F(SpvParserTest, ModuleScopeVar_ColMajorDecoration_Dropped) {
|
||||||
EXPECT_TRUE(p->error().empty());
|
EXPECT_TRUE(p->error().empty());
|
||||||
const auto module_str = p->module().to_str();
|
const auto module_str = p->module().to_str();
|
||||||
EXPECT_THAT(module_str, HasSubstr(R"(
|
EXPECT_THAT(module_str, HasSubstr(R"(
|
||||||
S -> __struct_S
|
S Struct{
|
||||||
Struct{
|
|
||||||
[[block]]
|
[[block]]
|
||||||
StructMember{field0: __mat_2_3__f32}
|
StructMember{field0: __mat_2_3__f32}
|
||||||
}
|
}
|
||||||
Variable{
|
Variable{
|
||||||
myvar
|
myvar
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
}
|
}
|
||||||
})")) << module_str;
|
})")) << module_str;
|
||||||
}
|
}
|
||||||
|
@ -1395,15 +1392,14 @@ TEST_F(SpvParserTest, ModuleScopeVar_MatrixStrideDecoration_Dropped) {
|
||||||
EXPECT_TRUE(p->error().empty());
|
EXPECT_TRUE(p->error().empty());
|
||||||
const auto module_str = p->module().to_str();
|
const auto module_str = p->module().to_str();
|
||||||
EXPECT_THAT(module_str, HasSubstr(R"(
|
EXPECT_THAT(module_str, HasSubstr(R"(
|
||||||
S -> __struct_S
|
S Struct{
|
||||||
Struct{
|
|
||||||
[[block]]
|
[[block]]
|
||||||
StructMember{field0: __mat_2_3__f32}
|
StructMember{field0: __mat_2_3__f32}
|
||||||
}
|
}
|
||||||
Variable{
|
Variable{
|
||||||
myvar
|
myvar
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__alias_S__struct_S
|
__struct_S
|
||||||
}
|
}
|
||||||
})")) << module_str;
|
})")) << module_str;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ TEST_F(SpvParserTest, NamedTypes_AnonStruct) {
|
||||||
%s = OpTypeStruct %uint %uint
|
%s = OpTypeStruct %uint %uint
|
||||||
)"));
|
)"));
|
||||||
EXPECT_TRUE(p->BuildAndParseInternalModule());
|
EXPECT_TRUE(p->BuildAndParseInternalModule());
|
||||||
EXPECT_THAT(p->module().to_str(), HasSubstr("S -> __struct_"));
|
EXPECT_THAT(p->module().to_str(), HasSubstr("S Struct"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SpvParserTest, NamedTypes_NamedStruct) {
|
TEST_F(SpvParserTest, NamedTypes_NamedStruct) {
|
||||||
|
@ -50,7 +50,7 @@ TEST_F(SpvParserTest, NamedTypes_NamedStruct) {
|
||||||
%s = OpTypeStruct %uint %uint
|
%s = OpTypeStruct %uint %uint
|
||||||
)"));
|
)"));
|
||||||
EXPECT_TRUE(p->BuildAndParseInternalModule());
|
EXPECT_TRUE(p->BuildAndParseInternalModule());
|
||||||
EXPECT_THAT(p->module().to_str(), HasSubstr("mystruct -> __struct_"));
|
EXPECT_THAT(p->module().to_str(), HasSubstr("mystruct Struct"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(SpvParserTest, NamedTypes_Dup_EmitBoth) {
|
TEST_F(SpvParserTest, NamedTypes_Dup_EmitBoth) {
|
||||||
|
@ -60,13 +60,11 @@ TEST_F(SpvParserTest, NamedTypes_Dup_EmitBoth) {
|
||||||
%s2 = OpTypeStruct %uint %uint
|
%s2 = OpTypeStruct %uint %uint
|
||||||
)"));
|
)"));
|
||||||
EXPECT_TRUE(p->BuildAndParseInternalModule()) << p->error();
|
EXPECT_TRUE(p->BuildAndParseInternalModule()) << p->error();
|
||||||
EXPECT_THAT(p->module().to_str(), HasSubstr(R"(S -> __struct_S
|
EXPECT_THAT(p->module().to_str(), HasSubstr(R"(S Struct{
|
||||||
Struct{
|
|
||||||
StructMember{field0: __u32}
|
StructMember{field0: __u32}
|
||||||
StructMember{field1: __u32}
|
StructMember{field1: __u32}
|
||||||
}
|
}
|
||||||
S_1 -> __struct_S_1
|
S_1 Struct{
|
||||||
Struct{
|
|
||||||
StructMember{field0: __u32}
|
StructMember{field0: __u32}
|
||||||
StructMember{field1: __u32}
|
StructMember{field1: __u32}
|
||||||
})"));
|
})"));
|
||||||
|
|
|
@ -168,17 +168,17 @@ Token ParserImpl::peek() {
|
||||||
return peek(0);
|
return peek(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParserImpl::register_alias(const std::string& name,
|
void ParserImpl::register_constructed(const std::string& name,
|
||||||
ast::type::Type* type) {
|
ast::type::Type* type) {
|
||||||
assert(type);
|
assert(type);
|
||||||
registered_aliases_[name] = type;
|
registered_constructs_[name] = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::type::Type* ParserImpl::get_alias(const std::string& name) {
|
ast::type::Type* ParserImpl::get_constructed(const std::string& name) {
|
||||||
if (registered_aliases_.find(name) == registered_aliases_.end()) {
|
if (registered_constructs_.find(name) == registered_constructs_.end()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return registered_aliases_[name];
|
return registered_constructs_[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ParserImpl::Parse() {
|
bool ParserImpl::Parse() {
|
||||||
|
@ -206,11 +206,13 @@ void ParserImpl::translation_unit() {
|
||||||
// | global_variable_decl SEMICLON
|
// | global_variable_decl SEMICLON
|
||||||
// | global_constant_decl SEMICOLON
|
// | global_constant_decl SEMICOLON
|
||||||
// | type_alias SEMICOLON
|
// | type_alias SEMICOLON
|
||||||
|
// | struct_decl SEMICOLON
|
||||||
// | function_decl
|
// | function_decl
|
||||||
void ParserImpl::global_decl() {
|
void ParserImpl::global_decl() {
|
||||||
auto t = peek();
|
auto t = peek();
|
||||||
if (t.IsEof())
|
if (t.IsEof()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (t.IsSemicolon()) {
|
if (t.IsSemicolon()) {
|
||||||
next(); // consume the peek
|
next(); // consume the peek
|
||||||
|
@ -218,8 +220,9 @@ void ParserImpl::global_decl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto gv = global_variable_decl();
|
auto gv = global_variable_decl();
|
||||||
if (has_error())
|
if (has_error()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
if (gv != nullptr) {
|
if (gv != nullptr) {
|
||||||
t = next();
|
t = next();
|
||||||
if (!t.IsSemicolon()) {
|
if (!t.IsSemicolon()) {
|
||||||
|
@ -231,8 +234,9 @@ void ParserImpl::global_decl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto gc = global_constant_decl();
|
auto gc = global_constant_decl();
|
||||||
if (has_error())
|
if (has_error()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
if (gc != nullptr) {
|
if (gc != nullptr) {
|
||||||
t = next();
|
t = next();
|
||||||
if (!t.IsSemicolon()) {
|
if (!t.IsSemicolon()) {
|
||||||
|
@ -244,21 +248,39 @@ void ParserImpl::global_decl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* ta = type_alias();
|
auto* ta = type_alias();
|
||||||
if (has_error())
|
if (has_error()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
if (ta != nullptr) {
|
if (ta != nullptr) {
|
||||||
t = next();
|
t = next();
|
||||||
if (!t.IsSemicolon()) {
|
if (!t.IsSemicolon()) {
|
||||||
set_error(t, "missing ';' for type alias");
|
set_error(t, "missing ';' for type alias");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
module_.AddAliasType(ta);
|
module_.AddConstructedType(ta);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto str = struct_decl("");
|
||||||
|
if (has_error()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (str != nullptr) {
|
||||||
|
t = next();
|
||||||
|
if (!t.IsSemicolon()) {
|
||||||
|
set_error(t, "missing ';' for struct declaration");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto* type = ctx_.type_mgr().Get(std::move(str));
|
||||||
|
register_constructed(type->AsStruct()->name(), type);
|
||||||
|
module_.AddConstructedType(type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto func = function_decl();
|
auto func = function_decl();
|
||||||
if (has_error())
|
if (has_error()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
if (func != nullptr) {
|
if (func != nullptr) {
|
||||||
module_.AddFunction(std::move(func));
|
module_.AddFunction(std::move(func));
|
||||||
return;
|
return;
|
||||||
|
@ -1059,7 +1081,7 @@ ast::StorageClass ParserImpl::variable_storage_decoration() {
|
||||||
// type_alias
|
// type_alias
|
||||||
// : TYPE IDENT EQUAL type_decl
|
// : TYPE IDENT EQUAL type_decl
|
||||||
// | TYPE IDENT EQUAL struct_decl
|
// | TYPE IDENT EQUAL struct_decl
|
||||||
ast::type::AliasType* ParserImpl::type_alias() {
|
ast::type::Type* ParserImpl::type_alias() {
|
||||||
auto t = peek();
|
auto t = peek();
|
||||||
if (!t.IsType())
|
if (!t.IsType())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -1092,6 +1114,8 @@ ast::type::AliasType* ParserImpl::type_alias() {
|
||||||
}
|
}
|
||||||
|
|
||||||
type = ctx_.type_mgr().Get(std::move(str));
|
type = ctx_.type_mgr().Get(std::move(str));
|
||||||
|
register_constructed(name, type);
|
||||||
|
return type;
|
||||||
}
|
}
|
||||||
if (type == nullptr) {
|
if (type == nullptr) {
|
||||||
set_error(peek(), "invalid type for alias");
|
set_error(peek(), "invalid type for alias");
|
||||||
|
@ -1100,7 +1124,7 @@ ast::type::AliasType* ParserImpl::type_alias() {
|
||||||
|
|
||||||
auto* alias =
|
auto* alias =
|
||||||
ctx_.type_mgr().Get(std::make_unique<ast::type::AliasType>(name, type));
|
ctx_.type_mgr().Get(std::make_unique<ast::type::AliasType>(name, type));
|
||||||
register_alias(name, alias);
|
register_constructed(name, alias);
|
||||||
|
|
||||||
return alias->AsAlias();
|
return alias->AsAlias();
|
||||||
}
|
}
|
||||||
|
@ -1133,12 +1157,12 @@ ast::type::Type* ParserImpl::type_decl() {
|
||||||
auto t = peek();
|
auto t = peek();
|
||||||
if (t.IsIdentifier()) {
|
if (t.IsIdentifier()) {
|
||||||
next(); // Consume the peek
|
next(); // Consume the peek
|
||||||
auto* alias = get_alias(t.to_str());
|
auto* ty = get_constructed(t.to_str());
|
||||||
if (alias == nullptr) {
|
if (ty == nullptr) {
|
||||||
set_error(t, "unknown type alias '" + t.to_str() + "'");
|
set_error(t, "unknown constructed type '" + t.to_str() + "'");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return alias;
|
return ty;
|
||||||
}
|
}
|
||||||
if (t.IsBool()) {
|
if (t.IsBool()) {
|
||||||
next(); // Consume the peek
|
next(); // Consume the peek
|
||||||
|
@ -1494,10 +1518,25 @@ std::unique_ptr<ast::type::StructType> ParserImpl::struct_decl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t = next();
|
t = peek();
|
||||||
if (!t.IsStruct()) {
|
if (!decos.empty() && !t.IsStruct()) {
|
||||||
set_error(t, "missing struct declaration");
|
set_error(t, "missing struct declaration");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
} else if (!t.IsStruct()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
next(); // Consume the peek
|
||||||
|
|
||||||
|
// If there is no name this is a global struct call. This check will go
|
||||||
|
// away when the type_alias struct entry is removed.
|
||||||
|
std::string str_name = name;
|
||||||
|
if (name.empty()) {
|
||||||
|
t = next();
|
||||||
|
if (!t.IsIdentifier()) {
|
||||||
|
set_error(t, "missing identifier for struct declaration");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
str_name = t.to_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto body = struct_body_decl();
|
auto body = struct_body_decl();
|
||||||
|
@ -1506,7 +1545,7 @@ std::unique_ptr<ast::type::StructType> ParserImpl::struct_decl(
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_unique<ast::type::StructType>(
|
return std::make_unique<ast::type::StructType>(
|
||||||
name,
|
str_name,
|
||||||
std::make_unique<ast::Struct>(source, std::move(decos), std::move(body)));
|
std::make_unique<ast::Struct>(source, std::move(decos), std::move(body)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -110,14 +110,14 @@ class ParserImpl {
|
||||||
/// @param msg the error message
|
/// @param msg the error message
|
||||||
void set_error(const Token& t, const std::string& msg);
|
void set_error(const Token& t, const std::string& msg);
|
||||||
|
|
||||||
/// Registers a type alias into the parser
|
/// Registers a constructed type into the parser
|
||||||
/// @param name the alias name
|
/// @param name the constructed name
|
||||||
/// @param type the alias'd type
|
/// @param type the constructed type
|
||||||
void register_alias(const std::string& name, ast::type::Type* type);
|
void register_constructed(const std::string& name, ast::type::Type* type);
|
||||||
/// Retrieves an aliased type
|
/// Retrieves a constructed type
|
||||||
/// @param name The alias name to lookup
|
/// @param name The name to lookup
|
||||||
/// @returns the alias type for |name| or nullptr if not found
|
/// @returns the constructed type for |name| or nullptr if not found
|
||||||
ast::type::Type* get_alias(const std::string& name);
|
ast::type::Type* get_constructed(const std::string& name);
|
||||||
|
|
||||||
/// Parses the `translation_unit` grammar element
|
/// Parses the `translation_unit` grammar element
|
||||||
void translation_unit();
|
void translation_unit();
|
||||||
|
@ -148,7 +148,7 @@ class ParserImpl {
|
||||||
ast::StorageClass variable_storage_decoration();
|
ast::StorageClass variable_storage_decoration();
|
||||||
/// Parses a `type_alias` grammar element
|
/// Parses a `type_alias` grammar element
|
||||||
/// @returns the type alias or nullptr on error
|
/// @returns the type alias or nullptr on error
|
||||||
ast::type::AliasType* type_alias();
|
ast::type::Type* type_alias();
|
||||||
/// Parses a `type_decl` grammar element
|
/// Parses a `type_decl` grammar element
|
||||||
/// @returns the parsed Type or nullptr if none matched.
|
/// @returns the parsed Type or nullptr if none matched.
|
||||||
ast::type::Type* type_decl();
|
ast::type::Type* type_decl();
|
||||||
|
@ -410,7 +410,7 @@ class ParserImpl {
|
||||||
std::string error_;
|
std::string error_;
|
||||||
std::unique_ptr<Lexer> lexer_;
|
std::unique_ptr<Lexer> lexer_;
|
||||||
std::deque<Token> token_queue_;
|
std::deque<Token> token_queue_;
|
||||||
std::unordered_map<std::string, ast::type::Type*> registered_aliases_;
|
std::unordered_map<std::string, ast::type::Type*> registered_constructs_;
|
||||||
ast::Module module_;
|
ast::Module module_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -119,7 +119,7 @@ TEST_F(ParserImplTest, ConstExpr_ConstLiteral_Invalid) {
|
||||||
auto e = p->const_expr();
|
auto e = p->const_expr();
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_EQ(e, nullptr);
|
ASSERT_EQ(e, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:1: unknown type alias 'invalid'");
|
EXPECT_EQ(p->error(), "1:1: unknown constructed type 'invalid'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, ConstExpr_Recursion) {
|
TEST_F(ParserImplTest, ConstExpr_Recursion) {
|
||||||
|
|
|
@ -89,7 +89,7 @@ TEST_F(ParserImplTest, FunctionHeader_InvalidReturnType) {
|
||||||
auto f = p->function_header();
|
auto f = p->function_header();
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_EQ(f, nullptr);
|
ASSERT_EQ(f, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:14: unknown type alias 'invalid'");
|
EXPECT_EQ(p->error(), "1:14: unknown constructed type 'invalid'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, FunctionHeader_MissingReturnType) {
|
TEST_F(ParserImplTest, FunctionHeader_MissingReturnType) {
|
||||||
|
|
|
@ -51,7 +51,7 @@ TEST_F(ParserImplTest, FunctionTypeDecl_InvalidType) {
|
||||||
auto* e = p->function_type_decl();
|
auto* e = p->function_type_decl();
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_EQ(e, nullptr);
|
ASSERT_EQ(e, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:6: unknown type alias 'invalid'");
|
EXPECT_EQ(p->error(), "1:6: unknown constructed type 'invalid'");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -51,7 +51,7 @@ TEST_F(ParserImplTest, GlobalConstantDecl_InvalidVariable) {
|
||||||
auto e = p->global_constant_decl();
|
auto e = p->global_constant_decl();
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_EQ(e, nullptr);
|
ASSERT_EQ(e, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:10: unknown type alias 'invalid'");
|
EXPECT_EQ(p->error(), "1:10: unknown constructed type 'invalid'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, GlobalConstantDecl_InvalidExpression) {
|
TEST_F(ParserImplTest, GlobalConstantDecl_InvalidExpression) {
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
#include "src/ast/type/array_type.h"
|
||||||
|
#include "src/ast/type/struct_type.h"
|
||||||
#include "src/reader/wgsl/parser_impl.h"
|
#include "src/reader/wgsl/parser_impl.h"
|
||||||
#include "src/reader/wgsl/parser_impl_test_helper.h"
|
#include "src/reader/wgsl/parser_impl_test_helper.h"
|
||||||
|
|
||||||
|
@ -43,7 +45,7 @@ TEST_F(ParserImplTest, GlobalDecl_GlobalVariable_Invalid) {
|
||||||
auto* p = parser("var<out> a : vec2<invalid>;");
|
auto* p = parser("var<out> a : vec2<invalid>;");
|
||||||
p->global_decl();
|
p->global_decl();
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
EXPECT_EQ(p->error(), "1:19: unknown type alias 'invalid'");
|
EXPECT_EQ(p->error(), "1:19: unknown constructed type 'invalid'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, GlobalDecl_GlobalVariable_MissingSemicolon) {
|
TEST_F(ParserImplTest, GlobalDecl_GlobalVariable_MissingSemicolon) {
|
||||||
|
@ -85,15 +87,48 @@ TEST_F(ParserImplTest, GlobalDecl_TypeAlias) {
|
||||||
ASSERT_FALSE(p->has_error()) << p->error();
|
ASSERT_FALSE(p->has_error()) << p->error();
|
||||||
|
|
||||||
auto m = p->module();
|
auto m = p->module();
|
||||||
ASSERT_EQ(m.alias_types().size(), 1u);
|
ASSERT_EQ(m.constructed_types().size(), 1u);
|
||||||
EXPECT_EQ(m.alias_types()[0]->name(), "A");
|
ASSERT_TRUE(m.constructed_types()[0]->IsAlias());
|
||||||
|
EXPECT_EQ(m.constructed_types()[0]->AsAlias()->name(), "A");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ParserImplTest, GlobalDecl_TypeAlias_Struct) {
|
||||||
|
auto* p = parser("type A = struct { a : f32; };");
|
||||||
|
p->global_decl();
|
||||||
|
ASSERT_FALSE(p->has_error()) << p->error();
|
||||||
|
|
||||||
|
auto m = p->module();
|
||||||
|
ASSERT_EQ(m.constructed_types().size(), 1u);
|
||||||
|
ASSERT_TRUE(m.constructed_types()[0]->IsStruct());
|
||||||
|
EXPECT_EQ(m.constructed_types()[0]->AsStruct()->name(), "A");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ParserImplTest, GlobalDecl_TypeAlias_StructIdent) {
|
||||||
|
auto* p = parser(R"(struct A {
|
||||||
|
a : f32;
|
||||||
|
};
|
||||||
|
type B = A;)");
|
||||||
|
p->global_decl();
|
||||||
|
p->global_decl();
|
||||||
|
ASSERT_FALSE(p->has_error()) << p->error();
|
||||||
|
|
||||||
|
auto m = p->module();
|
||||||
|
ASSERT_EQ(m.constructed_types().size(), 2u);
|
||||||
|
ASSERT_TRUE(m.constructed_types()[0]->IsStruct());
|
||||||
|
auto* str = m.constructed_types()[0]->AsStruct();
|
||||||
|
EXPECT_EQ(str->name(), "A");
|
||||||
|
|
||||||
|
ASSERT_TRUE(m.constructed_types()[1]->IsAlias());
|
||||||
|
auto* alias = m.constructed_types()[1]->AsAlias();
|
||||||
|
EXPECT_EQ(alias->name(), "B");
|
||||||
|
EXPECT_EQ(alias->type(), str);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, GlobalDecl_TypeAlias_Invalid) {
|
TEST_F(ParserImplTest, GlobalDecl_TypeAlias_Invalid) {
|
||||||
auto* p = parser("type A = invalid;");
|
auto* p = parser("type A = invalid;");
|
||||||
p->global_decl();
|
p->global_decl();
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
EXPECT_EQ(p->error(), "1:10: unknown type alias 'invalid'");
|
EXPECT_EQ(p->error(), "1:10: unknown constructed type 'invalid'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, GlobalDecl_TypeAlias_MissingSemicolon) {
|
TEST_F(ParserImplTest, GlobalDecl_TypeAlias_MissingSemicolon) {
|
||||||
|
@ -130,6 +165,95 @@ TEST_F(ParserImplTest, GlobalDecl_Function_Invalid) {
|
||||||
EXPECT_EQ(p->error(), "1:14: unable to determine function return type");
|
EXPECT_EQ(p->error(), "1:14: unable to determine function return type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ParserImplTest, GlobalDecl_ParsesStruct) {
|
||||||
|
auto* p = parser("struct A { b: i32; c: f32;};");
|
||||||
|
p->global_decl();
|
||||||
|
ASSERT_FALSE(p->has_error());
|
||||||
|
|
||||||
|
auto m = p->module();
|
||||||
|
ASSERT_EQ(m.constructed_types().size(), 1u);
|
||||||
|
|
||||||
|
auto* t = m.constructed_types()[0];
|
||||||
|
ASSERT_NE(t, nullptr);
|
||||||
|
ASSERT_TRUE(t->IsStruct());
|
||||||
|
|
||||||
|
auto* str = t->AsStruct();
|
||||||
|
EXPECT_EQ(str->name(), "A");
|
||||||
|
EXPECT_EQ(str->impl()->members().size(), 2u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ParserImplTest, GlobalDecl_Struct_WithStride) {
|
||||||
|
auto* p =
|
||||||
|
parser("struct A { [[offset(0)]] data: [[stride(4)]] array<f32>; };");
|
||||||
|
p->global_decl();
|
||||||
|
ASSERT_FALSE(p->has_error());
|
||||||
|
|
||||||
|
auto m = p->module();
|
||||||
|
ASSERT_EQ(m.constructed_types().size(), 1u);
|
||||||
|
|
||||||
|
auto* t = m.constructed_types()[0];
|
||||||
|
ASSERT_NE(t, nullptr);
|
||||||
|
ASSERT_TRUE(t->IsStruct());
|
||||||
|
|
||||||
|
auto* str = t->AsStruct();
|
||||||
|
EXPECT_EQ(str->name(), "A");
|
||||||
|
EXPECT_EQ(str->impl()->members().size(), 1u);
|
||||||
|
EXPECT_FALSE(str->IsBlockDecorated());
|
||||||
|
|
||||||
|
const auto* ty = str->impl()->members()[0]->type();
|
||||||
|
ASSERT_TRUE(ty->IsArray());
|
||||||
|
const auto* arr = ty->AsArray();
|
||||||
|
EXPECT_TRUE(arr->has_array_stride());
|
||||||
|
EXPECT_EQ(arr->array_stride(), 4u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ParserImplTest, GlobalDecl_Struct_WithDecoration) {
|
||||||
|
auto* p = parser("[[block]] struct A { [[offset(0)]] data: f32; };");
|
||||||
|
p->global_decl();
|
||||||
|
ASSERT_FALSE(p->has_error());
|
||||||
|
|
||||||
|
auto m = p->module();
|
||||||
|
ASSERT_EQ(m.constructed_types().size(), 1u);
|
||||||
|
|
||||||
|
auto* t = m.constructed_types()[0];
|
||||||
|
ASSERT_NE(t, nullptr);
|
||||||
|
ASSERT_TRUE(t->IsStruct());
|
||||||
|
|
||||||
|
auto* str = t->AsStruct();
|
||||||
|
EXPECT_EQ(str->name(), "A");
|
||||||
|
EXPECT_EQ(str->impl()->members().size(), 1u);
|
||||||
|
EXPECT_TRUE(str->IsBlockDecorated());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ParserImplTest, GlobalDecl_Struct_Invalid) {
|
||||||
|
auto* p = parser("[[block]] A {};");
|
||||||
|
p->global_decl();
|
||||||
|
ASSERT_TRUE(p->has_error());
|
||||||
|
EXPECT_EQ(p->error(), "1:11: missing struct declaration");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ParserImplTest, GlobalDecl_StructMissing_Semi) {
|
||||||
|
auto* p = parser("[[block]] struct A {}");
|
||||||
|
p->global_decl();
|
||||||
|
ASSERT_TRUE(p->has_error());
|
||||||
|
EXPECT_EQ(p->error(), "1:22: missing ';' for struct declaration");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This was failing due to not finding the missing ;. https://crbug.com/tint/218
|
||||||
|
TEST_F(ParserImplTest, TypeDecl_Struct_Empty) {
|
||||||
|
auto* p = parser("type str = struct {};");
|
||||||
|
p->global_decl();
|
||||||
|
ASSERT_FALSE(p->has_error()) << p->error();
|
||||||
|
|
||||||
|
auto module = p->module();
|
||||||
|
ASSERT_EQ(module.constructed_types().size(), 1u);
|
||||||
|
|
||||||
|
ASSERT_TRUE(module.constructed_types()[0]->IsStruct());
|
||||||
|
auto* str = module.constructed_types()[0]->AsStruct();
|
||||||
|
EXPECT_EQ(str->name(), "str");
|
||||||
|
EXPECT_EQ(str->impl()->members().size(), 0u);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace wgsl
|
} // namespace wgsl
|
||||||
} // namespace reader
|
} // namespace reader
|
||||||
|
|
|
@ -221,7 +221,7 @@ TEST_F(ParserImplTest, PrimaryExpression_Bitcast_InvalidType) {
|
||||||
auto e = p->primary_expression();
|
auto e = p->primary_expression();
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_EQ(e, nullptr);
|
ASSERT_EQ(e, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:9: unknown type alias 'invalid'");
|
EXPECT_EQ(p->error(), "1:9: unknown constructed type 'invalid'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, PrimaryExpression_Bitcast_MissingLeftParen) {
|
TEST_F(ParserImplTest, PrimaryExpression_Bitcast_MissingLeftParen) {
|
||||||
|
|
|
@ -24,6 +24,21 @@ namespace {
|
||||||
|
|
||||||
TEST_F(ParserImplTest, StructDecl_Parses) {
|
TEST_F(ParserImplTest, StructDecl_Parses) {
|
||||||
auto* p = parser(R"(
|
auto* p = parser(R"(
|
||||||
|
struct S {
|
||||||
|
a : i32;
|
||||||
|
[[offset(4)]] b : f32;
|
||||||
|
})");
|
||||||
|
auto s = p->struct_decl("");
|
||||||
|
ASSERT_FALSE(p->has_error());
|
||||||
|
ASSERT_NE(s, nullptr);
|
||||||
|
ASSERT_EQ(s->name(), "S");
|
||||||
|
ASSERT_EQ(s->impl()->members().size(), 2u);
|
||||||
|
EXPECT_EQ(s->impl()->members()[0]->name(), "a");
|
||||||
|
EXPECT_EQ(s->impl()->members()[1]->name(), "b");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ParserImplTest, StructDecl_Parses_WithoutName) {
|
||||||
|
auto* p = parser(R"(
|
||||||
struct {
|
struct {
|
||||||
a : i32;
|
a : i32;
|
||||||
[[offset(4)]] b : f32;
|
[[offset(4)]] b : f32;
|
||||||
|
@ -39,11 +54,11 @@ struct {
|
||||||
|
|
||||||
TEST_F(ParserImplTest, StructDecl_ParsesWithDecoration) {
|
TEST_F(ParserImplTest, StructDecl_ParsesWithDecoration) {
|
||||||
auto* p = parser(R"(
|
auto* p = parser(R"(
|
||||||
[[block]] struct {
|
[[block]] struct B {
|
||||||
a : f32;
|
a : f32;
|
||||||
b : f32;
|
b : f32;
|
||||||
})");
|
})");
|
||||||
auto s = p->struct_decl("B");
|
auto s = p->struct_decl("");
|
||||||
ASSERT_FALSE(p->has_error());
|
ASSERT_FALSE(p->has_error());
|
||||||
ASSERT_NE(s, nullptr);
|
ASSERT_NE(s, nullptr);
|
||||||
ASSERT_EQ(s->name(), "B");
|
ASSERT_EQ(s->name(), "B");
|
||||||
|
@ -57,11 +72,11 @@ TEST_F(ParserImplTest, StructDecl_ParsesWithDecoration) {
|
||||||
TEST_F(ParserImplTest, StructDecl_ParsesWithMultipleDecoration) {
|
TEST_F(ParserImplTest, StructDecl_ParsesWithMultipleDecoration) {
|
||||||
auto* p = parser(R"(
|
auto* p = parser(R"(
|
||||||
[[block]]
|
[[block]]
|
||||||
[[block]] struct {
|
[[block]] struct S {
|
||||||
a : f32;
|
a : f32;
|
||||||
b : f32;
|
b : f32;
|
||||||
})");
|
})");
|
||||||
auto s = p->struct_decl("S");
|
auto s = p->struct_decl("");
|
||||||
ASSERT_FALSE(p->has_error());
|
ASSERT_FALSE(p->has_error());
|
||||||
ASSERT_NE(s, nullptr);
|
ASSERT_NE(s, nullptr);
|
||||||
ASSERT_EQ(s->name(), "S");
|
ASSERT_EQ(s->name(), "S");
|
||||||
|
@ -74,40 +89,48 @@ TEST_F(ParserImplTest, StructDecl_ParsesWithMultipleDecoration) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, StructDecl_EmptyMembers) {
|
TEST_F(ParserImplTest, StructDecl_EmptyMembers) {
|
||||||
auto* p = parser("struct {}");
|
auto* p = parser("struct S {}");
|
||||||
auto s = p->struct_decl("S");
|
auto s = p->struct_decl("");
|
||||||
ASSERT_FALSE(p->has_error());
|
ASSERT_FALSE(p->has_error());
|
||||||
ASSERT_NE(s, nullptr);
|
ASSERT_NE(s, nullptr);
|
||||||
ASSERT_EQ(s->impl()->members().size(), 0u);
|
ASSERT_EQ(s->impl()->members().size(), 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, StructDecl_MissingBracketLeft) {
|
TEST_F(ParserImplTest, StructDecl_MissingIdent) {
|
||||||
auto* p = parser("struct }");
|
auto* p = parser("struct {}");
|
||||||
auto s = p->struct_decl("S");
|
auto s = p->struct_decl("");
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_EQ(s, nullptr);
|
ASSERT_EQ(s, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:8: missing { for struct declaration");
|
EXPECT_EQ(p->error(), "1:8: missing identifier for struct declaration");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ParserImplTest, StructDecl_MissingBracketLeft) {
|
||||||
|
auto* p = parser("struct S }");
|
||||||
|
auto s = p->struct_decl("");
|
||||||
|
ASSERT_TRUE(p->has_error());
|
||||||
|
ASSERT_EQ(s, nullptr);
|
||||||
|
EXPECT_EQ(p->error(), "1:10: missing { for struct declaration");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, StructDecl_InvalidStructBody) {
|
TEST_F(ParserImplTest, StructDecl_InvalidStructBody) {
|
||||||
auto* p = parser("struct { a : B; }");
|
auto* p = parser("struct S { a : B; }");
|
||||||
auto s = p->struct_decl("S");
|
auto s = p->struct_decl("");
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_EQ(s, nullptr);
|
ASSERT_EQ(s, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:14: unknown type alias 'B'");
|
EXPECT_EQ(p->error(), "1:16: unknown constructed type 'B'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, StructDecl_InvalidStructDecorationDecl) {
|
TEST_F(ParserImplTest, StructDecl_InvalidStructDecorationDecl) {
|
||||||
auto* p = parser("[[block struct { a : i32; }");
|
auto* p = parser("[[block struct S { a : i32; }");
|
||||||
auto s = p->struct_decl("S");
|
auto s = p->struct_decl("");
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_EQ(s, nullptr);
|
ASSERT_EQ(s, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:9: missing ]] for struct decoration");
|
EXPECT_EQ(p->error(), "1:9: missing ]] for struct decoration");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, StructDecl_MissingStruct) {
|
TEST_F(ParserImplTest, StructDecl_MissingStruct) {
|
||||||
auto* p = parser("[[block]] {}");
|
auto* p = parser("[[block]] S {}");
|
||||||
auto s = p->struct_decl("S");
|
auto s = p->struct_decl("");
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_EQ(s, nullptr);
|
ASSERT_EQ(s, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:11: missing struct declaration");
|
EXPECT_EQ(p->error(), "1:11: missing struct declaration");
|
||||||
|
|
|
@ -83,7 +83,7 @@ TEST_F(ParserImplTest, StructMember_InvalidVariable) {
|
||||||
auto m = p->struct_member();
|
auto m = p->struct_member();
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_EQ(m, nullptr);
|
ASSERT_EQ(m, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:19: unknown type alias 'B'");
|
EXPECT_EQ(p->error(), "1:19: unknown constructed type 'B'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, StructMember_MissingSemicolon) {
|
TEST_F(ParserImplTest, StructMember_MissingSemicolon) {
|
||||||
|
|
|
@ -58,16 +58,16 @@ fn main() -> { # missing return type
|
||||||
TEST_F(ParserImplTest, GetRegisteredType) {
|
TEST_F(ParserImplTest, GetRegisteredType) {
|
||||||
auto* p = parser("");
|
auto* p = parser("");
|
||||||
ast::type::I32Type i32;
|
ast::type::I32Type i32;
|
||||||
p->register_alias("my_alias", &i32);
|
p->register_constructed("my_alias", &i32);
|
||||||
|
|
||||||
auto* alias = p->get_alias("my_alias");
|
auto* alias = p->get_constructed("my_alias");
|
||||||
ASSERT_NE(alias, nullptr);
|
ASSERT_NE(alias, nullptr);
|
||||||
ASSERT_EQ(alias, &i32);
|
ASSERT_EQ(alias, &i32);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, GetUnregisteredType) {
|
TEST_F(ParserImplTest, GetUnregisteredType) {
|
||||||
auto* p = parser("");
|
auto* p = parser("");
|
||||||
auto* alias = p->get_alias("my_alias");
|
auto* alias = p->get_constructed("my_alias");
|
||||||
ASSERT_EQ(alias, nullptr);
|
ASSERT_EQ(alias, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_Invalid) {
|
||||||
auto* t = p->texture_sampler_types();
|
auto* t = p->texture_sampler_types();
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
EXPECT_EQ(t, nullptr);
|
EXPECT_EQ(t, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:20: unknown type alias 'abc'");
|
EXPECT_EQ(p->error(), "1:20: unknown constructed type 'abc'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_MissingType) {
|
TEST_F(ParserImplTest, TextureSamplerTypes_SampledTexture_MissingType) {
|
||||||
|
@ -140,7 +140,7 @@ TEST_F(ParserImplTest, TextureSamplerTypes_MultisampledTexture_Invalid) {
|
||||||
auto* t = p->texture_sampler_types();
|
auto* t = p->texture_sampler_types();
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
EXPECT_EQ(t, nullptr);
|
EXPECT_EQ(t, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:25: unknown type alias 'abc'");
|
EXPECT_EQ(p->error(), "1:25: unknown constructed type 'abc'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, TextureSamplerTypes_MultisampledTexture_MissingType) {
|
TEST_F(ParserImplTest, TextureSamplerTypes_MultisampledTexture_MissingType) {
|
||||||
|
|
|
@ -33,20 +33,28 @@ TEST_F(ParserImplTest, TypeDecl_ParsesType) {
|
||||||
auto* t = p->type_alias();
|
auto* t = p->type_alias();
|
||||||
ASSERT_FALSE(p->has_error());
|
ASSERT_FALSE(p->has_error());
|
||||||
ASSERT_NE(t, nullptr);
|
ASSERT_NE(t, nullptr);
|
||||||
ASSERT_TRUE(t->type()->IsI32());
|
ASSERT_TRUE(t->IsAlias());
|
||||||
ASSERT_EQ(t->type(), i32);
|
auto* alias = t->AsAlias();
|
||||||
|
ASSERT_TRUE(alias->type()->IsI32());
|
||||||
|
ASSERT_EQ(alias->type(), i32);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, TypeDecl_ParsesStruct) {
|
TEST_F(ParserImplTest, TypeDecl_ParsesStruct_Ident) {
|
||||||
auto* p = parser("type a = struct { b: i32; c: f32;}");
|
ast::type::StructType str("B", {});
|
||||||
|
|
||||||
|
auto* p = parser("type a = B");
|
||||||
|
p->register_constructed("B", &str);
|
||||||
|
|
||||||
auto* t = p->type_alias();
|
auto* t = p->type_alias();
|
||||||
ASSERT_FALSE(p->has_error());
|
ASSERT_FALSE(p->has_error());
|
||||||
ASSERT_NE(t, nullptr);
|
ASSERT_NE(t, nullptr);
|
||||||
EXPECT_EQ(t->name(), "a");
|
ASSERT_TRUE(t->IsAlias());
|
||||||
ASSERT_TRUE(t->type()->IsStruct());
|
auto* alias = t->AsAlias();
|
||||||
|
EXPECT_EQ(alias->name(), "a");
|
||||||
|
ASSERT_TRUE(alias->type()->IsStruct());
|
||||||
|
|
||||||
auto* s = t->type()->AsStruct();
|
auto* s = alias->type()->AsStruct();
|
||||||
EXPECT_EQ(s->impl()->members().size(), 2u);
|
EXPECT_EQ(s->name(), "B");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, TypeDecl_MissingIdent) {
|
TEST_F(ParserImplTest, TypeDecl_MissingIdent) {
|
||||||
|
@ -78,7 +86,18 @@ TEST_F(ParserImplTest, TypeDecl_InvalidType) {
|
||||||
auto* t = p->type_alias();
|
auto* t = p->type_alias();
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_EQ(t, nullptr);
|
ASSERT_EQ(t, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:10: unknown type alias 'B'");
|
EXPECT_EQ(p->error(), "1:10: unknown constructed type 'B'");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ParserImplTest, TypeDecl_ParsesStruct) {
|
||||||
|
auto* p = parser("type a = struct { b: i32; c: f32;}");
|
||||||
|
auto* t = p->type_alias();
|
||||||
|
ASSERT_FALSE(p->has_error());
|
||||||
|
ASSERT_NE(t, nullptr);
|
||||||
|
ASSERT_TRUE(t->IsStruct());
|
||||||
|
auto* str = t->AsStruct();
|
||||||
|
EXPECT_EQ(str->name(), "a");
|
||||||
|
EXPECT_EQ(str->impl()->members().size(), 2u);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, TypeDecl_InvalidStruct) {
|
TEST_F(ParserImplTest, TypeDecl_InvalidStruct) {
|
||||||
|
@ -97,35 +116,30 @@ TEST_F(ParserImplTest, TypeDecl_Struct_WithStride) {
|
||||||
auto* t = p->type_alias();
|
auto* t = p->type_alias();
|
||||||
ASSERT_FALSE(p->has_error());
|
ASSERT_FALSE(p->has_error());
|
||||||
ASSERT_NE(t, nullptr);
|
ASSERT_NE(t, nullptr);
|
||||||
EXPECT_EQ(t->name(), "a");
|
ASSERT_TRUE(t->IsStruct());
|
||||||
ASSERT_TRUE(t->type()->IsStruct());
|
auto* str = t->AsStruct();
|
||||||
|
EXPECT_EQ(str->name(), "a");
|
||||||
|
EXPECT_EQ(str->impl()->members().size(), 1u);
|
||||||
|
|
||||||
auto* s = t->type()->AsStruct();
|
const auto* ty = str->impl()->members()[0]->type();
|
||||||
EXPECT_EQ(s->impl()->members().size(), 1u);
|
|
||||||
|
|
||||||
const auto* ty = s->impl()->members()[0]->type();
|
|
||||||
ASSERT_TRUE(ty->IsArray());
|
ASSERT_TRUE(ty->IsArray());
|
||||||
const auto* arr = ty->AsArray();
|
const auto* arr = ty->AsArray();
|
||||||
EXPECT_TRUE(arr->has_array_stride());
|
EXPECT_TRUE(arr->has_array_stride());
|
||||||
EXPECT_EQ(arr->array_stride(), 4u);
|
EXPECT_EQ(arr->array_stride(), 4u);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This was failing due to not finding the missing ;. https://crbug.com/tint/218
|
|
||||||
TEST_F(ParserImplTest, TypeDecl_Struct_Empty) {
|
TEST_F(ParserImplTest, TypeDecl_Struct_Empty) {
|
||||||
auto* p = parser("type str = struct {};");
|
auto* p = parser("type str = struct {};");
|
||||||
p->global_decl();
|
p->global_decl();
|
||||||
ASSERT_FALSE(p->has_error()) << p->error();
|
ASSERT_FALSE(p->has_error()) << p->error();
|
||||||
|
|
||||||
auto module = p->module();
|
auto module = p->module();
|
||||||
ASSERT_EQ(module.alias_types().size(), 1u);
|
ASSERT_EQ(module.constructed_types().size(), 1u);
|
||||||
|
|
||||||
auto* t = module.alias_types()[0];
|
ASSERT_TRUE(module.constructed_types()[0]->IsStruct());
|
||||||
ASSERT_NE(t, nullptr);
|
auto* str = module.constructed_types()[0]->AsStruct();
|
||||||
EXPECT_EQ(t->name(), "str");
|
EXPECT_EQ(str->name(), "str");
|
||||||
|
EXPECT_EQ(str->impl()->members().size(), 0u);
|
||||||
ASSERT_TRUE(t->type()->IsStruct());
|
|
||||||
auto* s = t->type()->AsStruct();
|
|
||||||
EXPECT_EQ(s->impl()->members().size(), 0u);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -50,7 +50,7 @@ TEST_F(ParserImplTest, TypeDecl_Identifier) {
|
||||||
auto* alias_type =
|
auto* alias_type =
|
||||||
tm()->Get(std::make_unique<ast::type::AliasType>("A", int_type));
|
tm()->Get(std::make_unique<ast::type::AliasType>("A", int_type));
|
||||||
|
|
||||||
p->register_alias("A", alias_type);
|
p->register_constructed("A", alias_type);
|
||||||
|
|
||||||
auto* t = p->type_decl();
|
auto* t = p->type_decl();
|
||||||
ASSERT_NE(t, nullptr);
|
ASSERT_NE(t, nullptr);
|
||||||
|
@ -68,7 +68,7 @@ TEST_F(ParserImplTest, TypeDecl_Identifier_NotFound) {
|
||||||
auto* t = p->type_decl();
|
auto* t = p->type_decl();
|
||||||
ASSERT_EQ(t, nullptr);
|
ASSERT_EQ(t, nullptr);
|
||||||
EXPECT_TRUE(p->has_error());
|
EXPECT_TRUE(p->has_error());
|
||||||
EXPECT_EQ(p->error(), "1:1: unknown type alias 'B'");
|
EXPECT_EQ(p->error(), "1:1: unknown constructed type 'B'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, TypeDecl_Bool) {
|
TEST_F(ParserImplTest, TypeDecl_Bool) {
|
||||||
|
@ -248,7 +248,7 @@ TEST_P(VecBadType, Handles_Unknown_Type) {
|
||||||
auto* t = p->type_decl();
|
auto* t = p->type_decl();
|
||||||
ASSERT_EQ(t, nullptr);
|
ASSERT_EQ(t, nullptr);
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_EQ(p->error(), "1:6: unknown type alias 'unknown'");
|
ASSERT_EQ(p->error(), "1:6: unknown constructed type 'unknown'");
|
||||||
}
|
}
|
||||||
INSTANTIATE_TEST_SUITE_P(ParserImplTest,
|
INSTANTIATE_TEST_SUITE_P(ParserImplTest,
|
||||||
VecBadType,
|
VecBadType,
|
||||||
|
@ -378,7 +378,7 @@ TEST_F(ParserImplTest, TypeDecl_Ptr_BadType) {
|
||||||
auto* t = p->type_decl();
|
auto* t = p->type_decl();
|
||||||
ASSERT_EQ(t, nullptr);
|
ASSERT_EQ(t, nullptr);
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_EQ(p->error(), "1:15: unknown type alias 'unknown'");
|
ASSERT_EQ(p->error(), "1:15: unknown constructed type 'unknown'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, TypeDecl_Array) {
|
TEST_F(ParserImplTest, TypeDecl_Array) {
|
||||||
|
@ -544,7 +544,7 @@ TEST_F(ParserImplTest, TypeDecl_Array_BadType) {
|
||||||
auto* t = p->type_decl();
|
auto* t = p->type_decl();
|
||||||
ASSERT_EQ(t, nullptr);
|
ASSERT_EQ(t, nullptr);
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_EQ(p->error(), "1:7: unknown type alias 'unknown'");
|
ASSERT_EQ(p->error(), "1:7: unknown constructed type 'unknown'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, TypeDecl_Array_ZeroSize) {
|
TEST_F(ParserImplTest, TypeDecl_Array_ZeroSize) {
|
||||||
|
@ -746,7 +746,7 @@ TEST_P(MatrixBadType, Handles_Unknown_Type) {
|
||||||
auto* t = p->type_decl();
|
auto* t = p->type_decl();
|
||||||
ASSERT_EQ(t, nullptr);
|
ASSERT_EQ(t, nullptr);
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_EQ(p->error(), "1:8: unknown type alias 'unknown'");
|
ASSERT_EQ(p->error(), "1:8: unknown constructed type 'unknown'");
|
||||||
}
|
}
|
||||||
INSTANTIATE_TEST_SUITE_P(ParserImplTest,
|
INSTANTIATE_TEST_SUITE_P(ParserImplTest,
|
||||||
MatrixBadType,
|
MatrixBadType,
|
||||||
|
|
|
@ -76,7 +76,7 @@ TEST_F(ParserImplTest, VariableIdentDecl_InvalidType) {
|
||||||
auto* p = parser("my_var : invalid");
|
auto* p = parser("my_var : invalid");
|
||||||
auto r = p->variable_ident_decl();
|
auto r = p->variable_ident_decl();
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_EQ(p->error(), "1:10: unknown type alias 'invalid'");
|
ASSERT_EQ(p->error(), "1:10: unknown constructed type 'invalid'");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -53,7 +53,7 @@ TEST_F(ParserImplTest, VariableStmt_VariableDecl_Invalid) {
|
||||||
auto e = p->variable_stmt();
|
auto e = p->variable_stmt();
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_EQ(e, nullptr);
|
ASSERT_EQ(e, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:9: unknown type alias 'invalid'");
|
EXPECT_EQ(p->error(), "1:9: unknown constructed type 'invalid'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, VariableStmt_VariableDecl_ConstructorInvalid) {
|
TEST_F(ParserImplTest, VariableStmt_VariableDecl_ConstructorInvalid) {
|
||||||
|
@ -77,7 +77,7 @@ TEST_F(ParserImplTest, VariableStmt_Const_InvalidVarIdent) {
|
||||||
auto e = p->variable_stmt();
|
auto e = p->variable_stmt();
|
||||||
ASSERT_TRUE(p->has_error());
|
ASSERT_TRUE(p->has_error());
|
||||||
ASSERT_EQ(e, nullptr);
|
ASSERT_EQ(e, nullptr);
|
||||||
EXPECT_EQ(p->error(), "1:11: unknown type alias 'invalid'");
|
EXPECT_EQ(p->error(), "1:11: unknown constructed type 'invalid'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, VariableStmt_Const_MissingEqual) {
|
TEST_F(ParserImplTest, VariableStmt_Const_MissingEqual) {
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
#include "src/ast/struct_decoration.h"
|
#include "src/ast/struct_decoration.h"
|
||||||
#include "src/ast/struct_member.h"
|
#include "src/ast/struct_member.h"
|
||||||
#include "src/ast/struct_member_offset_decoration.h"
|
#include "src/ast/struct_member_offset_decoration.h"
|
||||||
#include "src/ast/type/alias_type.h"
|
|
||||||
#include "src/ast/type/array_type.h"
|
#include "src/ast/type/array_type.h"
|
||||||
#include "src/ast/type/f32_type.h"
|
#include "src/ast/type/f32_type.h"
|
||||||
#include "src/ast/type/i32_type.h"
|
#include "src/ast/type/i32_type.h"
|
||||||
|
@ -239,14 +238,13 @@ void VertexPullingTransform::AddVertexStorageBuffers() {
|
||||||
ctx_->type_mgr().Get(std::make_unique<ast::type::StructType>(
|
ctx_->type_mgr().Get(std::make_unique<ast::type::StructType>(
|
||||||
kStructName,
|
kStructName,
|
||||||
std::make_unique<ast::Struct>(std::move(decos), std::move(members))));
|
std::make_unique<ast::Struct>(std::move(decos), std::move(members))));
|
||||||
auto* alias = ctx_->type_mgr().Get(
|
|
||||||
std::make_unique<ast::type::AliasType>(kStructName, struct_type));
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < vertex_state_->vertex_buffers.size(); ++i) {
|
for (uint32_t i = 0; i < vertex_state_->vertex_buffers.size(); ++i) {
|
||||||
// The decorated variable with struct type
|
// The decorated variable with struct type
|
||||||
auto var = std::make_unique<ast::DecoratedVariable>(
|
auto var = std::make_unique<ast::DecoratedVariable>(
|
||||||
std::make_unique<ast::Variable>(
|
std::make_unique<ast::Variable>(GetVertexBufferName(i),
|
||||||
GetVertexBufferName(i), ast::StorageClass::kStorageBuffer, alias));
|
ast::StorageClass::kStorageBuffer,
|
||||||
|
struct_type));
|
||||||
|
|
||||||
// Add decorations
|
// Add decorations
|
||||||
ast::VariableDecorationList decorations;
|
ast::VariableDecorationList decorations;
|
||||||
|
@ -256,7 +254,7 @@ void VertexPullingTransform::AddVertexStorageBuffers() {
|
||||||
|
|
||||||
mod_->AddGlobalVariable(std::move(var));
|
mod_->AddGlobalVariable(std::move(var));
|
||||||
}
|
}
|
||||||
mod_->AddAliasType(alias->AsAlias());
|
mod_->AddConstructedType(struct_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VertexPullingTransform::AddVertexPullingPreamble(
|
void VertexPullingTransform::AddVertexPullingPreamble(
|
||||||
|
|
|
@ -136,8 +136,7 @@ TEST_F(VertexPullingTransformTest, OneAttribute) {
|
||||||
EXPECT_TRUE(transform()->Run());
|
EXPECT_TRUE(transform()->Run());
|
||||||
|
|
||||||
EXPECT_EQ(R"(Module{
|
EXPECT_EQ(R"(Module{
|
||||||
TintVertexData -> __struct_TintVertexData
|
TintVertexData Struct{
|
||||||
Struct{
|
|
||||||
[[block]]
|
[[block]]
|
||||||
StructMember{[[ offset 0 ]] _tint_vertex_data: __array__u32_stride_4}
|
StructMember{[[ offset 0 ]] _tint_vertex_data: __array__u32_stride_4}
|
||||||
}
|
}
|
||||||
|
@ -161,7 +160,7 @@ TEST_F(VertexPullingTransformTest, OneAttribute) {
|
||||||
}
|
}
|
||||||
_tint_pulling_vertex_buffer_0
|
_tint_pulling_vertex_buffer_0
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__alias_TintVertexData__struct_TintVertexData
|
__struct_TintVertexData
|
||||||
}
|
}
|
||||||
Function main -> __void
|
Function main -> __void
|
||||||
StageDecoration{vertex}
|
StageDecoration{vertex}
|
||||||
|
@ -222,8 +221,7 @@ TEST_F(VertexPullingTransformTest, OneInstancedAttribute) {
|
||||||
EXPECT_TRUE(transform()->Run());
|
EXPECT_TRUE(transform()->Run());
|
||||||
|
|
||||||
EXPECT_EQ(R"(Module{
|
EXPECT_EQ(R"(Module{
|
||||||
TintVertexData -> __struct_TintVertexData
|
TintVertexData Struct{
|
||||||
Struct{
|
|
||||||
[[block]]
|
[[block]]
|
||||||
StructMember{[[ offset 0 ]] _tint_vertex_data: __array__u32_stride_4}
|
StructMember{[[ offset 0 ]] _tint_vertex_data: __array__u32_stride_4}
|
||||||
}
|
}
|
||||||
|
@ -247,7 +245,7 @@ TEST_F(VertexPullingTransformTest, OneInstancedAttribute) {
|
||||||
}
|
}
|
||||||
_tint_pulling_vertex_buffer_0
|
_tint_pulling_vertex_buffer_0
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__alias_TintVertexData__struct_TintVertexData
|
__struct_TintVertexData
|
||||||
}
|
}
|
||||||
Function main -> __void
|
Function main -> __void
|
||||||
StageDecoration{vertex}
|
StageDecoration{vertex}
|
||||||
|
@ -308,8 +306,7 @@ TEST_F(VertexPullingTransformTest, OneAttributeDifferentOutputSet) {
|
||||||
EXPECT_TRUE(transform()->Run());
|
EXPECT_TRUE(transform()->Run());
|
||||||
|
|
||||||
EXPECT_EQ(R"(Module{
|
EXPECT_EQ(R"(Module{
|
||||||
TintVertexData -> __struct_TintVertexData
|
TintVertexData Struct{
|
||||||
Struct{
|
|
||||||
[[block]]
|
[[block]]
|
||||||
StructMember{[[ offset 0 ]] _tint_vertex_data: __array__u32_stride_4}
|
StructMember{[[ offset 0 ]] _tint_vertex_data: __array__u32_stride_4}
|
||||||
}
|
}
|
||||||
|
@ -333,7 +330,7 @@ TEST_F(VertexPullingTransformTest, OneAttributeDifferentOutputSet) {
|
||||||
}
|
}
|
||||||
_tint_pulling_vertex_buffer_0
|
_tint_pulling_vertex_buffer_0
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__alias_TintVertexData__struct_TintVertexData
|
__struct_TintVertexData
|
||||||
}
|
}
|
||||||
Function main -> __void
|
Function main -> __void
|
||||||
StageDecoration{vertex}
|
StageDecoration{vertex}
|
||||||
|
@ -424,8 +421,7 @@ TEST_F(VertexPullingTransformTest, ExistingVertexIndexAndInstanceIndex) {
|
||||||
EXPECT_TRUE(transform()->Run());
|
EXPECT_TRUE(transform()->Run());
|
||||||
|
|
||||||
EXPECT_EQ(R"(Module{
|
EXPECT_EQ(R"(Module{
|
||||||
TintVertexData -> __struct_TintVertexData
|
TintVertexData Struct{
|
||||||
Struct{
|
|
||||||
[[block]]
|
[[block]]
|
||||||
StructMember{[[ offset 0 ]] _tint_vertex_data: __array__u32_stride_4}
|
StructMember{[[ offset 0 ]] _tint_vertex_data: __array__u32_stride_4}
|
||||||
}
|
}
|
||||||
|
@ -462,7 +458,7 @@ TEST_F(VertexPullingTransformTest, ExistingVertexIndexAndInstanceIndex) {
|
||||||
}
|
}
|
||||||
_tint_pulling_vertex_buffer_0
|
_tint_pulling_vertex_buffer_0
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__alias_TintVertexData__struct_TintVertexData
|
__struct_TintVertexData
|
||||||
}
|
}
|
||||||
DecoratedVariable{
|
DecoratedVariable{
|
||||||
Decorations{
|
Decorations{
|
||||||
|
@ -471,7 +467,7 @@ TEST_F(VertexPullingTransformTest, ExistingVertexIndexAndInstanceIndex) {
|
||||||
}
|
}
|
||||||
_tint_pulling_vertex_buffer_1
|
_tint_pulling_vertex_buffer_1
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__alias_TintVertexData__struct_TintVertexData
|
__struct_TintVertexData
|
||||||
}
|
}
|
||||||
Function main -> __void
|
Function main -> __void
|
||||||
StageDecoration{vertex}
|
StageDecoration{vertex}
|
||||||
|
@ -565,8 +561,7 @@ TEST_F(VertexPullingTransformTest, TwoAttributesSameBuffer) {
|
||||||
EXPECT_TRUE(transform()->Run());
|
EXPECT_TRUE(transform()->Run());
|
||||||
|
|
||||||
EXPECT_EQ(R"(Module{
|
EXPECT_EQ(R"(Module{
|
||||||
TintVertexData -> __struct_TintVertexData
|
TintVertexData Struct{
|
||||||
Struct{
|
|
||||||
[[block]]
|
[[block]]
|
||||||
StructMember{[[ offset 0 ]] _tint_vertex_data: __array__u32_stride_4}
|
StructMember{[[ offset 0 ]] _tint_vertex_data: __array__u32_stride_4}
|
||||||
}
|
}
|
||||||
|
@ -595,7 +590,7 @@ TEST_F(VertexPullingTransformTest, TwoAttributesSameBuffer) {
|
||||||
}
|
}
|
||||||
_tint_pulling_vertex_buffer_0
|
_tint_pulling_vertex_buffer_0
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__alias_TintVertexData__struct_TintVertexData
|
__struct_TintVertexData
|
||||||
}
|
}
|
||||||
Function main -> __void
|
Function main -> __void
|
||||||
StageDecoration{vertex}
|
StageDecoration{vertex}
|
||||||
|
@ -751,8 +746,7 @@ TEST_F(VertexPullingTransformTest, FloatVectorAttributes) {
|
||||||
EXPECT_TRUE(transform()->Run());
|
EXPECT_TRUE(transform()->Run());
|
||||||
|
|
||||||
EXPECT_EQ(R"(Module{
|
EXPECT_EQ(R"(Module{
|
||||||
TintVertexData -> __struct_TintVertexData
|
TintVertexData Struct{
|
||||||
Struct{
|
|
||||||
[[block]]
|
[[block]]
|
||||||
StructMember{[[ offset 0 ]] _tint_vertex_data: __array__u32_stride_4}
|
StructMember{[[ offset 0 ]] _tint_vertex_data: __array__u32_stride_4}
|
||||||
}
|
}
|
||||||
|
@ -786,7 +780,7 @@ TEST_F(VertexPullingTransformTest, FloatVectorAttributes) {
|
||||||
}
|
}
|
||||||
_tint_pulling_vertex_buffer_0
|
_tint_pulling_vertex_buffer_0
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__alias_TintVertexData__struct_TintVertexData
|
__struct_TintVertexData
|
||||||
}
|
}
|
||||||
DecoratedVariable{
|
DecoratedVariable{
|
||||||
Decorations{
|
Decorations{
|
||||||
|
@ -795,7 +789,7 @@ TEST_F(VertexPullingTransformTest, FloatVectorAttributes) {
|
||||||
}
|
}
|
||||||
_tint_pulling_vertex_buffer_1
|
_tint_pulling_vertex_buffer_1
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__alias_TintVertexData__struct_TintVertexData
|
__struct_TintVertexData
|
||||||
}
|
}
|
||||||
DecoratedVariable{
|
DecoratedVariable{
|
||||||
Decorations{
|
Decorations{
|
||||||
|
@ -804,7 +798,7 @@ TEST_F(VertexPullingTransformTest, FloatVectorAttributes) {
|
||||||
}
|
}
|
||||||
_tint_pulling_vertex_buffer_2
|
_tint_pulling_vertex_buffer_2
|
||||||
storage_buffer
|
storage_buffer
|
||||||
__alias_TintVertexData__struct_TintVertexData
|
__struct_TintVertexData
|
||||||
}
|
}
|
||||||
Function main -> __void
|
Function main -> __void
|
||||||
StageDecoration{vertex}
|
StageDecoration{vertex}
|
||||||
|
|
|
@ -404,7 +404,7 @@ TEST_F(ValidateControlBlockTest, SwitchCaseAlias_Pass) {
|
||||||
block->append(
|
block->append(
|
||||||
std::make_unique<ast::SwitchStatement>(std::move(cond), std::move(body)));
|
std::make_unique<ast::SwitchStatement>(std::move(cond), std::move(body)));
|
||||||
|
|
||||||
mod()->AddAliasType(&my_int);
|
mod()->AddConstructedType(&my_int);
|
||||||
|
|
||||||
EXPECT_TRUE(td()->DetermineStatements(block.get())) << td()->error();
|
EXPECT_TRUE(td()->DetermineStatements(block.get())) << td()->error();
|
||||||
EXPECT_TRUE(v()->ValidateStatements(block.get())) << v()->error();
|
EXPECT_TRUE(v()->ValidateStatements(block.get())) << v()->error();
|
||||||
|
|
|
@ -113,12 +113,12 @@ bool GeneratorImpl::Generate(std::ostream& out) {
|
||||||
register_global(global.get());
|
register_global(global.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto* const alias : module_->alias_types()) {
|
for (auto* const ty : module_->constructed_types()) {
|
||||||
if (!EmitAliasType(out, alias)) {
|
if (!EmitConstructedType(out, ty)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!module_->alias_types().empty()) {
|
if (!module_->constructed_types().empty()) {
|
||||||
out << std::endl;
|
out << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,21 +196,33 @@ std::string GeneratorImpl::current_ep_var_name(VarType type) {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GeneratorImpl::EmitAliasType(std::ostream& out,
|
bool GeneratorImpl::EmitConstructedType(std::ostream& out,
|
||||||
const ast::type::AliasType* alias) {
|
const ast::type::Type* ty) {
|
||||||
make_indent(out);
|
make_indent(out);
|
||||||
|
|
||||||
if (alias->type()->IsStruct()) {
|
if (ty->IsAlias()) {
|
||||||
if (!EmitType(out, alias->type(), namer_.NameFor(alias->name()))) {
|
auto* alias = ty->AsAlias();
|
||||||
|
// This will go away once type_alias doesn't accept anonymous
|
||||||
|
// structs anymore
|
||||||
|
if (alias->type()->IsStruct() &&
|
||||||
|
alias->type()->AsStruct()->name() == alias->name()) {
|
||||||
|
if (!EmitStructType(out, alias->type()->AsStruct())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out << "typedef ";
|
||||||
|
if (!EmitType(out, alias->type(), "")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out << " " << namer_.NameFor(alias->name()) << ";" << std::endl;
|
||||||
|
}
|
||||||
|
} else if (ty->IsStruct()) {
|
||||||
|
if (!EmitStructType(out, ty->AsStruct())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out << ";" << std::endl;
|
|
||||||
} else {
|
} else {
|
||||||
out << "typedef ";
|
error_ = "unknown constructed type: " + ty->type_name();
|
||||||
if (!EmitType(out, alias->type(), "")) {
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
out << " " << namer_.NameFor(alias->name()) << ";" << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1942,36 +1954,7 @@ bool GeneratorImpl::EmitType(std::ostream& out,
|
||||||
error_ = "pointers not supported in HLSL";
|
error_ = "pointers not supported in HLSL";
|
||||||
return false;
|
return false;
|
||||||
} else if (type->IsStruct()) {
|
} else if (type->IsStruct()) {
|
||||||
auto* str = type->AsStruct()->impl();
|
out << type->AsStruct()->name();
|
||||||
// TODO(dsinclair): Block decoration?
|
|
||||||
// if (str->decoration() != ast::StructDecoration::kNone) {
|
|
||||||
// }
|
|
||||||
out << "struct";
|
|
||||||
// If a name was provided for the struct emit it.
|
|
||||||
if (!name.empty()) {
|
|
||||||
out << " " << name;
|
|
||||||
}
|
|
||||||
out << " {" << std::endl;
|
|
||||||
|
|
||||||
increment_indent();
|
|
||||||
for (const auto& mem : str->members()) {
|
|
||||||
make_indent(out);
|
|
||||||
// TODO(dsinclair): Handle [[offset]] annotation on structs
|
|
||||||
// https://bugs.chromium.org/p/tint/issues/detail?id=184
|
|
||||||
|
|
||||||
if (!EmitType(out, mem->type(), mem->name())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Array member name will be output with the type
|
|
||||||
if (!mem->type()->IsArray()) {
|
|
||||||
out << " " << namer_.NameFor(mem->name());
|
|
||||||
}
|
|
||||||
out << ";" << std::endl;
|
|
||||||
}
|
|
||||||
decrement_indent();
|
|
||||||
make_indent(out);
|
|
||||||
|
|
||||||
out << "}";
|
|
||||||
} else if (type->IsU32()) {
|
} else if (type->IsU32()) {
|
||||||
out << "uint";
|
out << "uint";
|
||||||
} else if (type->IsVector()) {
|
} else if (type->IsVector()) {
|
||||||
|
@ -1991,6 +1974,36 @@ bool GeneratorImpl::EmitType(std::ostream& out,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GeneratorImpl::EmitStructType(std::ostream& out,
|
||||||
|
const ast::type::StructType* str) {
|
||||||
|
// TODO(dsinclair): Block decoration?
|
||||||
|
// if (str->impl()->decoration() != ast::StructDecoration::kNone) {
|
||||||
|
// }
|
||||||
|
out << "struct " << str->name() << " {" << std::endl;
|
||||||
|
|
||||||
|
increment_indent();
|
||||||
|
for (const auto& mem : str->impl()->members()) {
|
||||||
|
make_indent(out);
|
||||||
|
// TODO(dsinclair): Handle [[offset]] annotation on structs
|
||||||
|
// https://bugs.chromium.org/p/tint/issues/detail?id=184
|
||||||
|
|
||||||
|
if (!EmitType(out, mem->type(), mem->name())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Array member name will be output with the type
|
||||||
|
if (!mem->type()->IsArray()) {
|
||||||
|
out << " " << namer_.NameFor(mem->name());
|
||||||
|
}
|
||||||
|
out << ";" << std::endl;
|
||||||
|
}
|
||||||
|
decrement_indent();
|
||||||
|
make_indent(out);
|
||||||
|
|
||||||
|
out << "};" << std::endl;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool GeneratorImpl::EmitUnaryOp(std::ostream& pre,
|
bool GeneratorImpl::EmitUnaryOp(std::ostream& pre,
|
||||||
std::ostream& out,
|
std::ostream& out,
|
||||||
ast::UnaryOpExpression* expr) {
|
ast::UnaryOpExpression* expr) {
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "src/ast/literal.h"
|
#include "src/ast/literal.h"
|
||||||
#include "src/ast/module.h"
|
#include "src/ast/module.h"
|
||||||
#include "src/ast/scalar_constructor_expression.h"
|
#include "src/ast/scalar_constructor_expression.h"
|
||||||
|
#include "src/ast/type/struct_type.h"
|
||||||
#include "src/ast/type_constructor_expression.h"
|
#include "src/ast/type_constructor_expression.h"
|
||||||
#include "src/scope_stack.h"
|
#include "src/scope_stack.h"
|
||||||
#include "src/writer/hlsl/namer.h"
|
#include "src/writer/hlsl/namer.h"
|
||||||
|
@ -60,11 +61,11 @@ class GeneratorImpl {
|
||||||
/// @returns true on successful generation; false otherwise
|
/// @returns true on successful generation; false otherwise
|
||||||
bool Generate(std::ostream& out);
|
bool Generate(std::ostream& out);
|
||||||
|
|
||||||
/// Handles generating an alias
|
/// Handles generating a constructed type
|
||||||
/// @param out the output stream
|
/// @param out the output stream
|
||||||
/// @param alias the alias to generate
|
/// @param ty the constructed type to generate
|
||||||
/// @returns true if the alias was emitted
|
/// @returns true if the constructed type was emitted
|
||||||
bool EmitAliasType(std::ostream& out, const ast::type::AliasType* alias);
|
bool EmitConstructedType(std::ostream& out, const ast::type::Type* ty);
|
||||||
/// Handles an array accessor expression
|
/// Handles an array accessor expression
|
||||||
/// @param pre the preamble for the expression stream
|
/// @param pre the preamble for the expression stream
|
||||||
/// @param out the output of the expression stream
|
/// @param out the output of the expression stream
|
||||||
|
@ -260,6 +261,11 @@ class GeneratorImpl {
|
||||||
bool EmitType(std::ostream& out,
|
bool EmitType(std::ostream& out,
|
||||||
ast::type::Type* type,
|
ast::type::Type* type,
|
||||||
const std::string& name);
|
const std::string& name);
|
||||||
|
/// Handles generating a structure declaration
|
||||||
|
/// @param out the output stream
|
||||||
|
/// @param ty the struct to generate
|
||||||
|
/// @returns true if the struct is emitted
|
||||||
|
bool EmitStructType(std::ostream& out, const ast::type::StructType* ty);
|
||||||
/// Handles a unary op expression
|
/// Handles a unary op expression
|
||||||
/// @param pre the preamble for the expression stream
|
/// @param pre the preamble for the expression stream
|
||||||
/// @param out the output of the expression stream
|
/// @param out the output of the expression stream
|
||||||
|
|
|
@ -33,7 +33,7 @@ TEST_F(HlslGeneratorImplTest_AliasType, EmitAliasType_F32) {
|
||||||
ast::type::F32Type f32;
|
ast::type::F32Type f32;
|
||||||
ast::type::AliasType alias("a", &f32);
|
ast::type::AliasType alias("a", &f32);
|
||||||
|
|
||||||
ASSERT_TRUE(gen().EmitAliasType(out(), &alias)) << gen().error();
|
ASSERT_TRUE(gen().EmitConstructedType(out(), &alias)) << gen().error();
|
||||||
EXPECT_EQ(result(), R"(typedef float a;
|
EXPECT_EQ(result(), R"(typedef float a;
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ TEST_F(HlslGeneratorImplTest_AliasType, EmitAliasType_NameCollision) {
|
||||||
ast::type::F32Type f32;
|
ast::type::F32Type f32;
|
||||||
ast::type::AliasType alias("float", &f32);
|
ast::type::AliasType alias("float", &f32);
|
||||||
|
|
||||||
ASSERT_TRUE(gen().EmitAliasType(out(), &alias)) << gen().error();
|
ASSERT_TRUE(gen().EmitConstructedType(out(), &alias)) << gen().error();
|
||||||
EXPECT_EQ(result(), R"(typedef float float_tint_0;
|
EXPECT_EQ(result(), R"(typedef float float_tint_0;
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ TEST_F(HlslGeneratorImplTest_AliasType, EmitAliasType_Struct) {
|
||||||
|
|
||||||
ast::Module m;
|
ast::Module m;
|
||||||
GeneratorImpl g(&m);
|
GeneratorImpl g(&m);
|
||||||
ASSERT_TRUE(gen().EmitAliasType(out(), &alias)) << gen().error();
|
ASSERT_TRUE(gen().EmitConstructedType(out(), &alias)) << gen().error();
|
||||||
EXPECT_EQ(result(), R"(struct a {
|
EXPECT_EQ(result(), R"(struct a {
|
||||||
float a;
|
float a;
|
||||||
int b;
|
int b;
|
||||||
|
|
|
@ -316,7 +316,7 @@ TEST_F(HlslGeneratorImplTest_Function,
|
||||||
std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
|
std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
|
||||||
"uniforms", ast::StorageClass::kUniform, alias.get()));
|
"uniforms", ast::StorageClass::kUniform, alias.get()));
|
||||||
|
|
||||||
mod()->AddAliasType(alias.get());
|
mod()->AddConstructedType(alias.get());
|
||||||
|
|
||||||
ast::VariableDecorationList decos;
|
ast::VariableDecorationList decos;
|
||||||
decos.push_back(std::make_unique<ast::BindingDecoration>(0));
|
decos.push_back(std::make_unique<ast::BindingDecoration>(0));
|
||||||
|
|
|
@ -163,6 +163,32 @@ TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Pointer) {
|
||||||
EXPECT_EQ(result(), "float*");
|
EXPECT_EQ(result(), "float*");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(HlslGeneratorImplTest_Type, EmitType_StructDecl) {
|
||||||
|
ast::type::I32Type i32;
|
||||||
|
ast::type::F32Type f32;
|
||||||
|
|
||||||
|
ast::StructMemberList members;
|
||||||
|
members.push_back(std::make_unique<ast::StructMember>(
|
||||||
|
"a", &i32, ast::StructMemberDecorationList{}));
|
||||||
|
|
||||||
|
ast::StructMemberDecorationList b_deco;
|
||||||
|
b_deco.push_back(std::make_unique<ast::StructMemberOffsetDecoration>(4));
|
||||||
|
members.push_back(
|
||||||
|
std::make_unique<ast::StructMember>("b", &f32, std::move(b_deco)));
|
||||||
|
|
||||||
|
auto str = std::make_unique<ast::Struct>();
|
||||||
|
str->set_members(std::move(members));
|
||||||
|
|
||||||
|
ast::type::StructType s("S", std::move(str));
|
||||||
|
|
||||||
|
ASSERT_TRUE(gen().EmitStructType(out(), &s)) << gen().error();
|
||||||
|
EXPECT_EQ(result(), R"(struct S {
|
||||||
|
int a;
|
||||||
|
float b;
|
||||||
|
};
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct) {
|
TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct) {
|
||||||
ast::type::I32Type i32;
|
ast::type::I32Type i32;
|
||||||
ast::type::F32Type f32;
|
ast::type::F32Type f32;
|
||||||
|
@ -182,10 +208,7 @@ TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct) {
|
||||||
ast::type::StructType s("S", std::move(str));
|
ast::type::StructType s("S", std::move(str));
|
||||||
|
|
||||||
ASSERT_TRUE(gen().EmitType(out(), &s, "")) << gen().error();
|
ASSERT_TRUE(gen().EmitType(out(), &s, "")) << gen().error();
|
||||||
EXPECT_EQ(result(), R"(struct {
|
EXPECT_EQ(result(), "S");
|
||||||
int a;
|
|
||||||
float b;
|
|
||||||
})");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Struct_InjectPadding) {
|
TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Struct_InjectPadding) {
|
||||||
|
@ -240,11 +263,12 @@ TEST_F(HlslGeneratorImplTest_Type, EmitType_Struct_NameCollision) {
|
||||||
|
|
||||||
ast::type::StructType s("S", std::move(str));
|
ast::type::StructType s("S", std::move(str));
|
||||||
|
|
||||||
ASSERT_TRUE(gen().EmitType(out(), &s, "")) << gen().error();
|
ASSERT_TRUE(gen().EmitStructType(out(), &s)) << gen().error();
|
||||||
EXPECT_EQ(result(), R"(struct {
|
EXPECT_EQ(result(), R"(struct S {
|
||||||
int double_tint_0;
|
int double_tint_0;
|
||||||
float float_tint_0;
|
float float_tint_0;
|
||||||
})");
|
};
|
||||||
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(dsinclair): How to translate [[block]]
|
// TODO(dsinclair): How to translate [[block]]
|
||||||
|
@ -269,8 +293,8 @@ TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_Struct_WithDecoration) {
|
||||||
|
|
||||||
ast::type::StructType s("S", std::move(str));
|
ast::type::StructType s("S", std::move(str));
|
||||||
|
|
||||||
ASSERT_TRUE(gen().EmitType(out(), &s, "")) << gen().error();
|
ASSERT_TRUE(gen().EmitStructType(out(), &s)) << gen().error();
|
||||||
EXPECT_EQ(result(), R"(struct {
|
EXPECT_EQ(result(), R"(struct S {
|
||||||
int a;
|
int a;
|
||||||
float b;
|
float b;
|
||||||
})");
|
})");
|
||||||
|
|
|
@ -103,12 +103,12 @@ bool GeneratorImpl::Generate() {
|
||||||
global_variables_.set(global->name(), global.get());
|
global_variables_.set(global->name(), global.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto* const alias : module_->alias_types()) {
|
for (auto* const ty : module_->constructed_types()) {
|
||||||
if (!EmitAliasType(alias)) {
|
if (!EmitConstructedType(ty)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!module_->alias_types().empty()) {
|
if (!module_->constructed_types().empty()) {
|
||||||
out_ << std::endl;
|
out_ << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,13 +239,33 @@ uint32_t GeneratorImpl::calculate_alignment_size(ast::type::Type* type) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GeneratorImpl::EmitAliasType(const ast::type::AliasType* alias) {
|
bool GeneratorImpl::EmitConstructedType(const ast::type::Type* ty) {
|
||||||
make_indent();
|
make_indent();
|
||||||
out_ << "typedef ";
|
|
||||||
if (!EmitType(alias->type(), "")) {
|
if (ty->IsAlias()) {
|
||||||
|
auto* alias = ty->AsAlias();
|
||||||
|
|
||||||
|
// This will go away once type_alias does not accept anonymous structs
|
||||||
|
if (alias->type()->IsStruct() &&
|
||||||
|
alias->type()->AsStruct()->name() == alias->name()) {
|
||||||
|
if (!EmitStructType(alias->type()->AsStruct())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out_ << "typedef ";
|
||||||
|
if (!EmitType(alias->type(), "")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out_ << " " << namer_.NameFor(alias->name()) << ";" << std::endl;
|
||||||
|
}
|
||||||
|
} else if (ty->IsStruct()) {
|
||||||
|
if (!EmitStructType(ty->AsStruct())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error_ = "unknown alias type: " + ty->type_name();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out_ << " " << namer_.NameFor(alias->name()) << ";" << std::endl;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1701,54 +1721,10 @@ bool GeneratorImpl::EmitType(ast::type::Type* type, const std::string& name) {
|
||||||
}
|
}
|
||||||
out_ << "*";
|
out_ << "*";
|
||||||
} else if (type->IsStruct()) {
|
} else if (type->IsStruct()) {
|
||||||
auto* str = type->AsStruct()->impl();
|
// The struct type emits as just the name. The declaration would be emitted
|
||||||
// TODO(dsinclair): Block decoration?
|
// as part of emitting the constructed types.
|
||||||
// if (str->decoration() != ast::StructDecoration::kNone) {
|
out_ << type->AsStruct()->name();
|
||||||
// }
|
|
||||||
out_ << "struct {" << std::endl;
|
|
||||||
|
|
||||||
increment_indent();
|
|
||||||
uint32_t current_offset = 0;
|
|
||||||
uint32_t pad_count = 0;
|
|
||||||
for (const auto& mem : str->members()) {
|
|
||||||
make_indent();
|
|
||||||
for (const auto& deco : mem->decorations()) {
|
|
||||||
if (deco->IsOffset()) {
|
|
||||||
uint32_t offset = deco->AsOffset()->offset();
|
|
||||||
if (offset != current_offset) {
|
|
||||||
out_ << "int8_t pad_" << pad_count << "["
|
|
||||||
<< (offset - current_offset) << "];" << std::endl;
|
|
||||||
pad_count++;
|
|
||||||
make_indent();
|
|
||||||
}
|
|
||||||
current_offset = offset;
|
|
||||||
} else {
|
|
||||||
error_ = "unsupported member decoration: " + deco->to_str();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!EmitType(mem->type(), mem->name())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto size = calculate_alignment_size(mem->type());
|
|
||||||
if (size == 0) {
|
|
||||||
error_ =
|
|
||||||
"unable to calculate byte size for: " + mem->type()->type_name();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
current_offset += size;
|
|
||||||
|
|
||||||
// Array member name will be output with the type
|
|
||||||
if (!mem->type()->IsArray()) {
|
|
||||||
out_ << " " << namer_.NameFor(mem->name());
|
|
||||||
}
|
|
||||||
out_ << ";" << std::endl;
|
|
||||||
}
|
|
||||||
decrement_indent();
|
|
||||||
make_indent();
|
|
||||||
|
|
||||||
out_ << "}";
|
|
||||||
} else if (type->IsU32()) {
|
} else if (type->IsU32()) {
|
||||||
out_ << "uint";
|
out_ << "uint";
|
||||||
} else if (type->IsVector()) {
|
} else if (type->IsVector()) {
|
||||||
|
@ -1767,6 +1743,56 @@ bool GeneratorImpl::EmitType(ast::type::Type* type, const std::string& name) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GeneratorImpl::EmitStructType(const ast::type::StructType* str) {
|
||||||
|
// TODO(dsinclair): Block decoration?
|
||||||
|
// if (str->impl()->decoration() != ast::StructDecoration::kNone) {
|
||||||
|
// }
|
||||||
|
out_ << "struct " << str->name() << " {" << std::endl;
|
||||||
|
|
||||||
|
increment_indent();
|
||||||
|
uint32_t current_offset = 0;
|
||||||
|
uint32_t pad_count = 0;
|
||||||
|
for (const auto& mem : str->impl()->members()) {
|
||||||
|
make_indent();
|
||||||
|
for (const auto& deco : mem->decorations()) {
|
||||||
|
if (deco->IsOffset()) {
|
||||||
|
uint32_t offset = deco->AsOffset()->offset();
|
||||||
|
if (offset != current_offset) {
|
||||||
|
out_ << "int8_t pad_" << pad_count << "[" << (offset - current_offset)
|
||||||
|
<< "];" << std::endl;
|
||||||
|
pad_count++;
|
||||||
|
make_indent();
|
||||||
|
}
|
||||||
|
current_offset = offset;
|
||||||
|
} else {
|
||||||
|
error_ = "unsupported member decoration: " + deco->to_str();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EmitType(mem->type(), mem->name())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto size = calculate_alignment_size(mem->type());
|
||||||
|
if (size == 0) {
|
||||||
|
error_ = "unable to calculate byte size for: " + mem->type()->type_name();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
current_offset += size;
|
||||||
|
|
||||||
|
// Array member name will be output with the type
|
||||||
|
if (!mem->type()->IsArray()) {
|
||||||
|
out_ << " " << namer_.NameFor(mem->name());
|
||||||
|
}
|
||||||
|
out_ << ";" << std::endl;
|
||||||
|
}
|
||||||
|
decrement_indent();
|
||||||
|
make_indent();
|
||||||
|
|
||||||
|
out_ << "};" << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool GeneratorImpl::EmitUnaryOp(ast::UnaryOpExpression* expr) {
|
bool GeneratorImpl::EmitUnaryOp(ast::UnaryOpExpression* expr) {
|
||||||
switch (expr->op()) {
|
switch (expr->op()) {
|
||||||
case ast::UnaryOp::kNot:
|
case ast::UnaryOp::kNot:
|
||||||
|
|
|
@ -54,10 +54,10 @@ class GeneratorImpl : public TextGenerator {
|
||||||
/// @returns the largest alignment value
|
/// @returns the largest alignment value
|
||||||
uint32_t calculate_largest_alignment(ast::type::StructType* type);
|
uint32_t calculate_largest_alignment(ast::type::StructType* type);
|
||||||
|
|
||||||
/// Handles generating an alias
|
/// Handles generating a constructed
|
||||||
/// @param alias the alias to generate
|
/// @param ty the constructed type to generate
|
||||||
/// @returns true if the alias was emitted
|
/// @returns true if the constructed type was emitted
|
||||||
bool EmitAliasType(const ast::type::AliasType* alias);
|
bool EmitConstructedType(const ast::type::Type* ty);
|
||||||
/// Handles an array accessor expression
|
/// Handles an array accessor expression
|
||||||
/// @param expr the expression to emit
|
/// @param expr the expression to emit
|
||||||
/// @returns true if the array accessor was emitted
|
/// @returns true if the array accessor was emitted
|
||||||
|
@ -183,6 +183,10 @@ class GeneratorImpl : public TextGenerator {
|
||||||
/// @param name the name of the variable, only used for array emission
|
/// @param name the name of the variable, only used for array emission
|
||||||
/// @returns true if the type is emitted
|
/// @returns true if the type is emitted
|
||||||
bool EmitType(ast::type::Type* type, const std::string& name);
|
bool EmitType(ast::type::Type* type, const std::string& name);
|
||||||
|
/// Handles generating a struct declaration
|
||||||
|
/// @param str the struct to generate
|
||||||
|
/// @returns true if the struct is emitted
|
||||||
|
bool EmitStructType(const ast::type::StructType* str);
|
||||||
/// Handles emitting a type constructor
|
/// Handles emitting a type constructor
|
||||||
/// @param expr the type constructor expression
|
/// @param expr the type constructor expression
|
||||||
/// @returns true if the constructor is emitted
|
/// @returns true if the constructor is emitted
|
||||||
|
|
|
@ -30,29 +30,57 @@ namespace {
|
||||||
|
|
||||||
using MslGeneratorImplTest = testing::Test;
|
using MslGeneratorImplTest = testing::Test;
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, EmitAliasType_F32) {
|
TEST_F(MslGeneratorImplTest, EmitConstructedType_F32) {
|
||||||
ast::type::F32Type f32;
|
ast::type::F32Type f32;
|
||||||
ast::type::AliasType alias("a", &f32);
|
ast::type::AliasType alias("a", &f32);
|
||||||
|
|
||||||
ast::Module m;
|
ast::Module m;
|
||||||
GeneratorImpl g(&m);
|
GeneratorImpl g(&m);
|
||||||
ASSERT_TRUE(g.EmitAliasType(&alias)) << g.error();
|
ASSERT_TRUE(g.EmitConstructedType(&alias)) << g.error();
|
||||||
EXPECT_EQ(g.result(), R"(typedef float a;
|
EXPECT_EQ(g.result(), R"(typedef float a;
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, EmitAliasType_NameCollision) {
|
TEST_F(MslGeneratorImplTest, EmitConstructedType_NameCollision) {
|
||||||
ast::type::F32Type f32;
|
ast::type::F32Type f32;
|
||||||
ast::type::AliasType alias("float", &f32);
|
ast::type::AliasType alias("float", &f32);
|
||||||
|
|
||||||
ast::Module m;
|
ast::Module m;
|
||||||
GeneratorImpl g(&m);
|
GeneratorImpl g(&m);
|
||||||
ASSERT_TRUE(g.EmitAliasType(&alias)) << g.error();
|
ASSERT_TRUE(g.EmitConstructedType(&alias)) << g.error();
|
||||||
EXPECT_EQ(g.result(), R"(typedef float float_tint_0;
|
EXPECT_EQ(g.result(), R"(typedef float float_tint_0;
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, EmitAliasType_Struct) {
|
TEST_F(MslGeneratorImplTest, EmitConstructedType_Struct) {
|
||||||
|
ast::type::I32Type i32;
|
||||||
|
ast::type::F32Type f32;
|
||||||
|
|
||||||
|
ast::StructMemberList members;
|
||||||
|
members.push_back(std::make_unique<ast::StructMember>(
|
||||||
|
"a", &f32, ast::StructMemberDecorationList{}));
|
||||||
|
|
||||||
|
ast::StructMemberDecorationList b_deco;
|
||||||
|
b_deco.push_back(std::make_unique<ast::StructMemberOffsetDecoration>(4));
|
||||||
|
members.push_back(
|
||||||
|
std::make_unique<ast::StructMember>("b", &i32, std::move(b_deco)));
|
||||||
|
|
||||||
|
auto str = std::make_unique<ast::Struct>();
|
||||||
|
str->set_members(std::move(members));
|
||||||
|
|
||||||
|
ast::type::StructType s("a", std::move(str));
|
||||||
|
|
||||||
|
ast::Module m;
|
||||||
|
GeneratorImpl g(&m);
|
||||||
|
ASSERT_TRUE(g.EmitConstructedType(&s)) << g.error();
|
||||||
|
EXPECT_EQ(g.result(), R"(struct a {
|
||||||
|
float a;
|
||||||
|
int b;
|
||||||
|
};
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MslGeneratorImplTest, EmitConstructedType_AliasMatchStruct) {
|
||||||
ast::type::I32Type i32;
|
ast::type::I32Type i32;
|
||||||
ast::type::F32Type f32;
|
ast::type::F32Type f32;
|
||||||
|
|
||||||
|
@ -73,11 +101,37 @@ TEST_F(MslGeneratorImplTest, EmitAliasType_Struct) {
|
||||||
|
|
||||||
ast::Module m;
|
ast::Module m;
|
||||||
GeneratorImpl g(&m);
|
GeneratorImpl g(&m);
|
||||||
ASSERT_TRUE(g.EmitAliasType(&alias)) << g.error();
|
ASSERT_TRUE(g.EmitConstructedType(&alias)) << g.error();
|
||||||
EXPECT_EQ(g.result(), R"(typedef struct {
|
EXPECT_EQ(g.result(), R"(struct a {
|
||||||
float a;
|
float a;
|
||||||
int b;
|
int b;
|
||||||
} a;
|
};
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MslGeneratorImplTest, EmitConstructedType_AliasStructIdent) {
|
||||||
|
ast::type::I32Type i32;
|
||||||
|
ast::type::F32Type f32;
|
||||||
|
|
||||||
|
ast::StructMemberList members;
|
||||||
|
members.push_back(std::make_unique<ast::StructMember>(
|
||||||
|
"a", &f32, ast::StructMemberDecorationList{}));
|
||||||
|
|
||||||
|
ast::StructMemberDecorationList b_deco;
|
||||||
|
b_deco.push_back(std::make_unique<ast::StructMemberOffsetDecoration>(4));
|
||||||
|
members.push_back(
|
||||||
|
std::make_unique<ast::StructMember>("b", &i32, std::move(b_deco)));
|
||||||
|
|
||||||
|
auto str = std::make_unique<ast::Struct>();
|
||||||
|
str->set_members(std::move(members));
|
||||||
|
|
||||||
|
ast::type::StructType s("b", std::move(str));
|
||||||
|
ast::type::AliasType alias("a", &s);
|
||||||
|
|
||||||
|
ast::Module m;
|
||||||
|
GeneratorImpl g(&m);
|
||||||
|
ASSERT_TRUE(g.EmitConstructedType(&alias)) << g.error();
|
||||||
|
EXPECT_EQ(g.result(), R"(typedef b a;
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -213,10 +213,35 @@ TEST_F(MslGeneratorImplTest, EmitType_Struct) {
|
||||||
ast::Module m;
|
ast::Module m;
|
||||||
GeneratorImpl g(&m);
|
GeneratorImpl g(&m);
|
||||||
ASSERT_TRUE(g.EmitType(&s, "")) << g.error();
|
ASSERT_TRUE(g.EmitType(&s, "")) << g.error();
|
||||||
EXPECT_EQ(g.result(), R"(struct {
|
EXPECT_EQ(g.result(), "S");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(MslGeneratorImplTest, EmitType_StructDecl) {
|
||||||
|
ast::type::I32Type i32;
|
||||||
|
ast::type::F32Type f32;
|
||||||
|
|
||||||
|
ast::StructMemberList members;
|
||||||
|
members.push_back(std::make_unique<ast::StructMember>(
|
||||||
|
"a", &i32, ast::StructMemberDecorationList{}));
|
||||||
|
|
||||||
|
ast::StructMemberDecorationList b_deco;
|
||||||
|
b_deco.push_back(std::make_unique<ast::StructMemberOffsetDecoration>(4));
|
||||||
|
members.push_back(
|
||||||
|
std::make_unique<ast::StructMember>("b", &f32, std::move(b_deco)));
|
||||||
|
|
||||||
|
auto str = std::make_unique<ast::Struct>();
|
||||||
|
str->set_members(std::move(members));
|
||||||
|
|
||||||
|
ast::type::StructType s("S", std::move(str));
|
||||||
|
|
||||||
|
ast::Module m;
|
||||||
|
GeneratorImpl g(&m);
|
||||||
|
ASSERT_TRUE(g.EmitStructType(&s)) << g.error();
|
||||||
|
EXPECT_EQ(g.result(), R"(struct S {
|
||||||
int a;
|
int a;
|
||||||
float b;
|
float b;
|
||||||
})");
|
};
|
||||||
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, EmitType_Struct_InjectPadding) {
|
TEST_F(MslGeneratorImplTest, EmitType_Struct_InjectPadding) {
|
||||||
|
@ -245,15 +270,16 @@ TEST_F(MslGeneratorImplTest, EmitType_Struct_InjectPadding) {
|
||||||
|
|
||||||
ast::Module m;
|
ast::Module m;
|
||||||
GeneratorImpl g(&m);
|
GeneratorImpl g(&m);
|
||||||
ASSERT_TRUE(g.EmitType(&s, "")) << g.error();
|
ASSERT_TRUE(g.EmitStructType(&s)) << g.error();
|
||||||
EXPECT_EQ(g.result(), R"(struct {
|
EXPECT_EQ(g.result(), R"(struct S {
|
||||||
int8_t pad_0[4];
|
int8_t pad_0[4];
|
||||||
int a;
|
int a;
|
||||||
int8_t pad_1[24];
|
int8_t pad_1[24];
|
||||||
float b;
|
float b;
|
||||||
int8_t pad_2[92];
|
int8_t pad_2[92];
|
||||||
float c;
|
float c;
|
||||||
})");
|
};
|
||||||
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MslGeneratorImplTest, EmitType_Struct_NameCollision) {
|
TEST_F(MslGeneratorImplTest, EmitType_Struct_NameCollision) {
|
||||||
|
@ -275,11 +301,12 @@ TEST_F(MslGeneratorImplTest, EmitType_Struct_NameCollision) {
|
||||||
|
|
||||||
ast::Module m;
|
ast::Module m;
|
||||||
GeneratorImpl g(&m);
|
GeneratorImpl g(&m);
|
||||||
ASSERT_TRUE(g.EmitType(&s, "")) << g.error();
|
ASSERT_TRUE(g.EmitStructType(&s)) << g.error();
|
||||||
EXPECT_EQ(g.result(), R"(struct {
|
EXPECT_EQ(g.result(), R"(struct S {
|
||||||
int main_tint_0;
|
int main_tint_0;
|
||||||
float float_tint_0;
|
float float_tint_0;
|
||||||
})");
|
};
|
||||||
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(dsinclair): How to translate [[block]]
|
// TODO(dsinclair): How to translate [[block]]
|
||||||
|
|
|
@ -113,10 +113,7 @@ TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Struct) {
|
||||||
g.increment_indent();
|
g.increment_indent();
|
||||||
|
|
||||||
ASSERT_TRUE(g.EmitStatement(&stmt)) << g.error();
|
ASSERT_TRUE(g.EmitStatement(&stmt)) << g.error();
|
||||||
EXPECT_EQ(g.result(), R"( struct {
|
EXPECT_EQ(g.result(), R"( S a = {};
|
||||||
float a;
|
|
||||||
float b;
|
|
||||||
} a = {};
|
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2410,11 +2410,6 @@ bool Builder::GenerateArrayType(ast::type::ArrayType* ary,
|
||||||
{result, Operand::Int(elem_type), Operand::Int(len_id)});
|
{result, Operand::Int(elem_type), Operand::Int(len_id)});
|
||||||
}
|
}
|
||||||
|
|
||||||
// SPIR-V explicitly requires no array stride if the array contains a struct
|
|
||||||
// which has a Block decoration.
|
|
||||||
if (ary->type()->IsStruct() && ary->type()->AsStruct()->IsBlockDecorated()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (ary->has_array_stride()) {
|
if (ary->has_array_stride()) {
|
||||||
push_annot(spv::Op::OpDecorate,
|
push_annot(spv::Op::OpDecorate,
|
||||||
{Operand::Int(result_id), Operand::Int(SpvDecorationArrayStride),
|
{Operand::Int(result_id), Operand::Int(SpvDecorationArrayStride),
|
||||||
|
|
|
@ -76,12 +76,12 @@ GeneratorImpl::GeneratorImpl() = default;
|
||||||
GeneratorImpl::~GeneratorImpl() = default;
|
GeneratorImpl::~GeneratorImpl() = default;
|
||||||
|
|
||||||
bool GeneratorImpl::Generate(const ast::Module& module) {
|
bool GeneratorImpl::Generate(const ast::Module& module) {
|
||||||
for (auto* const alias : module.alias_types()) {
|
for (auto* const ty : module.constructed_types()) {
|
||||||
if (!EmitAliasType(alias)) {
|
if (!EmitConstructedType(ty)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!module.alias_types().empty())
|
if (!module.constructed_types().empty())
|
||||||
out_ << std::endl;
|
out_ << std::endl;
|
||||||
|
|
||||||
for (const auto& var : module.global_variables()) {
|
for (const auto& var : module.global_variables()) {
|
||||||
|
@ -112,13 +112,14 @@ bool GeneratorImpl::GenerateEntryPoint(const ast::Module& module,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(dsinclair): We always emit aliases even if they aren't strictly needed
|
// TODO(dsinclair): We always emit constructed types even if they aren't
|
||||||
for (auto* const alias : module.alias_types()) {
|
// strictly needed
|
||||||
if (!EmitAliasType(alias)) {
|
for (auto* const ty : module.constructed_types()) {
|
||||||
|
if (!EmitConstructedType(ty)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!module.alias_types().empty()) {
|
if (!module.constructed_types().empty()) {
|
||||||
out_ << std::endl;
|
out_ << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,13 +164,33 @@ bool GeneratorImpl::GenerateEntryPoint(const ast::Module& module,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GeneratorImpl::EmitAliasType(const ast::type::AliasType* alias) {
|
bool GeneratorImpl::EmitConstructedType(const ast::type::Type* ty) {
|
||||||
make_indent();
|
make_indent();
|
||||||
out_ << "type " << alias->name() << " = ";
|
if (ty->IsAlias()) {
|
||||||
if (!EmitType(alias->type())) {
|
auto* alias = ty->AsAlias();
|
||||||
|
// Emitting an alias to a struct where the names are the same means we can
|
||||||
|
// skip emitting the alias and just emit the struct. This will go away once
|
||||||
|
// the anonymous structs are removed from the type alias
|
||||||
|
if (alias->type()->IsStruct() &&
|
||||||
|
alias->type()->AsStruct()->name() == alias->name()) {
|
||||||
|
if (!EmitStructType(alias->type()->AsStruct())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out_ << "type " << alias->name() << " = ";
|
||||||
|
if (!EmitType(alias->type())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out_ << ";" << std::endl;
|
||||||
|
}
|
||||||
|
} else if (ty->IsStruct()) {
|
||||||
|
if (!EmitStructType(ty->AsStruct())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error_ = "unknown constructed type: " + ty->type_name();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out_ << ";" << std::endl;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -391,8 +412,7 @@ bool GeneratorImpl::EmitImageFormat(const ast::type::ImageFormat fmt) {
|
||||||
|
|
||||||
bool GeneratorImpl::EmitType(ast::type::Type* type) {
|
bool GeneratorImpl::EmitType(ast::type::Type* type) {
|
||||||
if (type->IsAlias()) {
|
if (type->IsAlias()) {
|
||||||
auto* alias = type->AsAlias();
|
out_ << type->AsAlias()->name();
|
||||||
out_ << alias->name();
|
|
||||||
} else if (type->IsArray()) {
|
} else if (type->IsArray()) {
|
||||||
auto* ary = type->AsArray();
|
auto* ary = type->AsArray();
|
||||||
|
|
||||||
|
@ -439,32 +459,9 @@ bool GeneratorImpl::EmitType(ast::type::Type* type) {
|
||||||
out_ << "_comparison";
|
out_ << "_comparison";
|
||||||
}
|
}
|
||||||
} else if (type->IsStruct()) {
|
} else if (type->IsStruct()) {
|
||||||
auto* str = type->AsStruct()->impl();
|
// The struct, as a type, is just the name. We should have already emitted
|
||||||
for (auto deco : str->decorations()) {
|
// the declaration through a call to |EmitStructType| earlier.
|
||||||
out_ << "[[" << deco << "]]" << std::endl;
|
out_ << type->AsStruct()->name();
|
||||||
}
|
|
||||||
out_ << "struct {" << std::endl;
|
|
||||||
|
|
||||||
increment_indent();
|
|
||||||
for (const auto& mem : str->members()) {
|
|
||||||
for (const auto& deco : mem->decorations()) {
|
|
||||||
make_indent();
|
|
||||||
|
|
||||||
// TODO(dsinclair): Split this out when we have more then one
|
|
||||||
assert(deco->IsOffset());
|
|
||||||
out_ << "[[offset(" << deco->AsOffset()->offset() << ")]]" << std::endl;
|
|
||||||
}
|
|
||||||
make_indent();
|
|
||||||
out_ << mem->name() << " : ";
|
|
||||||
if (!EmitType(mem->type())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
out_ << ";" << std::endl;
|
|
||||||
}
|
|
||||||
decrement_indent();
|
|
||||||
make_indent();
|
|
||||||
|
|
||||||
out_ << "}";
|
|
||||||
} else if (type->IsTexture()) {
|
} else if (type->IsTexture()) {
|
||||||
auto* texture = type->AsTexture();
|
auto* texture = type->AsTexture();
|
||||||
|
|
||||||
|
@ -563,6 +560,36 @@ bool GeneratorImpl::EmitType(ast::type::Type* type) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GeneratorImpl::EmitStructType(const ast::type::StructType* str) {
|
||||||
|
auto* impl = str->impl();
|
||||||
|
for (auto deco : impl->decorations()) {
|
||||||
|
out_ << "[[" << deco << "]]" << std::endl;
|
||||||
|
}
|
||||||
|
out_ << "struct " << str->name() << " {" << std::endl;
|
||||||
|
|
||||||
|
increment_indent();
|
||||||
|
for (const auto& mem : impl->members()) {
|
||||||
|
for (const auto& deco : mem->decorations()) {
|
||||||
|
make_indent();
|
||||||
|
|
||||||
|
// TODO(dsinclair): Split this out when we have more then one
|
||||||
|
assert(deco->IsOffset());
|
||||||
|
out_ << "[[offset(" << deco->AsOffset()->offset() << ")]]" << std::endl;
|
||||||
|
}
|
||||||
|
make_indent();
|
||||||
|
out_ << mem->name() << " : ";
|
||||||
|
if (!EmitType(mem->type())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out_ << ";" << std::endl;
|
||||||
|
}
|
||||||
|
decrement_indent();
|
||||||
|
make_indent();
|
||||||
|
|
||||||
|
out_ << "};" << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool GeneratorImpl::EmitVariable(ast::Variable* var) {
|
bool GeneratorImpl::EmitVariable(ast::Variable* var) {
|
||||||
make_indent();
|
make_indent();
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,8 @@
|
||||||
#include "src/ast/identifier_expression.h"
|
#include "src/ast/identifier_expression.h"
|
||||||
#include "src/ast/module.h"
|
#include "src/ast/module.h"
|
||||||
#include "src/ast/scalar_constructor_expression.h"
|
#include "src/ast/scalar_constructor_expression.h"
|
||||||
#include "src/ast/type/alias_type.h"
|
|
||||||
#include "src/ast/type/storage_texture_type.h"
|
#include "src/ast/type/storage_texture_type.h"
|
||||||
|
#include "src/ast/type/struct_type.h"
|
||||||
#include "src/ast/type/type.h"
|
#include "src/ast/type/type.h"
|
||||||
#include "src/ast/type_constructor_expression.h"
|
#include "src/ast/type_constructor_expression.h"
|
||||||
#include "src/ast/variable.h"
|
#include "src/ast/variable.h"
|
||||||
|
@ -55,10 +55,10 @@ class GeneratorImpl : public TextGenerator {
|
||||||
ast::PipelineStage stage,
|
ast::PipelineStage stage,
|
||||||
const std::string& name);
|
const std::string& name);
|
||||||
|
|
||||||
/// Handles generating an alias
|
/// Handles generating a constructed type
|
||||||
/// @param alias the alias to generate
|
/// @param ty the constructed to generate
|
||||||
/// @returns true if the alias was emitted
|
/// @returns true if the constructed was emitted
|
||||||
bool EmitAliasType(const ast::type::AliasType* alias);
|
bool EmitConstructedType(const ast::type::Type* ty);
|
||||||
/// Handles an array accessor expression
|
/// Handles an array accessor expression
|
||||||
/// @param expr the expression to emit
|
/// @param expr the expression to emit
|
||||||
/// @returns true if the array accessor was emitted
|
/// @returns true if the array accessor was emitted
|
||||||
|
@ -167,6 +167,10 @@ class GeneratorImpl : public TextGenerator {
|
||||||
/// @param type the type to generate
|
/// @param type the type to generate
|
||||||
/// @returns true if the type is emitted
|
/// @returns true if the type is emitted
|
||||||
bool EmitType(ast::type::Type* type);
|
bool EmitType(ast::type::Type* type);
|
||||||
|
/// Handles generating a struct declaration
|
||||||
|
/// @param str the struct
|
||||||
|
/// @returns true if the struct is emitted
|
||||||
|
bool EmitStructType(const ast::type::StructType* str);
|
||||||
/// Handles emitting an image format
|
/// Handles emitting an image format
|
||||||
/// @param fmt the format to generate
|
/// @param fmt the format to generate
|
||||||
/// @returns true if the format is emitted
|
/// @returns true if the format is emitted
|
||||||
|
|
|
@ -34,11 +34,43 @@ TEST_F(WgslGeneratorImplTest, EmitAliasType_F32) {
|
||||||
ast::type::AliasType alias("a", &f32);
|
ast::type::AliasType alias("a", &f32);
|
||||||
|
|
||||||
GeneratorImpl g;
|
GeneratorImpl g;
|
||||||
ASSERT_TRUE(g.EmitAliasType(&alias)) << g.error();
|
ASSERT_TRUE(g.EmitConstructedType(&alias)) << g.error();
|
||||||
EXPECT_EQ(g.result(), R"(type a = f32;
|
EXPECT_EQ(g.result(), R"(type a = f32;
|
||||||
)");
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(WgslGeneratorImplTest, EmitAliasType_Struct_Ident) {
|
||||||
|
ast::type::I32Type i32;
|
||||||
|
ast::type::F32Type f32;
|
||||||
|
|
||||||
|
ast::StructMemberList members;
|
||||||
|
members.push_back(std::make_unique<ast::StructMember>(
|
||||||
|
"a", &f32, ast::StructMemberDecorationList{}));
|
||||||
|
|
||||||
|
ast::StructMemberDecorationList b_deco;
|
||||||
|
b_deco.push_back(std::make_unique<ast::StructMemberOffsetDecoration>(4));
|
||||||
|
members.push_back(
|
||||||
|
std::make_unique<ast::StructMember>("b", &i32, std::move(b_deco)));
|
||||||
|
|
||||||
|
auto str = std::make_unique<ast::Struct>();
|
||||||
|
str->set_members(std::move(members));
|
||||||
|
|
||||||
|
ast::type::StructType s("A", std::move(str));
|
||||||
|
ast::type::AliasType alias("B", &s);
|
||||||
|
|
||||||
|
GeneratorImpl g;
|
||||||
|
ASSERT_TRUE(g.EmitConstructedType(&s)) << g.error();
|
||||||
|
ASSERT_TRUE(g.EmitConstructedType(&alias)) << g.error();
|
||||||
|
EXPECT_EQ(g.result(), R"(struct A {
|
||||||
|
a : f32;
|
||||||
|
[[offset(4)]]
|
||||||
|
b : i32;
|
||||||
|
};
|
||||||
|
type B = A;
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should go away once type_alias'd anonymous structs are removed.
|
||||||
TEST_F(WgslGeneratorImplTest, EmitAliasType_Struct) {
|
TEST_F(WgslGeneratorImplTest, EmitAliasType_Struct) {
|
||||||
ast::type::I32Type i32;
|
ast::type::I32Type i32;
|
||||||
ast::type::F32Type f32;
|
ast::type::F32Type f32;
|
||||||
|
@ -55,12 +87,12 @@ TEST_F(WgslGeneratorImplTest, EmitAliasType_Struct) {
|
||||||
auto str = std::make_unique<ast::Struct>();
|
auto str = std::make_unique<ast::Struct>();
|
||||||
str->set_members(std::move(members));
|
str->set_members(std::move(members));
|
||||||
|
|
||||||
ast::type::StructType s("a", std::move(str));
|
ast::type::StructType s("A", std::move(str));
|
||||||
ast::type::AliasType alias("a", &s);
|
ast::type::AliasType alias("A", &s);
|
||||||
|
|
||||||
GeneratorImpl g;
|
GeneratorImpl g;
|
||||||
ASSERT_TRUE(g.EmitAliasType(&alias)) << g.error();
|
ASSERT_TRUE(g.EmitConstructedType(&alias)) << g.error();
|
||||||
EXPECT_EQ(g.result(), R"(type a = struct {
|
EXPECT_EQ(g.result(), R"(struct A {
|
||||||
a : f32;
|
a : f32;
|
||||||
[[offset(4)]]
|
[[offset(4)]]
|
||||||
b : i32;
|
b : i32;
|
||||||
|
|
|
@ -159,11 +159,35 @@ TEST_F(WgslGeneratorImplTest, EmitType_Struct) {
|
||||||
|
|
||||||
GeneratorImpl g;
|
GeneratorImpl g;
|
||||||
ASSERT_TRUE(g.EmitType(&s)) << g.error();
|
ASSERT_TRUE(g.EmitType(&s)) << g.error();
|
||||||
EXPECT_EQ(g.result(), R"(struct {
|
EXPECT_EQ(g.result(), "S");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WgslGeneratorImplTest, EmitType_StructDecl) {
|
||||||
|
ast::type::I32Type i32;
|
||||||
|
ast::type::F32Type f32;
|
||||||
|
|
||||||
|
ast::StructMemberList members;
|
||||||
|
members.push_back(std::make_unique<ast::StructMember>(
|
||||||
|
"a", &i32, ast::StructMemberDecorationList{}));
|
||||||
|
|
||||||
|
ast::StructMemberDecorationList b_deco;
|
||||||
|
b_deco.push_back(std::make_unique<ast::StructMemberOffsetDecoration>(4));
|
||||||
|
members.push_back(
|
||||||
|
std::make_unique<ast::StructMember>("b", &f32, std::move(b_deco)));
|
||||||
|
|
||||||
|
auto str = std::make_unique<ast::Struct>();
|
||||||
|
str->set_members(std::move(members));
|
||||||
|
|
||||||
|
ast::type::StructType s("S", std::move(str));
|
||||||
|
|
||||||
|
GeneratorImpl g;
|
||||||
|
ASSERT_TRUE(g.EmitStructType(&s)) << g.error();
|
||||||
|
EXPECT_EQ(g.result(), R"(struct S {
|
||||||
a : i32;
|
a : i32;
|
||||||
[[offset(4)]]
|
[[offset(4)]]
|
||||||
b : f32;
|
b : f32;
|
||||||
})");
|
};
|
||||||
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WgslGeneratorImplTest, EmitType_Struct_WithDecoration) {
|
TEST_F(WgslGeneratorImplTest, EmitType_Struct_WithDecoration) {
|
||||||
|
@ -188,13 +212,14 @@ TEST_F(WgslGeneratorImplTest, EmitType_Struct_WithDecoration) {
|
||||||
ast::type::StructType s("S", std::move(str));
|
ast::type::StructType s("S", std::move(str));
|
||||||
|
|
||||||
GeneratorImpl g;
|
GeneratorImpl g;
|
||||||
ASSERT_TRUE(g.EmitType(&s)) << g.error();
|
ASSERT_TRUE(g.EmitStructType(&s)) << g.error();
|
||||||
EXPECT_EQ(g.result(), R"([[block]]
|
EXPECT_EQ(g.result(), R"([[block]]
|
||||||
struct {
|
struct S {
|
||||||
a : i32;
|
a : i32;
|
||||||
[[offset(4)]]
|
[[offset(4)]]
|
||||||
b : f32;
|
b : f32;
|
||||||
})");
|
};
|
||||||
|
)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WgslGeneratorImplTest, EmitType_U32) {
|
TEST_F(WgslGeneratorImplTest, EmitType_U32) {
|
||||||
|
|
|
@ -39,12 +39,12 @@ fn frag_main() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
# compute shader
|
# compute shader
|
||||||
type Particle = [[block]] struct {
|
[[block]] struct Particle {
|
||||||
[[offset(0)]] pos : vec2<f32>;
|
[[offset(0)]] pos : vec2<f32>;
|
||||||
[[offset(8)]] vel : vec2<f32>;
|
[[offset(8)]] vel : vec2<f32>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SimParams = [[block]] struct {
|
[[block]] struct SimParams {
|
||||||
[[offset(0)]] deltaT : f32;
|
[[offset(0)]] deltaT : f32;
|
||||||
[[offset(4)]] rule1Distance : f32;
|
[[offset(4)]] rule1Distance : f32;
|
||||||
[[offset(8)]] rule2Distance : f32;
|
[[offset(8)]] rule2Distance : f32;
|
||||||
|
@ -54,7 +54,7 @@ type SimParams = [[block]] struct {
|
||||||
[[offset(24)]] rule3Scale : f32;
|
[[offset(24)]] rule3Scale : f32;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Particles = [[block]] struct {
|
[[block]] struct Particles {
|
||||||
[[offset(0)]] particles : [[stride(16)]] array<Particle, 5>;
|
[[offset(0)]] particles : [[stride(16)]] array<Particle, 5>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
# Vertex shader
|
# Vertex shader
|
||||||
type Uniforms = [[block]] struct {
|
[[block]] struct Uniforms {
|
||||||
[[offset(0)]] modelViewProjectionMatrix : mat4x4<f32>;
|
[[offset(0)]] modelViewProjectionMatrix : mat4x4<f32>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue