sem: Fold together sem::Array and sem::ArrayType

There's now no need to have both.
Removes a whole bunch of Sem().Get() smell, and simplifies the resolver.

Also fixes a long-standing issue where an array with an explicit, but equal-to-implicit-stride attribute would result in a different type to an array without the decoration.

Bug: tint:724
Fixed: tint:782
Change-Id: I0202459009cd45be427cdb621993a5a3b07ff51e
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/50301
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
Ben Clayton 2021-05-07 20:58:34 +00:00 committed by Commit Bot service account
parent 6732e8561c
commit 4cd5eea87e
62 changed files with 486 additions and 591 deletions

View File

@ -450,8 +450,6 @@ libtint_source_set("libtint_core_all_src") {
"sem/alias_type.cc", "sem/alias_type.cc",
"sem/alias_type.h", "sem/alias_type.h",
"sem/array.h", "sem/array.h",
"sem/array_type.cc",
"sem/array_type.h",
"sem/bool_type.cc", "sem/bool_type.cc",
"sem/bool_type.h", "sem/bool_type.h",
"sem/call.h", "sem/call.h",

View File

@ -289,8 +289,6 @@ set(TINT_LIB_SRCS
sem/access_control_type.h sem/access_control_type.h
sem/alias_type.cc sem/alias_type.cc
sem/alias_type.h sem/alias_type.h
sem/array_type.cc
sem/array_type.h
sem/bool_type.cc sem/bool_type.cc
sem/bool_type.h sem/bool_type.h
sem/depth_texture_type.cc sem/depth_texture_type.cc
@ -568,7 +566,6 @@ if(${TINT_BUILD_TESTS})
test_main.cc test_main.cc
sem/access_control_type_test.cc sem/access_control_type_test.cc
sem/alias_type_test.cc sem/alias_type_test.cc
sem/array_type_test.cc
sem/bool_type_test.cc sem/bool_type_test.cc
sem/depth_texture_type_test.cc sem/depth_texture_type_test.cc
sem/external_texture_type_test.cc sem/external_texture_type_test.cc
@ -579,6 +576,7 @@ if(${TINT_BUILD_TESTS})
sem/pointer_type_test.cc sem/pointer_type_test.cc
sem/sampled_texture_type_test.cc sem/sampled_texture_type_test.cc
sem/sampler_type_test.cc sem/sampler_type_test.cc
sem/sem_array_test.cc
sem/sem_struct_test.cc sem/sem_struct_test.cc
sem/storage_texture_type_test.cc sem/storage_texture_type_test.cc
sem/texture_type_test.cc sem/texture_type_test.cc

View File

@ -24,7 +24,7 @@
#include "src/ast/sint_literal.h" #include "src/ast/sint_literal.h"
#include "src/ast/uint_literal.h" #include "src/ast/uint_literal.h"
#include "src/sem/access_control_type.h" #include "src/sem/access_control_type.h"
#include "src/sem/array_type.h" #include "src/sem/array.h"
#include "src/sem/f32_type.h" #include "src/sem/f32_type.h"
#include "src/sem/function.h" #include "src/sem/function.h"
#include "src/sem/i32_type.h" #include "src/sem/i32_type.h"
@ -76,13 +76,13 @@ TypeTextureDimensionToResourceBindingTextureDimension(
return ResourceBinding::TextureDimension::kNone; return ResourceBinding::TextureDimension::kNone;
} }
ResourceBinding::SampledKind BaseTypeToSampledKind(sem::Type* base_type) { ResourceBinding::SampledKind BaseTypeToSampledKind(const sem::Type* base_type) {
if (!base_type) { if (!base_type) {
return ResourceBinding::SampledKind::kUnknown; return ResourceBinding::SampledKind::kUnknown;
} }
if (auto* at = base_type->As<sem::ArrayType>()) { if (auto* at = base_type->As<sem::Array>()) {
base_type = at->type(); base_type = const_cast<sem::Type*>(at->ElemType());
} else if (auto* mt = base_type->As<sem::Matrix>()) { } else if (auto* mt = base_type->As<sem::Matrix>()) {
base_type = mt->type(); base_type = mt->type();
} else if (auto* vt = base_type->As<sem::Vector>()) { } else if (auto* vt = base_type->As<sem::Vector>()) {
@ -650,7 +650,7 @@ std::vector<ResourceBinding> Inspector::GetSampledTextureResourceBindingsImpl(
entry.dim = TypeTextureDimensionToResourceBindingTextureDimension( entry.dim = TypeTextureDimensionToResourceBindingTextureDimension(
texture_type->dim()); texture_type->dim());
sem::Type* base_type = nullptr; const sem::Type* base_type = nullptr;
if (multisampled_only) { if (multisampled_only) {
base_type = texture_type->As<sem::MultisampledTexture>() base_type = texture_type->As<sem::MultisampledTexture>()
->type() ->type()
@ -702,7 +702,7 @@ std::vector<ResourceBinding> Inspector::GetStorageTextureResourceBindingsImpl(
entry.dim = TypeTextureDimensionToResourceBindingTextureDimension( entry.dim = TypeTextureDimensionToResourceBindingTextureDimension(
texture_type->dim()); texture_type->dim());
sem::Type* base_type = texture_type->type()->UnwrapIfNeeded(); auto* base_type = texture_type->type()->UnwrapIfNeeded();
entry.sampled_kind = BaseTypeToSampledKind(base_type); entry.sampled_kind = BaseTypeToSampledKind(base_type);
entry.image_format = TypeImageFormatToResourceBindingImageFormat( entry.image_format = TypeImageFormatToResourceBindingImageFormat(
texture_type->image_format()); texture_type->image_format());

View File

@ -408,9 +408,9 @@ class ArrayBuilder : public Builder {
: element_builder_(element_builder) {} : element_builder_(element_builder) {}
bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override { bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
if (auto* arr = ty->As<sem::ArrayType>()) { if (auto* arr = ty->As<sem::Array>()) {
if (arr->size() == 0) { if (arr->IsRuntimeSized()) {
return element_builder_->Match(state, arr->type()); return element_builder_->Match(state, arr->ElemType());
} }
} }
return false; return false;
@ -418,8 +418,7 @@ class ArrayBuilder : public Builder {
sem::Type* Build(BuildState& state) const override { sem::Type* Build(BuildState& state) const override {
auto* el = element_builder_->Build(state); auto* el = element_builder_->Build(state);
return state.ty_mgr.Get<sem::ArrayType>(const_cast<sem::Type*>(el), 0, return state.ty_mgr.Get<sem::Array>(el, 0, 0, 0, 0, true);
ast::DecorationList{});
} }
std::string str() const override { std::string str() const override {

View File

@ -202,14 +202,15 @@ TEST_F(IntrinsicTableTest, MismatchPointer) {
} }
TEST_F(IntrinsicTableTest, MatchArray) { TEST_F(IntrinsicTableTest, MatchArray) {
auto result = table->Lookup(*this, IntrinsicType::kArrayLength, auto* arr = create<sem::Array>(create<sem::U32>(), 0, 4, 4, 4, true);
{ty.array<f32>()}, Source{}); auto result =
table->Lookup(*this, IntrinsicType::kArrayLength, {arr}, Source{});
ASSERT_NE(result.intrinsic, nullptr); ASSERT_NE(result.intrinsic, nullptr);
ASSERT_EQ(result.diagnostics.str(), ""); ASSERT_EQ(result.diagnostics.str(), "");
EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kArrayLength); EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kArrayLength);
EXPECT_THAT(result.intrinsic->ReturnType(), ty.u32()); EXPECT_THAT(result.intrinsic->ReturnType(), ty.u32());
EXPECT_THAT(result.intrinsic->Parameters(), ASSERT_EQ(result.intrinsic->Parameters().size(), 1u);
ElementsAre(Parameter{ty.array<f32>()})); EXPECT_TRUE(result.intrinsic->Parameters()[0].type->Is<sem::Array>());
} }
TEST_F(IntrinsicTableTest, MismatchArray) { TEST_F(IntrinsicTableTest, MismatchArray) {

View File

@ -61,7 +61,7 @@
#include "src/program_id.h" #include "src/program_id.h"
#include "src/sem/access_control_type.h" #include "src/sem/access_control_type.h"
#include "src/sem/alias_type.h" #include "src/sem/alias_type.h"
#include "src/sem/array_type.h" #include "src/sem/array.h"
#include "src/sem/bool_type.h" #include "src/sem/bool_type.h"
#include "src/sem/depth_texture_type.h" #include "src/sem/depth_texture_type.h"
#include "src/sem/external_texture_type.h" #include "src/sem/external_texture_type.h"
@ -595,15 +595,11 @@ class ProgramBuilder {
/// @param n the array size. 0 represents a runtime-array /// @param n the array size. 0 represents a runtime-array
/// @param decos the optional decorations for the array /// @param decos the optional decorations for the array
/// @return the tint AST type for a array of size `n` of type `T` /// @return the tint AST type for a array of size `n` of type `T`
typ::Array array(typ::Type subtype, ast::Array* array(typ::Type subtype,
uint32_t n = 0, uint32_t n = 0,
ast::DecorationList decos = {}) const { ast::DecorationList decos = {}) const {
subtype = MaybeCreateTypename(subtype); subtype = MaybeCreateTypename(subtype);
return {subtype.ast ? builder->create<ast::Array>(subtype, n, decos) return builder->create<ast::Array>(subtype, n, decos);
: nullptr,
subtype.sem ? builder->create<sem::ArrayType>(subtype, n,
std::move(decos))
: nullptr};
} }
/// @param source the Source of the node /// @param source the Source of the node
@ -611,24 +607,19 @@ class ProgramBuilder {
/// @param n the array size. 0 represents a runtime-array /// @param n the array size. 0 represents a runtime-array
/// @param decos the optional decorations for the array /// @param decos the optional decorations for the array
/// @return the tint AST type for a array of size `n` of type `T` /// @return the tint AST type for a array of size `n` of type `T`
typ::Array array(const Source& source, ast::Array* array(const Source& source,
typ::Type subtype, typ::Type subtype,
uint32_t n = 0, uint32_t n = 0,
ast::DecorationList decos = {}) const { ast::DecorationList decos = {}) const {
subtype = MaybeCreateTypename(subtype); subtype = MaybeCreateTypename(subtype);
return { return builder->create<ast::Array>(source, subtype, n, decos);
subtype.ast ? builder->create<ast::Array>(source, subtype, n, decos)
: nullptr,
subtype.sem
? builder->create<sem::ArrayType>(subtype, n, std::move(decos))
: nullptr};
} }
/// @param subtype the array element type /// @param subtype the array element type
/// @param n the array size. 0 represents a runtime-array /// @param n the array size. 0 represents a runtime-array
/// @param stride the array stride /// @param stride the array stride
/// @return the tint AST type for a array of size `n` of type `T` /// @return the tint AST type for a array of size `n` of type `T`
typ::Array array(typ::Type subtype, uint32_t n, uint32_t stride) const { ast::Array* array(typ::Type subtype, uint32_t n, uint32_t stride) const {
subtype = MaybeCreateTypename(subtype); subtype = MaybeCreateTypename(subtype);
return array(subtype, n, return array(subtype, n,
{builder->create<ast::StrideDecoration>(stride)}); {builder->create<ast::StrideDecoration>(stride)});
@ -639,7 +630,7 @@ class ProgramBuilder {
/// @param n the array size. 0 represents a runtime-array /// @param n the array size. 0 represents a runtime-array
/// @param stride the array stride /// @param stride the array stride
/// @return the tint AST type for a array of size `n` of type `T` /// @return the tint AST type for a array of size `n` of type `T`
typ::Array array(const Source& source, ast::Array* array(const Source& source,
typ::Type subtype, typ::Type subtype,
uint32_t n, uint32_t n,
uint32_t stride) const { uint32_t stride) const {
@ -650,14 +641,14 @@ class ProgramBuilder {
/// @return the tint AST type for an array of size `N` of type `T` /// @return the tint AST type for an array of size `N` of type `T`
template <typename T, int N = 0> template <typename T, int N = 0>
typ::Array array() const { ast::Array* array() const {
return array(Of<T>(), N); return array(Of<T>(), N);
} }
/// @param stride the array stride /// @param stride the array stride
/// @return the tint AST type for an array of size `N` of type `T` /// @return the tint AST type for an array of size `N` of type `T`
template <typename T, int N = 0> template <typename T, int N = 0>
typ::Array array(uint32_t stride) const { ast::Array* array(uint32_t stride) const {
return array(Of<T>(), N, stride); return array(Of<T>(), N, stride);
} }

View File

@ -122,7 +122,7 @@ using ArrayDecorationTest = TestWithParams;
TEST_P(ArrayDecorationTest, IsValid) { TEST_P(ArrayDecorationTest, IsValid) {
auto& params = GetParam(); auto& params = GetParam();
auto arr = auto* arr =
ty.array(ty.f32(), 0, ty.array(ty.f32(), 0,
{ {
createDecoration(Source{{12, 34}}, *this, params.kind), createDecoration(Source{{12, 34}}, *this, params.kind),
@ -360,7 +360,7 @@ TEST_P(ArrayStrideTest, All) {
<< ", should_pass: " << params.should_pass; << ", should_pass: " << params.should_pass;
SCOPED_TRACE(ss.str()); SCOPED_TRACE(ss.str());
auto arr = ty.array(Source{{12, 34}}, el_ty, 4, params.stride); auto* arr = ty.array(Source{{12, 34}}, el_ty, 4, params.stride);
Global("myarray", arr, ast::StorageClass::kInput); Global("myarray", arr, ast::StorageClass::kInput);
@ -445,7 +445,7 @@ INSTANTIATE_TEST_SUITE_P(
Params{ast_mat4x4<f32>, (default_mat4x4.align - 1) * 7, false})); Params{ast_mat4x4<f32>, (default_mat4x4.align - 1) * 7, false}));
TEST_F(ArrayStrideTest, MultipleDecorations) { TEST_F(ArrayStrideTest, MultipleDecorations) {
auto arr = ty.array(Source{{12, 34}}, ty.i32(), 4, auto* arr = ty.array(Source{{12, 34}}, ty.i32(), 4,
{ {
create<ast::StrideDecoration>(4), create<ast::StrideDecoration>(4),
create<ast::StrideDecoration>(4), create<ast::StrideDecoration>(4),
@ -468,7 +468,7 @@ using StructBlockTest = ResolverTest;
TEST_F(StructBlockTest, StructUsedAsArrayElement) { TEST_F(StructBlockTest, StructUsedAsArrayElement) {
auto* s = Structure("S", {Member("x", ty.i32())}, auto* s = Structure("S", {Member("x", ty.i32())},
{create<ast::StructBlockDecoration>()}); {create<ast::StructBlockDecoration>()});
auto a = ty.array(s, 4); auto* a = ty.array(s, 4);
Global("G", a, ast::StorageClass::kPrivate); Global("G", a, ast::StorageClass::kPrivate);
EXPECT_FALSE(r()->Resolve()); EXPECT_FALSE(r()->Resolve());

View File

@ -756,7 +756,7 @@ INSTANTIATE_TEST_SUITE_P(
using ResolverIntrinsicDataTest = ResolverTest; using ResolverIntrinsicDataTest = ResolverTest;
TEST_F(ResolverIntrinsicDataTest, ArrayLength_Vector) { TEST_F(ResolverIntrinsicDataTest, ArrayLength_Vector) {
auto ary = ty.array<i32>(); auto* ary = ty.array<i32>();
auto* str = Structure("S", {Member("x", ary)}, auto* str = Structure("S", {Member("x", ary)},
{create<ast::StructBlockDecoration>()}); {create<ast::StructBlockDecoration>()});
auto ac = ty.access(ast::AccessControl::kReadOnly, str); auto ac = ty.access(ast::AccessControl::kReadOnly, str);

View File

@ -98,11 +98,13 @@ TEST_F(ResolverIsHostShareable, AccessControlI32) {
} }
TEST_F(ResolverIsHostShareable, ArraySizedOfHostShareable) { TEST_F(ResolverIsHostShareable, ArraySizedOfHostShareable) {
EXPECT_TRUE(r()->IsHostShareable(ty.array(ty.i32(), 5))); auto* arr = create<sem::Array>(create<sem::I32>(), 5, 4, 20, 4, true);
EXPECT_TRUE(r()->IsHostShareable(arr));
} }
TEST_F(ResolverIsHostShareable, ArrayUnsizedOfHostShareable) { TEST_F(ResolverIsHostShareable, ArrayUnsizedOfHostShareable) {
EXPECT_TRUE(r()->IsHostShareable(ty.array<i32>())); auto* arr = create<sem::Array>(create<sem::I32>(), 0, 4, 4, 4, true);
EXPECT_TRUE(r()->IsHostShareable(arr));
} }
// Note: Structure tests covered in host_shareable_validation_test.cc // Note: Structure tests covered in host_shareable_validation_test.cc

View File

@ -82,11 +82,13 @@ TEST_F(ResolverIsStorableTest, AccessControlI32) {
} }
TEST_F(ResolverIsStorableTest, ArraySizedOfStorable) { TEST_F(ResolverIsStorableTest, ArraySizedOfStorable) {
EXPECT_TRUE(r()->IsStorable(ty.array(ty.i32(), 5))); auto* arr = create<sem::Array>(create<sem::I32>(), 5, 4, 20, 4, true);
EXPECT_TRUE(r()->IsStorable(arr));
} }
TEST_F(ResolverIsStorableTest, ArrayUnsizedOfStorable) { TEST_F(ResolverIsStorableTest, ArrayUnsizedOfStorable) {
EXPECT_TRUE(r()->IsStorable(ty.array<i32>())); auto* arr = create<sem::Array>(create<sem::I32>(), 0, 4, 4, 4, true);
EXPECT_TRUE(r()->IsStorable(arr));
} }
TEST_F(ResolverIsStorableTest, Struct_AllMembersStorable) { TEST_F(ResolverIsStorableTest, Struct_AllMembersStorable) {

View File

@ -178,8 +178,8 @@ bool Resolver::IsStorable(const sem::Type* type) {
if (type->is_scalar() || type->Is<sem::Vector>() || type->Is<sem::Matrix>()) { if (type->is_scalar() || type->Is<sem::Vector>() || type->Is<sem::Matrix>()) {
return true; return true;
} }
if (auto* arr = type->As<sem::ArrayType>()) { if (auto* arr = type->As<sem::Array>()) {
return IsStorable(arr->type()); return IsStorable(arr->ElemType());
} }
if (auto* str = type->As<sem::Struct>()) { if (auto* str = type->As<sem::Struct>()) {
for (const auto* member : str->Members()) { for (const auto* member : str->Members()) {
@ -204,8 +204,8 @@ bool Resolver::IsHostShareable(const sem::Type* type) {
if (auto* mat = type->As<sem::Matrix>()) { if (auto* mat = type->As<sem::Matrix>()) {
return IsHostShareable(mat->type()); return IsHostShareable(mat->type());
} }
if (auto* arr = type->As<sem::ArrayType>()) { if (auto* arr = type->As<sem::Array>()) {
return IsHostShareable(arr->type()); return IsHostShareable(arr->ElemType());
} }
if (auto* str = type->As<sem::Struct>()) { if (auto* str = type->As<sem::Struct>()) {
for (auto* member : str->Members()) { for (auto* member : str->Members()) {
@ -287,7 +287,7 @@ bool Resolver::ResolveInternal() {
// TODO(crbug.com/tint/724) - Remove once tint:724 is complete. // TODO(crbug.com/tint/724) - Remove once tint:724 is complete.
// ast::AccessDecorations are generated by the WGSL parser, used to // ast::AccessDecorations are generated by the WGSL parser, used to
// build sem::AccessControls and then leaked. // build sem::AccessControls and then leaked.
// ast::StrideDecoration are used to build a sem::ArrayTypes, but // ast::StrideDecoration are used to build a sem::Arrays, but
// multiple arrays of the same stride, size and element type are // multiple arrays of the same stride, size and element type are
// currently de-duplicated by the type manager, and we leak these // currently de-duplicated by the type manager, and we leak these
// decorations. // decorations.
@ -350,14 +350,7 @@ sem::Type* Resolver::Type(const ast::Type* ty) {
return nullptr; return nullptr;
} }
if (auto* t = ty->As<ast::Array>()) { if (auto* t = ty->As<ast::Array>()) {
if (auto* el = Type(t->type())) { return Array(t);
auto* sem = builder_->create<sem::ArrayType>(
const_cast<sem::Type*>(el), t->size(), t->decorations());
if (Array(sem, ty->source())) {
return sem;
}
}
return nullptr;
} }
if (auto* t = ty->As<ast::Pointer>()) { if (auto* t = ty->As<ast::Pointer>()) {
if (auto* el = Type(t->type())) { if (auto* el = Type(t->type())) {
@ -420,8 +413,9 @@ sem::Type* Resolver::Type(const ast::Type* ty) {
return s; return s;
} }
Resolver::VariableInfo* Resolver::Variable(ast::Variable* var, Resolver::VariableInfo* Resolver::Variable(
sem::Type* type, /* = nullptr */ ast::Variable* var,
const sem::Type* type, /* = nullptr */
std::string type_name /* = "" */) { std::string type_name /* = "" */) {
auto it = variable_to_info_.find(var); auto it = variable_to_info_.find(var);
if (it != variable_to_info_.end()) { if (it != variable_to_info_.end()) {
@ -436,18 +430,10 @@ Resolver::VariableInfo* Resolver::Variable(ast::Variable* var,
return nullptr; return nullptr;
} }
auto* ctype = Canonical(type); auto* ctype = Canonical(const_cast<sem::Type*>(type));
auto* info = variable_infos_.Create(var, ctype, type_name); auto* info = variable_infos_.Create(var, ctype, type_name);
variable_to_info_.emplace(var, info); variable_to_info_.emplace(var, info);
// TODO(bclayton): Why is this here? Needed?
// Resolve variable's type
if (auto* arr = info->type->As<sem::ArrayType>()) {
if (!Array(arr, var->source())) {
return nullptr;
}
}
return info; return info;
} }
@ -596,8 +582,8 @@ bool Resolver::ValidateGlobalVariable(const VariableInfo* info) {
bool Resolver::ValidateVariable(const ast::Variable* var) { bool Resolver::ValidateVariable(const ast::Variable* var) {
auto* type = variable_to_info_[var]->type; auto* type = variable_to_info_[var]->type;
if (auto* r = type->As<sem::ArrayType>()) { if (auto* r = type->As<sem::Array>()) {
if (r->IsRuntimeArray()) { if (r->IsRuntimeSized()) {
diagnostics_.add_error( diagnostics_.add_error(
"v-0015", "v-0015",
"runtime arrays may only appear as the last member of a struct", "runtime arrays may only appear as the last member of a struct",
@ -873,8 +859,8 @@ bool Resolver::ValidateEntryPoint(const ast::Function* func,
builder_->Symbols().NameFor(func->symbol()), builder_->Symbols().NameFor(func->symbol()),
func->source()); func->source());
return false; return false;
} else if (auto* arr = member_ty->As<sem::ArrayType>()) { } else if (auto* arr = member_ty->As<sem::Array>()) {
if (arr->IsRuntimeArray()) { if (arr->IsRuntimeSized()) {
diagnostics_.add_error( diagnostics_.add_error(
"entry point IO types cannot contain runtime sized arrays", "entry point IO types cannot contain runtime sized arrays",
member->Declaration()->source()); member->Declaration()->source());
@ -1276,9 +1262,9 @@ bool Resolver::ArrayAccessor(ast::ArrayAccessorExpression* expr) {
auto* res = TypeOf(expr->array()); auto* res = TypeOf(expr->array());
auto* parent_type = res->UnwrapAll(); auto* parent_type = res->UnwrapAll();
sem::Type* ret = nullptr; const sem::Type* ret = nullptr;
if (auto* arr = parent_type->As<sem::ArrayType>()) { if (auto* arr = parent_type->As<sem::Array>()) {
ret = arr->type(); ret = arr->ElemType();
} else if (auto* vec = parent_type->As<sem::Vector>()) { } else if (auto* vec = parent_type->As<sem::Vector>()) {
ret = vec->type(); ret = vec->type();
} else if (auto* mat = parent_type->As<sem::Matrix>()) { } else if (auto* mat = parent_type->As<sem::Matrix>()) {
@ -1293,8 +1279,8 @@ bool Resolver::ArrayAccessor(ast::ArrayAccessorExpression* expr) {
// If we're extracting from a pointer, we return a pointer. // If we're extracting from a pointer, we return a pointer.
if (auto* ptr = res->As<sem::Pointer>()) { if (auto* ptr = res->As<sem::Pointer>()) {
ret = builder_->create<sem::Pointer>(ret, ptr->storage_class()); ret = builder_->create<sem::Pointer>(ret, ptr->storage_class());
} else if (auto* arr = parent_type->As<sem::ArrayType>()) { } else if (auto* arr = parent_type->As<sem::Array>()) {
if (!arr->type()->is_scalar()) { if (!arr->ElemType()->is_scalar()) {
// If we extract a non-scalar from an array then we also get a pointer. We // If we extract a non-scalar from an array then we also get a pointer. We
// will generate a Function storage class variable to store this into. // will generate a Function storage class variable to store this into.
ret = builder_->create<sem::Pointer>(ret, ast::StorageClass::kFunction); ret = builder_->create<sem::Pointer>(ret, ast::StorageClass::kFunction);
@ -1459,7 +1445,7 @@ bool Resolver::ValidateVectorConstructor(
value_cardinality_sum++; value_cardinality_sum++;
} else if (auto* value_vec = value_type->As<sem::Vector>()) { } else if (auto* value_vec = value_type->As<sem::Vector>()) {
sem::Type* value_elem_type = value_vec->type()->UnwrapAll(); auto* value_elem_type = value_vec->type()->UnwrapAll();
// A mismatch of vector type parameter T is only an error if multiple // A mismatch of vector type parameter T is only an error if multiple
// arguments are present. A single argument constructor constitutes a // arguments are present. A single argument constructor constitutes a
// type conversion expression. // type conversion expression.
@ -1754,8 +1740,8 @@ bool Resolver::ValidateBinary(ast::BinaryExpression* expr) {
auto* lhs_declared_type = TypeOf(expr->lhs())->UnwrapAll(); auto* lhs_declared_type = TypeOf(expr->lhs())->UnwrapAll();
auto* rhs_declared_type = TypeOf(expr->rhs())->UnwrapAll(); auto* rhs_declared_type = TypeOf(expr->rhs())->UnwrapAll();
auto* lhs_type = Canonical(lhs_declared_type); auto* lhs_type = Canonical(const_cast<sem::Type*>(lhs_declared_type));
auto* rhs_type = Canonical(rhs_declared_type); auto* rhs_type = Canonical(const_cast<sem::Type*>(rhs_declared_type));
auto* lhs_vec = lhs_type->As<Vector>(); auto* lhs_vec = lhs_type->As<Vector>();
auto* lhs_vec_elem_type = lhs_vec ? lhs_vec->type() : nullptr; auto* lhs_vec_elem_type = lhs_vec ? lhs_vec->type() : nullptr;
@ -2006,7 +1992,7 @@ bool Resolver::VariableDeclStatement(const ast::VariableDeclStatement* stmt) {
// If the variable has a declared type, resolve it. // If the variable has a declared type, resolve it.
std::string type_name; std::string type_name;
sem::Type* type = nullptr; const sem::Type* type = nullptr;
if (auto* ast_ty = var->type()) { if (auto* ast_ty = var->type()) {
type_name = ast_ty->FriendlyName(builder_->Symbols()); type_name = ast_ty->FriendlyName(builder_->Symbols());
type = Type(ast_ty); type = Type(ast_ty);
@ -2065,7 +2051,7 @@ bool Resolver::VariableDeclStatement(const ast::VariableDeclStatement* stmt) {
} }
// TODO(bclayton): Remove this and fix tests. We're overriding the semantic // TODO(bclayton): Remove this and fix tests. We're overriding the semantic
// type stored in info->type here with a possibly non-canonicalized type. // type stored in info->type here with a possibly non-canonicalized type.
info->type = type; info->type = const_cast<sem::Type*>(type);
variable_stack_.set(var->symbol(), info); variable_stack_.set(var->symbol(), info);
current_block_->decls.push_back(var); current_block_->decls.push_back(var);
@ -2251,8 +2237,7 @@ void Resolver::CreateSemanticNodes() const {
bool Resolver::DefaultAlignAndSize(const sem::Type* ty, bool Resolver::DefaultAlignAndSize(const sem::Type* ty,
uint32_t& align, uint32_t& align,
uint32_t& size, uint32_t& size) {
const Source& source) {
static constexpr uint32_t vector_size[] = { static constexpr uint32_t vector_size[] = {
/* padding */ 0, /* padding */ 0,
/* padding */ 0, /* padding */ 0,
@ -2297,76 +2282,71 @@ bool Resolver::DefaultAlignAndSize(const sem::Type* ty,
align = s->Align(); align = s->Align();
size = s->Size(); size = s->Size();
return true; return true;
} else if (cty->Is<sem::ArrayType>()) { } else if (auto* a = cty->As<sem::Array>()) {
if (auto* sem = align = a->Align();
Array(ty->UnwrapAliasIfNeeded()->As<sem::ArrayType>(), source)) { size = a->SizeInBytes();
align = sem->Align();
size = sem->Size();
return true; return true;
} }
return false;
}
TINT_UNREACHABLE(diagnostics_) << "Invalid type " << ty->TypeInfo().name; TINT_UNREACHABLE(diagnostics_) << "Invalid type " << ty->TypeInfo().name;
return false; return false;
} }
const sem::Array* Resolver::Array(const sem::ArrayType* arr, sem::Array* Resolver::Array(const ast::Array* arr) {
const Source& source) { auto source = arr->source();
if (auto* sem = builder_->Sem().Get(arr)) {
// Semantic info already constructed for this array type
return sem;
}
if (!ValidateArray(arr, source)) { auto* el_ty = Type(arr->type());
if (!el_ty) {
return nullptr; return nullptr;
} }
auto* el_ty = arr->type();
uint32_t el_align = 0; uint32_t el_align = 0;
uint32_t el_size = 0; uint32_t el_size = 0;
if (!DefaultAlignAndSize(el_ty, el_align, el_size, source)) { if (!DefaultAlignAndSize(el_ty, el_align, el_size)) {
return nullptr; return nullptr;
} }
auto create_semantic = [&](uint32_t stride) -> sem::Array* {
auto align = el_align;
// WebGPU requires runtime arrays have at least one element, but the AST
// records an element count of 0 for it.
auto size = std::max<uint32_t>(arr->size(), 1) * stride;
auto* sem = builder_->create<sem::Array>(const_cast<sem::ArrayType*>(arr),
align, size, stride);
builder_->Sem().Add(arr, sem);
return sem;
};
// Look for explicit stride via [[stride(n)]] decoration // Look for explicit stride via [[stride(n)]] decoration
uint32_t explicit_stride = 0; uint32_t explicit_stride = 0;
for (auto* deco : arr->decorations()) { for (auto* deco : arr->decorations()) {
Mark(deco); Mark(deco);
if (auto* stride = deco->As<ast::StrideDecoration>()) { if (auto* sd = deco->As<ast::StrideDecoration>()) {
if (explicit_stride) { if (explicit_stride) {
diagnostics_.add_error( diagnostics_.add_error(
"array must have at most one [[stride]] decoration", source); "array must have at most one [[stride]] decoration", source);
return nullptr; return nullptr;
} }
explicit_stride = stride->stride(); explicit_stride = sd->stride();
if (!ValidateArrayStrideDecoration(stride, el_size, el_align, source)) { if (!ValidateArrayStrideDecoration(sd, el_size, el_align, source)) {
return nullptr; return nullptr;
} }
continue;
} }
}
if (explicit_stride) { diagnostics_.add_error("decoration is not valid for array types",
return create_semantic(explicit_stride); deco->source());
return nullptr;
} }
// Calculate implicit stride // Calculate implicit stride
auto implicit_stride = utils::RoundUp(el_align, el_size); auto implicit_stride = utils::RoundUp(el_align, el_size);
return create_semantic(implicit_stride);
auto stride = explicit_stride ? explicit_stride : implicit_stride;
// WebGPU requires runtime arrays have at least one element, but the AST
// records an element count of 0 for it.
auto size = std::max<uint32_t>(arr->size(), 1) * stride;
auto* sem = builder_->create<sem::Array>(el_ty, arr->size(), el_align, size,
stride, stride == implicit_stride);
if (!ValidateArray(sem, source)) {
return nullptr;
}
return sem;
} }
bool Resolver::ValidateArray(const sem::ArrayType* arr, const Source& source) { bool Resolver::ValidateArray(const sem::Array* arr, const Source& source) {
auto* el_ty = arr->type(); auto* el_ty = arr->ElemType();
if (!IsStorable(el_ty)) { if (!IsStorable(el_ty)) {
builder_->Diagnostics().add_error( builder_->Diagnostics().add_error(
@ -2416,8 +2396,8 @@ bool Resolver::ValidateArrayStrideDecoration(const ast::StrideDecoration* deco,
bool Resolver::ValidateStructure(const sem::Struct* str) { bool Resolver::ValidateStructure(const sem::Struct* str) {
for (auto* member : str->Members()) { for (auto* member : str->Members()) {
if (auto* r = member->Type()->UnwrapAll()->As<sem::ArrayType>()) { if (auto* r = member->Type()->UnwrapAll()->As<sem::Array>()) {
if (r->IsRuntimeArray()) { if (r->IsRuntimeSized()) {
if (member != str->Members().back()) { if (member != str->Members().back()) {
diagnostics_.add_error( diagnostics_.add_error(
"v-0015", "v-0015",
@ -2434,14 +2414,6 @@ bool Resolver::ValidateStructure(const sem::Struct* str) {
member->Declaration()->source()); member->Declaration()->source());
return false; return false;
} }
for (auto* deco : r->decorations()) {
if (!deco->Is<ast::StrideDecoration>()) {
diagnostics_.add_error("decoration is not valid for array types",
deco->source());
return false;
}
}
} }
} }
@ -2511,7 +2483,7 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
uint32_t offset = struct_size; uint32_t offset = struct_size;
uint32_t align = 0; uint32_t align = 0;
uint32_t size = 0; uint32_t size = 0;
if (!DefaultAlignAndSize(type, align, size, member->source())) { if (!DefaultAlignAndSize(type, align, size)) {
return nullptr; return nullptr;
} }
@ -2779,7 +2751,7 @@ bool Resolver::Assignment(ast::AssignmentStatement* a) {
bool Resolver::ApplyStorageClassUsageToType(ast::StorageClass sc, bool Resolver::ApplyStorageClassUsageToType(ast::StorageClass sc,
sem::Type* ty, sem::Type* ty,
const Source& usage) { const Source& usage) {
ty = ty->UnwrapIfNeeded(); ty = const_cast<sem::Type*>(ty->UnwrapIfNeeded());
if (auto* str = ty->As<sem::Struct>()) { if (auto* str = ty->As<sem::Struct>()) {
if (str->StorageClassUsage().count(sc)) { if (str->StorageClassUsage().count(sc)) {
@ -2801,8 +2773,9 @@ bool Resolver::ApplyStorageClassUsageToType(ast::StorageClass sc,
return true; return true;
} }
if (auto* arr = ty->As<sem::ArrayType>()) { if (auto* arr = ty->As<sem::Array>()) {
return ApplyStorageClassUsageToType(sc, arr->type(), usage); return ApplyStorageClassUsageToType(
sc, const_cast<sem::Type*>(arr->ElemType()), usage);
} }
if (ast::IsHostShareable(sc) && !IsHostShareable(ty)) { if (ast::IsHostShareable(sc) && !IsHostShareable(ty)) {
@ -2829,7 +2802,8 @@ bool Resolver::BlockScope(const ast::BlockStatement* block,
return result; return result;
} }
std::string Resolver::VectorPretty(uint32_t size, sem::Type* element_type) { std::string Resolver::VectorPretty(uint32_t size,
const sem::Type* element_type) {
sem::Vector vec_type(element_type, size); sem::Vector vec_type(element_type, size);
return vec_type.FriendlyName(builder_->Symbols()); return vec_type.FriendlyName(builder_->Symbols());
} }

View File

@ -224,7 +224,7 @@ class Resolver {
// AST and Type validation methods // AST and Type validation methods
// Each return true on success, false on failure. // Each return true on success, false on failure.
bool ValidateArray(const sem::ArrayType* arr, const Source& source); bool ValidateArray(const sem::Array* arr, const Source& source);
bool ValidateArrayStrideDecoration(const ast::StrideDecoration* deco, bool ValidateArrayStrideDecoration(const ast::StrideDecoration* deco,
uint32_t el_size, uint32_t el_size,
uint32_t el_align, uint32_t el_align,
@ -250,15 +250,18 @@ class Resolver {
/// @param ty the ast::Type /// @param ty the ast::Type
sem::Type* Type(const ast::Type* ty); sem::Type* Type(const ast::Type* ty);
/// @returns the semantic information for the array `arr`, building it if it /// Builds and returns the semantic information for the array `arr`.
/// hasn't been constructed already. If an error is raised, nullptr is /// This method does not mark the ast::Array node, nor attach the generated
/// returned. /// semantic information to the AST node.
/// @returns the semantic Array information, or nullptr if an error is raised.
/// @param arr the Array to get semantic information for /// @param arr the Array to get semantic information for
/// @param source the Source of the ast node with this array as its type sem::Array* Array(const ast::Array* arr);
const sem::Array* Array(const sem::ArrayType* arr, const Source& source);
/// @returns the sem::Struct for the AST structure `str`. If an error is /// Builds and returns the semantic information for the structure `str`.
/// raised, nullptr is returned. /// This method does not mark the ast::Struct node, nor attach the generated
/// semantic information to the AST node.
/// @returns the semantic Struct information, or nullptr if an error is
/// raised. raised, nullptr is returned.
sem::Struct* Structure(const ast::Struct* str); sem::Struct* Structure(const ast::Struct* str);
/// @returns the VariableInfo for the variable `var`, building it if it hasn't /// @returns the VariableInfo for the variable `var`, building it if it hasn't
@ -268,7 +271,7 @@ class Resolver {
/// @param type_name optional type name of `var` to use instead of /// @param type_name optional type name of `var` to use instead of
/// `var->type()->FriendlyName()`. /// `var->type()->FriendlyName()`.
VariableInfo* Variable(ast::Variable* var, VariableInfo* Variable(ast::Variable* var,
sem::Type* type = nullptr, const sem::Type* type = nullptr,
std::string type_name = ""); std::string type_name = "");
/// Records the storage class usage for the given type, and any transient /// Records the storage class usage for the given type, and any transient
@ -285,12 +288,10 @@ class Resolver {
/// @param align the output default alignment in bytes for the type `ty` /// @param align the output default alignment in bytes for the type `ty`
/// @param size the output default size in bytes for the type `ty` /// @param size the output default size in bytes for the type `ty`
/// @param source the Source of the variable declaration of type `ty`
/// @returns true on success, false on error /// @returns true on success, false on error
bool DefaultAlignAndSize(const sem::Type* ty, bool DefaultAlignAndSize(const sem::Type* ty,
uint32_t& align, uint32_t& align,
uint32_t& size, uint32_t& size);
const Source& source);
/// @returns the resolved type of the ast::Expression `expr` /// @returns the resolved type of the ast::Expression `expr`
/// @param expr the expression /// @param expr the expression
@ -333,7 +334,7 @@ class Resolver {
/// @param size the vector dimension /// @param size the vector dimension
/// @param element_type scalar vector sub-element type /// @param element_type scalar vector sub-element type
/// @return pretty string representation /// @return pretty string representation
std::string VectorPretty(uint32_t size, sem::Type* element_type); std::string VectorPretty(uint32_t size, const sem::Type* element_type);
/// Mark records that the given AST node has been visited, and asserts that /// Mark records that the given AST node has been visited, and asserts that
/// the given node has not already been seen. Diamonds in the AST are illegal. /// the given node has not already been seen. Diamonds in the AST are illegal.

View File

@ -61,7 +61,7 @@ TEST_F(ResolverStorageClassValidationTest, StorageBufferPointer) {
TEST_F(ResolverStorageClassValidationTest, StorageBufferArray) { TEST_F(ResolverStorageClassValidationTest, StorageBufferArray) {
// var<storage> g : [[access(read)]] array<S, 3>; // var<storage> g : [[access(read)]] array<S, 3>;
auto* s = Structure("S", {Member("a", ty.f32())}); auto* s = Structure("S", {Member("a", ty.f32())});
auto a = ty.array(s, 3); auto* a = ty.array(s, 3);
auto ac = ty.access(ast::AccessControl::kReadOnly, a); auto ac = ty.access(ast::AccessControl::kReadOnly, a);
Global(Source{{56, 78}}, "g", ac, ast::StorageClass::kStorage); Global(Source{{56, 78}}, "g", ac, ast::StorageClass::kStorage);
@ -169,7 +169,7 @@ TEST_F(ResolverStorageClassValidationTest, UniformBufferPointer) {
TEST_F(ResolverStorageClassValidationTest, UniformBufferArray) { TEST_F(ResolverStorageClassValidationTest, UniformBufferArray) {
// var<uniform> g : [[access(read)]] array<S, 3>; // var<uniform> g : [[access(read)]] array<S, 3>;
auto* s = Structure("S", {Member("a", ty.f32())}); auto* s = Structure("S", {Member("a", ty.f32())});
auto a = ty.array(s, 3); auto* a = ty.array(s, 3);
auto ac = ty.access(ast::AccessControl::kReadOnly, a); auto ac = ty.access(ast::AccessControl::kReadOnly, a);
Global(Source{{56, 78}}, "g", ac, ast::StorageClass::kUniform); Global(Source{{56, 78}}, "g", ac, ast::StorageClass::kUniform);

View File

@ -173,8 +173,8 @@ TEST_F(ResolverStructLayoutTest, ExplicitStrideArrayRuntimeSized) {
} }
TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayOfExplicitStrideArray) { TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayOfExplicitStrideArray) {
auto inner = ty.array<i32, 2>(/*stride*/ 16); // size: 32 auto* inner = ty.array<i32, 2>(/*stride*/ 16); // size: 32
auto outer = ty.array(inner, 12); // size: 12 * 32 auto* outer = ty.array(inner, 12); // size: 12 * 32
auto* s = Structure("S", { auto* s = Structure("S", {
Member("c", outer), Member("c", outer),
}); });
@ -198,7 +198,7 @@ TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayOfStructure) {
Member("b", ty.vec3<i32>()), Member("b", ty.vec3<i32>()),
Member("c", ty.vec4<i32>()), Member("c", ty.vec4<i32>()),
}); // size: 48 }); // size: 48
auto outer = ty.array(inner, 12); // size: 12 * 48 auto* outer = ty.array(inner, 12); // size: 12 * 48
auto* s = Structure("S", { auto* s = Structure("S", {
Member("c", outer), Member("c", outer),
}); });

View File

@ -105,7 +105,7 @@ TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalStruct) {
TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalArray) { TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalArray) {
auto* s = Structure("S", {Member("a", ty.f32())}); auto* s = Structure("S", {Member("a", ty.f32())});
auto a = ty.array(s, 3); auto* a = ty.array(s, 3);
Global("g", a, ast::StorageClass::kPrivate); Global("g", a, ast::StorageClass::kPrivate);
ASSERT_TRUE(r()->Resolve()) << r()->error(); ASSERT_TRUE(r()->Resolve()) << r()->error();
@ -158,7 +158,7 @@ TEST_F(ResolverStorageClassUseTest, StructReachableViaLocalStruct) {
TEST_F(ResolverStorageClassUseTest, StructReachableViaLocalArray) { TEST_F(ResolverStorageClassUseTest, StructReachableViaLocalArray) {
auto* s = Structure("S", {Member("a", ty.f32())}); auto* s = Structure("S", {Member("a", ty.f32())});
auto a = ty.array(s, 3); auto* a = ty.array(s, 3);
WrapInFunction(Var("g", a, ast::StorageClass::kFunction)); WrapInFunction(Var("g", a, ast::StorageClass::kFunction));
ASSERT_TRUE(r()->Resolve()) << r()->error(); ASSERT_TRUE(r()->Resolve()) << r()->error();

View File

@ -37,7 +37,7 @@ TEST_F(AccessControlTest, Is) {
Type* ty = &at; Type* ty = &at;
EXPECT_TRUE(ty->Is<AccessControl>()); EXPECT_TRUE(ty->Is<AccessControl>());
EXPECT_FALSE(ty->Is<Alias>()); EXPECT_FALSE(ty->Is<Alias>());
EXPECT_FALSE(ty->Is<ArrayType>()); EXPECT_FALSE(ty->Is<Array>());
EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<Bool>());
EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<F32>());
EXPECT_FALSE(ty->Is<I32>()); EXPECT_FALSE(ty->Is<I32>());

View File

@ -33,7 +33,7 @@ TEST_F(AliasTest, Is) {
sem::Type* ty = at; sem::Type* ty = at;
EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<AccessControl>());
EXPECT_TRUE(ty->Is<Alias>()); EXPECT_TRUE(ty->Is<Alias>());
EXPECT_FALSE(ty->Is<ArrayType>()); EXPECT_FALSE(ty->Is<Array>());
EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<Bool>());
EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<F32>());
EXPECT_FALSE(ty->Is<I32>()); EXPECT_FALSE(ty->Is<I32>());

View File

@ -14,16 +14,53 @@
#include "src/sem/array.h" #include "src/sem/array.h"
#include <string>
#include "src/debug.h"
TINT_INSTANTIATE_TYPEINFO(tint::sem::Array); TINT_INSTANTIATE_TYPEINFO(tint::sem::Array);
namespace tint { namespace tint {
namespace sem { namespace sem {
Array::Array(sem::ArrayType* type, Array::Array(const Type* element,
uint32_t count,
uint32_t align, uint32_t align,
uint32_t size, uint32_t size,
uint32_t stride) uint32_t stride,
: type_(type), align_(align), size_(size), stride_(stride) {} bool stride_implicit)
: element_(element),
count_(count),
align_(align),
size_(size),
stride_(stride),
stride_implicit_(stride_implicit) {
TINT_ASSERT(element_);
}
std::string Array::type_name() const {
std::string type_name = "__array" + element_->type_name();
type_name += "_count_" + std::to_string(count_);
type_name += "_align_" + std::to_string(align_);
type_name += "_size_" + std::to_string(size_);
type_name += "_stride_" + std::to_string(stride_);
// Note: stride_implicit is not part of the type_name string as this is a
// property derived from the other fields.
return type_name;
}
std::string Array::FriendlyName(const SymbolTable& symbols) const {
std::ostringstream out;
if (!stride_implicit_) {
out << "[[stride(" << stride_ << ")]] ";
}
out << "array<" << element_->FriendlyName(symbols);
if (!IsRuntimeSized()) {
out << ", " << count_;
}
out << ">";
return out.str();
}
} // namespace sem } // namespace sem
} // namespace tint } // namespace tint

View File

@ -16,28 +16,47 @@
#define SRC_SEM_ARRAY_H_ #define SRC_SEM_ARRAY_H_
#include <stdint.h> #include <stdint.h>
#include <string>
#include "src/sem/node.h" #include "src/sem/node.h"
#include "src/sem/type.h"
// Forward declarations
namespace tint {
namespace ast {
class Array;
} // namespace ast
} // namespace tint
namespace tint { namespace tint {
namespace sem { namespace sem {
// Forward declarations
class ArrayType;
/// Array holds the semantic information for Array nodes. /// Array holds the semantic information for Array nodes.
class Array : public Castable<Array, Node> { class Array : public Castable<Array, Type> {
public: public:
/// Constructor /// Constructor
/// @param type the Array type /// @param element the array element type
/// @param align the byte alignment of the structure /// @param count the number of elements in the array. 0 represents a
/// @param size the byte size of the structure /// runtime-sized array.
/// @param align the byte alignment of the array
/// @param size the byte size of the array
/// @param stride the number of bytes from the start of one element of the /// @param stride the number of bytes from the start of one element of the
/// array to the start of the next element /// array to the start of the next element
Array(sem::ArrayType* type, uint32_t align, uint32_t size, uint32_t stride); /// @param stride_implicit is true if the value of `stride` matches the
/// element's natural stride.
Array(Type const* element,
uint32_t count,
uint32_t align,
uint32_t size,
uint32_t stride,
bool stride_implicit);
/// @return the resolved type of the Array /// @return the array element type
sem::ArrayType* Type() const { return type_; } Type const* ElemType() const { return element_; }
/// @returns the number of elements in the array. 0 represents a runtime-sized
/// array.
uint32_t Count() const { return count_; }
/// @returns the byte alignment of the array /// @returns the byte alignment of the array
/// @note this may differ from the alignment of a structure member of this /// @note this may differ from the alignment of a structure member of this
@ -47,17 +66,34 @@ class Array : public Castable<Array, Node> {
/// @returns the byte size of the array /// @returns the byte size of the array
/// @note this may differ from the size of a structure member of this array /// @note this may differ from the size of a structure member of this array
/// type, if the member is annotated with the `[[size(n)]]` decoration. /// type, if the member is annotated with the `[[size(n)]]` decoration.
uint32_t Size() const { return size_; } uint32_t SizeInBytes() const { return size_; }
/// @returns the number of bytes from the start of one element of the /// @returns the number of bytes from the start of one element of the
/// array to the start of the next element /// array to the start of the next element
uint32_t Stride() const { return stride_; } uint32_t Stride() const { return stride_; }
/// @returns true if the value returned by Stride() does matches the
/// element's natural stride
bool IsStrideImplicit() const { return stride_implicit_; }
/// @returns true if this array is runtime sized
bool IsRuntimeSized() const { return count_ == 0; }
/// @returns the name for the type
std::string type_name() const override;
/// @param symbols the program's symbol table
/// @returns the name for this type that closely resembles how it would be
/// declared in WGSL.
std::string FriendlyName(const SymbolTable& symbols) const override;
private: private:
sem::ArrayType* const type_; Type const* const element_;
uint32_t const count_;
uint32_t const align_; uint32_t const align_;
uint32_t const size_; uint32_t const size_;
uint32_t const stride_; uint32_t const stride_;
bool const stride_implicit_;
}; };
} // namespace sem } // namespace sem

View File

@ -1,67 +0,0 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/sem/array_type.h"
#include <cmath>
#include "src/program_builder.h"
TINT_INSTANTIATE_TYPEINFO(tint::sem::ArrayType);
namespace tint {
namespace sem {
ArrayType::ArrayType(Type* subtype,
uint32_t size,
ast::DecorationList decorations)
: subtype_(subtype), size_(size), decos_(decorations) {}
ArrayType::ArrayType(ArrayType&&) = default;
ArrayType::~ArrayType() = default;
std::string ArrayType::type_name() const {
TINT_ASSERT(subtype_);
std::string type_name = "__array" + subtype_->type_name();
if (!IsRuntimeArray()) {
type_name += "_" + std::to_string(size_);
}
for (auto* deco : decos_) {
if (auto* stride = deco->As<ast::StrideDecoration>()) {
type_name += "_stride_" + std::to_string(stride->stride());
}
}
return type_name;
}
std::string ArrayType::FriendlyName(const SymbolTable& symbols) const {
std::ostringstream out;
for (auto* deco : decos_) {
if (auto* stride = deco->As<ast::StrideDecoration>()) {
out << "[[stride(" << stride->stride() << ")]] ";
}
}
out << "array<" << subtype_->FriendlyName(symbols);
if (!IsRuntimeArray()) {
out << ", " << size_;
}
out << ">";
return out.str();
}
} // namespace sem
} // namespace tint

View File

@ -1,70 +0,0 @@
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_SEM_ARRAY_TYPE_H_
#define SRC_SEM_ARRAY_TYPE_H_
#include <string>
#include "src/ast/decoration.h"
#include "src/sem/type.h"
namespace tint {
namespace sem {
/// An array type. If size is zero then it is a runtime array.
// TODO(amaiorano): https://crbug.com/tint/724 Fold into sem::Array once parsers
// don't create this anymore.
class ArrayType : public Castable<ArrayType, Type> {
public:
/// Constructor
/// @param subtype the type of the array elements
/// @param size the number of elements in the array. `0` represents a
/// runtime-sized array.
/// @param decorations the array decorations
ArrayType(Type* subtype, uint32_t size, ast::DecorationList decorations);
/// Move constructor
ArrayType(ArrayType&&);
~ArrayType() override;
/// @returns true if this is a runtime array.
/// i.e. the size is determined at runtime
bool IsRuntimeArray() const { return size_ == 0; }
/// @returns the array decorations
const ast::DecorationList& decorations() const { return decos_; }
/// @returns the array type
Type* type() const { return subtype_; }
/// @returns the array size. Size is 0 for a runtime array
uint32_t size() const { return size_; }
/// @returns the name for the type
std::string type_name() const override;
/// @param symbols the program's symbol table
/// @returns the name for this type that closely resembles how it would be
/// declared in WGSL.
std::string FriendlyName(const SymbolTable& symbols) const override;
private:
Type* const subtype_;
uint32_t const size_;
ast::DecorationList const decos_;
};
} // namespace sem
} // namespace tint
#endif // SRC_SEM_ARRAY_TYPE_H_

View File

@ -27,7 +27,7 @@ TEST_F(BoolTest, Is) {
Type* ty = &b; Type* ty = &b;
EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<AccessControl>());
EXPECT_FALSE(ty->Is<Alias>()); EXPECT_FALSE(ty->Is<Alias>());
EXPECT_FALSE(ty->Is<ArrayType>()); EXPECT_FALSE(ty->Is<Array>());
EXPECT_TRUE(ty->Is<Bool>()); EXPECT_TRUE(ty->Is<Bool>());
EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<F32>());
EXPECT_FALSE(ty->Is<I32>()); EXPECT_FALSE(ty->Is<I32>());

View File

@ -32,7 +32,7 @@ TEST_F(DepthTextureTest, Is) {
Type* ty = &d; Type* ty = &d;
EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<AccessControl>());
EXPECT_FALSE(ty->Is<Alias>()); EXPECT_FALSE(ty->Is<Alias>());
EXPECT_FALSE(ty->Is<ArrayType>()); EXPECT_FALSE(ty->Is<Array>());
EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<Bool>());
EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<F32>());
EXPECT_FALSE(ty->Is<I32>()); EXPECT_FALSE(ty->Is<I32>());

View File

@ -33,7 +33,7 @@ TEST_F(ExternalTextureTest, Is) {
Type* ty = &s; Type* ty = &s;
EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<AccessControl>());
EXPECT_FALSE(ty->Is<Alias>()); EXPECT_FALSE(ty->Is<Alias>());
EXPECT_FALSE(ty->Is<ArrayType>()); EXPECT_FALSE(ty->Is<Array>());
EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<Bool>());
EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<F32>());
EXPECT_FALSE(ty->Is<I32>()); EXPECT_FALSE(ty->Is<I32>());

View File

@ -27,7 +27,7 @@ TEST_F(F32Test, Is) {
Type* ty = &f; Type* ty = &f;
EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<AccessControl>());
EXPECT_FALSE(ty->Is<Alias>()); EXPECT_FALSE(ty->Is<Alias>());
EXPECT_FALSE(ty->Is<ArrayType>()); EXPECT_FALSE(ty->Is<Array>());
EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<Bool>());
EXPECT_TRUE(ty->Is<F32>()); EXPECT_TRUE(ty->Is<F32>());
EXPECT_FALSE(ty->Is<I32>()); EXPECT_FALSE(ty->Is<I32>());

View File

@ -27,7 +27,7 @@ TEST_F(I32Test, Is) {
Type* ty = &i; Type* ty = &i;
EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<AccessControl>());
EXPECT_FALSE(ty->Is<Alias>()); EXPECT_FALSE(ty->Is<Alias>());
EXPECT_FALSE(ty->Is<ArrayType>()); EXPECT_FALSE(ty->Is<Array>());
EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<Bool>());
EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<F32>());
EXPECT_TRUE(ty->Is<I32>()); EXPECT_TRUE(ty->Is<I32>());

View File

@ -38,7 +38,7 @@ TEST_F(MatrixTest, Is) {
Type* ty = &m; Type* ty = &m;
EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<AccessControl>());
EXPECT_FALSE(ty->Is<Alias>()); EXPECT_FALSE(ty->Is<Alias>());
EXPECT_FALSE(ty->Is<ArrayType>()); EXPECT_FALSE(ty->Is<Array>());
EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<Bool>());
EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<F32>());
EXPECT_FALSE(ty->Is<I32>()); EXPECT_FALSE(ty->Is<I32>());

View File

@ -33,7 +33,7 @@ TEST_F(MultisampledTextureTest, Is) {
Type* ty = &s; Type* ty = &s;
EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<AccessControl>());
EXPECT_FALSE(ty->Is<Alias>()); EXPECT_FALSE(ty->Is<Alias>());
EXPECT_FALSE(ty->Is<ArrayType>()); EXPECT_FALSE(ty->Is<Array>());
EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<Bool>());
EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<F32>());
EXPECT_FALSE(ty->Is<I32>()); EXPECT_FALSE(ty->Is<I32>());

View File

@ -21,7 +21,7 @@ TINT_INSTANTIATE_TYPEINFO(tint::sem::Pointer);
namespace tint { namespace tint {
namespace sem { namespace sem {
Pointer::Pointer(Type* subtype, ast::StorageClass storage_class) Pointer::Pointer(const Type* subtype, ast::StorageClass storage_class)
: subtype_(subtype), storage_class_(storage_class) {} : subtype_(subtype), storage_class_(storage_class) {}
std::string Pointer::type_name() const { std::string Pointer::type_name() const {

View File

@ -26,16 +26,16 @@ namespace sem {
/// A pointer type. /// A pointer type.
class Pointer : public Castable<Pointer, Type> { class Pointer : public Castable<Pointer, Type> {
public: public:
/// Construtor /// Constructor
/// @param subtype the pointee type /// @param subtype the pointee type
/// @param storage_class the storage class of the pointer /// @param storage_class the storage class of the pointer
Pointer(Type* subtype, ast::StorageClass storage_class); Pointer(const Type* subtype, ast::StorageClass storage_class);
/// Move constructor /// Move constructor
Pointer(Pointer&&); Pointer(Pointer&&);
~Pointer() override; ~Pointer() override;
/// @returns the pointee type /// @returns the pointee type
Type* type() const { return subtype_; } const Type* type() const { return subtype_; }
/// @returns the storage class of the pointer /// @returns the storage class of the pointer
ast::StorageClass storage_class() const { return storage_class_; } ast::StorageClass storage_class() const { return storage_class_; }
@ -48,7 +48,7 @@ class Pointer : public Castable<Pointer, Type> {
std::string FriendlyName(const SymbolTable& symbols) const override; std::string FriendlyName(const SymbolTable& symbols) const override;
private: private:
Type* const subtype_; Type const* const subtype_;
ast::StorageClass const storage_class_; ast::StorageClass const storage_class_;
}; };

View File

@ -35,7 +35,7 @@ TEST_F(PointerTest, Is) {
Type* ty = &p; Type* ty = &p;
EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<AccessControl>());
EXPECT_FALSE(ty->Is<Alias>()); EXPECT_FALSE(ty->Is<Alias>());
EXPECT_FALSE(ty->Is<ArrayType>()); EXPECT_FALSE(ty->Is<Array>());
EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<Bool>());
EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<F32>());
EXPECT_FALSE(ty->Is<I32>()); EXPECT_FALSE(ty->Is<I32>());

View File

@ -32,7 +32,7 @@ TEST_F(SampledTextureTest, Is) {
Type* ty = &s; Type* ty = &s;
EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<AccessControl>());
EXPECT_FALSE(ty->Is<Alias>()); EXPECT_FALSE(ty->Is<Alias>());
EXPECT_FALSE(ty->Is<ArrayType>()); EXPECT_FALSE(ty->Is<Array>());
EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<Bool>());
EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<F32>());
EXPECT_FALSE(ty->Is<I32>()); EXPECT_FALSE(ty->Is<I32>());

View File

@ -38,7 +38,7 @@ TEST_F(SamplerTest, Is) {
Type* ty = &s; Type* ty = &s;
EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<AccessControl>());
EXPECT_FALSE(ty->Is<Alias>()); EXPECT_FALSE(ty->Is<Alias>());
EXPECT_FALSE(ty->Is<ArrayType>()); EXPECT_FALSE(ty->Is<Array>());
EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<Bool>());
EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<F32>());
EXPECT_FALSE(ty->Is<I32>()); EXPECT_FALSE(ty->Is<I32>());

View File

@ -24,30 +24,35 @@ using ArrayTest = TestHelper;
TEST_F(ArrayTest, CreateSizedArray) { TEST_F(ArrayTest, CreateSizedArray) {
U32 u32; U32 u32;
ArrayType arr{&u32, 3, ast::DecorationList{}}; auto* arr = create<Array>(&u32, 2, 4, 8, 16, true);
EXPECT_EQ(arr.type(), &u32); EXPECT_EQ(arr->ElemType(), &u32);
EXPECT_EQ(arr.size(), 3u); EXPECT_EQ(arr->Count(), 2u);
EXPECT_TRUE(arr.Is<ArrayType>()); EXPECT_EQ(arr->Align(), 4u);
EXPECT_FALSE(arr.IsRuntimeArray()); EXPECT_EQ(arr->SizeInBytes(), 8u);
EXPECT_EQ(arr->Stride(), 16u);
EXPECT_TRUE(arr->IsStrideImplicit());
EXPECT_FALSE(arr->IsRuntimeSized());
} }
TEST_F(ArrayTest, CreateRuntimeArray) { TEST_F(ArrayTest, CreateRuntimeArray) {
U32 u32; U32 u32;
ArrayType arr{&u32, 0, ast::DecorationList{}}; auto* arr = create<Array>(&u32, 0, 4, 8, 16, true);
EXPECT_EQ(arr.type(), &u32); EXPECT_EQ(arr->ElemType(), &u32);
EXPECT_EQ(arr.size(), 0u); EXPECT_EQ(arr->Count(), 0u);
EXPECT_TRUE(arr.Is<ArrayType>()); EXPECT_EQ(arr->Align(), 4u);
EXPECT_TRUE(arr.IsRuntimeArray()); EXPECT_EQ(arr->SizeInBytes(), 8u);
EXPECT_EQ(arr->Stride(), 16u);
EXPECT_TRUE(arr->IsStrideImplicit());
EXPECT_TRUE(arr->IsRuntimeSized());
} }
TEST_F(ArrayTest, Is) { TEST_F(ArrayTest, Is) {
I32 i32; I32 i32;
ArrayType arr{&i32, 3, ast::DecorationList{}}; Type* ty = create<Array>(&i32, 2, 4, 8, 4, true);
Type* ty = &arr;
EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<AccessControl>());
EXPECT_FALSE(ty->Is<Alias>()); EXPECT_FALSE(ty->Is<Alias>());
EXPECT_TRUE(ty->Is<ArrayType>()); EXPECT_TRUE(ty->Is<Array>());
EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<Bool>());
EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<F32>());
EXPECT_FALSE(ty->Is<I32>()); EXPECT_FALSE(ty->Is<I32>());
@ -62,37 +67,34 @@ TEST_F(ArrayTest, Is) {
TEST_F(ArrayTest, TypeName) { TEST_F(ArrayTest, TypeName) {
I32 i32; I32 i32;
ArrayType arr{&i32, 0, ast::DecorationList{}}; auto* arr = create<Array>(&i32, 2, 0, 4, 4, true);
EXPECT_EQ(arr.type_name(), "__array__i32"); EXPECT_EQ(arr->type_name(), "__array__i32_count_2_align_0_size_4_stride_4");
} }
TEST_F(ArrayTest, FriendlyNameRuntimeSized) { TEST_F(ArrayTest, FriendlyNameRuntimeSized) {
ArrayType arr{ty.i32(), 0, ast::DecorationList{}}; auto* arr = create<Array>(ty.i32(), 0, 0, 4, 4, true);
EXPECT_EQ(arr.FriendlyName(Symbols()), "array<i32>"); EXPECT_EQ(arr->FriendlyName(Symbols()), "array<i32>");
} }
TEST_F(ArrayTest, FriendlyNameStaticSized) { TEST_F(ArrayTest, FriendlyNameStaticSized) {
ArrayType arr{ty.i32(), 5, ast::DecorationList{}}; auto* arr = create<Array>(ty.i32(), 5, 4, 20, 4, true);
EXPECT_EQ(arr.FriendlyName(Symbols()), "array<i32, 5>"); EXPECT_EQ(arr->FriendlyName(Symbols()), "array<i32, 5>");
} }
TEST_F(ArrayTest, FriendlyNameWithStride) { TEST_F(ArrayTest, FriendlyNameRuntimeSizedNonImplicitStride) {
ArrayType arr{ty.i32(), 5, auto* arr = create<Array>(ty.i32(), 0, 0, 4, 4, false);
ast::DecorationList{create<ast::StrideDecoration>(32)}}; EXPECT_EQ(arr->FriendlyName(Symbols()), "[[stride(4)]] array<i32>");
EXPECT_EQ(arr.FriendlyName(Symbols()), "[[stride(32)]] array<i32, 5>"); }
TEST_F(ArrayTest, FriendlyNameStaticSizedNonImplicitStride) {
auto* arr = create<Array>(ty.i32(), 5, 4, 20, 4, false);
EXPECT_EQ(arr->FriendlyName(Symbols()), "[[stride(4)]] array<i32, 5>");
} }
TEST_F(ArrayTest, TypeName_RuntimeArray) { TEST_F(ArrayTest, TypeName_RuntimeArray) {
I32 i32; I32 i32;
ArrayType arr{&i32, 3, ast::DecorationList{}}; auto* arr = create<Array>(&i32, 2, 4, 8, 16, true);
EXPECT_EQ(arr.type_name(), "__array__i32_3"); EXPECT_EQ(arr->type_name(), "__array__i32_count_2_align_4_size_8_stride_16");
}
TEST_F(ArrayTest, TypeName_WithStride) {
I32 i32;
ArrayType arr{&i32, 3,
ast::DecorationList{create<ast::StrideDecoration>(16)}};
EXPECT_EQ(arr.type_name(), "__array__i32_3_stride_16");
} }
} // namespace } // namespace

View File

@ -45,7 +45,7 @@ TEST_F(StructTest, Is) {
sem::Type* ty = s; sem::Type* ty = s;
EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<AccessControl>());
EXPECT_FALSE(ty->Is<Alias>()); EXPECT_FALSE(ty->Is<Alias>());
EXPECT_FALSE(ty->Is<ArrayType>()); EXPECT_FALSE(ty->Is<Array>());
EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<Bool>());
EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<F32>());
EXPECT_FALSE(ty->Is<I32>()); EXPECT_FALSE(ty->Is<I32>());

View File

@ -34,7 +34,7 @@ TEST_F(StorageTextureTest, Is) {
Type* ty = s; Type* ty = s;
EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<AccessControl>());
EXPECT_FALSE(ty->Is<Alias>()); EXPECT_FALSE(ty->Is<Alias>());
EXPECT_FALSE(ty->Is<ArrayType>()); EXPECT_FALSE(ty->Is<Array>());
EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<Bool>());
EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<F32>());
EXPECT_FALSE(ty->Is<I32>()); EXPECT_FALSE(ty->Is<I32>());

View File

@ -37,22 +37,22 @@ Type::Type(Type&&) = default;
Type::~Type() = default; Type::~Type() = default;
Type* Type::UnwrapPtrIfNeeded() { const Type* Type::UnwrapPtrIfNeeded() const {
if (auto* ptr = As<sem::Pointer>()) { if (auto* ptr = As<sem::Pointer>()) {
return ptr->type(); return ptr->type();
} }
return this; return this;
} }
Type* Type::UnwrapAliasIfNeeded() { const Type* Type::UnwrapAliasIfNeeded() const {
Type* unwrapped = this; const Type* unwrapped = this;
while (auto* ptr = unwrapped->As<sem::Alias>()) { while (auto* ptr = unwrapped->As<sem::Alias>()) {
unwrapped = ptr->type(); unwrapped = ptr->type();
} }
return unwrapped; return unwrapped;
} }
Type* Type::UnwrapIfNeeded() { const Type* Type::UnwrapIfNeeded() const {
auto* where = this; auto* where = this;
while (true) { while (true) {
if (auto* alias = where->As<sem::Alias>()) { if (auto* alias = where->As<sem::Alias>()) {
@ -66,7 +66,7 @@ Type* Type::UnwrapIfNeeded() {
return where; return where;
} }
Type* Type::UnwrapAll() { const Type* Type::UnwrapAll() const {
return UnwrapIfNeeded()->UnwrapPtrIfNeeded()->UnwrapIfNeeded(); return UnwrapIfNeeded()->UnwrapPtrIfNeeded()->UnwrapIfNeeded();
} }

View File

@ -46,22 +46,11 @@ class Type : public Castable<Type, Node> {
virtual std::string FriendlyName(const SymbolTable& symbols) const = 0; virtual std::string FriendlyName(const SymbolTable& symbols) const = 0;
/// @returns the pointee type if this is a pointer, `this` otherwise /// @returns the pointee type if this is a pointer, `this` otherwise
Type* UnwrapPtrIfNeeded(); const Type* UnwrapPtrIfNeeded() const;
/// @returns the pointee type if this is a pointer, `this` otherwise
const Type* UnwrapPtrIfNeeded() const {
return const_cast<Type*>(this)->UnwrapPtrIfNeeded();
}
/// @returns the most deeply nested aliased type if this is an alias, `this` /// @returns the most deeply nested aliased type if this is an alias, `this`
/// otherwise /// otherwise
Type* UnwrapAliasIfNeeded(); const Type* UnwrapAliasIfNeeded() const;
/// @returns the most deeply nested aliased type if this is an alias, `this`
/// otherwise
const Type* UnwrapAliasIfNeeded() const {
return const_cast<Type*>(this)->UnwrapAliasIfNeeded();
}
/// Removes all levels of aliasing and access control. /// Removes all levels of aliasing and access control.
/// This is just enough to assist with WGSL translation /// This is just enough to assist with WGSL translation
@ -69,31 +58,14 @@ class Type : public Castable<Type, Node> {
/// identifier-like expression as an l-value to its corresponding r-value, /// identifier-like expression as an l-value to its corresponding r-value,
/// plus see through the wrappers on either side. /// plus see through the wrappers on either side.
/// @returns the completely unaliased type. /// @returns the completely unaliased type.
Type* UnwrapIfNeeded(); const Type* UnwrapIfNeeded() const;
/// Removes all levels of aliasing and access control.
/// This is just enough to assist with WGSL translation
/// in that you want see through one level of pointer to get from an
/// identifier-like expression as an l-value to its corresponding r-value,
/// plus see through the wrappers on either side.
/// @returns the completely unaliased type.
const Type* UnwrapIfNeeded() const {
return const_cast<Type*>(this)->UnwrapIfNeeded();
}
/// Returns the type found after: /// Returns the type found after:
/// - removing all layers of aliasing and access control if they exist, then /// - removing all layers of aliasing and access control if they exist, then
/// - removing the pointer, if it exists, then /// - removing the pointer, if it exists, then
/// - removing all further layers of aliasing or access control, if they exist /// - removing all further layers of aliasing or access control, if they exist
/// @returns the unwrapped type /// @returns the unwrapped type
Type* UnwrapAll(); const Type* UnwrapAll() const;
/// Returns the type found after:
/// - removing all layers of aliasing and access control if they exist, then
/// - removing the pointer, if it exists, then
/// - removing all further layers of aliasing or access control, if they exist
/// @returns the unwrapped type
const Type* UnwrapAll() const { return const_cast<Type*>(this)->UnwrapAll(); }
/// @returns true if this type is a scalar /// @returns true if this type is a scalar
bool is_scalar() const; bool is_scalar() const;

View File

@ -34,7 +34,6 @@ class Variable;
namespace sem { namespace sem {
// Forward declarations // Forward declarations
class Array; class Array;
class ArrayType;
class Call; class Call;
class Expression; class Expression;
class Function; class Function;
@ -51,7 +50,6 @@ class Variable;
/// rules will be used to infer the return type based on the argument type. /// rules will be used to infer the return type based on the argument type.
struct TypeMappings { struct TypeMappings {
//! @cond Doxygen_Suppress //! @cond Doxygen_Suppress
Array* operator()(sem::ArrayType*);
Call* operator()(ast::CallExpression*); Call* operator()(ast::CallExpression*);
Expression* operator()(ast::Expression*); Expression* operator()(ast::Expression*);
Function* operator()(ast::Function*); Function* operator()(ast::Function*);

View File

@ -27,7 +27,7 @@ TEST_F(U32Test, Is) {
Type* ty = &u; Type* ty = &u;
EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<AccessControl>());
EXPECT_FALSE(ty->Is<Alias>()); EXPECT_FALSE(ty->Is<Alias>());
EXPECT_FALSE(ty->Is<ArrayType>()); EXPECT_FALSE(ty->Is<Array>());
EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<Bool>());
EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<F32>());
EXPECT_FALSE(ty->Is<I32>()); EXPECT_FALSE(ty->Is<I32>());

View File

@ -35,7 +35,7 @@ TEST_F(VectorTest, Is) {
Type* ty = &v; Type* ty = &v;
EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<AccessControl>());
EXPECT_FALSE(ty->Is<Alias>()); EXPECT_FALSE(ty->Is<Alias>());
EXPECT_FALSE(ty->Is<ArrayType>()); EXPECT_FALSE(ty->Is<Array>());
EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<Bool>());
EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<F32>());
EXPECT_FALSE(ty->Is<I32>()); EXPECT_FALSE(ty->Is<I32>());

View File

@ -42,7 +42,7 @@ ast::ArrayAccessorExpression* BoundArrayAccessors::Transform(
auto& diags = ctx->dst->Diagnostics(); auto& diags = ctx->dst->Diagnostics();
auto* ret_type = ctx->src->Sem().Get(expr->array())->Type()->UnwrapAll(); auto* ret_type = ctx->src->Sem().Get(expr->array())->Type()->UnwrapAll();
if (!ret_type->Is<sem::ArrayType>() && !ret_type->Is<sem::Matrix>() && if (!ret_type->Is<sem::Array>() && !ret_type->Is<sem::Matrix>() &&
!ret_type->Is<sem::Vector>()) { !ret_type->Is<sem::Vector>()) {
return nullptr; return nullptr;
} }
@ -52,10 +52,10 @@ ast::ArrayAccessorExpression* BoundArrayAccessors::Transform(
uint32_t size = 0; uint32_t size = 0;
bool is_vec = ret_type->Is<sem::Vector>(); bool is_vec = ret_type->Is<sem::Vector>();
bool is_arr = ret_type->Is<sem::ArrayType>(); bool is_arr = ret_type->Is<sem::Array>();
if (is_vec || is_arr) { if (is_vec || is_arr) {
size = is_vec ? ret_type->As<sem::Vector>()->size() size = is_vec ? ret_type->As<sem::Vector>()->size()
: ret_type->As<sem::ArrayType>()->size(); : ret_type->As<sem::Array>()->Count();
} else { } else {
// The row accessor would have been an embedded array accessor and already // The row accessor would have been an embedded array accessor and already
// handled, so we just need to do columns here. // handled, so we just need to do columns here.

View File

@ -77,8 +77,8 @@ Output CalculateArrayLength::Run(const Program* in, const DataMap&) {
// get_buffer_size_intrinsic() emits the function decorated with // get_buffer_size_intrinsic() emits the function decorated with
// BufferSizeIntrinsic that is transformed by the HLSL writer into a call to // BufferSizeIntrinsic that is transformed by the HLSL writer into a call to
// [RW]ByteAddressBuffer.GetDimensions(). // [RW]ByteAddressBuffer.GetDimensions().
std::unordered_map<sem::Struct*, Symbol> buffer_size_intrinsics; std::unordered_map<const sem::Struct*, Symbol> buffer_size_intrinsics;
auto get_buffer_size_intrinsic = [&](sem::Struct* buffer_type) { auto get_buffer_size_intrinsic = [&](const sem::Struct* buffer_type) {
return utils::GetOrCreate(buffer_size_intrinsics, buffer_type, [&] { return utils::GetOrCreate(buffer_size_intrinsics, buffer_type, [&] {
auto name = ctx.dst->Sym(); auto name = ctx.dst->Sym();
auto* buffer_typename = auto* buffer_typename =

View File

@ -175,8 +175,8 @@ std::unique_ptr<Offset> Mul(LHS&& lhs_, RHS&& rhs_) {
/// TypePair is a pair of types that can be used as a unordered map or set key. /// TypePair is a pair of types that can be used as a unordered map or set key.
struct TypePair { struct TypePair {
sem::Type* first; sem::Type const* first;
sem::Type* second; sem::Type const* second;
bool operator==(const TypePair& rhs) const { bool operator==(const TypePair& rhs) const {
return first == rhs.first && second == rhs.second; return first == rhs.first && second == rhs.second;
} }
@ -188,20 +188,20 @@ struct TypePair {
}; };
/// @returns the size in bytes of a scalar /// @returns the size in bytes of a scalar
uint32_t ScalarSize(sem::Type*) { uint32_t ScalarSize(const sem::Type*) {
// TODO(bclayton): Assumes 32-bit elements // TODO(bclayton): Assumes 32-bit elements
return 4; return 4;
} }
/// @returns the numer of bytes between columns of the given matrix /// @returns the numer of bytes between columns of the given matrix
uint32_t MatrixColumnStride(sem::Matrix* mat) { uint32_t MatrixColumnStride(const sem::Matrix* mat) {
return ScalarSize(mat->type()) * ((mat->rows() == 2) ? 2 : 4); return ScalarSize(mat->type()) * ((mat->rows() == 2) ? 2 : 4);
} }
/// @returns a DecomposeStorageAccess::Intrinsic decoration that can be applied /// @returns a DecomposeStorageAccess::Intrinsic decoration that can be applied
/// to a stub function to load the type `ty`. /// to a stub function to load the type `ty`.
DecomposeStorageAccess::Intrinsic* IntrinsicLoadFor(ProgramBuilder* builder, DecomposeStorageAccess::Intrinsic* IntrinsicLoadFor(ProgramBuilder* builder,
sem::Type* ty) { const sem::Type* ty) {
using Intrinsic = DecomposeStorageAccess::Intrinsic; using Intrinsic = DecomposeStorageAccess::Intrinsic;
auto intrinsic = [builder](Intrinsic::Type type) { auto intrinsic = [builder](Intrinsic::Type type) {
@ -260,7 +260,7 @@ DecomposeStorageAccess::Intrinsic* IntrinsicLoadFor(ProgramBuilder* builder,
/// @returns a DecomposeStorageAccess::Intrinsic decoration that can be applied /// @returns a DecomposeStorageAccess::Intrinsic decoration that can be applied
/// to a stub function to store the type `ty`. /// to a stub function to store the type `ty`.
DecomposeStorageAccess::Intrinsic* IntrinsicStoreFor(ProgramBuilder* builder, DecomposeStorageAccess::Intrinsic* IntrinsicStoreFor(ProgramBuilder* builder,
sem::Type* ty) { const sem::Type* ty) {
using Intrinsic = DecomposeStorageAccess::Intrinsic; using Intrinsic = DecomposeStorageAccess::Intrinsic;
auto intrinsic = [builder](Intrinsic::Type type) { auto intrinsic = [builder](Intrinsic::Type type) {
@ -331,7 +331,7 @@ void InsertGlobal(CloneContext& ctx,
} }
/// @returns the unwrapped, user-declared constructed type of ty. /// @returns the unwrapped, user-declared constructed type of ty.
const ast::NamedType* ConstructedTypeOf(sem::Type* ty) { const ast::NamedType* ConstructedTypeOf(const sem::Type* ty) {
while (true) { while (true) {
if (auto* ptr = ty->As<sem::Pointer>()) { if (auto* ptr = ty->As<sem::Pointer>()) {
ty = ptr->type(); ty = ptr->type();
@ -350,7 +350,7 @@ const ast::NamedType* ConstructedTypeOf(sem::Type* ty) {
} }
/// @returns the given type with all pointers and aliases removed. /// @returns the given type with all pointers and aliases removed.
sem::Type* UnwrapPtrAndAlias(sem::Type* ty) { const sem::Type* UnwrapPtrAndAlias(const sem::Type* ty) {
return ty->UnwrapPtrIfNeeded()->UnwrapAliasIfNeeded()->UnwrapPtrIfNeeded(); return ty->UnwrapPtrIfNeeded()->UnwrapAliasIfNeeded()->UnwrapPtrIfNeeded();
} }
@ -358,7 +358,7 @@ sem::Type* UnwrapPtrAndAlias(sem::Type* ty) {
struct StorageBufferAccess { struct StorageBufferAccess {
sem::Expression const* var = nullptr; // Storage buffer variable sem::Expression const* var = nullptr; // Storage buffer variable
std::unique_ptr<Offset> offset; // The byte offset on var std::unique_ptr<Offset> offset; // The byte offset on var
sem::Type* type = nullptr; // The type of the access sem::Type const* type = nullptr; // The type of the access
operator bool() const { return var; } // Returns true if valid operator bool() const { return var; } // Returns true if valid
}; };
@ -422,8 +422,8 @@ struct DecomposeStorageAccess::State {
/// @return the name of the function that performs the load /// @return the name of the function that performs the load
Symbol LoadFunc(CloneContext& ctx, Symbol LoadFunc(CloneContext& ctx,
const ast::NamedType* insert_after, const ast::NamedType* insert_after,
sem::Type* buf_ty, const sem::Type* buf_ty,
sem::Type* el_ty) { const sem::Type* el_ty) {
return utils::GetOrCreate(load_funcs, TypePair{buf_ty, el_ty}, [&] { return utils::GetOrCreate(load_funcs, TypePair{buf_ty, el_ty}, [&] {
auto* buf_ast_ty = CreateASTTypeFor(&ctx, buf_ty); auto* buf_ast_ty = CreateASTTypeFor(&ctx, buf_ty);
ast::VariableList params = { ast::VariableList params = {
@ -458,13 +458,11 @@ struct DecomposeStorageAccess::State {
member->Type()->UnwrapAll()); member->Type()->UnwrapAll());
values.emplace_back(ctx.dst->Call(load, "buffer", offset)); values.emplace_back(ctx.dst->Call(load, "buffer", offset));
} }
} else if (auto* arr_ty = el_ty->As<sem::ArrayType>()) { } else if (auto* arr = el_ty->As<sem::Array>()) {
auto& sem = ctx.src->Sem(); for (uint32_t i = 0; i < arr->Count(); i++) {
auto* arr = sem.Get(arr_ty);
for (uint32_t i = 0; i < arr_ty->size(); i++) {
auto* offset = ctx.dst->Add("offset", arr->Stride() * i); auto* offset = ctx.dst->Add("offset", arr->Stride() * i);
Symbol load = LoadFunc(ctx, insert_after, buf_ty, Symbol load = LoadFunc(ctx, insert_after, buf_ty,
arr_ty->type()->UnwrapAll()); arr->ElemType()->UnwrapAll());
values.emplace_back(ctx.dst->Call(load, "buffer", offset)); values.emplace_back(ctx.dst->Call(load, "buffer", offset));
} }
} }
@ -491,8 +489,8 @@ struct DecomposeStorageAccess::State {
/// @return the name of the function that performs the store /// @return the name of the function that performs the store
Symbol StoreFunc(CloneContext& ctx, Symbol StoreFunc(CloneContext& ctx,
const ast::NamedType* insert_after, const ast::NamedType* insert_after,
sem::Type* buf_ty, const sem::Type* buf_ty,
sem::Type* el_ty) { const sem::Type* el_ty) {
return utils::GetOrCreate(store_funcs, TypePair{buf_ty, el_ty}, [&] { return utils::GetOrCreate(store_funcs, TypePair{buf_ty, el_ty}, [&] {
auto* buf_ast_ty = CreateASTTypeFor(&ctx, buf_ty); auto* buf_ast_ty = CreateASTTypeFor(&ctx, buf_ty);
auto* el_ast_ty = CreateASTTypeFor(&ctx, el_ty); auto* el_ast_ty = CreateASTTypeFor(&ctx, el_ty);
@ -533,14 +531,12 @@ struct DecomposeStorageAccess::State {
auto* call = ctx.dst->Call(store, "buffer", offset, access); auto* call = ctx.dst->Call(store, "buffer", offset, access);
body.emplace_back(ctx.dst->create<ast::CallStatement>(call)); body.emplace_back(ctx.dst->create<ast::CallStatement>(call));
} }
} else if (auto* arr_ty = el_ty->As<sem::ArrayType>()) { } else if (auto* arr = el_ty->As<sem::Array>()) {
auto& sem = ctx.src->Sem(); for (uint32_t i = 0; i < arr->Count(); i++) {
auto* arr = sem.Get(arr_ty);
for (uint32_t i = 0; i < arr_ty->size(); i++) {
auto* offset = ctx.dst->Add("offset", arr->Stride() * i); auto* offset = ctx.dst->Add("offset", arr->Stride() * i);
auto* access = ctx.dst->IndexAccessor("value", ctx.dst->Expr(i)); auto* access = ctx.dst->IndexAccessor("value", ctx.dst->Expr(i));
Symbol store = StoreFunc(ctx, insert_after, buf_ty, Symbol store = StoreFunc(ctx, insert_after, buf_ty,
arr_ty->type()->UnwrapAll()); arr->ElemType()->UnwrapAll());
auto* call = ctx.dst->Call(store, "buffer", offset, access); auto* call = ctx.dst->Call(store, "buffer", offset, access);
body.emplace_back(ctx.dst->create<ast::CallStatement>(call)); body.emplace_back(ctx.dst->create<ast::CallStatement>(call));
} }
@ -689,14 +685,13 @@ Output DecomposeStorageAccess::Run(const Program* in, const DataMap&) {
if (auto* accessor = node->As<ast::ArrayAccessorExpression>()) { if (auto* accessor = node->As<ast::ArrayAccessorExpression>()) {
if (auto access = state.TakeAccess(accessor->array())) { if (auto access = state.TakeAccess(accessor->array())) {
// X[Y] // X[Y]
if (auto* arr_ty = access.type->As<sem::ArrayType>()) { if (auto* arr = access.type->As<sem::Array>()) {
auto stride = sem.Get(arr_ty)->Stride(); auto offset = Mul(arr->Stride(), accessor->idx_expr());
auto offset = Mul(stride, accessor->idx_expr());
state.AddAccess(accessor, state.AddAccess(accessor,
{ {
access.var, access.var,
Add(std::move(access.offset), std::move(offset)), Add(std::move(access.offset), std::move(offset)),
arr_ty->type()->UnwrapAll(), arr->ElemType()->UnwrapAll(),
}); });
continue; continue;
} }

View File

@ -96,7 +96,7 @@ void Hlsl::PromoteInitializersToConstVar(CloneContext& ctx) const {
} }
auto* src_ty = src_sem_expr->Type(); auto* src_ty = src_sem_expr->Type();
if (src_ty->IsAnyOf<sem::ArrayType, sem::Struct>()) { if (src_ty->IsAnyOf<sem::Array, sem::Struct>()) {
// Create a new symbol for the constant // Create a new symbol for the constant
auto dst_symbol = ctx.dst->Sym(); auto dst_symbol = ctx.dst->Sym();
// Clone the type // Clone the type

View File

@ -230,7 +230,7 @@ void Spirv::HandleSampleMaskBuiltins(CloneContext& ctx) const {
// Use the same name as the old variable. // Use the same name as the old variable.
auto var_name = ctx.Clone(var->symbol()); auto var_name = ctx.Clone(var->symbol());
// Use `array<u32, 1>` for the new variable. // Use `array<u32, 1>` for the new variable.
auto type = ctx.dst->ty.array(ctx.dst->ty.u32(), 1u); auto* type = ctx.dst->ty.array(ctx.dst->ty.u32(), 1u);
// Create the new variable. // Create the new variable.
auto* var_arr = ctx.dst->Var(var->source(), var_name, type, auto* var_arr = ctx.dst->Var(var->source(), var_name, type,
var->declared_storage_class(), nullptr, var->declared_storage_class(), nullptr,

View File

@ -95,10 +95,13 @@ ast::Type* Transform::CreateASTTypeFor(CloneContext* ctx, const sem::Type* ty) {
auto* el = CreateASTTypeFor(ctx, v->type()); auto* el = CreateASTTypeFor(ctx, v->type());
return ctx->dst->create<ast::Vector>(el, v->size()); return ctx->dst->create<ast::Vector>(el, v->size());
} }
if (auto* a = ty->As<sem::ArrayType>()) { if (auto* a = ty->As<sem::Array>()) {
auto* el = CreateASTTypeFor(ctx, a->type()); auto* el = CreateASTTypeFor(ctx, a->ElemType());
auto decos = ctx->Clone(a->decorations()); ast::DecorationList decos;
return ctx->dst->create<ast::Array>(el, a->size(), std::move(decos)); if (!a->IsStrideImplicit()) {
decos.emplace_back(ctx->dst->create<ast::StrideDecoration>(a->Stride()));
}
return ctx->dst->create<ast::Array>(el, a->Count(), std::move(decos));
} }
if (auto* ac = ty->As<sem::AccessControl>()) { if (auto* ac = ty->As<sem::AccessControl>()) {
auto* el = CreateASTTypeFor(ctx, ac->type()); auto* el = CreateASTTypeFor(ctx, ac->type());

View File

@ -76,16 +76,23 @@ TEST_F(CreateASTTypeForTest, Vector) {
ASSERT_EQ(vec->As<ast::Vector>()->size(), 2u); ASSERT_EQ(vec->As<ast::Vector>()->size(), 2u);
} }
TEST_F(CreateASTTypeForTest, Array) { TEST_F(CreateASTTypeForTest, ArrayImplicitStride) {
auto* arr = create([](ProgramBuilder& b) { auto* arr = create([](ProgramBuilder& b) {
return b.create<sem::ArrayType>(b.create<sem::F32>(), 4, return b.create<sem::Array>(b.create<sem::F32>(), 2, 4, 4, 32u, true);
ast::DecorationList{
b.create<ast::StrideDecoration>(32u),
});
}); });
ASSERT_TRUE(arr->Is<ast::Array>()); ASSERT_TRUE(arr->Is<ast::Array>());
ASSERT_TRUE(arr->As<ast::Array>()->type()->Is<ast::F32>()); ASSERT_TRUE(arr->As<ast::Array>()->type()->Is<ast::F32>());
ASSERT_EQ(arr->As<ast::Array>()->size(), 4u); ASSERT_EQ(arr->As<ast::Array>()->size(), 2u);
ASSERT_EQ(arr->As<ast::Array>()->decorations().size(), 0u);
}
TEST_F(CreateASTTypeForTest, ArrayNonImplicitStride) {
auto* arr = create([](ProgramBuilder& b) {
return b.create<sem::Array>(b.create<sem::F32>(), 2, 4, 4, 32u, false);
});
ASSERT_TRUE(arr->Is<ast::Array>());
ASSERT_TRUE(arr->As<ast::Array>()->type()->Is<ast::F32>());
ASSERT_EQ(arr->As<ast::Array>()->size(), 2u);
ASSERT_EQ(arr->As<ast::Array>()->decorations().size(), 1u); ASSERT_EQ(arr->As<ast::Array>()->decorations().size(), 1u);
ASSERT_TRUE( ASSERT_TRUE(
arr->As<ast::Array>()->decorations()[0]->Is<ast::StrideDecoration>()); arr->As<ast::Array>()->decorations()[0]->Is<ast::StrideDecoration>());
@ -95,7 +102,6 @@ TEST_F(CreateASTTypeForTest, Array) {
->stride(), ->stride(),
32u); 32u);
} }
TEST_F(CreateASTTypeForTest, AccessControl) { TEST_F(CreateASTTypeForTest, AccessControl) {
auto* ac = create([](ProgramBuilder& b) { auto* ac = create([](ProgramBuilder& b) {
auto* decl = b.Structure("S", {}, {}); auto* decl = b.Structure("S", {}, {});

View File

@ -57,7 +57,7 @@ class Void;
namespace sem { namespace sem {
class AccessControl; class AccessControl;
class Alias; class Alias;
class ArrayType; class Array;
class Bool; class Bool;
class DepthTexture; class DepthTexture;
class ExternalTexture; class ExternalTexture;
@ -241,7 +241,7 @@ using Type = TypePair<ast::Type, sem::Type>;
using AccessControl = TypePair<ast::AccessControl, sem::AccessControl>; using AccessControl = TypePair<ast::AccessControl, sem::AccessControl>;
using Alias = TypePair<ast::Alias, sem::Alias>; using Alias = TypePair<ast::Alias, sem::Alias>;
using Array = TypePair<ast::Array, sem::ArrayType>; using Array = TypePair<ast::Array, sem::Array>;
using Bool = TypePair<ast::Bool, sem::Bool>; using Bool = TypePair<ast::Bool, sem::Bool>;
using DepthTexture = TypePair<ast::DepthTexture, sem::DepthTexture>; using DepthTexture = TypePair<ast::DepthTexture, sem::DepthTexture>;
using ExternalTexture = TypePair<ast::ExternalTexture, sem::ExternalTexture>; using ExternalTexture = TypePair<ast::ExternalTexture, sem::ExternalTexture>;

View File

@ -39,7 +39,7 @@ ast::TypeConstructorExpression* AppendVector(ProgramBuilder* b,
ast::Expression* vector, ast::Expression* vector,
ast::Expression* scalar) { ast::Expression* scalar) {
uint32_t packed_size; uint32_t packed_size;
sem::Type* packed_el_sem_ty; const sem::Type* packed_el_sem_ty;
auto* vector_sem = b->Sem().Get(vector); auto* vector_sem = b->Sem().Get(vector);
auto* vector_ty = vector_sem->Type()->UnwrapPtrIfNeeded(); auto* vector_ty = vector_sem->Type()->UnwrapPtrIfNeeded();
if (auto* vec = vector_ty->As<sem::Vector>()) { if (auto* vec = vector_ty->As<sem::Vector>()) {

View File

@ -1325,7 +1325,7 @@ bool GeneratorImpl::EmitTypeConstructor(std::ostream& pre,
} }
bool brackets = bool brackets =
type->UnwrapAliasIfNeeded()->IsAnyOf<sem::ArrayType, sem::Struct>(); type->UnwrapAliasIfNeeded()->IsAnyOf<sem::Array, sem::Struct>();
if (brackets) { if (brackets) {
out << "{"; out << "{";
@ -1651,7 +1651,7 @@ bool GeneratorImpl::EmitFunctionInternal(std::ostream& out,
return false; return false;
} }
// Array name is output as part of the type // Array name is output as part of the type
if (!type->Is<sem::ArrayType>()) { if (!type->Is<sem::Array>()) {
out << " " << builder_.Symbols().NameFor(v->Declaration()->symbol()); out << " " << builder_.Symbols().NameFor(v->Declaration()->symbol());
} }
} }
@ -1918,7 +1918,7 @@ bool GeneratorImpl::EmitEntryPointData(
if (!EmitType(out, var->Type(), var->StorageClass(), name)) { if (!EmitType(out, var->Type(), var->StorageClass(), name)) {
return false; return false;
} }
if (!var->Type()->UnwrapAliasIfNeeded()->Is<sem::ArrayType>()) { if (!var->Type()->UnwrapAliasIfNeeded()->Is<sem::Array>()) {
out << " " << name; out << " " << name;
} }
@ -2406,18 +2406,18 @@ bool GeneratorImpl::EmitType(std::ostream& out,
if (auto* alias = type->As<sem::Alias>()) { if (auto* alias = type->As<sem::Alias>()) {
out << builder_.Symbols().NameFor(alias->symbol()); out << builder_.Symbols().NameFor(alias->symbol());
} else if (auto* ary = type->As<sem::ArrayType>()) { } else if (auto* ary = type->As<sem::Array>()) {
const sem::Type* base_type = ary; const sem::Type* base_type = ary;
std::vector<uint32_t> sizes; std::vector<uint32_t> sizes;
while (auto* arr = base_type->As<sem::ArrayType>()) { while (auto* arr = base_type->As<sem::Array>()) {
if (arr->IsRuntimeArray()) { if (arr->IsRuntimeSized()) {
TINT_ICE(diagnostics_) TINT_ICE(diagnostics_)
<< "Runtime arrays may only exist in storage buffers, which should " << "Runtime arrays may only exist in storage buffers, which should "
"have been transformed into a ByteAddressBuffer"; "have been transformed into a ByteAddressBuffer";
return false; return false;
} }
sizes.push_back(arr->size()); sizes.push_back(arr->Count());
base_type = arr->type(); base_type = arr->ElemType();
} }
if (!EmitType(out, base_type, storage_class, "")) { if (!EmitType(out, base_type, storage_class, "")) {
return false; return false;
@ -2571,7 +2571,7 @@ bool GeneratorImpl::EmitStructType(std::ostream& out,
return false; return false;
} }
// Array member name will be output with the type // Array member name will be output with the type
if (!mem->Type()->Is<sem::ArrayType>()) { if (!mem->Type()->Is<sem::Array>()) {
out << " " << mem_name; out << " " << mem_name;
} }
@ -2669,7 +2669,7 @@ bool GeneratorImpl::EmitVariable(std::ostream& out,
builder_.Symbols().NameFor(var->symbol()))) { builder_.Symbols().NameFor(var->symbol()))) {
return false; return false;
} }
if (!type->Is<sem::ArrayType>()) { if (!type->Is<sem::Array>()) {
out << " " << builder_.Symbols().NameFor(var->symbol()); out << " " << builder_.Symbols().NameFor(var->symbol());
} }
out << constructor_out.str() << ";" << std::endl; out << constructor_out.str() << ";" << std::endl;
@ -2731,7 +2731,7 @@ bool GeneratorImpl::EmitProgramConstVariable(std::ostream& out,
builder_.Symbols().NameFor(var->symbol()))) { builder_.Symbols().NameFor(var->symbol()))) {
return false; return false;
} }
if (!type->Is<sem::ArrayType>()) { if (!type->Is<sem::Array>()) {
out << " " << builder_.Symbols().NameFor(var->symbol()); out << " " << builder_.Symbols().NameFor(var->symbol());
} }

View File

@ -43,21 +43,25 @@ TEST_F(HlslGeneratorImplTest_Type, EmitType_Alias) {
} }
TEST_F(HlslGeneratorImplTest_Type, EmitType_Array) { TEST_F(HlslGeneratorImplTest_Type, EmitType_Array) {
auto arr = ty.array<bool, 4>(); auto* arr = ty.array<bool, 4>();
Global("G", arr, ast::StorageClass::kPrivate);
GeneratorImpl& gen = Build(); GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.EmitType(out, arr, ast::StorageClass::kNone, "ary")) ASSERT_TRUE(
gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone, "ary"))
<< gen.error(); << gen.error();
EXPECT_EQ(result(), "bool ary[4]"); EXPECT_EQ(result(), "bool ary[4]");
} }
TEST_F(HlslGeneratorImplTest_Type, EmitType_ArrayOfArray) { TEST_F(HlslGeneratorImplTest_Type, EmitType_ArrayOfArray) {
auto arr = ty.array(ty.array<bool, 4>(), 5); auto* arr = ty.array(ty.array<bool, 4>(), 5);
Global("G", arr, ast::StorageClass::kPrivate);
GeneratorImpl& gen = Build(); GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.EmitType(out, arr, ast::StorageClass::kNone, "ary")) ASSERT_TRUE(
gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone, "ary"))
<< gen.error(); << gen.error();
EXPECT_EQ(result(), "bool ary[5][4]"); EXPECT_EQ(result(), "bool ary[5][4]");
} }
@ -65,41 +69,49 @@ TEST_F(HlslGeneratorImplTest_Type, EmitType_ArrayOfArray) {
// TODO(dsinclair): Is this possible? What order should it output in? // TODO(dsinclair): Is this possible? What order should it output in?
TEST_F(HlslGeneratorImplTest_Type, TEST_F(HlslGeneratorImplTest_Type,
DISABLED_EmitType_ArrayOfArrayOfRuntimeArray) { DISABLED_EmitType_ArrayOfArrayOfRuntimeArray) {
auto arr = ty.array(ty.array(ty.array<bool, 4>(), 5), 0); auto* arr = ty.array(ty.array(ty.array<bool, 4>(), 5), 0);
Global("G", arr, ast::StorageClass::kPrivate);
GeneratorImpl& gen = Build(); GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.EmitType(out, arr, ast::StorageClass::kNone, "ary")) ASSERT_TRUE(
gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone, "ary"))
<< gen.error(); << gen.error();
EXPECT_EQ(result(), "bool ary[5][4][1]"); EXPECT_EQ(result(), "bool ary[5][4][1]");
} }
TEST_F(HlslGeneratorImplTest_Type, EmitType_ArrayOfArrayOfArray) { TEST_F(HlslGeneratorImplTest_Type, EmitType_ArrayOfArrayOfArray) {
auto arr = ty.array(ty.array(ty.array<bool, 4>(), 5), 6); auto* arr = ty.array(ty.array(ty.array<bool, 4>(), 5), 6);
Global("G", arr, ast::StorageClass::kPrivate);
GeneratorImpl& gen = Build(); GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.EmitType(out, arr, ast::StorageClass::kNone, "ary")) ASSERT_TRUE(
gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone, "ary"))
<< gen.error(); << gen.error();
EXPECT_EQ(result(), "bool ary[6][5][4]"); EXPECT_EQ(result(), "bool ary[6][5][4]");
} }
TEST_F(HlslGeneratorImplTest_Type, EmitType_Array_WithoutName) { TEST_F(HlslGeneratorImplTest_Type, EmitType_Array_WithoutName) {
auto arr = ty.array<bool, 4>(); auto* arr = ty.array<bool, 4>();
Global("G", arr, ast::StorageClass::kPrivate);
GeneratorImpl& gen = Build(); GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.EmitType(out, arr, ast::StorageClass::kNone, "")) ASSERT_TRUE(
gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone, ""))
<< gen.error(); << gen.error();
EXPECT_EQ(result(), "bool[4]"); EXPECT_EQ(result(), "bool[4]");
} }
TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_RuntimeArray) { TEST_F(HlslGeneratorImplTest_Type, DISABLED_EmitType_RuntimeArray) {
auto arr = ty.array<bool>(); auto* arr = ty.array<bool>();
Global("G", arr, ast::StorageClass::kPrivate);
GeneratorImpl& gen = Build(); GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.EmitType(out, arr, ast::StorageClass::kNone, "ary")) ASSERT_TRUE(
gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone, "ary"))
<< gen.error(); << gen.error();
EXPECT_EQ(result(), "bool ary[]"); EXPECT_EQ(result(), "bool ary[]");
} }

View File

@ -19,6 +19,7 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "src/ast/alias.h"
#include "src/ast/bool_literal.h" #include "src/ast/bool_literal.h"
#include "src/ast/call_statement.h" #include "src/ast/call_statement.h"
#include "src/ast/fallthrough_statement.h" #include "src/ast/fallthrough_statement.h"
@ -32,7 +33,6 @@
#include "src/sem/access_control_type.h" #include "src/sem/access_control_type.h"
#include "src/sem/alias_type.h" #include "src/sem/alias_type.h"
#include "src/sem/array.h" #include "src/sem/array.h"
#include "src/sem/array_type.h"
#include "src/sem/bool_type.h" #include "src/sem/bool_type.h"
#include "src/sem/call.h" #include "src/sem/call.h"
#include "src/sem/depth_texture_type.h" #include "src/sem/depth_texture_type.h"
@ -88,10 +88,12 @@ bool GeneratorImpl::Generate() {
} }
for (auto* const ty : program_->AST().ConstructedTypes()) { for (auto* const ty : program_->AST().ConstructedTypes()) {
if (!ty->Is<ast::Alias>()) {
if (!EmitConstructedType(TypeOf(ty))) { if (!EmitConstructedType(TypeOf(ty))) {
return false; return false;
} }
} }
}
if (!program_->AST().ConstructedTypes().empty()) { if (!program_->AST().ConstructedTypes().empty()) {
out_ << std::endl; out_ << std::endl;
} }
@ -889,7 +891,7 @@ bool GeneratorImpl::EmitContinue(ast::ContinueStatement*) {
bool GeneratorImpl::EmitTypeConstructor(ast::TypeConstructorExpression* expr) { bool GeneratorImpl::EmitTypeConstructor(ast::TypeConstructorExpression* expr) {
auto* type = TypeOf(expr); auto* type = TypeOf(expr);
if (type->IsAnyOf<sem::ArrayType, sem::Struct>()) { if (type->IsAnyOf<sem::Array, sem::Struct>()) {
out_ << "{"; out_ << "{";
} else { } else {
if (!EmitType(type, "")) { if (!EmitType(type, "")) {
@ -918,7 +920,7 @@ bool GeneratorImpl::EmitTypeConstructor(ast::TypeConstructorExpression* expr) {
} }
} }
if (type->IsAnyOf<sem::ArrayType, sem::Struct>()) { if (type->IsAnyOf<sem::Array, sem::Struct>()) {
out_ << "}"; out_ << "}";
} else { } else {
out_ << ")"; out_ << ")";
@ -942,9 +944,9 @@ bool GeneratorImpl::EmitZeroValue(typ::Type type) {
return EmitZeroValue(vec->type()); return EmitZeroValue(vec->type());
} else if (auto* mat = type->As<sem::Matrix>()) { } else if (auto* mat = type->As<sem::Matrix>()) {
return EmitZeroValue(mat->type()); return EmitZeroValue(mat->type());
} else if (auto* arr = type->As<sem::ArrayType>()) { } else if (auto* arr = type->As<sem::Array>()) {
out_ << "{"; out_ << "{";
if (!EmitZeroValue(arr->type())) { if (!EmitZeroValue(arr->ElemType())) {
return false; return false;
} }
out_ << "}"; out_ << "}";
@ -1331,7 +1333,7 @@ bool GeneratorImpl::EmitFunctionInternal(ast::Function* func,
return false; return false;
} }
// Array name is output as part of the type // Array name is output as part of the type
if (!type->Is<sem::ArrayType>()) { if (!type->Is<sem::Array>()) {
out_ << " " << program_->Symbols().NameFor(v->symbol()); out_ << " " << program_->Symbols().NameFor(v->symbol());
} }
} }
@ -1918,16 +1920,16 @@ bool GeneratorImpl::EmitType(typ::Type type, const std::string& name) {
if (auto* alias = type->As<sem::Alias>()) { if (auto* alias = type->As<sem::Alias>()) {
out_ << program_->Symbols().NameFor(alias->symbol()); out_ << program_->Symbols().NameFor(alias->symbol());
} else if (auto* ary = type->As<sem::ArrayType>()) { } else if (auto* ary = type->As<sem::Array>()) {
sem::Type* base_type = ary; const sem::Type* base_type = ary;
std::vector<uint32_t> sizes; std::vector<uint32_t> sizes;
while (auto* arr = base_type->As<sem::ArrayType>()) { while (auto* arr = base_type->As<sem::Array>()) {
if (arr->IsRuntimeArray()) { if (arr->IsRuntimeSized()) {
sizes.push_back(1); sizes.push_back(1);
} else { } else {
sizes.push_back(arr->size()); sizes.push_back(arr->Count());
} }
base_type = arr->type(); base_type = arr->ElemType();
} }
if (!EmitType(base_type, "")) { if (!EmitType(base_type, "")) {
return false; return false;
@ -2122,7 +2124,7 @@ bool GeneratorImpl::EmitStructType(const sem::Struct* str) {
auto* ty = mem->Type()->UnwrapAliasIfNeeded(); auto* ty = mem->Type()->UnwrapAliasIfNeeded();
// Array member name will be output with the type // Array member name will be output with the type
if (!ty->Is<sem::ArrayType>()) { if (!ty->Is<sem::Array>()) {
out_ << " " << name; out_ << " " << name;
} }
@ -2223,7 +2225,7 @@ bool GeneratorImpl::EmitVariable(const sem::Variable* var,
if (!EmitType(var->Type(), program_->Symbols().NameFor(decl->symbol()))) { if (!EmitType(var->Type(), program_->Symbols().NameFor(decl->symbol()))) {
return false; return false;
} }
if (!var->Type()->Is<sem::ArrayType>()) { if (!var->Type()->Is<sem::Array>()) {
out_ << " " << program_->Symbols().NameFor(decl->symbol()); out_ << " " << program_->Symbols().NameFor(decl->symbol());
} }
@ -2266,7 +2268,7 @@ bool GeneratorImpl::EmitProgramConstVariable(const ast::Variable* var) {
if (!EmitType(type, program_->Symbols().NameFor(var->symbol()))) { if (!EmitType(type, program_->Symbols().NameFor(var->symbol()))) {
return false; return false;
} }
if (!type->Is<sem::ArrayType>()) { if (!type->Is<sem::Array>()) {
out_ << " " << program_->Symbols().NameFor(var->symbol()); out_ << " " << program_->Symbols().NameFor(var->symbol());
} }
@ -2284,7 +2286,7 @@ bool GeneratorImpl::EmitProgramConstVariable(const ast::Variable* var) {
} }
GeneratorImpl::SizeAndAlign GeneratorImpl::MslPackedTypeSizeAndAlign( GeneratorImpl::SizeAndAlign GeneratorImpl::MslPackedTypeSizeAndAlign(
sem::Type* ty) { const sem::Type* ty) {
ty = ty->UnwrapAliasIfNeeded(); ty = ty->UnwrapAliasIfNeeded();
if (ty->IsAnyOf<sem::U32, sem::I32, sem::F32>()) { if (ty->IsAnyOf<sem::U32, sem::I32, sem::F32>()) {
@ -2327,14 +2329,9 @@ GeneratorImpl::SizeAndAlign GeneratorImpl::MslPackedTypeSizeAndAlign(
} }
} }
if (auto* arr = ty->As<sem::ArrayType>()) { if (auto* arr = ty->As<sem::Array>()) {
auto* sem = program_->Sem().Get(arr); auto el_size_align = MslPackedTypeSizeAndAlign(arr->ElemType());
if (!sem) { if (arr->Stride() != el_size_align.size) {
TINT_ICE(diagnostics_) << "Array missing semantic info";
return {};
}
auto el_size_align = MslPackedTypeSizeAndAlign(arr->type());
if (sem->Stride() != el_size_align.size) {
// TODO(crbug.com/tint/649): transform::Msl needs to replace these arrays // TODO(crbug.com/tint/649): transform::Msl needs to replace these arrays
// with a new array type that has the element type padded to the required // with a new array type that has the element type padded to the required
// stride. // stride.
@ -2342,7 +2339,7 @@ GeneratorImpl::SizeAndAlign GeneratorImpl::MslPackedTypeSizeAndAlign(
<< "Arrays with custom strides not yet implemented"; << "Arrays with custom strides not yet implemented";
return {}; return {};
} }
auto num_els = std::max<uint32_t>(arr->size(), 1); auto num_els = std::max<uint32_t>(arr->Count(), 1);
return SizeAndAlign{el_size_align.size * num_els, el_size_align.align}; return SizeAndAlign{el_size_align.size * num_els, el_size_align.align};
} }

View File

@ -287,7 +287,7 @@ class GeneratorImpl : public TextGenerator {
/// @returns the MSL packed type size and alignment in bytes for the given /// @returns the MSL packed type size and alignment in bytes for the given
/// type. /// type.
SizeAndAlign MslPackedTypeSizeAndAlign(sem::Type* ty); SizeAndAlign MslPackedTypeSizeAndAlign(const sem::Type* ty);
ScopeStack<const sem::Variable*> global_variables_; ScopeStack<const sem::Variable*> global_variables_;
Symbol current_ep_sym_; Symbol current_ep_sym_;

View File

@ -67,62 +67,68 @@ TEST_F(MslGeneratorImplTest, EmitType_Alias) {
} }
TEST_F(MslGeneratorImplTest, EmitType_Array) { TEST_F(MslGeneratorImplTest, EmitType_Array) {
auto arr = ty.array<bool, 4>(); auto* arr = ty.array<bool, 4>();
Global("G", arr, ast::StorageClass::kPrivate);
GeneratorImpl& gen = Build(); GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.EmitType(arr, "ary")) << gen.error(); ASSERT_TRUE(gen.EmitType(program->TypeOf(arr), "ary")) << gen.error();
EXPECT_EQ(gen.result(), "bool ary[4]"); EXPECT_EQ(gen.result(), "bool ary[4]");
} }
TEST_F(MslGeneratorImplTest, EmitType_ArrayOfArray) { TEST_F(MslGeneratorImplTest, EmitType_ArrayOfArray) {
auto a = ty.array<bool, 4>(); auto* a = ty.array<bool, 4>();
auto b = ty.array(a, 5); auto* b = ty.array(a, 5);
Global("G", b, ast::StorageClass::kPrivate);
GeneratorImpl& gen = Build(); GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.EmitType(b, "ary")) << gen.error(); ASSERT_TRUE(gen.EmitType(program->TypeOf(b), "ary")) << gen.error();
EXPECT_EQ(gen.result(), "bool ary[5][4]"); EXPECT_EQ(gen.result(), "bool ary[5][4]");
} }
// TODO(dsinclair): Is this possible? What order should it output in? // TODO(dsinclair): Is this possible? What order should it output in?
TEST_F(MslGeneratorImplTest, DISABLED_EmitType_ArrayOfArrayOfRuntimeArray) { TEST_F(MslGeneratorImplTest, DISABLED_EmitType_ArrayOfArrayOfRuntimeArray) {
auto a = ty.array<bool, 4>(); auto* a = ty.array<bool, 4>();
auto b = ty.array(a, 5); auto* b = ty.array(a, 5);
auto c = ty.array(b, 0); auto* c = ty.array(b, 0);
Global("G", c, ast::StorageClass::kPrivate);
GeneratorImpl& gen = Build(); GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.EmitType(c, "ary")) << gen.error(); ASSERT_TRUE(gen.EmitType(program->TypeOf(c), "ary")) << gen.error();
EXPECT_EQ(gen.result(), "bool ary[5][4][1]"); EXPECT_EQ(gen.result(), "bool ary[5][4][1]");
} }
TEST_F(MslGeneratorImplTest, EmitType_ArrayOfArrayOfArray) { TEST_F(MslGeneratorImplTest, EmitType_ArrayOfArrayOfArray) {
auto a = ty.array<bool, 4>(); auto* a = ty.array<bool, 4>();
auto b = ty.array(a, 5); auto* b = ty.array(a, 5);
auto c = ty.array(b, 6); auto* c = ty.array(b, 6);
Global("G", c, ast::StorageClass::kPrivate);
GeneratorImpl& gen = Build(); GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.EmitType(c, "ary")) << gen.error(); ASSERT_TRUE(gen.EmitType(program->TypeOf(c), "ary")) << gen.error();
EXPECT_EQ(gen.result(), "bool ary[6][5][4]"); EXPECT_EQ(gen.result(), "bool ary[6][5][4]");
} }
TEST_F(MslGeneratorImplTest, EmitType_Array_WithoutName) { TEST_F(MslGeneratorImplTest, EmitType_Array_WithoutName) {
auto arr = ty.array<bool, 4>(); auto* arr = ty.array<bool, 4>();
Global("G", arr, ast::StorageClass::kPrivate);
GeneratorImpl& gen = Build(); GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.EmitType(arr, "")) << gen.error(); ASSERT_TRUE(gen.EmitType(program->TypeOf(arr), "")) << gen.error();
EXPECT_EQ(gen.result(), "bool[4]"); EXPECT_EQ(gen.result(), "bool[4]");
} }
TEST_F(MslGeneratorImplTest, EmitType_RuntimeArray) { TEST_F(MslGeneratorImplTest, EmitType_RuntimeArray) {
auto arr = ty.array<bool, 1>(); auto* arr = ty.array<bool, 1>();
Global("G", arr, ast::StorageClass::kPrivate);
GeneratorImpl& gen = Build(); GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.EmitType(arr, "ary")) << gen.error(); ASSERT_TRUE(gen.EmitType(program->TypeOf(arr), "ary")) << gen.error();
EXPECT_EQ(gen.result(), "bool ary[1]"); EXPECT_EQ(gen.result(), "bool ary[1]");
} }
@ -411,13 +417,13 @@ TEST_F(MslGeneratorImplTest, EmitType_Struct_Layout_ArrayDefaultStride) {
}); });
// array_x: size(28), align(4) // array_x: size(28), align(4)
auto array_x = ty.array<f32, 7>(); auto* array_x = ty.array<f32, 7>();
// array_y: size(4096), align(512) // array_y: size(4096), align(512)
auto array_y = ty.array(inner, 4); auto* array_y = ty.array(inner, 4);
// array_z: size(4), align(4) // array_z: size(4), align(4)
auto array_z = ty.array<f32>(); auto* array_z = ty.array<f32>();
auto* s = auto* s =
Structure("S", Structure("S",

View File

@ -111,9 +111,9 @@ uint32_t IndexFromName(char name) {
/// one or more levels of an arrays inside of `type`. /// one or more levels of an arrays inside of `type`.
/// @param type the given type, which must not be null /// @param type the given type, which must not be null
/// @returns the nested matrix type, or nullptr if none /// @returns the nested matrix type, or nullptr if none
sem::Matrix* GetNestedMatrixType(sem::Type* type) { const sem::Matrix* GetNestedMatrixType(const sem::Type* type) {
while (auto* arr = type->As<sem::ArrayType>()) { while (auto* arr = type->As<sem::Array>()) {
type = arr->type(); type = arr->ElemType();
} }
return type->As<sem::Matrix>(); return type->As<sem::Matrix>();
} }
@ -251,7 +251,7 @@ uint32_t intrinsic_to_glsl_method(const sem::Intrinsic* intrinsic) {
} }
/// @return the vector element type if ty is a vector, otherwise return ty. /// @return the vector element type if ty is a vector, otherwise return ty.
sem::Type* ElementTypeOf(sem::Type* ty) { const sem::Type* ElementTypeOf(const sem::Type* ty) {
if (auto* v = ty->As<sem::Vector>()) { if (auto* v = ty->As<sem::Vector>()) {
return v->type(); return v->type();
} }
@ -1067,9 +1067,9 @@ uint32_t Builder::GenerateAccessorExpression(ast::Expression* expr) {
// how the Resolver currently determines the type of these expression. This // how the Resolver currently determines the type of these expression. This
// should be fixed when proper support for ptr/ref types is implemented. // should be fixed when proper support for ptr/ref types is implemented.
if (auto* array = accessors[0]->As<ast::ArrayAccessorExpression>()) { if (auto* array = accessors[0]->As<ast::ArrayAccessorExpression>()) {
auto* ary_res_type = TypeOf(array->array())->As<sem::ArrayType>(); auto* ary_res_type = TypeOf(array->array())->As<sem::Array>();
if (ary_res_type && if (ary_res_type &&
(!ary_res_type->type()->is_scalar() || (!ary_res_type->ElemType()->is_scalar() ||
!array->idx_expr()->Is<ast::ScalarConstructorExpression>())) { !array->idx_expr()->Is<ast::ScalarConstructorExpression>())) {
// Wrap the source type in a pointer to function storage. // Wrap the source type in a pointer to function storage.
auto ptr = auto ptr =
@ -1167,7 +1167,7 @@ uint32_t Builder::GenerateIdentifierExpression(
return 0; return 0;
} }
uint32_t Builder::GenerateLoadIfNeeded(sem::Type* type, uint32_t id) { uint32_t Builder::GenerateLoadIfNeeded(const sem::Type* type, uint32_t id) {
if (!type->Is<sem::Pointer>()) { if (!type->Is<sem::Pointer>()) {
return id; return id;
} }
@ -1287,13 +1287,13 @@ bool Builder::is_constructor_const(ast::Expression* expr, bool is_global_init) {
continue; continue;
} }
sem::Type* subtype = result_type->UnwrapAll(); const sem::Type* subtype = result_type->UnwrapAll();
if (auto* vec = subtype->As<sem::Vector>()) { if (auto* vec = subtype->As<sem::Vector>()) {
subtype = vec->type()->UnwrapAll(); subtype = vec->type()->UnwrapAll();
} else if (auto* mat = subtype->As<sem::Matrix>()) { } else if (auto* mat = subtype->As<sem::Matrix>()) {
subtype = mat->type()->UnwrapAll(); subtype = mat->type()->UnwrapAll();
} else if (auto* arr = subtype->As<sem::ArrayType>()) { } else if (auto* arr = subtype->As<sem::Array>()) {
subtype = arr->type()->UnwrapAll(); subtype = arr->ElemType()->UnwrapAll();
} else if (auto* str = subtype->As<sem::Struct>()) { } else if (auto* str = subtype->As<sem::Struct>()) {
subtype = str->Members()[i]->Type()->UnwrapAll(); subtype = str->Members()[i]->Type()->UnwrapAll();
} }
@ -1373,7 +1373,7 @@ uint32_t Builder::GenerateTypeConstructorExpression(
// If the result is not a vector then we should have validated that the // If the result is not a vector then we should have validated that the
// value type is a correctly sized vector so we can just use it directly. // value type is a correctly sized vector so we can just use it directly.
if (result_type == value_type || result_type->Is<sem::Matrix>() || if (result_type == value_type || result_type->Is<sem::Matrix>() ||
result_type->Is<sem::ArrayType>() || result_type->Is<sem::Struct>()) { result_type->Is<sem::Array>() || result_type->Is<sem::Struct>()) {
out << "_" << id; out << "_" << id;
ops.push_back(Operand::Int(id)); ops.push_back(Operand::Int(id));
@ -2574,7 +2574,7 @@ bool Builder::GenerateControlBarrierIntrinsic(const sem::Intrinsic* intrinsic) {
}); });
} }
uint32_t Builder::GenerateSampledImage(sem::Type* texture_type, uint32_t Builder::GenerateSampledImage(const sem::Type* texture_type,
Operand texture_operand, Operand texture_operand,
Operand sampler_operand) { Operand sampler_operand) {
uint32_t sampled_image_type_id = 0; uint32_t sampled_image_type_id = 0;
@ -2996,7 +2996,7 @@ uint32_t Builder::GenerateTypeIfNeeded(const sem::Type* type) {
result)) { result)) {
return 0; return 0;
} }
} else if (auto* arr = type->As<sem::ArrayType>()) { } else if (auto* arr = type->As<sem::Array>()) {
if (!GenerateArrayType(arr, result)) { if (!GenerateArrayType(arr, result)) {
return 0; return 0;
} }
@ -3126,18 +3126,17 @@ bool Builder::GenerateTextureType(const sem::Texture* texture,
return true; return true;
} }
bool Builder::GenerateArrayType(const sem::ArrayType* ary, bool Builder::GenerateArrayType(const sem::Array* ary, const Operand& result) {
const Operand& result) { auto elem_type = GenerateTypeIfNeeded(ary->ElemType());
auto elem_type = GenerateTypeIfNeeded(ary->type());
if (elem_type == 0) { if (elem_type == 0) {
return false; return false;
} }
auto result_id = result.to_i(); auto result_id = result.to_i();
if (ary->IsRuntimeArray()) { if (ary->IsRuntimeSized()) {
push_type(spv::Op::OpTypeRuntimeArray, {result, Operand::Int(elem_type)}); push_type(spv::Op::OpTypeRuntimeArray, {result, Operand::Int(elem_type)});
} else { } else {
auto len_id = GenerateConstantIfNeeded(ScalarConstant::U32(ary->size())); auto len_id = GenerateConstantIfNeeded(ScalarConstant::U32(ary->Count()));
if (len_id == 0) { if (len_id == 0) {
return false; return false;
} }
@ -3146,14 +3145,9 @@ bool Builder::GenerateArrayType(const sem::ArrayType* ary,
{result, Operand::Int(elem_type), Operand::Int(len_id)}); {result, Operand::Int(elem_type), Operand::Int(len_id)});
} }
auto* sem_arr = builder_.Sem().Get(ary);
if (!sem_arr) {
error_ = "array type missing semantic info";
return false;
}
push_annot(spv::Op::OpDecorate, push_annot(spv::Op::OpDecorate,
{Operand::Int(result_id), Operand::Int(SpvDecorationArrayStride), {Operand::Int(result_id), Operand::Int(SpvDecorationArrayStride),
Operand::Int(sem_arr->Stride())}); Operand::Int(ary->Stride())});
return true; return true;
} }

View File

@ -65,7 +65,7 @@ class Builder {
uint32_t source_id; uint32_t source_id;
/// The type of the current chain source. This type matches the deduced /// The type of the current chain source. This type matches the deduced
/// result_type of the current source defined above. /// result_type of the current source defined above.
sem::Type* source_type; const sem::Type* source_type;
/// A list of access chain indices to emit. Note, we _only_ have access /// A list of access chain indices to emit. Note, we _only_ have access
/// chain indices if the source is pointer. /// chain indices if the source is pointer.
std::vector<uint32_t> access_chain_indices; std::vector<uint32_t> access_chain_indices;
@ -263,7 +263,7 @@ class Builder {
/// @param type the type to generate for /// @param type the type to generate for
/// @param struct_id the struct id /// @param struct_id the struct id
/// @param member_idx the member index /// @param member_idx the member index
void GenerateMemberAccessControlIfNeeded(sem::Type* type, void GenerateMemberAccessControlIfNeeded(const sem::Type* type,
uint32_t struct_id, uint32_t struct_id,
uint32_t member_idx); uint32_t member_idx);
/// Generates a function variable /// Generates a function variable
@ -372,7 +372,7 @@ class Builder {
/// @param texture_operand the texture operand /// @param texture_operand the texture operand
/// @param sampler_operand the sampler operand /// @param sampler_operand the sampler operand
/// @returns the expression ID /// @returns the expression ID
uint32_t GenerateSampledImage(sem::Type* texture_type, uint32_t GenerateSampledImage(const sem::Type* texture_type,
Operand texture_operand, Operand texture_operand,
Operand sampler_operand); Operand sampler_operand);
/// Generates a cast or object copy for the expression result, /// Generates a cast or object copy for the expression result,
@ -413,7 +413,7 @@ class Builder {
/// @param type the type to load /// @param type the type to load
/// @param id the variable id to load /// @param id the variable id to load
/// @returns the ID of the loaded value or `id` if type is not a pointer /// @returns the ID of the loaded value or `id` if type is not a pointer
uint32_t GenerateLoadIfNeeded(sem::Type* type, uint32_t id); uint32_t GenerateLoadIfNeeded(const sem::Type* type, uint32_t id);
/// Generates an OpStore. Emits an error and returns false if we're /// Generates an OpStore. Emits an error and returns false if we're
/// currently outside a function. /// currently outside a function.
/// @param to the ID to store too /// @param to the ID to store too
@ -433,7 +433,7 @@ class Builder {
/// @param ary the array to generate /// @param ary the array to generate
/// @param result the result operand /// @param result the result operand
/// @returns true if the array was successfully generated /// @returns true if the array was successfully generated
bool GenerateArrayType(const sem::ArrayType* ary, const Operand& result); bool GenerateArrayType(const sem::Array* ary, const Operand& result);
/// Generates a matrix type declaration /// Generates a matrix type declaration
/// @param mat the matrix to generate /// @param mat the matrix to generate
/// @param result the result operand /// @param result the result operand
@ -488,7 +488,7 @@ class Builder {
/// @returns the resolved type of the ast::Expression `expr` /// @returns the resolved type of the ast::Expression `expr`
/// @param expr the expression /// @param expr the expression
sem::Type* TypeOf(ast::Expression* expr) const { const sem::Type* TypeOf(ast::Expression* expr) const {
return builder_.TypeOf(expr); return builder_.TypeOf(expr);
} }

View File

@ -135,7 +135,7 @@ TEST_F(BuilderTest, ArrayAccessor_Dynamic) {
} }
TEST_F(BuilderTest, ArrayAccessor_MultiLevel) { TEST_F(BuilderTest, ArrayAccessor_MultiLevel) {
auto ary4 = ty.array(ty.vec3<f32>(), 4); auto* ary4 = ty.array(ty.vec3<f32>(), 4);
// ary = array<vec3<f32>, 4> // ary = array<vec3<f32>, 4>
// ary[3][2]; // ary[3][2];
@ -173,7 +173,7 @@ TEST_F(BuilderTest, ArrayAccessor_MultiLevel) {
} }
TEST_F(BuilderTest, Accessor_ArrayWithSwizzle) { TEST_F(BuilderTest, Accessor_ArrayWithSwizzle) {
auto ary4 = ty.array(ty.vec3<f32>(), 4); auto* ary4 = ty.array(ty.vec3<f32>(), 4);
// var a : array<vec3<f32>, 4>; // var a : array<vec3<f32>, 4>;
// a[2].xy; // a[2].xy;
@ -696,10 +696,10 @@ TEST_F(BuilderTest, Accessor_Mixed_ArrayAndMember) {
auto* c_type = Structure("C", {Member("baz", ty.vec3<f32>())}); auto* c_type = Structure("C", {Member("baz", ty.vec3<f32>())});
auto* b_type = Structure("B", {Member("bar", c_type)}); auto* b_type = Structure("B", {Member("bar", c_type)});
auto b_ary_type = ty.array(b_type, 3); auto* b_ary_type = ty.array(b_type, 3);
auto* a_type = Structure("A", {Member("foo", b_ary_type)}); auto* a_type = Structure("A", {Member("foo", b_ary_type)});
auto a_ary_type = ty.array(a_type, 2); auto* a_ary_type = ty.array(a_type, 2);
auto* var = Global("index", a_ary_type, ast::StorageClass::kFunction); auto* var = Global("index", a_ary_type, ast::StorageClass::kFunction);
auto* expr = MemberAccessor( auto* expr = MemberAccessor(
MemberAccessor( MemberAccessor(

View File

@ -58,7 +58,7 @@ TEST_F(BuilderTest_Type, ReturnsGeneratedAlias) {
} }
TEST_F(BuilderTest_Type, GenerateRuntimeArray) { TEST_F(BuilderTest_Type, GenerateRuntimeArray) {
auto ary = ty.array(ty.i32(), 0); auto* ary = ty.array(ty.i32(), 0);
auto* str = Structure("S", {Member("x", ary)}, auto* str = Structure("S", {Member("x", ary)},
{create<ast::StructBlockDecoration>()}); {create<ast::StructBlockDecoration>()});
auto ac = ty.access(ast::AccessControl::kReadOnly, str); auto ac = ty.access(ast::AccessControl::kReadOnly, str);
@ -66,7 +66,7 @@ TEST_F(BuilderTest_Type, GenerateRuntimeArray) {
spirv::Builder& b = Build(); spirv::Builder& b = Build();
auto id = b.GenerateTypeIfNeeded(ary); auto id = b.GenerateTypeIfNeeded(program->TypeOf(ary));
ASSERT_FALSE(b.has_error()) << b.error(); ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(1u, id); EXPECT_EQ(1u, id);
@ -76,7 +76,7 @@ TEST_F(BuilderTest_Type, GenerateRuntimeArray) {
} }
TEST_F(BuilderTest_Type, ReturnsGeneratedRuntimeArray) { TEST_F(BuilderTest_Type, ReturnsGeneratedRuntimeArray) {
auto ary = ty.array(ty.i32(), 0); auto* ary = ty.array(ty.i32(), 0);
auto* str = Structure("S", {Member("x", ary)}, auto* str = Structure("S", {Member("x", ary)},
{create<ast::StructBlockDecoration>()}); {create<ast::StructBlockDecoration>()});
auto ac = ty.access(ast::AccessControl::kReadOnly, str); auto ac = ty.access(ast::AccessControl::kReadOnly, str);
@ -84,8 +84,8 @@ TEST_F(BuilderTest_Type, ReturnsGeneratedRuntimeArray) {
spirv::Builder& b = Build(); spirv::Builder& b = Build();
EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u); EXPECT_EQ(b.GenerateTypeIfNeeded(program->TypeOf(ary)), 1u);
EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u); EXPECT_EQ(b.GenerateTypeIfNeeded(program->TypeOf(ary)), 1u);
ASSERT_FALSE(b.has_error()) << b.error(); ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1 EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
@ -94,12 +94,12 @@ TEST_F(BuilderTest_Type, ReturnsGeneratedRuntimeArray) {
} }
TEST_F(BuilderTest_Type, GenerateArray) { TEST_F(BuilderTest_Type, GenerateArray) {
auto ary = ty.array(ty.i32(), 4); auto* ary = ty.array(ty.i32(), 4);
Global("a", ary, ast::StorageClass::kInput); Global("a", ary, ast::StorageClass::kInput);
spirv::Builder& b = Build(); spirv::Builder& b = Build();
auto id = b.GenerateTypeIfNeeded(ary); auto id = b.GenerateTypeIfNeeded(program->TypeOf(ary));
ASSERT_FALSE(b.has_error()) << b.error(); ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(1u, id); EXPECT_EQ(1u, id);
@ -111,12 +111,12 @@ TEST_F(BuilderTest_Type, GenerateArray) {
} }
TEST_F(BuilderTest_Type, GenerateArray_WithStride) { TEST_F(BuilderTest_Type, GenerateArray_WithStride) {
auto ary = ty.array(ty.i32(), 4, 16u); auto* ary = ty.array(ty.i32(), 4, 16u);
Global("a", ary, ast::StorageClass::kInput); Global("a", ary, ast::StorageClass::kInput);
spirv::Builder& b = Build(); spirv::Builder& b = Build();
auto id = b.GenerateTypeIfNeeded(ary); auto id = b.GenerateTypeIfNeeded(program->TypeOf(ary));
ASSERT_FALSE(b.has_error()) << b.error(); ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(1u, id); EXPECT_EQ(1u, id);
@ -131,13 +131,13 @@ TEST_F(BuilderTest_Type, GenerateArray_WithStride) {
} }
TEST_F(BuilderTest_Type, ReturnsGeneratedArray) { TEST_F(BuilderTest_Type, ReturnsGeneratedArray) {
auto ary = ty.array(ty.i32(), 4); auto* ary = ty.array(ty.i32(), 4);
Global("a", ary, ast::StorageClass::kInput); Global("a", ary, ast::StorageClass::kInput);
spirv::Builder& b = Build(); spirv::Builder& b = Build();
EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u); EXPECT_EQ(b.GenerateTypeIfNeeded(program->TypeOf(ary)), 1u);
EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u); EXPECT_EQ(b.GenerateTypeIfNeeded(program->TypeOf(ary)), 1u);
ASSERT_FALSE(b.has_error()) << b.error(); ASSERT_FALSE(b.has_error()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1 EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
@ -445,9 +445,9 @@ TEST_F(BuilderTest_Type, GenerateStruct_DecoratedMembers_LayoutArraysOfMatrix) {
// We have to infer layout for matrix when it also has an offset. // We have to infer layout for matrix when it also has an offset.
// The decoration goes on the struct member, even if the matrix is buried // The decoration goes on the struct member, even if the matrix is buried
// in levels of arrays. // in levels of arrays.
auto arr_mat2x2 = ty.array(ty.mat2x2<f32>(), 1); // Singly nested array auto* arr_mat2x2 = ty.array(ty.mat2x2<f32>(), 1); // Singly nested array
auto arr_arr_mat2x3 = ty.array(ty.mat2x3<f32>(), 1); // Doubly nested array auto* arr_arr_mat2x3 = ty.array(ty.mat2x3<f32>(), 1); // Doubly nested array
auto rtarr_mat4x4 = ty.array(ty.mat4x4<f32>(), 0); // Runtime array auto* rtarr_mat4x4 = ty.array(ty.mat4x4<f32>(), 0); // Runtime array
auto* s = auto* s =
Structure("S", Structure("S",

View File

@ -37,7 +37,7 @@ TEST_F(WgslGeneratorImplTest, EmitType_Alias) {
} }
TEST_F(WgslGeneratorImplTest, EmitType_Array) { TEST_F(WgslGeneratorImplTest, EmitType_Array) {
auto arr = ty.array<bool, 4>(); auto* arr = ty.array<bool, 4>();
AST().AddConstructedType(ty.alias("make_type_reachable", arr)); AST().AddConstructedType(ty.alias("make_type_reachable", arr));
GeneratorImpl& gen = Build(); GeneratorImpl& gen = Build();
@ -73,7 +73,7 @@ TEST_F(WgslGeneratorImplTest, EmitType_AccessControl_ReadWrite) {
} }
TEST_F(WgslGeneratorImplTest, EmitType_Array_Decoration) { TEST_F(WgslGeneratorImplTest, EmitType_Array_Decoration) {
auto a = ty.array(ty.bool_(), 4, 16u); auto* a = ty.array(ty.bool_(), 4, 16u);
AST().AddConstructedType(ty.alias("make_type_reachable", a)); AST().AddConstructedType(ty.alias("make_type_reachable", a));
GeneratorImpl& gen = Build(); GeneratorImpl& gen = Build();
@ -83,7 +83,7 @@ TEST_F(WgslGeneratorImplTest, EmitType_Array_Decoration) {
} }
TEST_F(WgslGeneratorImplTest, EmitType_RuntimeArray) { TEST_F(WgslGeneratorImplTest, EmitType_RuntimeArray) {
auto a = ty.array(ty.bool_(), 0); auto* a = ty.array(ty.bool_(), 0);
AST().AddConstructedType(ty.alias("make_type_reachable", a)); AST().AddConstructedType(ty.alias("make_type_reachable", a));
GeneratorImpl& gen = Build(); GeneratorImpl& gen = Build();

View File

@ -270,7 +270,6 @@ tint_unittests_source_set("tint_unittests_core_src") {
"../src/scope_stack_test.cc", "../src/scope_stack_test.cc",
"../src/sem/access_control_type_test.cc", "../src/sem/access_control_type_test.cc",
"../src/sem/alias_type_test.cc", "../src/sem/alias_type_test.cc",
"../src/sem/array_type_test.cc",
"../src/sem/bool_type_test.cc", "../src/sem/bool_type_test.cc",
"../src/sem/depth_texture_type_test.cc", "../src/sem/depth_texture_type_test.cc",
"../src/sem/external_texture_type_test.cc", "../src/sem/external_texture_type_test.cc",
@ -281,6 +280,7 @@ tint_unittests_source_set("tint_unittests_core_src") {
"../src/sem/pointer_type_test.cc", "../src/sem/pointer_type_test.cc",
"../src/sem/sampled_texture_type_test.cc", "../src/sem/sampled_texture_type_test.cc",
"../src/sem/sampler_type_test.cc", "../src/sem/sampler_type_test.cc",
"../src/sem/sem_array_test.cc",
"../src/sem/sem_struct_test.cc", "../src/sem/sem_struct_test.cc",
"../src/sem/storage_texture_type_test.cc", "../src/sem/storage_texture_type_test.cc",
"../src/sem/texture_type_test.cc", "../src/sem/texture_type_test.cc",

8
test/bug_tint_782.wgsl Normal file
View File

@ -0,0 +1,8 @@
type ArrayExplicitStride = [[stride(4)]] array<i32, 2>;
type ArrayImplicitStride = array<i32, 2>;
fn foo() {
var explicit : ArrayExplicitStride;
var implict : ArrayImplicitStride;
implict = explicit;
}