Rename attribute values.
This CL renames the attributes to store `expr` instead of `value`. This closer matches what is stored. Bug: tint:1633 Change-Id: If1db34d1f9186afa111c394f18ed049dd2f56f91 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/101525 Reviewed-by: Ben Clayton <bclayton@google.com> Commit-Queue: Dan Sinclair <dsinclair@chromium.org> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
7ca82ac4d0
commit
c4e076ffe6
src/tint
ast
binding_attribute.ccbinding_attribute.hbinding_attribute_test.ccgroup_attribute.ccgroup_attribute.hgroup_attribute_test.ccid_attribute.ccid_attribute.hid_attribute_test.cclocation_attribute.cclocation_attribute.hlocation_attribute_test.ccstruct_member_align_attribute.ccstruct_member_align_attribute.hstruct_member_align_attribute_test.ccvariable_test.cc
reader
spirv
wgsl
resolver
writer/wgsl
|
@ -25,8 +25,8 @@ namespace tint::ast {
|
||||||
BindingAttribute::BindingAttribute(ProgramID pid,
|
BindingAttribute::BindingAttribute(ProgramID pid,
|
||||||
NodeID nid,
|
NodeID nid,
|
||||||
const Source& src,
|
const Source& src,
|
||||||
const ast::Expression* val)
|
const ast::Expression* exp)
|
||||||
: Base(pid, nid, src), value(val) {}
|
: Base(pid, nid, src), expr(exp) {}
|
||||||
|
|
||||||
BindingAttribute::~BindingAttribute() = default;
|
BindingAttribute::~BindingAttribute() = default;
|
||||||
|
|
||||||
|
@ -37,8 +37,8 @@ std::string BindingAttribute::Name() const {
|
||||||
const BindingAttribute* BindingAttribute::Clone(CloneContext* ctx) const {
|
const BindingAttribute* BindingAttribute::Clone(CloneContext* ctx) const {
|
||||||
// Clone arguments outside of create() call to have deterministic ordering
|
// Clone arguments outside of create() call to have deterministic ordering
|
||||||
auto src = ctx->Clone(source);
|
auto src = ctx->Clone(source);
|
||||||
auto* value_ = ctx->Clone(value);
|
auto* expr_ = ctx->Clone(expr);
|
||||||
return ctx->dst->create<BindingAttribute>(src, value_);
|
return ctx->dst->create<BindingAttribute>(src, expr_);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tint::ast
|
} // namespace tint::ast
|
||||||
|
|
|
@ -29,8 +29,8 @@ class BindingAttribute final : public Castable<BindingAttribute, Attribute> {
|
||||||
/// @param pid the identifier of the program that owns this node
|
/// @param pid the identifier of the program that owns this node
|
||||||
/// @param nid the unique node identifier
|
/// @param nid the unique node identifier
|
||||||
/// @param src the source of this node
|
/// @param src the source of this node
|
||||||
/// @param value the binding value expression
|
/// @param expr the binding expression
|
||||||
BindingAttribute(ProgramID pid, NodeID nid, const Source& src, const ast::Expression* value);
|
BindingAttribute(ProgramID pid, NodeID nid, const Source& src, const ast::Expression* expr);
|
||||||
~BindingAttribute() override;
|
~BindingAttribute() override;
|
||||||
|
|
||||||
/// @returns the WGSL name for the attribute
|
/// @returns the WGSL name for the attribute
|
||||||
|
@ -42,8 +42,8 @@ class BindingAttribute final : public Castable<BindingAttribute, Attribute> {
|
||||||
/// @return the newly cloned node
|
/// @return the newly cloned node
|
||||||
const BindingAttribute* Clone(CloneContext* ctx) const override;
|
const BindingAttribute* Clone(CloneContext* ctx) const override;
|
||||||
|
|
||||||
/// the binding value expression
|
/// the binding expression
|
||||||
const ast::Expression* const value;
|
const ast::Expression* const expr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tint::ast
|
} // namespace tint::ast
|
||||||
|
|
|
@ -22,7 +22,7 @@ using BindingAttributeTest = TestHelper;
|
||||||
|
|
||||||
TEST_F(BindingAttributeTest, Creation) {
|
TEST_F(BindingAttributeTest, Creation) {
|
||||||
auto* d = Binding(2_a);
|
auto* d = Binding(2_a);
|
||||||
EXPECT_TRUE(d->value->Is<IntLiteralExpression>());
|
EXPECT_TRUE(d->expr->Is<IntLiteralExpression>());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -25,8 +25,8 @@ namespace tint::ast {
|
||||||
GroupAttribute::GroupAttribute(ProgramID pid,
|
GroupAttribute::GroupAttribute(ProgramID pid,
|
||||||
NodeID nid,
|
NodeID nid,
|
||||||
const Source& src,
|
const Source& src,
|
||||||
const ast::Expression* val)
|
const ast::Expression* exp)
|
||||||
: Base(pid, nid, src), value(val) {}
|
: Base(pid, nid, src), expr(exp) {}
|
||||||
|
|
||||||
GroupAttribute::~GroupAttribute() = default;
|
GroupAttribute::~GroupAttribute() = default;
|
||||||
|
|
||||||
|
@ -37,8 +37,8 @@ std::string GroupAttribute::Name() const {
|
||||||
const GroupAttribute* GroupAttribute::Clone(CloneContext* ctx) const {
|
const GroupAttribute* GroupAttribute::Clone(CloneContext* ctx) const {
|
||||||
// Clone arguments outside of create() call to have deterministic ordering
|
// Clone arguments outside of create() call to have deterministic ordering
|
||||||
auto src = ctx->Clone(source);
|
auto src = ctx->Clone(source);
|
||||||
auto* value_ = ctx->Clone(value);
|
auto* expr_ = ctx->Clone(expr);
|
||||||
return ctx->dst->create<GroupAttribute>(src, value_);
|
return ctx->dst->create<GroupAttribute>(src, expr_);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tint::ast
|
} // namespace tint::ast
|
||||||
|
|
|
@ -29,8 +29,8 @@ class GroupAttribute final : public Castable<GroupAttribute, Attribute> {
|
||||||
/// @param pid the identifier of the program that owns this node
|
/// @param pid the identifier of the program that owns this node
|
||||||
/// @param nid the unique node identifier
|
/// @param nid the unique node identifier
|
||||||
/// @param src the source of this node
|
/// @param src the source of this node
|
||||||
/// @param value the group value expression
|
/// @param expr the group expression
|
||||||
GroupAttribute(ProgramID pid, NodeID nid, const Source& src, const ast::Expression* value);
|
GroupAttribute(ProgramID pid, NodeID nid, const Source& src, const ast::Expression* expr);
|
||||||
~GroupAttribute() override;
|
~GroupAttribute() override;
|
||||||
|
|
||||||
/// @returns the WGSL name for the attribute
|
/// @returns the WGSL name for the attribute
|
||||||
|
@ -42,8 +42,8 @@ class GroupAttribute final : public Castable<GroupAttribute, Attribute> {
|
||||||
/// @return the newly cloned node
|
/// @return the newly cloned node
|
||||||
const GroupAttribute* Clone(CloneContext* ctx) const override;
|
const GroupAttribute* Clone(CloneContext* ctx) const override;
|
||||||
|
|
||||||
/// The group value expression
|
/// The group expression
|
||||||
const ast::Expression* const value;
|
const ast::Expression* const expr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tint::ast
|
} // namespace tint::ast
|
||||||
|
|
|
@ -22,7 +22,7 @@ using GroupAttributeTest = TestHelper;
|
||||||
|
|
||||||
TEST_F(GroupAttributeTest, Creation) {
|
TEST_F(GroupAttributeTest, Creation) {
|
||||||
auto* d = Group(2_a);
|
auto* d = Group(2_a);
|
||||||
EXPECT_TRUE(d->value->Is<IntLiteralExpression>());
|
EXPECT_TRUE(d->expr->Is<IntLiteralExpression>());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -22,8 +22,8 @@ TINT_INSTANTIATE_TYPEINFO(tint::ast::IdAttribute);
|
||||||
|
|
||||||
namespace tint::ast {
|
namespace tint::ast {
|
||||||
|
|
||||||
IdAttribute::IdAttribute(ProgramID pid, NodeID nid, const Source& src, const ast::Expression* val)
|
IdAttribute::IdAttribute(ProgramID pid, NodeID nid, const Source& src, const ast::Expression* exp)
|
||||||
: Base(pid, nid, src), value(val) {}
|
: Base(pid, nid, src), expr(exp) {}
|
||||||
|
|
||||||
IdAttribute::~IdAttribute() = default;
|
IdAttribute::~IdAttribute() = default;
|
||||||
|
|
||||||
|
@ -34,8 +34,8 @@ std::string IdAttribute::Name() const {
|
||||||
const IdAttribute* IdAttribute::Clone(CloneContext* ctx) const {
|
const IdAttribute* IdAttribute::Clone(CloneContext* ctx) const {
|
||||||
// Clone arguments outside of create() call to have deterministic ordering
|
// Clone arguments outside of create() call to have deterministic ordering
|
||||||
auto src = ctx->Clone(source);
|
auto src = ctx->Clone(source);
|
||||||
auto* value_ = ctx->Clone(value);
|
auto* expr_ = ctx->Clone(expr);
|
||||||
return ctx->dst->create<IdAttribute>(src, value_);
|
return ctx->dst->create<IdAttribute>(src, expr_);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tint::ast
|
} // namespace tint::ast
|
||||||
|
|
|
@ -29,8 +29,8 @@ class IdAttribute final : public Castable<IdAttribute, Attribute> {
|
||||||
/// @param pid the identifier of the program that owns this node
|
/// @param pid the identifier of the program that owns this node
|
||||||
/// @param nid the unique node identifier
|
/// @param nid the unique node identifier
|
||||||
/// @param src the source of this node
|
/// @param src the source of this node
|
||||||
/// @param val the numeric id value expression
|
/// @param expr the numeric id expression
|
||||||
IdAttribute(ProgramID pid, NodeID nid, const Source& src, const ast::Expression* val);
|
IdAttribute(ProgramID pid, NodeID nid, const Source& src, const ast::Expression* expr);
|
||||||
~IdAttribute() override;
|
~IdAttribute() override;
|
||||||
|
|
||||||
/// @returns the WGSL name for the attribute
|
/// @returns the WGSL name for the attribute
|
||||||
|
@ -42,8 +42,8 @@ class IdAttribute final : public Castable<IdAttribute, Attribute> {
|
||||||
/// @return the newly cloned node
|
/// @return the newly cloned node
|
||||||
const IdAttribute* Clone(CloneContext* ctx) const override;
|
const IdAttribute* Clone(CloneContext* ctx) const override;
|
||||||
|
|
||||||
/// The id value expression
|
/// The id expression
|
||||||
const ast::Expression* const value;
|
const ast::Expression* const expr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tint::ast
|
} // namespace tint::ast
|
||||||
|
|
|
@ -24,7 +24,7 @@ using IdAttributeTest = TestHelper;
|
||||||
|
|
||||||
TEST_F(IdAttributeTest, Creation) {
|
TEST_F(IdAttributeTest, Creation) {
|
||||||
auto* d = Id(12_a);
|
auto* d = Id(12_a);
|
||||||
EXPECT_TRUE(d->value->Is<ast::IntLiteralExpression>());
|
EXPECT_TRUE(d->expr->Is<ast::IntLiteralExpression>());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -25,8 +25,8 @@ namespace tint::ast {
|
||||||
LocationAttribute::LocationAttribute(ProgramID pid,
|
LocationAttribute::LocationAttribute(ProgramID pid,
|
||||||
NodeID nid,
|
NodeID nid,
|
||||||
const Source& src,
|
const Source& src,
|
||||||
const ast::Expression* val)
|
const ast::Expression* exp)
|
||||||
: Base(pid, nid, src), value(val) {}
|
: Base(pid, nid, src), expr(exp) {}
|
||||||
|
|
||||||
LocationAttribute::~LocationAttribute() = default;
|
LocationAttribute::~LocationAttribute() = default;
|
||||||
|
|
||||||
|
@ -37,8 +37,8 @@ std::string LocationAttribute::Name() const {
|
||||||
const LocationAttribute* LocationAttribute::Clone(CloneContext* ctx) const {
|
const LocationAttribute* LocationAttribute::Clone(CloneContext* ctx) const {
|
||||||
// Clone arguments outside of create() call to have deterministic ordering
|
// Clone arguments outside of create() call to have deterministic ordering
|
||||||
auto src = ctx->Clone(source);
|
auto src = ctx->Clone(source);
|
||||||
auto value_ = ctx->Clone(value);
|
auto expr_ = ctx->Clone(expr);
|
||||||
return ctx->dst->create<LocationAttribute>(src, value_);
|
return ctx->dst->create<LocationAttribute>(src, expr_);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tint::ast
|
} // namespace tint::ast
|
||||||
|
|
|
@ -29,8 +29,8 @@ class LocationAttribute final : public Castable<LocationAttribute, Attribute> {
|
||||||
/// @param pid the identifier of the program that owns this node
|
/// @param pid the identifier of the program that owns this node
|
||||||
/// @param nid the unique node identifier
|
/// @param nid the unique node identifier
|
||||||
/// @param src the source of this node
|
/// @param src the source of this node
|
||||||
/// @param value the location value expression
|
/// @param expr the location expression
|
||||||
LocationAttribute(ProgramID pid, NodeID nid, const Source& src, const ast::Expression* value);
|
LocationAttribute(ProgramID pid, NodeID nid, const Source& src, const ast::Expression* expr);
|
||||||
~LocationAttribute() override;
|
~LocationAttribute() override;
|
||||||
|
|
||||||
/// @returns the WGSL name for the attribute
|
/// @returns the WGSL name for the attribute
|
||||||
|
@ -42,8 +42,8 @@ class LocationAttribute final : public Castable<LocationAttribute, Attribute> {
|
||||||
/// @return the newly cloned node
|
/// @return the newly cloned node
|
||||||
const LocationAttribute* Clone(CloneContext* ctx) const override;
|
const LocationAttribute* Clone(CloneContext* ctx) const override;
|
||||||
|
|
||||||
/// The location value
|
/// The location expression
|
||||||
const ast::Expression* const value;
|
const ast::Expression* const expr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tint::ast
|
} // namespace tint::ast
|
||||||
|
|
|
@ -22,7 +22,7 @@ using LocationAttributeTest = TestHelper;
|
||||||
|
|
||||||
TEST_F(LocationAttributeTest, Creation) {
|
TEST_F(LocationAttributeTest, Creation) {
|
||||||
auto* d = Location(2_a);
|
auto* d = Location(2_a);
|
||||||
EXPECT_TRUE(d->value->Is<IntLiteralExpression>());
|
EXPECT_TRUE(d->expr->Is<IntLiteralExpression>());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -27,7 +27,7 @@ StructMemberAlignAttribute::StructMemberAlignAttribute(ProgramID pid,
|
||||||
NodeID nid,
|
NodeID nid,
|
||||||
const Source& src,
|
const Source& src,
|
||||||
const ast::Expression* a)
|
const ast::Expression* a)
|
||||||
: Base(pid, nid, src), align(a) {}
|
: Base(pid, nid, src), expr(a) {}
|
||||||
|
|
||||||
StructMemberAlignAttribute::~StructMemberAlignAttribute() = default;
|
StructMemberAlignAttribute::~StructMemberAlignAttribute() = default;
|
||||||
|
|
||||||
|
@ -38,8 +38,8 @@ std::string StructMemberAlignAttribute::Name() const {
|
||||||
const StructMemberAlignAttribute* StructMemberAlignAttribute::Clone(CloneContext* ctx) const {
|
const StructMemberAlignAttribute* StructMemberAlignAttribute::Clone(CloneContext* ctx) const {
|
||||||
// Clone arguments outside of create() call to have deterministic ordering
|
// Clone arguments outside of create() call to have deterministic ordering
|
||||||
auto src = ctx->Clone(source);
|
auto src = ctx->Clone(source);
|
||||||
auto* align_ = ctx->Clone(align);
|
auto* expr_ = ctx->Clone(expr);
|
||||||
return ctx->dst->create<StructMemberAlignAttribute>(src, align_);
|
return ctx->dst->create<StructMemberAlignAttribute>(src, expr_);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tint::ast
|
} // namespace tint::ast
|
||||||
|
|
|
@ -30,7 +30,7 @@ class StructMemberAlignAttribute final : public Castable<StructMemberAlignAttrib
|
||||||
/// @param pid the identifier of the program that owns this node
|
/// @param pid the identifier of the program that owns this node
|
||||||
/// @param nid the unique node identifier
|
/// @param nid the unique node identifier
|
||||||
/// @param src the source of this node
|
/// @param src the source of this node
|
||||||
/// @param align the align value expression
|
/// @param align the align expression
|
||||||
StructMemberAlignAttribute(ProgramID pid,
|
StructMemberAlignAttribute(ProgramID pid,
|
||||||
NodeID nid,
|
NodeID nid,
|
||||||
const Source& src,
|
const Source& src,
|
||||||
|
@ -46,8 +46,8 @@ class StructMemberAlignAttribute final : public Castable<StructMemberAlignAttrib
|
||||||
/// @return the newly cloned node
|
/// @return the newly cloned node
|
||||||
const StructMemberAlignAttribute* Clone(CloneContext* ctx) const override;
|
const StructMemberAlignAttribute* Clone(CloneContext* ctx) const override;
|
||||||
|
|
||||||
/// The align value expression
|
/// The align expression
|
||||||
const ast::Expression* const align;
|
const ast::Expression* const expr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tint::ast
|
} // namespace tint::ast
|
||||||
|
|
|
@ -24,8 +24,8 @@ using StructMemberAlignAttributeTest = TestHelper;
|
||||||
TEST_F(StructMemberAlignAttributeTest, Creation) {
|
TEST_F(StructMemberAlignAttributeTest, Creation) {
|
||||||
auto* val = Expr("ident");
|
auto* val = Expr("ident");
|
||||||
auto* d = create<StructMemberAlignAttribute>(val);
|
auto* d = create<StructMemberAlignAttribute>(val);
|
||||||
EXPECT_EQ(val, d->align);
|
EXPECT_EQ(val, d->expr);
|
||||||
EXPECT_TRUE(d->align->Is<IdentifierExpression>());
|
EXPECT_TRUE(d->expr->Is<IdentifierExpression>());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -102,8 +102,8 @@ TEST_F(VariableTest, WithAttributes) {
|
||||||
|
|
||||||
auto* location = ast::GetAttribute<ast::LocationAttribute>(attributes);
|
auto* location = ast::GetAttribute<ast::LocationAttribute>(attributes);
|
||||||
ASSERT_NE(nullptr, location);
|
ASSERT_NE(nullptr, location);
|
||||||
ASSERT_NE(nullptr, location->value);
|
ASSERT_NE(nullptr, location->expr);
|
||||||
EXPECT_TRUE(location->value->Is<ast::IntLiteralExpression>());
|
EXPECT_TRUE(location->expr->Is<ast::IntLiteralExpression>());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(VariableTest, HasBindingPoint_BothProvided) {
|
TEST_F(VariableTest, HasBindingPoint_BothProvided) {
|
||||||
|
|
|
@ -1110,8 +1110,7 @@ void FunctionEmitter::IncrementLocation(AttributeList* attributes) {
|
||||||
// The old one doesn't leak because it's kept in the builder's AST node
|
// The old one doesn't leak because it's kept in the builder's AST node
|
||||||
// list.
|
// list.
|
||||||
attr = builder_.Location(
|
attr = builder_.Location(
|
||||||
loc_attr->source,
|
loc_attr->source, AInt(loc_attr->expr->As<ast::IntLiteralExpression>()->value + 1));
|
||||||
AInt(loc_attr->value->As<ast::IntLiteralExpression>()->value + 1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -256,9 +256,9 @@ TEST_F(ParserImplTest, FunctionDecl_ReturnTypeAttributeList) {
|
||||||
ASSERT_EQ(ret_type_attributes.Length(), 1u);
|
ASSERT_EQ(ret_type_attributes.Length(), 1u);
|
||||||
auto* loc = ret_type_attributes[0]->As<ast::LocationAttribute>();
|
auto* loc = ret_type_attributes[0]->As<ast::LocationAttribute>();
|
||||||
ASSERT_TRUE(loc != nullptr);
|
ASSERT_TRUE(loc != nullptr);
|
||||||
EXPECT_TRUE(loc->value->Is<ast::IntLiteralExpression>());
|
EXPECT_TRUE(loc->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
|
||||||
auto* exp = loc->value->As<ast::IntLiteralExpression>();
|
auto* exp = loc->expr->As<ast::IntLiteralExpression>();
|
||||||
EXPECT_EQ(1u, exp->value);
|
EXPECT_EQ(1u, exp->value);
|
||||||
|
|
||||||
auto* body = f->body;
|
auto* body = f->body;
|
||||||
|
|
|
@ -57,8 +57,8 @@ TEST_F(ParserImplTest, FunctionHeader_AttributeReturnType) {
|
||||||
|
|
||||||
auto* loc = f->return_type_attributes[0]->As<ast::LocationAttribute>();
|
auto* loc = f->return_type_attributes[0]->As<ast::LocationAttribute>();
|
||||||
ASSERT_TRUE(loc != nullptr);
|
ASSERT_TRUE(loc != nullptr);
|
||||||
ASSERT_TRUE(loc->value->Is<ast::IntLiteralExpression>());
|
ASSERT_TRUE(loc->expr->Is<ast::IntLiteralExpression>());
|
||||||
auto* exp = loc->value->As<ast::IntLiteralExpression>();
|
auto* exp = loc->expr->As<ast::IntLiteralExpression>();
|
||||||
EXPECT_EQ(exp->value, 1u);
|
EXPECT_EQ(exp->value, 1u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -201,7 +201,7 @@ TEST_F(ParserImplTest, GlobalOverrideDecl_WithId) {
|
||||||
|
|
||||||
auto* override_attr = ast::GetAttribute<ast::IdAttribute>(override->attributes);
|
auto* override_attr = ast::GetAttribute<ast::IdAttribute>(override->attributes);
|
||||||
ASSERT_NE(override_attr, nullptr);
|
ASSERT_NE(override_attr, nullptr);
|
||||||
EXPECT_TRUE(override_attr->value->Is<ast::IntLiteralExpression>());
|
EXPECT_TRUE(override_attr->expr->Is<ast::IntLiteralExpression>());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, GlobalOverrideDecl_WithId_TrailingComma) {
|
TEST_F(ParserImplTest, GlobalOverrideDecl_WithId_TrailingComma) {
|
||||||
|
@ -231,7 +231,7 @@ TEST_F(ParserImplTest, GlobalOverrideDecl_WithId_TrailingComma) {
|
||||||
|
|
||||||
auto* override_attr = ast::GetAttribute<ast::IdAttribute>(override->attributes);
|
auto* override_attr = ast::GetAttribute<ast::IdAttribute>(override->attributes);
|
||||||
ASSERT_NE(override_attr, nullptr);
|
ASSERT_NE(override_attr, nullptr);
|
||||||
EXPECT_TRUE(override_attr->value->Is<ast::IntLiteralExpression>());
|
EXPECT_TRUE(override_attr->expr->Is<ast::IntLiteralExpression>());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ParserImplTest, GlobalOverrideDecl_WithoutId) {
|
TEST_F(ParserImplTest, GlobalOverrideDecl_WithoutId) {
|
||||||
|
|
|
@ -120,8 +120,8 @@ TEST_F(ParserImplTest, ParamList_Attributes) {
|
||||||
|
|
||||||
ASSERT_TRUE(attrs_1[0]->Is<ast::LocationAttribute>());
|
ASSERT_TRUE(attrs_1[0]->Is<ast::LocationAttribute>());
|
||||||
auto* attr = attrs_1[0]->As<ast::LocationAttribute>();
|
auto* attr = attrs_1[0]->As<ast::LocationAttribute>();
|
||||||
ASSERT_TRUE(attr->value->Is<ast::IntLiteralExpression>());
|
ASSERT_TRUE(attr->expr->Is<ast::IntLiteralExpression>());
|
||||||
auto* loc = attr->value->As<ast::IntLiteralExpression>();
|
auto* loc = attr->expr->As<ast::IntLiteralExpression>();
|
||||||
EXPECT_EQ(loc->value, 1u);
|
EXPECT_EQ(loc->value, 1u);
|
||||||
|
|
||||||
EXPECT_EQ(e.value[1]->source.range.begin.line, 1u);
|
EXPECT_EQ(e.value[1]->source.range.begin.line, 1u);
|
||||||
|
|
|
@ -102,9 +102,9 @@ TEST_F(ParserImplTest, Attribute_Align) {
|
||||||
ASSERT_TRUE(member_attr->Is<ast::StructMemberAlignAttribute>());
|
ASSERT_TRUE(member_attr->Is<ast::StructMemberAlignAttribute>());
|
||||||
|
|
||||||
auto* o = member_attr->As<ast::StructMemberAlignAttribute>();
|
auto* o = member_attr->As<ast::StructMemberAlignAttribute>();
|
||||||
ASSERT_TRUE(o->align->Is<ast::IntLiteralExpression>());
|
ASSERT_TRUE(o->expr->Is<ast::IntLiteralExpression>());
|
||||||
EXPECT_EQ(o->align->As<ast::IntLiteralExpression>()->value, 4);
|
EXPECT_EQ(o->expr->As<ast::IntLiteralExpression>()->value, 4);
|
||||||
EXPECT_EQ(o->align->As<ast::IntLiteralExpression>()->suffix,
|
EXPECT_EQ(o->expr->As<ast::IntLiteralExpression>()->suffix,
|
||||||
ast::IntLiteralExpression::Suffix::kNone);
|
ast::IntLiteralExpression::Suffix::kNone);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,9 +121,9 @@ TEST_F(ParserImplTest, Attribute_Align_TrailingComma) {
|
||||||
ASSERT_TRUE(member_attr->Is<ast::StructMemberAlignAttribute>());
|
ASSERT_TRUE(member_attr->Is<ast::StructMemberAlignAttribute>());
|
||||||
|
|
||||||
auto* o = member_attr->As<ast::StructMemberAlignAttribute>();
|
auto* o = member_attr->As<ast::StructMemberAlignAttribute>();
|
||||||
ASSERT_TRUE(o->align->Is<ast::IntLiteralExpression>());
|
ASSERT_TRUE(o->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
|
||||||
auto* expr = o->align->As<ast::IntLiteralExpression>();
|
auto* expr = o->expr->As<ast::IntLiteralExpression>();
|
||||||
EXPECT_EQ(expr->value, 4);
|
EXPECT_EQ(expr->value, 4);
|
||||||
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,9 +51,8 @@ TEST_F(ParserImplTest, StructMember_ParsesWithAlignAttribute) {
|
||||||
EXPECT_TRUE(m->attributes[0]->Is<ast::StructMemberAlignAttribute>());
|
EXPECT_TRUE(m->attributes[0]->Is<ast::StructMemberAlignAttribute>());
|
||||||
|
|
||||||
auto* attr = m->attributes[0]->As<ast::StructMemberAlignAttribute>();
|
auto* attr = m->attributes[0]->As<ast::StructMemberAlignAttribute>();
|
||||||
ASSERT_TRUE(attr->align->Is<ast::IntLiteralExpression>());
|
ASSERT_TRUE(attr->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
auto* expr = attr->expr->As<ast::IntLiteralExpression>();
|
||||||
auto* expr = attr->align->As<ast::IntLiteralExpression>();
|
|
||||||
EXPECT_EQ(expr->value, 2);
|
EXPECT_EQ(expr->value, 2);
|
||||||
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
|
|
||||||
|
@ -101,8 +100,8 @@ TEST_F(ParserImplTest, StructMember_ParsesWithMultipleattributes) {
|
||||||
ASSERT_TRUE(m->attributes[1]->Is<ast::StructMemberAlignAttribute>());
|
ASSERT_TRUE(m->attributes[1]->Is<ast::StructMemberAlignAttribute>());
|
||||||
auto* attr = m->attributes[1]->As<ast::StructMemberAlignAttribute>();
|
auto* attr = m->attributes[1]->As<ast::StructMemberAlignAttribute>();
|
||||||
|
|
||||||
ASSERT_TRUE(attr->align->Is<ast::IntLiteralExpression>());
|
ASSERT_TRUE(attr->expr->Is<ast::IntLiteralExpression>());
|
||||||
auto* expr = attr->align->As<ast::IntLiteralExpression>();
|
auto* expr = attr->expr->As<ast::IntLiteralExpression>();
|
||||||
EXPECT_EQ(expr->value, 4);
|
EXPECT_EQ(expr->value, 4);
|
||||||
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
|
|
||||||
|
|
|
@ -33,9 +33,8 @@ TEST_F(ParserImplTest, AttributeList_Parses) {
|
||||||
ASSERT_TRUE(attr_0->Is<ast::LocationAttribute>());
|
ASSERT_TRUE(attr_0->Is<ast::LocationAttribute>());
|
||||||
|
|
||||||
auto* loc = attr_0->As<ast::LocationAttribute>();
|
auto* loc = attr_0->As<ast::LocationAttribute>();
|
||||||
ASSERT_TRUE(loc->value->Is<ast::IntLiteralExpression>());
|
ASSERT_TRUE(loc->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
auto* exp = loc->expr->As<ast::IntLiteralExpression>();
|
||||||
auto* exp = loc->value->As<ast::IntLiteralExpression>();
|
|
||||||
EXPECT_EQ(exp->value, 4u);
|
EXPECT_EQ(exp->value, 4u);
|
||||||
|
|
||||||
ASSERT_TRUE(attr_1->Is<ast::BuiltinAttribute>());
|
ASSERT_TRUE(attr_1->Is<ast::BuiltinAttribute>());
|
||||||
|
|
|
@ -29,8 +29,8 @@ TEST_F(ParserImplTest, Attribute_Location) {
|
||||||
ASSERT_TRUE(var_attr->Is<ast::LocationAttribute>());
|
ASSERT_TRUE(var_attr->Is<ast::LocationAttribute>());
|
||||||
|
|
||||||
auto* loc = var_attr->As<ast::LocationAttribute>();
|
auto* loc = var_attr->As<ast::LocationAttribute>();
|
||||||
ASSERT_TRUE(loc->value->Is<ast::IntLiteralExpression>());
|
ASSERT_TRUE(loc->expr->Is<ast::IntLiteralExpression>());
|
||||||
auto* exp = loc->value->As<ast::IntLiteralExpression>();
|
auto* exp = loc->expr->As<ast::IntLiteralExpression>();
|
||||||
EXPECT_EQ(exp->value, 4u);
|
EXPECT_EQ(exp->value, 4u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,8 +46,8 @@ TEST_F(ParserImplTest, Attribute_Location_TrailingComma) {
|
||||||
ASSERT_TRUE(var_attr->Is<ast::LocationAttribute>());
|
ASSERT_TRUE(var_attr->Is<ast::LocationAttribute>());
|
||||||
|
|
||||||
auto* loc = var_attr->As<ast::LocationAttribute>();
|
auto* loc = var_attr->As<ast::LocationAttribute>();
|
||||||
ASSERT_TRUE(loc->value->Is<ast::IntLiteralExpression>());
|
ASSERT_TRUE(loc->expr->Is<ast::IntLiteralExpression>());
|
||||||
auto* exp = loc->value->As<ast::IntLiteralExpression>();
|
auto* exp = loc->expr->As<ast::IntLiteralExpression>();
|
||||||
EXPECT_EQ(exp->value, 4u);
|
EXPECT_EQ(exp->value, 4u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,9 +368,8 @@ TEST_F(ParserImplTest, Attribute_Binding) {
|
||||||
ASSERT_TRUE(var_attr->Is<ast::BindingAttribute>());
|
ASSERT_TRUE(var_attr->Is<ast::BindingAttribute>());
|
||||||
|
|
||||||
auto* binding = var_attr->As<ast::BindingAttribute>();
|
auto* binding = var_attr->As<ast::BindingAttribute>();
|
||||||
ASSERT_TRUE(binding->value->Is<ast::IntLiteralExpression>());
|
ASSERT_TRUE(binding->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
auto* expr = binding->expr->As<ast::IntLiteralExpression>();
|
||||||
auto* expr = binding->value->As<ast::IntLiteralExpression>();
|
|
||||||
EXPECT_EQ(expr->value, 4);
|
EXPECT_EQ(expr->value, 4);
|
||||||
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
}
|
}
|
||||||
|
@ -387,9 +386,8 @@ TEST_F(ParserImplTest, Attribute_Binding_TrailingComma) {
|
||||||
ASSERT_TRUE(var_attr->Is<ast::BindingAttribute>());
|
ASSERT_TRUE(var_attr->Is<ast::BindingAttribute>());
|
||||||
|
|
||||||
auto* binding = var_attr->As<ast::BindingAttribute>();
|
auto* binding = var_attr->As<ast::BindingAttribute>();
|
||||||
ASSERT_TRUE(binding->value->Is<ast::IntLiteralExpression>());
|
ASSERT_TRUE(binding->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
auto* expr = binding->expr->As<ast::IntLiteralExpression>();
|
||||||
auto* expr = binding->value->As<ast::IntLiteralExpression>();
|
|
||||||
EXPECT_EQ(expr->value, 4);
|
EXPECT_EQ(expr->value, 4);
|
||||||
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
}
|
}
|
||||||
|
@ -446,9 +444,8 @@ TEST_F(ParserImplTest, Attribute_group) {
|
||||||
ASSERT_TRUE(var_attr->Is<ast::GroupAttribute>());
|
ASSERT_TRUE(var_attr->Is<ast::GroupAttribute>());
|
||||||
|
|
||||||
auto* group = var_attr->As<ast::GroupAttribute>();
|
auto* group = var_attr->As<ast::GroupAttribute>();
|
||||||
ASSERT_TRUE(group->value->Is<ast::IntLiteralExpression>());
|
ASSERT_TRUE(group->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
auto* expr = group->expr->As<ast::IntLiteralExpression>();
|
||||||
auto* expr = group->value->As<ast::IntLiteralExpression>();
|
|
||||||
EXPECT_EQ(expr->value, 4);
|
EXPECT_EQ(expr->value, 4);
|
||||||
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
}
|
}
|
||||||
|
@ -465,9 +462,8 @@ TEST_F(ParserImplTest, Attribute_group_TrailingComma) {
|
||||||
ASSERT_TRUE(var_attr->Is<ast::GroupAttribute>());
|
ASSERT_TRUE(var_attr->Is<ast::GroupAttribute>());
|
||||||
|
|
||||||
auto* group = var_attr->As<ast::GroupAttribute>();
|
auto* group = var_attr->As<ast::GroupAttribute>();
|
||||||
ASSERT_TRUE(group->value->Is<ast::IntLiteralExpression>());
|
ASSERT_TRUE(group->expr->Is<ast::IntLiteralExpression>());
|
||||||
|
auto* expr = group->expr->As<ast::IntLiteralExpression>();
|
||||||
auto* expr = group->value->As<ast::IntLiteralExpression>();
|
|
||||||
EXPECT_EQ(expr->value, 4);
|
EXPECT_EQ(expr->value, 4);
|
||||||
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
EXPECT_EQ(expr->suffix, ast::IntLiteralExpression::Suffix::kNone);
|
||||||
}
|
}
|
||||||
|
|
|
@ -442,7 +442,7 @@ sem::Variable* Resolver::Override(const ast::Override* v) {
|
||||||
sem->SetConstructor(rhs);
|
sem->SetConstructor(rhs);
|
||||||
|
|
||||||
if (auto* id_attr = ast::GetAttribute<ast::IdAttribute>(v->attributes)) {
|
if (auto* id_attr = ast::GetAttribute<ast::IdAttribute>(v->attributes)) {
|
||||||
auto* materialize = Materialize(Expression(id_attr->value));
|
auto* materialize = Materialize(Expression(id_attr->expr));
|
||||||
if (!materialize) {
|
if (!materialize) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -607,7 +607,7 @@ sem::Variable* Resolver::Var(const ast::Var* var, bool is_global) {
|
||||||
uint32_t binding = 0;
|
uint32_t binding = 0;
|
||||||
{
|
{
|
||||||
auto* attr = ast::GetAttribute<ast::BindingAttribute>(var->attributes);
|
auto* attr = ast::GetAttribute<ast::BindingAttribute>(var->attributes);
|
||||||
auto* materialize = Materialize(Expression(attr->value));
|
auto* materialize = Materialize(Expression(attr->expr));
|
||||||
if (!materialize) {
|
if (!materialize) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -623,7 +623,7 @@ sem::Variable* Resolver::Var(const ast::Var* var, bool is_global) {
|
||||||
uint32_t group = 0;
|
uint32_t group = 0;
|
||||||
{
|
{
|
||||||
auto* attr = ast::GetAttribute<ast::GroupAttribute>(var->attributes);
|
auto* attr = ast::GetAttribute<ast::GroupAttribute>(var->attributes);
|
||||||
auto* materialize = Materialize(Expression(attr->value));
|
auto* materialize = Materialize(Expression(attr->expr));
|
||||||
if (!materialize) {
|
if (!materialize) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -640,7 +640,7 @@ sem::Variable* Resolver::Var(const ast::Var* var, bool is_global) {
|
||||||
|
|
||||||
std::optional<uint32_t> location;
|
std::optional<uint32_t> location;
|
||||||
if (auto* attr = ast::GetAttribute<ast::LocationAttribute>(var->attributes)) {
|
if (auto* attr = ast::GetAttribute<ast::LocationAttribute>(var->attributes)) {
|
||||||
auto* materialize = Materialize(Expression(attr->value));
|
auto* materialize = Materialize(Expression(attr->expr));
|
||||||
if (!materialize) {
|
if (!materialize) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -705,7 +705,7 @@ sem::Parameter* Resolver::Parameter(const ast::Parameter* param, uint32_t index)
|
||||||
if (param->HasBindingPoint()) {
|
if (param->HasBindingPoint()) {
|
||||||
{
|
{
|
||||||
auto* attr = ast::GetAttribute<ast::BindingAttribute>(param->attributes);
|
auto* attr = ast::GetAttribute<ast::BindingAttribute>(param->attributes);
|
||||||
auto* materialize = Materialize(Expression(attr->value));
|
auto* materialize = Materialize(Expression(attr->expr));
|
||||||
if (!materialize) {
|
if (!materialize) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -719,7 +719,7 @@ sem::Parameter* Resolver::Parameter(const ast::Parameter* param, uint32_t index)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto* attr = ast::GetAttribute<ast::GroupAttribute>(param->attributes);
|
auto* attr = ast::GetAttribute<ast::GroupAttribute>(param->attributes);
|
||||||
auto* materialize = Materialize(Expression(attr->value));
|
auto* materialize = Materialize(Expression(attr->expr));
|
||||||
if (!materialize) {
|
if (!materialize) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -735,7 +735,7 @@ sem::Parameter* Resolver::Parameter(const ast::Parameter* param, uint32_t index)
|
||||||
|
|
||||||
std::optional<uint32_t> location;
|
std::optional<uint32_t> location;
|
||||||
if (auto* l = ast::GetAttribute<ast::LocationAttribute>(param->attributes)) {
|
if (auto* l = ast::GetAttribute<ast::LocationAttribute>(param->attributes)) {
|
||||||
auto* materialize = Materialize(Expression(l->value));
|
auto* materialize = Materialize(Expression(l->expr));
|
||||||
if (!materialize) {
|
if (!materialize) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -944,7 +944,7 @@ sem::Function* Resolver::Function(const ast::Function* decl) {
|
||||||
Mark(attr);
|
Mark(attr);
|
||||||
|
|
||||||
if (auto* a = attr->As<ast::LocationAttribute>()) {
|
if (auto* a = attr->As<ast::LocationAttribute>()) {
|
||||||
auto* materialize = Materialize(Expression(a->value));
|
auto* materialize = Materialize(Expression(a->expr));
|
||||||
if (!materialize) {
|
if (!materialize) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -2811,13 +2811,13 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
||||||
align = 1;
|
align = 1;
|
||||||
has_offset_attr = true;
|
has_offset_attr = true;
|
||||||
} else if (auto* a = attr->As<ast::StructMemberAlignAttribute>()) {
|
} else if (auto* a = attr->As<ast::StructMemberAlignAttribute>()) {
|
||||||
auto* materialized = Materialize(Expression(a->align));
|
auto* materialized = Materialize(Expression(a->expr));
|
||||||
if (!materialized) {
|
if (!materialized) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto const_value = materialized->ConstantValue();
|
auto const_value = materialized->ConstantValue();
|
||||||
if (!const_value) {
|
if (!const_value) {
|
||||||
AddError("'align' must be constant expression", a->align->source);
|
AddError("'align' must be constant expression", a->expr->source);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto value = const_value->As<AInt>();
|
auto value = const_value->As<AInt>();
|
||||||
|
@ -2838,7 +2838,7 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
||||||
size = s->size;
|
size = s->size;
|
||||||
has_size_attr = true;
|
has_size_attr = true;
|
||||||
} else if (auto* l = attr->As<ast::LocationAttribute>()) {
|
} else if (auto* l = attr->As<ast::LocationAttribute>()) {
|
||||||
auto* materialize = Materialize(Expression(l->value));
|
auto* materialize = Materialize(Expression(l->expr));
|
||||||
if (!materialize) {
|
if (!materialize) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -741,7 +741,7 @@ bool GeneratorImpl::EmitAttributes(std::ostream& out,
|
||||||
},
|
},
|
||||||
[&](const ast::BindingAttribute* binding) {
|
[&](const ast::BindingAttribute* binding) {
|
||||||
out << "binding(";
|
out << "binding(";
|
||||||
if (!EmitExpression(out, binding->value)) {
|
if (!EmitExpression(out, binding->expr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out << ")";
|
out << ")";
|
||||||
|
@ -749,7 +749,7 @@ bool GeneratorImpl::EmitAttributes(std::ostream& out,
|
||||||
},
|
},
|
||||||
[&](const ast::GroupAttribute* group) {
|
[&](const ast::GroupAttribute* group) {
|
||||||
out << "group(";
|
out << "group(";
|
||||||
if (!EmitExpression(out, group->value)) {
|
if (!EmitExpression(out, group->expr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out << ")";
|
out << ")";
|
||||||
|
@ -757,7 +757,7 @@ bool GeneratorImpl::EmitAttributes(std::ostream& out,
|
||||||
},
|
},
|
||||||
[&](const ast::LocationAttribute* location) {
|
[&](const ast::LocationAttribute* location) {
|
||||||
out << "location(";
|
out << "location(";
|
||||||
if (!EmitExpression(out, location->value)) {
|
if (!EmitExpression(out, location->expr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out << ")";
|
out << ")";
|
||||||
|
@ -781,7 +781,7 @@ bool GeneratorImpl::EmitAttributes(std::ostream& out,
|
||||||
},
|
},
|
||||||
[&](const ast::IdAttribute* override_deco) {
|
[&](const ast::IdAttribute* override_deco) {
|
||||||
out << "id(";
|
out << "id(";
|
||||||
if (!EmitExpression(out, override_deco->value)) {
|
if (!EmitExpression(out, override_deco->expr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out << ")";
|
out << ")";
|
||||||
|
@ -793,7 +793,7 @@ bool GeneratorImpl::EmitAttributes(std::ostream& out,
|
||||||
},
|
},
|
||||||
[&](const ast::StructMemberAlignAttribute* align) {
|
[&](const ast::StructMemberAlignAttribute* align) {
|
||||||
out << "align(";
|
out << "align(";
|
||||||
if (!EmitExpression(out, align->align)) {
|
if (!EmitExpression(out, align->expr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
out << ")";
|
out << ")";
|
||||||
|
|
Loading…
Reference in New Issue