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>
diff --git a/src/BUILD.gn b/src/BUILD.gn index 699c066..0be18bd 100644 --- a/src/BUILD.gn +++ b/src/BUILD.gn
@@ -450,8 +450,6 @@ "sem/alias_type.cc", "sem/alias_type.h", "sem/array.h", - "sem/array_type.cc", - "sem/array_type.h", "sem/bool_type.cc", "sem/bool_type.h", "sem/call.h",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b2a3369..b067715 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt
@@ -289,8 +289,6 @@ sem/access_control_type.h sem/alias_type.cc sem/alias_type.h - sem/array_type.cc - sem/array_type.h sem/bool_type.cc sem/bool_type.h sem/depth_texture_type.cc @@ -568,7 +566,6 @@ test_main.cc sem/access_control_type_test.cc sem/alias_type_test.cc - sem/array_type_test.cc sem/bool_type_test.cc sem/depth_texture_type_test.cc sem/external_texture_type_test.cc @@ -579,6 +576,7 @@ sem/pointer_type_test.cc sem/sampled_texture_type_test.cc sem/sampler_type_test.cc + sem/sem_array_test.cc sem/sem_struct_test.cc sem/storage_texture_type_test.cc sem/texture_type_test.cc
diff --git a/src/inspector/inspector.cc b/src/inspector/inspector.cc index c91edc1..f1100dc 100644 --- a/src/inspector/inspector.cc +++ b/src/inspector/inspector.cc
@@ -24,7 +24,7 @@ #include "src/ast/sint_literal.h" #include "src/ast/uint_literal.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/function.h" #include "src/sem/i32_type.h" @@ -76,13 +76,13 @@ return ResourceBinding::TextureDimension::kNone; } -ResourceBinding::SampledKind BaseTypeToSampledKind(sem::Type* base_type) { +ResourceBinding::SampledKind BaseTypeToSampledKind(const sem::Type* base_type) { if (!base_type) { return ResourceBinding::SampledKind::kUnknown; } - if (auto* at = base_type->As<sem::ArrayType>()) { - base_type = at->type(); + if (auto* at = base_type->As<sem::Array>()) { + base_type = const_cast<sem::Type*>(at->ElemType()); } else if (auto* mt = base_type->As<sem::Matrix>()) { base_type = mt->type(); } else if (auto* vt = base_type->As<sem::Vector>()) { @@ -650,7 +650,7 @@ entry.dim = TypeTextureDimensionToResourceBindingTextureDimension( texture_type->dim()); - sem::Type* base_type = nullptr; + const sem::Type* base_type = nullptr; if (multisampled_only) { base_type = texture_type->As<sem::MultisampledTexture>() ->type() @@ -702,7 +702,7 @@ entry.dim = TypeTextureDimensionToResourceBindingTextureDimension( 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.image_format = TypeImageFormatToResourceBindingImageFormat( texture_type->image_format());
diff --git a/src/intrinsic_table.cc b/src/intrinsic_table.cc index 276f52c..ec28a4f 100644 --- a/src/intrinsic_table.cc +++ b/src/intrinsic_table.cc
@@ -408,9 +408,9 @@ : element_builder_(element_builder) {} bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override { - if (auto* arr = ty->As<sem::ArrayType>()) { - if (arr->size() == 0) { - return element_builder_->Match(state, arr->type()); + if (auto* arr = ty->As<sem::Array>()) { + if (arr->IsRuntimeSized()) { + return element_builder_->Match(state, arr->ElemType()); } } return false; @@ -418,8 +418,7 @@ sem::Type* Build(BuildState& state) const override { auto* el = element_builder_->Build(state); - return state.ty_mgr.Get<sem::ArrayType>(const_cast<sem::Type*>(el), 0, - ast::DecorationList{}); + return state.ty_mgr.Get<sem::Array>(el, 0, 0, 0, 0, true); } std::string str() const override {
diff --git a/src/intrinsic_table_test.cc b/src/intrinsic_table_test.cc index deae118..3f7874a 100644 --- a/src/intrinsic_table_test.cc +++ b/src/intrinsic_table_test.cc
@@ -202,14 +202,15 @@ } TEST_F(IntrinsicTableTest, MatchArray) { - auto result = table->Lookup(*this, IntrinsicType::kArrayLength, - {ty.array<f32>()}, Source{}); + auto* arr = create<sem::Array>(create<sem::U32>(), 0, 4, 4, 4, true); + auto result = + table->Lookup(*this, IntrinsicType::kArrayLength, {arr}, Source{}); ASSERT_NE(result.intrinsic, nullptr); ASSERT_EQ(result.diagnostics.str(), ""); EXPECT_THAT(result.intrinsic->Type(), IntrinsicType::kArrayLength); EXPECT_THAT(result.intrinsic->ReturnType(), ty.u32()); - EXPECT_THAT(result.intrinsic->Parameters(), - ElementsAre(Parameter{ty.array<f32>()})); + ASSERT_EQ(result.intrinsic->Parameters().size(), 1u); + EXPECT_TRUE(result.intrinsic->Parameters()[0].type->Is<sem::Array>()); } TEST_F(IntrinsicTableTest, MismatchArray) {
diff --git a/src/program_builder.h b/src/program_builder.h index 7b0fdce..b013ffb 100644 --- a/src/program_builder.h +++ b/src/program_builder.h
@@ -61,7 +61,7 @@ #include "src/program_id.h" #include "src/sem/access_control_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/depth_texture_type.h" #include "src/sem/external_texture_type.h" @@ -595,15 +595,11 @@ /// @param n the array size. 0 represents a runtime-array /// @param decos the optional decorations for the array /// @return the tint AST type for a array of size `n` of type `T` - typ::Array array(typ::Type subtype, - uint32_t n = 0, - ast::DecorationList decos = {}) const { + ast::Array* array(typ::Type subtype, + uint32_t n = 0, + ast::DecorationList decos = {}) const { subtype = MaybeCreateTypename(subtype); - return {subtype.ast ? builder->create<ast::Array>(subtype, n, decos) - : nullptr, - subtype.sem ? builder->create<sem::ArrayType>(subtype, n, - std::move(decos)) - : nullptr}; + return builder->create<ast::Array>(subtype, n, decos); } /// @param source the Source of the node @@ -611,24 +607,19 @@ /// @param n the array size. 0 represents a runtime-array /// @param decos the optional decorations for the array /// @return the tint AST type for a array of size `n` of type `T` - typ::Array array(const Source& source, - typ::Type subtype, - uint32_t n = 0, - ast::DecorationList decos = {}) const { + ast::Array* array(const Source& source, + typ::Type subtype, + uint32_t n = 0, + ast::DecorationList decos = {}) const { subtype = MaybeCreateTypename(subtype); - return { - subtype.ast ? builder->create<ast::Array>(source, subtype, n, decos) - : nullptr, - subtype.sem - ? builder->create<sem::ArrayType>(subtype, n, std::move(decos)) - : nullptr}; + return builder->create<ast::Array>(source, subtype, n, decos); } /// @param subtype the array element type /// @param n the array size. 0 represents a runtime-array /// @param stride the array stride /// @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); return array(subtype, n, {builder->create<ast::StrideDecoration>(stride)}); @@ -639,10 +630,10 @@ /// @param n the array size. 0 represents a runtime-array /// @param stride the array stride /// @return the tint AST type for a array of size `n` of type `T` - typ::Array array(const Source& source, - typ::Type subtype, - uint32_t n, - uint32_t stride) const { + ast::Array* array(const Source& source, + typ::Type subtype, + uint32_t n, + uint32_t stride) const { subtype = MaybeCreateTypename(subtype); return array(source, subtype, n, {builder->create<ast::StrideDecoration>(stride)}); @@ -650,14 +641,14 @@ /// @return the tint AST type for an array of size `N` of type `T` template <typename T, int N = 0> - typ::Array array() const { + ast::Array* array() const { return array(Of<T>(), N); } /// @param stride the array stride /// @return the tint AST type for an array of size `N` of type `T` 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); }
diff --git a/src/resolver/decoration_validation_test.cc b/src/resolver/decoration_validation_test.cc index 75ba8a3..2ec17c5 100644 --- a/src/resolver/decoration_validation_test.cc +++ b/src/resolver/decoration_validation_test.cc
@@ -122,7 +122,7 @@ TEST_P(ArrayDecorationTest, IsValid) { auto& params = GetParam(); - auto arr = + auto* arr = ty.array(ty.f32(), 0, { createDecoration(Source{{12, 34}}, *this, params.kind), @@ -360,7 +360,7 @@ << ", should_pass: " << params.should_pass; 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); @@ -445,11 +445,11 @@ Params{ast_mat4x4<f32>, (default_mat4x4.align - 1) * 7, false})); TEST_F(ArrayStrideTest, MultipleDecorations) { - auto arr = ty.array(Source{{12, 34}}, ty.i32(), 4, - { - create<ast::StrideDecoration>(4), - create<ast::StrideDecoration>(4), - }); + auto* arr = ty.array(Source{{12, 34}}, ty.i32(), 4, + { + create<ast::StrideDecoration>(4), + create<ast::StrideDecoration>(4), + }); Global("myarray", arr, ast::StorageClass::kInput); @@ -468,7 +468,7 @@ TEST_F(StructBlockTest, StructUsedAsArrayElement) { auto* s = Structure("S", {Member("x", ty.i32())}, {create<ast::StructBlockDecoration>()}); - auto a = ty.array(s, 4); + auto* a = ty.array(s, 4); Global("G", a, ast::StorageClass::kPrivate); EXPECT_FALSE(r()->Resolve());
diff --git a/src/resolver/intrinsic_test.cc b/src/resolver/intrinsic_test.cc index f73b19a..876fa1d 100644 --- a/src/resolver/intrinsic_test.cc +++ b/src/resolver/intrinsic_test.cc
@@ -756,7 +756,7 @@ using ResolverIntrinsicDataTest = ResolverTest; TEST_F(ResolverIntrinsicDataTest, ArrayLength_Vector) { - auto ary = ty.array<i32>(); + auto* ary = ty.array<i32>(); auto* str = Structure("S", {Member("x", ary)}, {create<ast::StructBlockDecoration>()}); auto ac = ty.access(ast::AccessControl::kReadOnly, str);
diff --git a/src/resolver/is_host_shareable_test.cc b/src/resolver/is_host_shareable_test.cc index 346a132..7f50b06 100644 --- a/src/resolver/is_host_shareable_test.cc +++ b/src/resolver/is_host_shareable_test.cc
@@ -98,11 +98,13 @@ } 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) { - 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
diff --git a/src/resolver/is_storeable_test.cc b/src/resolver/is_storeable_test.cc index 72f9cc6..62010e9 100644 --- a/src/resolver/is_storeable_test.cc +++ b/src/resolver/is_storeable_test.cc
@@ -82,11 +82,13 @@ } 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) { - 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) {
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc index 32273e2..a3162c2 100644 --- a/src/resolver/resolver.cc +++ b/src/resolver/resolver.cc
@@ -178,8 +178,8 @@ if (type->is_scalar() || type->Is<sem::Vector>() || type->Is<sem::Matrix>()) { return true; } - if (auto* arr = type->As<sem::ArrayType>()) { - return IsStorable(arr->type()); + if (auto* arr = type->As<sem::Array>()) { + return IsStorable(arr->ElemType()); } if (auto* str = type->As<sem::Struct>()) { for (const auto* member : str->Members()) { @@ -204,8 +204,8 @@ if (auto* mat = type->As<sem::Matrix>()) { return IsHostShareable(mat->type()); } - if (auto* arr = type->As<sem::ArrayType>()) { - return IsHostShareable(arr->type()); + if (auto* arr = type->As<sem::Array>()) { + return IsHostShareable(arr->ElemType()); } if (auto* str = type->As<sem::Struct>()) { for (auto* member : str->Members()) { @@ -287,7 +287,7 @@ // TODO(crbug.com/tint/724) - Remove once tint:724 is complete. // ast::AccessDecorations are generated by the WGSL parser, used to // 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 // currently de-duplicated by the type manager, and we leak these // decorations. @@ -350,14 +350,7 @@ return nullptr; } if (auto* t = ty->As<ast::Array>()) { - if (auto* el = Type(t->type())) { - auto* sem = builder_->create<sem::ArrayType>( - const_cast<sem::Type*>(el), t->size(), t->decorations()); - if (Array(sem, ty->source())) { - return sem; - } - } - return nullptr; + return Array(t); } if (auto* t = ty->As<ast::Pointer>()) { if (auto* el = Type(t->type())) { @@ -420,9 +413,10 @@ return s; } -Resolver::VariableInfo* Resolver::Variable(ast::Variable* var, - sem::Type* type, /* = nullptr */ - std::string type_name /* = "" */) { +Resolver::VariableInfo* Resolver::Variable( + ast::Variable* var, + const sem::Type* type, /* = nullptr */ + std::string type_name /* = "" */) { auto it = variable_to_info_.find(var); if (it != variable_to_info_.end()) { return it->second; @@ -436,18 +430,10 @@ return nullptr; } - auto* ctype = Canonical(type); + auto* ctype = Canonical(const_cast<sem::Type*>(type)); auto* info = variable_infos_.Create(var, ctype, type_name); 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; } @@ -596,8 +582,8 @@ bool Resolver::ValidateVariable(const ast::Variable* var) { auto* type = variable_to_info_[var]->type; - if (auto* r = type->As<sem::ArrayType>()) { - if (r->IsRuntimeArray()) { + if (auto* r = type->As<sem::Array>()) { + if (r->IsRuntimeSized()) { diagnostics_.add_error( "v-0015", "runtime arrays may only appear as the last member of a struct", @@ -873,8 +859,8 @@ builder_->Symbols().NameFor(func->symbol()), func->source()); return false; - } else if (auto* arr = member_ty->As<sem::ArrayType>()) { - if (arr->IsRuntimeArray()) { + } else if (auto* arr = member_ty->As<sem::Array>()) { + if (arr->IsRuntimeSized()) { diagnostics_.add_error( "entry point IO types cannot contain runtime sized arrays", member->Declaration()->source()); @@ -1276,9 +1262,9 @@ auto* res = TypeOf(expr->array()); auto* parent_type = res->UnwrapAll(); - sem::Type* ret = nullptr; - if (auto* arr = parent_type->As<sem::ArrayType>()) { - ret = arr->type(); + const sem::Type* ret = nullptr; + if (auto* arr = parent_type->As<sem::Array>()) { + ret = arr->ElemType(); } else if (auto* vec = parent_type->As<sem::Vector>()) { ret = vec->type(); } else if (auto* mat = parent_type->As<sem::Matrix>()) { @@ -1293,8 +1279,8 @@ // If we're extracting from a pointer, we return a pointer. if (auto* ptr = res->As<sem::Pointer>()) { ret = builder_->create<sem::Pointer>(ret, ptr->storage_class()); - } else if (auto* arr = parent_type->As<sem::ArrayType>()) { - if (!arr->type()->is_scalar()) { + } else if (auto* arr = parent_type->As<sem::Array>()) { + if (!arr->ElemType()->is_scalar()) { // 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. ret = builder_->create<sem::Pointer>(ret, ast::StorageClass::kFunction); @@ -1459,7 +1445,7 @@ value_cardinality_sum++; } 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 // arguments are present. A single argument constructor constitutes a // type conversion expression. @@ -1754,8 +1740,8 @@ auto* lhs_declared_type = TypeOf(expr->lhs())->UnwrapAll(); auto* rhs_declared_type = TypeOf(expr->rhs())->UnwrapAll(); - auto* lhs_type = Canonical(lhs_declared_type); - auto* rhs_type = Canonical(rhs_declared_type); + auto* lhs_type = Canonical(const_cast<sem::Type*>(lhs_declared_type)); + auto* rhs_type = Canonical(const_cast<sem::Type*>(rhs_declared_type)); auto* lhs_vec = lhs_type->As<Vector>(); auto* lhs_vec_elem_type = lhs_vec ? lhs_vec->type() : nullptr; @@ -2006,7 +1992,7 @@ // If the variable has a declared type, resolve it. std::string type_name; - sem::Type* type = nullptr; + const sem::Type* type = nullptr; if (auto* ast_ty = var->type()) { type_name = ast_ty->FriendlyName(builder_->Symbols()); type = Type(ast_ty); @@ -2065,7 +2051,7 @@ } // TODO(bclayton): Remove this and fix tests. We're overriding the semantic // 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); current_block_->decls.push_back(var); @@ -2251,8 +2237,7 @@ bool Resolver::DefaultAlignAndSize(const sem::Type* ty, uint32_t& align, - uint32_t& size, - const Source& source) { + uint32_t& size) { static constexpr uint32_t vector_size[] = { /* padding */ 0, /* padding */ 0, @@ -2297,76 +2282,71 @@ align = s->Align(); size = s->Size(); return true; - } else if (cty->Is<sem::ArrayType>()) { - if (auto* sem = - Array(ty->UnwrapAliasIfNeeded()->As<sem::ArrayType>(), source)) { - align = sem->Align(); - size = sem->Size(); - return true; - } - return false; + } else if (auto* a = cty->As<sem::Array>()) { + align = a->Align(); + size = a->SizeInBytes(); + return true; } TINT_UNREACHABLE(diagnostics_) << "Invalid type " << ty->TypeInfo().name; return false; } -const sem::Array* Resolver::Array(const sem::ArrayType* arr, - const Source& source) { - if (auto* sem = builder_->Sem().Get(arr)) { - // Semantic info already constructed for this array type - return sem; - } +sem::Array* Resolver::Array(const ast::Array* arr) { + auto source = arr->source(); - if (!ValidateArray(arr, source)) { + auto* el_ty = Type(arr->type()); + if (!el_ty) { return nullptr; } - auto* el_ty = arr->type(); - uint32_t el_align = 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; } - 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 uint32_t explicit_stride = 0; for (auto* deco : arr->decorations()) { Mark(deco); - if (auto* stride = deco->As<ast::StrideDecoration>()) { + if (auto* sd = deco->As<ast::StrideDecoration>()) { if (explicit_stride) { diagnostics_.add_error( "array must have at most one [[stride]] decoration", source); return nullptr; } - explicit_stride = stride->stride(); - if (!ValidateArrayStrideDecoration(stride, el_size, el_align, source)) { + explicit_stride = sd->stride(); + if (!ValidateArrayStrideDecoration(sd, el_size, el_align, source)) { return nullptr; } + continue; } - } - if (explicit_stride) { - return create_semantic(explicit_stride); + + diagnostics_.add_error("decoration is not valid for array types", + deco->source()); + return nullptr; } // Calculate implicit stride 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) { - auto* el_ty = arr->type(); +bool Resolver::ValidateArray(const sem::Array* arr, const Source& source) { + auto* el_ty = arr->ElemType(); if (!IsStorable(el_ty)) { builder_->Diagnostics().add_error( @@ -2416,8 +2396,8 @@ bool Resolver::ValidateStructure(const sem::Struct* str) { for (auto* member : str->Members()) { - if (auto* r = member->Type()->UnwrapAll()->As<sem::ArrayType>()) { - if (r->IsRuntimeArray()) { + if (auto* r = member->Type()->UnwrapAll()->As<sem::Array>()) { + if (r->IsRuntimeSized()) { if (member != str->Members().back()) { diagnostics_.add_error( "v-0015", @@ -2434,14 +2414,6 @@ member->Declaration()->source()); 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 @@ uint32_t offset = struct_size; uint32_t align = 0; uint32_t size = 0; - if (!DefaultAlignAndSize(type, align, size, member->source())) { + if (!DefaultAlignAndSize(type, align, size)) { return nullptr; } @@ -2779,7 +2751,7 @@ bool Resolver::ApplyStorageClassUsageToType(ast::StorageClass sc, sem::Type* ty, const Source& usage) { - ty = ty->UnwrapIfNeeded(); + ty = const_cast<sem::Type*>(ty->UnwrapIfNeeded()); if (auto* str = ty->As<sem::Struct>()) { if (str->StorageClassUsage().count(sc)) { @@ -2801,8 +2773,9 @@ return true; } - if (auto* arr = ty->As<sem::ArrayType>()) { - return ApplyStorageClassUsageToType(sc, arr->type(), usage); + if (auto* arr = ty->As<sem::Array>()) { + return ApplyStorageClassUsageToType( + sc, const_cast<sem::Type*>(arr->ElemType()), usage); } if (ast::IsHostShareable(sc) && !IsHostShareable(ty)) { @@ -2829,7 +2802,8 @@ 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); return vec_type.FriendlyName(builder_->Symbols()); }
diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h index 4e94403..a5f72f8 100644 --- a/src/resolver/resolver.h +++ b/src/resolver/resolver.h
@@ -224,7 +224,7 @@ // AST and Type validation methods // 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, uint32_t el_size, uint32_t el_align, @@ -250,15 +250,18 @@ /// @param ty the ast::Type sem::Type* Type(const ast::Type* ty); - /// @returns the semantic information for the array `arr`, building it if it - /// hasn't been constructed already. If an error is raised, nullptr is - /// returned. + /// Builds and returns the semantic information for the array `arr`. + /// This method does not mark the ast::Array node, nor attach the generated + /// 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 source the Source of the ast node with this array as its type - const sem::Array* Array(const sem::ArrayType* arr, const Source& source); + sem::Array* Array(const ast::Array* arr); - /// @returns the sem::Struct for the AST structure `str`. If an error is - /// raised, nullptr is returned. + /// Builds and returns the semantic information for the structure `str`. + /// 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); /// @returns the VariableInfo for the variable `var`, building it if it hasn't @@ -268,7 +271,7 @@ /// @param type_name optional type name of `var` to use instead of /// `var->type()->FriendlyName()`. VariableInfo* Variable(ast::Variable* var, - sem::Type* type = nullptr, + const sem::Type* type = nullptr, std::string type_name = ""); /// Records the storage class usage for the given type, and any transient @@ -285,12 +288,10 @@ /// @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 source the Source of the variable declaration of type `ty` /// @returns true on success, false on error bool DefaultAlignAndSize(const sem::Type* ty, uint32_t& align, - uint32_t& size, - const Source& source); + uint32_t& size); /// @returns the resolved type of the ast::Expression `expr` /// @param expr the expression @@ -333,7 +334,7 @@ /// @param size the vector dimension /// @param element_type scalar vector sub-element type /// @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 /// the given node has not already been seen. Diamonds in the AST are illegal.
diff --git a/src/resolver/storage_class_validation_test.cc b/src/resolver/storage_class_validation_test.cc index db6be8e..9ee3547 100644 --- a/src/resolver/storage_class_validation_test.cc +++ b/src/resolver/storage_class_validation_test.cc
@@ -61,7 +61,7 @@ TEST_F(ResolverStorageClassValidationTest, StorageBufferArray) { // var<storage> g : [[access(read)]] array<S, 3>; 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); Global(Source{{56, 78}}, "g", ac, ast::StorageClass::kStorage); @@ -169,7 +169,7 @@ TEST_F(ResolverStorageClassValidationTest, UniformBufferArray) { // var<uniform> g : [[access(read)]] array<S, 3>; 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); Global(Source{{56, 78}}, "g", ac, ast::StorageClass::kUniform);
diff --git a/src/resolver/struct_layout_test.cc b/src/resolver/struct_layout_test.cc index 55d5d2e..c0d0507 100644 --- a/src/resolver/struct_layout_test.cc +++ b/src/resolver/struct_layout_test.cc
@@ -173,8 +173,8 @@ } TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayOfExplicitStrideArray) { - auto inner = ty.array<i32, 2>(/*stride*/ 16); // size: 32 - auto outer = ty.array(inner, 12); // size: 12 * 32 + auto* inner = ty.array<i32, 2>(/*stride*/ 16); // size: 32 + auto* outer = ty.array(inner, 12); // size: 12 * 32 auto* s = Structure("S", { Member("c", outer), }); @@ -198,7 +198,7 @@ Member("b", ty.vec3<i32>()), Member("c", ty.vec4<i32>()), }); // size: 48 - auto outer = ty.array(inner, 12); // size: 12 * 48 + auto* outer = ty.array(inner, 12); // size: 12 * 48 auto* s = Structure("S", { Member("c", outer), });
diff --git a/src/resolver/struct_storage_class_use_test.cc b/src/resolver/struct_storage_class_use_test.cc index b41a5e3..d087069 100644 --- a/src/resolver/struct_storage_class_use_test.cc +++ b/src/resolver/struct_storage_class_use_test.cc
@@ -105,7 +105,7 @@ TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalArray) { 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); ASSERT_TRUE(r()->Resolve()) << r()->error(); @@ -158,7 +158,7 @@ TEST_F(ResolverStorageClassUseTest, StructReachableViaLocalArray) { 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)); ASSERT_TRUE(r()->Resolve()) << r()->error();
diff --git a/src/sem/access_control_type_test.cc b/src/sem/access_control_type_test.cc index 3b3138b..828b721 100644 --- a/src/sem/access_control_type_test.cc +++ b/src/sem/access_control_type_test.cc
@@ -37,7 +37,7 @@ Type* ty = &at; EXPECT_TRUE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<Alias>()); - EXPECT_FALSE(ty->Is<ArrayType>()); + EXPECT_FALSE(ty->Is<Array>()); EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<I32>());
diff --git a/src/sem/alias_type_test.cc b/src/sem/alias_type_test.cc index 27d5084..ed8d611 100644 --- a/src/sem/alias_type_test.cc +++ b/src/sem/alias_type_test.cc
@@ -33,7 +33,7 @@ sem::Type* ty = at; EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_TRUE(ty->Is<Alias>()); - EXPECT_FALSE(ty->Is<ArrayType>()); + EXPECT_FALSE(ty->Is<Array>()); EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<I32>());
diff --git a/src/sem/array.cc b/src/sem/array.cc index fefd8a1..1c6bc9c 100644 --- a/src/sem/array.cc +++ b/src/sem/array.cc
@@ -14,16 +14,53 @@ #include "src/sem/array.h" +#include <string> + +#include "src/debug.h" + TINT_INSTANTIATE_TYPEINFO(tint::sem::Array); namespace tint { namespace sem { -Array::Array(sem::ArrayType* type, +Array::Array(const Type* element, + uint32_t count, uint32_t align, uint32_t size, - uint32_t stride) - : type_(type), align_(align), size_(size), stride_(stride) {} + uint32_t 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 tint
diff --git a/src/sem/array.h b/src/sem/array.h index 68297b9..df363c0 100644 --- a/src/sem/array.h +++ b/src/sem/array.h
@@ -16,28 +16,47 @@ #define SRC_SEM_ARRAY_H_ #include <stdint.h> +#include <string> #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 sem { -// Forward declarations -class ArrayType; /// Array holds the semantic information for Array nodes. -class Array : public Castable<Array, Node> { +class Array : public Castable<Array, Type> { public: /// Constructor - /// @param type the Array type - /// @param align the byte alignment of the structure - /// @param size the byte size of the structure + /// @param element the array element type + /// @param count the number of elements in the array. 0 represents a + /// 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 /// 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 - sem::ArrayType* Type() const { return type_; } + /// @return the array element 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 /// @note this may differ from the alignment of a structure member of this @@ -47,17 +66,34 @@ /// @returns the byte size of the 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. - 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 /// array to the start of the next element 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: - sem::ArrayType* const type_; + Type const* const element_; + uint32_t const count_; uint32_t const align_; uint32_t const size_; uint32_t const stride_; + bool const stride_implicit_; }; } // namespace sem
diff --git a/src/sem/array_type.cc b/src/sem/array_type.cc deleted file mode 100644 index f0846e4..0000000 --- a/src/sem/array_type.cc +++ /dev/null
@@ -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
diff --git a/src/sem/array_type.h b/src/sem/array_type.h deleted file mode 100644 index 77fe676..0000000 --- a/src/sem/array_type.h +++ /dev/null
@@ -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_
diff --git a/src/sem/array_type_test.cc b/src/sem/array_type_test.cc deleted file mode 100644 index 636520b..0000000 --- a/src/sem/array_type_test.cc +++ /dev/null
@@ -1,100 +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/access_control_type.h" -#include "src/sem/test_helper.h" -#include "src/sem/texture_type.h" - -namespace tint { -namespace sem { -namespace { - -using ArrayTest = TestHelper; - -TEST_F(ArrayTest, CreateSizedArray) { - U32 u32; - ArrayType arr{&u32, 3, ast::DecorationList{}}; - EXPECT_EQ(arr.type(), &u32); - EXPECT_EQ(arr.size(), 3u); - EXPECT_TRUE(arr.Is<ArrayType>()); - EXPECT_FALSE(arr.IsRuntimeArray()); -} - -TEST_F(ArrayTest, CreateRuntimeArray) { - U32 u32; - ArrayType arr{&u32, 0, ast::DecorationList{}}; - EXPECT_EQ(arr.type(), &u32); - EXPECT_EQ(arr.size(), 0u); - EXPECT_TRUE(arr.Is<ArrayType>()); - EXPECT_TRUE(arr.IsRuntimeArray()); -} - -TEST_F(ArrayTest, Is) { - I32 i32; - - ArrayType arr{&i32, 3, ast::DecorationList{}}; - Type* ty = &arr; - EXPECT_FALSE(ty->Is<AccessControl>()); - EXPECT_FALSE(ty->Is<Alias>()); - EXPECT_TRUE(ty->Is<ArrayType>()); - EXPECT_FALSE(ty->Is<Bool>()); - EXPECT_FALSE(ty->Is<F32>()); - EXPECT_FALSE(ty->Is<I32>()); - EXPECT_FALSE(ty->Is<Matrix>()); - EXPECT_FALSE(ty->Is<Pointer>()); - EXPECT_FALSE(ty->Is<Sampler>()); - EXPECT_FALSE(ty->Is<Struct>()); - EXPECT_FALSE(ty->Is<Texture>()); - EXPECT_FALSE(ty->Is<U32>()); - EXPECT_FALSE(ty->Is<Vector>()); -} - -TEST_F(ArrayTest, TypeName) { - I32 i32; - ArrayType arr{&i32, 0, ast::DecorationList{}}; - EXPECT_EQ(arr.type_name(), "__array__i32"); -} - -TEST_F(ArrayTest, FriendlyNameRuntimeSized) { - ArrayType arr{ty.i32(), 0, ast::DecorationList{}}; - EXPECT_EQ(arr.FriendlyName(Symbols()), "array<i32>"); -} - -TEST_F(ArrayTest, FriendlyNameStaticSized) { - ArrayType arr{ty.i32(), 5, ast::DecorationList{}}; - EXPECT_EQ(arr.FriendlyName(Symbols()), "array<i32, 5>"); -} - -TEST_F(ArrayTest, FriendlyNameWithStride) { - ArrayType arr{ty.i32(), 5, - ast::DecorationList{create<ast::StrideDecoration>(32)}}; - EXPECT_EQ(arr.FriendlyName(Symbols()), "[[stride(32)]] array<i32, 5>"); -} - -TEST_F(ArrayTest, TypeName_RuntimeArray) { - I32 i32; - ArrayType arr{&i32, 3, ast::DecorationList{}}; - EXPECT_EQ(arr.type_name(), "__array__i32_3"); -} - -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 sem -} // namespace tint
diff --git a/src/sem/bool_type_test.cc b/src/sem/bool_type_test.cc index a76f1fb..51d6ef0 100644 --- a/src/sem/bool_type_test.cc +++ b/src/sem/bool_type_test.cc
@@ -27,7 +27,7 @@ Type* ty = &b; EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<Alias>()); - EXPECT_FALSE(ty->Is<ArrayType>()); + EXPECT_FALSE(ty->Is<Array>()); EXPECT_TRUE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<I32>());
diff --git a/src/sem/depth_texture_type_test.cc b/src/sem/depth_texture_type_test.cc index 3f1ddcd..e51ebfc 100644 --- a/src/sem/depth_texture_type_test.cc +++ b/src/sem/depth_texture_type_test.cc
@@ -32,7 +32,7 @@ Type* ty = &d; EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<Alias>()); - EXPECT_FALSE(ty->Is<ArrayType>()); + EXPECT_FALSE(ty->Is<Array>()); EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<I32>());
diff --git a/src/sem/external_texture_type_test.cc b/src/sem/external_texture_type_test.cc index afbb98e..3e48ea8 100644 --- a/src/sem/external_texture_type_test.cc +++ b/src/sem/external_texture_type_test.cc
@@ -33,7 +33,7 @@ Type* ty = &s; EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<Alias>()); - EXPECT_FALSE(ty->Is<ArrayType>()); + EXPECT_FALSE(ty->Is<Array>()); EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<I32>());
diff --git a/src/sem/f32_type_test.cc b/src/sem/f32_type_test.cc index bc18606..670d831 100644 --- a/src/sem/f32_type_test.cc +++ b/src/sem/f32_type_test.cc
@@ -27,7 +27,7 @@ Type* ty = &f; EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<Alias>()); - EXPECT_FALSE(ty->Is<ArrayType>()); + EXPECT_FALSE(ty->Is<Array>()); EXPECT_FALSE(ty->Is<Bool>()); EXPECT_TRUE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<I32>());
diff --git a/src/sem/i32_type_test.cc b/src/sem/i32_type_test.cc index 3d14f35..437aa16 100644 --- a/src/sem/i32_type_test.cc +++ b/src/sem/i32_type_test.cc
@@ -27,7 +27,7 @@ Type* ty = &i; EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<Alias>()); - EXPECT_FALSE(ty->Is<ArrayType>()); + EXPECT_FALSE(ty->Is<Array>()); EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<F32>()); EXPECT_TRUE(ty->Is<I32>());
diff --git a/src/sem/matrix_type_test.cc b/src/sem/matrix_type_test.cc index 4896ca2..dbcb898 100644 --- a/src/sem/matrix_type_test.cc +++ b/src/sem/matrix_type_test.cc
@@ -38,7 +38,7 @@ Type* ty = &m; EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<Alias>()); - EXPECT_FALSE(ty->Is<ArrayType>()); + EXPECT_FALSE(ty->Is<Array>()); EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<I32>());
diff --git a/src/sem/multisampled_texture_type_test.cc b/src/sem/multisampled_texture_type_test.cc index e35a2e9..d202970 100644 --- a/src/sem/multisampled_texture_type_test.cc +++ b/src/sem/multisampled_texture_type_test.cc
@@ -33,7 +33,7 @@ Type* ty = &s; EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<Alias>()); - EXPECT_FALSE(ty->Is<ArrayType>()); + EXPECT_FALSE(ty->Is<Array>()); EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<I32>());
diff --git a/src/sem/pointer_type.cc b/src/sem/pointer_type.cc index 1bb9c12..699ea9c 100644 --- a/src/sem/pointer_type.cc +++ b/src/sem/pointer_type.cc
@@ -21,7 +21,7 @@ namespace tint { 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) {} std::string Pointer::type_name() const {
diff --git a/src/sem/pointer_type.h b/src/sem/pointer_type.h index 843167b..929853c 100644 --- a/src/sem/pointer_type.h +++ b/src/sem/pointer_type.h
@@ -26,16 +26,16 @@ /// A pointer type. class Pointer : public Castable<Pointer, Type> { public: - /// Construtor + /// Constructor /// @param subtype the pointee type /// @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 Pointer(Pointer&&); ~Pointer() override; /// @returns the pointee type - Type* type() const { return subtype_; } + const Type* type() const { return subtype_; } /// @returns the storage class of the pointer ast::StorageClass storage_class() const { return storage_class_; } @@ -48,7 +48,7 @@ std::string FriendlyName(const SymbolTable& symbols) const override; private: - Type* const subtype_; + Type const* const subtype_; ast::StorageClass const storage_class_; };
diff --git a/src/sem/pointer_type_test.cc b/src/sem/pointer_type_test.cc index 00cec4b..e9f0893 100644 --- a/src/sem/pointer_type_test.cc +++ b/src/sem/pointer_type_test.cc
@@ -35,7 +35,7 @@ Type* ty = &p; EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<Alias>()); - EXPECT_FALSE(ty->Is<ArrayType>()); + EXPECT_FALSE(ty->Is<Array>()); EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<I32>());
diff --git a/src/sem/sampled_texture_type_test.cc b/src/sem/sampled_texture_type_test.cc index cb00b03..85b8e2b 100644 --- a/src/sem/sampled_texture_type_test.cc +++ b/src/sem/sampled_texture_type_test.cc
@@ -32,7 +32,7 @@ Type* ty = &s; EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<Alias>()); - EXPECT_FALSE(ty->Is<ArrayType>()); + EXPECT_FALSE(ty->Is<Array>()); EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<I32>());
diff --git a/src/sem/sampler_type_test.cc b/src/sem/sampler_type_test.cc index c419ee6..9ee25d3 100644 --- a/src/sem/sampler_type_test.cc +++ b/src/sem/sampler_type_test.cc
@@ -38,7 +38,7 @@ Type* ty = &s; EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<Alias>()); - EXPECT_FALSE(ty->Is<ArrayType>()); + EXPECT_FALSE(ty->Is<Array>()); EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<I32>());
diff --git a/src/sem/sem_array_test.cc b/src/sem/sem_array_test.cc new file mode 100644 index 0000000..9620af9 --- /dev/null +++ b/src/sem/sem_array_test.cc
@@ -0,0 +1,102 @@ +// 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/access_control_type.h" +#include "src/sem/test_helper.h" +#include "src/sem/texture_type.h" + +namespace tint { +namespace sem { +namespace { + +using ArrayTest = TestHelper; + +TEST_F(ArrayTest, CreateSizedArray) { + U32 u32; + auto* arr = create<Array>(&u32, 2, 4, 8, 16, true); + EXPECT_EQ(arr->ElemType(), &u32); + EXPECT_EQ(arr->Count(), 2u); + EXPECT_EQ(arr->Align(), 4u); + EXPECT_EQ(arr->SizeInBytes(), 8u); + EXPECT_EQ(arr->Stride(), 16u); + EXPECT_TRUE(arr->IsStrideImplicit()); + EXPECT_FALSE(arr->IsRuntimeSized()); +} + +TEST_F(ArrayTest, CreateRuntimeArray) { + U32 u32; + auto* arr = create<Array>(&u32, 0, 4, 8, 16, true); + EXPECT_EQ(arr->ElemType(), &u32); + EXPECT_EQ(arr->Count(), 0u); + EXPECT_EQ(arr->Align(), 4u); + EXPECT_EQ(arr->SizeInBytes(), 8u); + EXPECT_EQ(arr->Stride(), 16u); + EXPECT_TRUE(arr->IsStrideImplicit()); + EXPECT_TRUE(arr->IsRuntimeSized()); +} + +TEST_F(ArrayTest, Is) { + I32 i32; + + Type* ty = create<Array>(&i32, 2, 4, 8, 4, true); + EXPECT_FALSE(ty->Is<AccessControl>()); + EXPECT_FALSE(ty->Is<Alias>()); + EXPECT_TRUE(ty->Is<Array>()); + EXPECT_FALSE(ty->Is<Bool>()); + EXPECT_FALSE(ty->Is<F32>()); + EXPECT_FALSE(ty->Is<I32>()); + EXPECT_FALSE(ty->Is<Matrix>()); + EXPECT_FALSE(ty->Is<Pointer>()); + EXPECT_FALSE(ty->Is<Sampler>()); + EXPECT_FALSE(ty->Is<Struct>()); + EXPECT_FALSE(ty->Is<Texture>()); + EXPECT_FALSE(ty->Is<U32>()); + EXPECT_FALSE(ty->Is<Vector>()); +} + +TEST_F(ArrayTest, TypeName) { + I32 i32; + auto* arr = create<Array>(&i32, 2, 0, 4, 4, true); + EXPECT_EQ(arr->type_name(), "__array__i32_count_2_align_0_size_4_stride_4"); +} + +TEST_F(ArrayTest, FriendlyNameRuntimeSized) { + auto* arr = create<Array>(ty.i32(), 0, 0, 4, 4, true); + EXPECT_EQ(arr->FriendlyName(Symbols()), "array<i32>"); +} + +TEST_F(ArrayTest, FriendlyNameStaticSized) { + auto* arr = create<Array>(ty.i32(), 5, 4, 20, 4, true); + EXPECT_EQ(arr->FriendlyName(Symbols()), "array<i32, 5>"); +} + +TEST_F(ArrayTest, FriendlyNameRuntimeSizedNonImplicitStride) { + auto* arr = create<Array>(ty.i32(), 0, 0, 4, 4, false); + EXPECT_EQ(arr->FriendlyName(Symbols()), "[[stride(4)]] array<i32>"); +} + +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) { + I32 i32; + auto* arr = create<Array>(&i32, 2, 4, 8, 16, true); + EXPECT_EQ(arr->type_name(), "__array__i32_count_2_align_4_size_8_stride_16"); +} + +} // namespace +} // namespace sem +} // namespace tint
diff --git a/src/sem/sem_struct_test.cc b/src/sem/sem_struct_test.cc index 0e5742a..88309fa 100644 --- a/src/sem/sem_struct_test.cc +++ b/src/sem/sem_struct_test.cc
@@ -45,7 +45,7 @@ sem::Type* ty = s; EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<Alias>()); - EXPECT_FALSE(ty->Is<ArrayType>()); + EXPECT_FALSE(ty->Is<Array>()); EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<I32>());
diff --git a/src/sem/storage_texture_type_test.cc b/src/sem/storage_texture_type_test.cc index 77b8944..3fef3ef 100644 --- a/src/sem/storage_texture_type_test.cc +++ b/src/sem/storage_texture_type_test.cc
@@ -34,7 +34,7 @@ Type* ty = s; EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<Alias>()); - EXPECT_FALSE(ty->Is<ArrayType>()); + EXPECT_FALSE(ty->Is<Array>()); EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<I32>());
diff --git a/src/sem/type.cc b/src/sem/type.cc index 589688f..349506c 100644 --- a/src/sem/type.cc +++ b/src/sem/type.cc
@@ -37,28 +37,28 @@ Type::~Type() = default; -Type* Type::UnwrapPtrIfNeeded() { +const Type* Type::UnwrapPtrIfNeeded() const { if (auto* ptr = As<sem::Pointer>()) { return ptr->type(); } return this; } -Type* Type::UnwrapAliasIfNeeded() { - Type* unwrapped = this; +const Type* Type::UnwrapAliasIfNeeded() const { + const Type* unwrapped = this; while (auto* ptr = unwrapped->As<sem::Alias>()) { unwrapped = ptr->type(); } return unwrapped; } -Type* Type::UnwrapIfNeeded() { +const Type* Type::UnwrapIfNeeded() const { auto* where = this; while (true) { if (auto* alias = where->As<sem::Alias>()) { - where = alias->type(); + where = alias->type(); } else if (auto* access = where->As<sem::AccessControl>()) { - where = access->type(); + where = access->type(); } else { break; } @@ -66,7 +66,7 @@ return where; } -Type* Type::UnwrapAll() { +const Type* Type::UnwrapAll() const { return UnwrapIfNeeded()->UnwrapPtrIfNeeded()->UnwrapIfNeeded(); }
diff --git a/src/sem/type.h b/src/sem/type.h index ca7bdd4..45de49c 100644 --- a/src/sem/type.h +++ b/src/sem/type.h
@@ -46,22 +46,11 @@ virtual std::string FriendlyName(const SymbolTable& symbols) const = 0; /// @returns the pointee type if this is a pointer, `this` otherwise - Type* UnwrapPtrIfNeeded(); - - /// @returns the pointee type if this is a pointer, `this` otherwise - const Type* UnwrapPtrIfNeeded() const { - return const_cast<Type*>(this)->UnwrapPtrIfNeeded(); - } + const Type* UnwrapPtrIfNeeded() const; /// @returns the most deeply nested aliased type if this is an alias, `this` /// otherwise - Type* UnwrapAliasIfNeeded(); - - /// @returns the most deeply nested aliased type if this is an alias, `this` - /// otherwise - const Type* UnwrapAliasIfNeeded() const { - return const_cast<Type*>(this)->UnwrapAliasIfNeeded(); - } + const Type* UnwrapAliasIfNeeded() const; /// Removes all levels of aliasing and access control. /// This is just enough to assist with WGSL translation @@ -69,31 +58,14 @@ /// 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. - Type* UnwrapIfNeeded(); - - /// 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(); - } + const Type* UnwrapIfNeeded() 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 - Type* UnwrapAll(); - - /// 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(); } + const Type* UnwrapAll() const; /// @returns true if this type is a scalar bool is_scalar() const;
diff --git a/src/sem/type_mappings.h b/src/sem/type_mappings.h index e963a33..7193547 100644 --- a/src/sem/type_mappings.h +++ b/src/sem/type_mappings.h
@@ -34,7 +34,6 @@ namespace sem { // Forward declarations class Array; -class ArrayType; class Call; class Expression; class Function; @@ -51,7 +50,6 @@ /// rules will be used to infer the return type based on the argument type. struct TypeMappings { //! @cond Doxygen_Suppress - Array* operator()(sem::ArrayType*); Call* operator()(ast::CallExpression*); Expression* operator()(ast::Expression*); Function* operator()(ast::Function*);
diff --git a/src/sem/u32_type_test.cc b/src/sem/u32_type_test.cc index bbb9e08..00d0428 100644 --- a/src/sem/u32_type_test.cc +++ b/src/sem/u32_type_test.cc
@@ -27,7 +27,7 @@ Type* ty = &u; EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<Alias>()); - EXPECT_FALSE(ty->Is<ArrayType>()); + EXPECT_FALSE(ty->Is<Array>()); EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<I32>());
diff --git a/src/sem/vector_type_test.cc b/src/sem/vector_type_test.cc index 6e52afb..78b1e09 100644 --- a/src/sem/vector_type_test.cc +++ b/src/sem/vector_type_test.cc
@@ -35,7 +35,7 @@ Type* ty = &v; EXPECT_FALSE(ty->Is<AccessControl>()); EXPECT_FALSE(ty->Is<Alias>()); - EXPECT_FALSE(ty->Is<ArrayType>()); + EXPECT_FALSE(ty->Is<Array>()); EXPECT_FALSE(ty->Is<Bool>()); EXPECT_FALSE(ty->Is<F32>()); EXPECT_FALSE(ty->Is<I32>());
diff --git a/src/transform/bound_array_accessors.cc b/src/transform/bound_array_accessors.cc index 3f56cf0..82c221b 100644 --- a/src/transform/bound_array_accessors.cc +++ b/src/transform/bound_array_accessors.cc
@@ -42,7 +42,7 @@ auto& diags = ctx->dst->Diagnostics(); 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>()) { return nullptr; } @@ -52,10 +52,10 @@ uint32_t size = 0; 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) { size = is_vec ? ret_type->As<sem::Vector>()->size() - : ret_type->As<sem::ArrayType>()->size(); + : ret_type->As<sem::Array>()->Count(); } else { // The row accessor would have been an embedded array accessor and already // handled, so we just need to do columns here.
diff --git a/src/transform/calculate_array_length.cc b/src/transform/calculate_array_length.cc index c36acce..22407a4 100644 --- a/src/transform/calculate_array_length.cc +++ b/src/transform/calculate_array_length.cc
@@ -77,8 +77,8 @@ // get_buffer_size_intrinsic() emits the function decorated with // BufferSizeIntrinsic that is transformed by the HLSL writer into a call to // [RW]ByteAddressBuffer.GetDimensions(). - std::unordered_map<sem::Struct*, Symbol> buffer_size_intrinsics; - auto get_buffer_size_intrinsic = [&](sem::Struct* buffer_type) { + std::unordered_map<const sem::Struct*, Symbol> buffer_size_intrinsics; + auto get_buffer_size_intrinsic = [&](const sem::Struct* buffer_type) { return utils::GetOrCreate(buffer_size_intrinsics, buffer_type, [&] { auto name = ctx.dst->Sym(); auto* buffer_typename =
diff --git a/src/transform/decompose_storage_access.cc b/src/transform/decompose_storage_access.cc index b4ff19b..9d51bbb 100644 --- a/src/transform/decompose_storage_access.cc +++ b/src/transform/decompose_storage_access.cc
@@ -175,8 +175,8 @@ /// TypePair is a pair of types that can be used as a unordered map or set key. struct TypePair { - sem::Type* first; - sem::Type* second; + sem::Type const* first; + sem::Type const* second; bool operator==(const TypePair& rhs) const { return first == rhs.first && second == rhs.second; } @@ -188,20 +188,20 @@ }; /// @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 return 4; } /// @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); } /// @returns a DecomposeStorageAccess::Intrinsic decoration that can be applied /// to a stub function to load the type `ty`. DecomposeStorageAccess::Intrinsic* IntrinsicLoadFor(ProgramBuilder* builder, - sem::Type* ty) { + const sem::Type* ty) { using Intrinsic = DecomposeStorageAccess::Intrinsic; auto intrinsic = [builder](Intrinsic::Type type) { @@ -260,7 +260,7 @@ /// @returns a DecomposeStorageAccess::Intrinsic decoration that can be applied /// to a stub function to store the type `ty`. DecomposeStorageAccess::Intrinsic* IntrinsicStoreFor(ProgramBuilder* builder, - sem::Type* ty) { + const sem::Type* ty) { using Intrinsic = DecomposeStorageAccess::Intrinsic; auto intrinsic = [builder](Intrinsic::Type type) { @@ -331,7 +331,7 @@ } /// @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) { if (auto* ptr = ty->As<sem::Pointer>()) { ty = ptr->type(); @@ -350,7 +350,7 @@ } /// @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(); } @@ -358,7 +358,7 @@ struct StorageBufferAccess { sem::Expression const* var = nullptr; // Storage buffer variable 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 }; @@ -422,8 +422,8 @@ /// @return the name of the function that performs the load Symbol LoadFunc(CloneContext& ctx, const ast::NamedType* insert_after, - sem::Type* buf_ty, - sem::Type* el_ty) { + const sem::Type* buf_ty, + const sem::Type* el_ty) { return utils::GetOrCreate(load_funcs, TypePair{buf_ty, el_ty}, [&] { auto* buf_ast_ty = CreateASTTypeFor(&ctx, buf_ty); ast::VariableList params = { @@ -458,13 +458,11 @@ member->Type()->UnwrapAll()); values.emplace_back(ctx.dst->Call(load, "buffer", offset)); } - } else if (auto* arr_ty = el_ty->As<sem::ArrayType>()) { - auto& sem = ctx.src->Sem(); - auto* arr = sem.Get(arr_ty); - for (uint32_t i = 0; i < arr_ty->size(); i++) { + } else if (auto* arr = el_ty->As<sem::Array>()) { + for (uint32_t i = 0; i < arr->Count(); i++) { auto* offset = ctx.dst->Add("offset", arr->Stride() * i); Symbol load = LoadFunc(ctx, insert_after, buf_ty, - arr_ty->type()->UnwrapAll()); + arr->ElemType()->UnwrapAll()); values.emplace_back(ctx.dst->Call(load, "buffer", offset)); } } @@ -491,8 +489,8 @@ /// @return the name of the function that performs the store Symbol StoreFunc(CloneContext& ctx, const ast::NamedType* insert_after, - sem::Type* buf_ty, - sem::Type* el_ty) { + const sem::Type* buf_ty, + const sem::Type* el_ty) { return utils::GetOrCreate(store_funcs, TypePair{buf_ty, el_ty}, [&] { auto* buf_ast_ty = CreateASTTypeFor(&ctx, buf_ty); auto* el_ast_ty = CreateASTTypeFor(&ctx, el_ty); @@ -533,14 +531,12 @@ auto* call = ctx.dst->Call(store, "buffer", offset, access); body.emplace_back(ctx.dst->create<ast::CallStatement>(call)); } - } else if (auto* arr_ty = el_ty->As<sem::ArrayType>()) { - auto& sem = ctx.src->Sem(); - auto* arr = sem.Get(arr_ty); - for (uint32_t i = 0; i < arr_ty->size(); i++) { + } else if (auto* arr = el_ty->As<sem::Array>()) { + for (uint32_t i = 0; i < arr->Count(); i++) { auto* offset = ctx.dst->Add("offset", arr->Stride() * i); auto* access = ctx.dst->IndexAccessor("value", ctx.dst->Expr(i)); Symbol store = StoreFunc(ctx, insert_after, buf_ty, - arr_ty->type()->UnwrapAll()); + arr->ElemType()->UnwrapAll()); auto* call = ctx.dst->Call(store, "buffer", offset, access); body.emplace_back(ctx.dst->create<ast::CallStatement>(call)); } @@ -689,14 +685,13 @@ if (auto* accessor = node->As<ast::ArrayAccessorExpression>()) { if (auto access = state.TakeAccess(accessor->array())) { // X[Y] - if (auto* arr_ty = access.type->As<sem::ArrayType>()) { - auto stride = sem.Get(arr_ty)->Stride(); - auto offset = Mul(stride, accessor->idx_expr()); + if (auto* arr = access.type->As<sem::Array>()) { + auto offset = Mul(arr->Stride(), accessor->idx_expr()); state.AddAccess(accessor, { access.var, Add(std::move(access.offset), std::move(offset)), - arr_ty->type()->UnwrapAll(), + arr->ElemType()->UnwrapAll(), }); continue; }
diff --git a/src/transform/hlsl.cc b/src/transform/hlsl.cc index d3d1656..15df034 100644 --- a/src/transform/hlsl.cc +++ b/src/transform/hlsl.cc
@@ -96,7 +96,7 @@ } 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 auto dst_symbol = ctx.dst->Sym(); // Clone the type
diff --git a/src/transform/spirv.cc b/src/transform/spirv.cc index 0a8d45d..219524e 100644 --- a/src/transform/spirv.cc +++ b/src/transform/spirv.cc
@@ -230,7 +230,7 @@ // Use the same name as the old variable. auto var_name = ctx.Clone(var->symbol()); // 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. auto* var_arr = ctx.dst->Var(var->source(), var_name, type, var->declared_storage_class(), nullptr,
diff --git a/src/transform/transform.cc b/src/transform/transform.cc index d53cd7a..804566d 100644 --- a/src/transform/transform.cc +++ b/src/transform/transform.cc
@@ -95,10 +95,13 @@ auto* el = CreateASTTypeFor(ctx, v->type()); return ctx->dst->create<ast::Vector>(el, v->size()); } - if (auto* a = ty->As<sem::ArrayType>()) { - auto* el = CreateASTTypeFor(ctx, a->type()); - auto decos = ctx->Clone(a->decorations()); - return ctx->dst->create<ast::Array>(el, a->size(), std::move(decos)); + if (auto* a = ty->As<sem::Array>()) { + auto* el = CreateASTTypeFor(ctx, a->ElemType()); + ast::DecorationList 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>()) { auto* el = CreateASTTypeFor(ctx, ac->type());
diff --git a/src/transform/transform_test.cc b/src/transform/transform_test.cc index e36cacd..5d30d0f 100644 --- a/src/transform/transform_test.cc +++ b/src/transform/transform_test.cc
@@ -76,16 +76,23 @@ ASSERT_EQ(vec->As<ast::Vector>()->size(), 2u); } -TEST_F(CreateASTTypeForTest, Array) { +TEST_F(CreateASTTypeForTest, ArrayImplicitStride) { auto* arr = create([](ProgramBuilder& b) { - return b.create<sem::ArrayType>(b.create<sem::F32>(), 4, - ast::DecorationList{ - b.create<ast::StrideDecoration>(32u), - }); + return b.create<sem::Array>(b.create<sem::F32>(), 2, 4, 4, 32u, true); }); ASSERT_TRUE(arr->Is<ast::Array>()); 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_TRUE( arr->As<ast::Array>()->decorations()[0]->Is<ast::StrideDecoration>()); @@ -95,7 +102,6 @@ ->stride(), 32u); } - TEST_F(CreateASTTypeForTest, AccessControl) { auto* ac = create([](ProgramBuilder& b) { auto* decl = b.Structure("S", {}, {});
diff --git a/src/typepair.h b/src/typepair.h index d258dea..2e6d679 100644 --- a/src/typepair.h +++ b/src/typepair.h
@@ -57,7 +57,7 @@ namespace sem { class AccessControl; class Alias; -class ArrayType; +class Array; class Bool; class DepthTexture; class ExternalTexture; @@ -241,7 +241,7 @@ using AccessControl = TypePair<ast::AccessControl, sem::AccessControl>; 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 DepthTexture = TypePair<ast::DepthTexture, sem::DepthTexture>; using ExternalTexture = TypePair<ast::ExternalTexture, sem::ExternalTexture>;
diff --git a/src/writer/append_vector.cc b/src/writer/append_vector.cc index 7e76346..f080887 100644 --- a/src/writer/append_vector.cc +++ b/src/writer/append_vector.cc
@@ -39,7 +39,7 @@ ast::Expression* vector, ast::Expression* scalar) { 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_ty = vector_sem->Type()->UnwrapPtrIfNeeded(); if (auto* vec = vector_ty->As<sem::Vector>()) {
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc index ebd29de..1fb4c42 100644 --- a/src/writer/hlsl/generator_impl.cc +++ b/src/writer/hlsl/generator_impl.cc
@@ -1325,7 +1325,7 @@ } bool brackets = - type->UnwrapAliasIfNeeded()->IsAnyOf<sem::ArrayType, sem::Struct>(); + type->UnwrapAliasIfNeeded()->IsAnyOf<sem::Array, sem::Struct>(); if (brackets) { out << "{"; @@ -1651,7 +1651,7 @@ return false; } // 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()); } } @@ -1918,7 +1918,7 @@ if (!EmitType(out, var->Type(), var->StorageClass(), name)) { return false; } - if (!var->Type()->UnwrapAliasIfNeeded()->Is<sem::ArrayType>()) { + if (!var->Type()->UnwrapAliasIfNeeded()->Is<sem::Array>()) { out << " " << name; } @@ -2406,18 +2406,18 @@ if (auto* alias = type->As<sem::Alias>()) { 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; std::vector<uint32_t> sizes; - while (auto* arr = base_type->As<sem::ArrayType>()) { - if (arr->IsRuntimeArray()) { + while (auto* arr = base_type->As<sem::Array>()) { + if (arr->IsRuntimeSized()) { TINT_ICE(diagnostics_) << "Runtime arrays may only exist in storage buffers, which should " "have been transformed into a ByteAddressBuffer"; return false; } - sizes.push_back(arr->size()); - base_type = arr->type(); + sizes.push_back(arr->Count()); + base_type = arr->ElemType(); } if (!EmitType(out, base_type, storage_class, "")) { return false; @@ -2571,7 +2571,7 @@ return false; } // Array member name will be output with the type - if (!mem->Type()->Is<sem::ArrayType>()) { + if (!mem->Type()->Is<sem::Array>()) { out << " " << mem_name; } @@ -2669,7 +2669,7 @@ builder_.Symbols().NameFor(var->symbol()))) { return false; } - if (!type->Is<sem::ArrayType>()) { + if (!type->Is<sem::Array>()) { out << " " << builder_.Symbols().NameFor(var->symbol()); } out << constructor_out.str() << ";" << std::endl; @@ -2731,7 +2731,7 @@ builder_.Symbols().NameFor(var->symbol()))) { return false; } - if (!type->Is<sem::ArrayType>()) { + if (!type->Is<sem::Array>()) { out << " " << builder_.Symbols().NameFor(var->symbol()); }
diff --git a/src/writer/hlsl/generator_impl_type_test.cc b/src/writer/hlsl/generator_impl_type_test.cc index e4cf34e..9015afe 100644 --- a/src/writer/hlsl/generator_impl_type_test.cc +++ b/src/writer/hlsl/generator_impl_type_test.cc
@@ -43,21 +43,25 @@ } 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(); - ASSERT_TRUE(gen.EmitType(out, arr, ast::StorageClass::kNone, "ary")) + ASSERT_TRUE( + gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone, "ary")) << gen.error(); EXPECT_EQ(result(), "bool ary[4]"); } 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(); - ASSERT_TRUE(gen.EmitType(out, arr, ast::StorageClass::kNone, "ary")) + ASSERT_TRUE( + gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone, "ary")) << gen.error(); EXPECT_EQ(result(), "bool ary[5][4]"); } @@ -65,41 +69,49 @@ // TODO(dsinclair): Is this possible? What order should it output in? TEST_F(HlslGeneratorImplTest_Type, 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(); - ASSERT_TRUE(gen.EmitType(out, arr, ast::StorageClass::kNone, "ary")) + ASSERT_TRUE( + gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone, "ary")) << gen.error(); EXPECT_EQ(result(), "bool ary[5][4][1]"); } 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(); - ASSERT_TRUE(gen.EmitType(out, arr, ast::StorageClass::kNone, "ary")) + ASSERT_TRUE( + gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone, "ary")) << gen.error(); EXPECT_EQ(result(), "bool ary[6][5][4]"); } 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(); - ASSERT_TRUE(gen.EmitType(out, arr, ast::StorageClass::kNone, "")) + ASSERT_TRUE( + gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone, "")) << gen.error(); EXPECT_EQ(result(), "bool[4]"); } 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(); - ASSERT_TRUE(gen.EmitType(out, arr, ast::StorageClass::kNone, "ary")) + ASSERT_TRUE( + gen.EmitType(out, program->TypeOf(arr), ast::StorageClass::kNone, "ary")) << gen.error(); EXPECT_EQ(result(), "bool ary[]"); }
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc index a2d4513..15d3946 100644 --- a/src/writer/msl/generator_impl.cc +++ b/src/writer/msl/generator_impl.cc
@@ -19,6 +19,7 @@ #include <utility> #include <vector> +#include "src/ast/alias.h" #include "src/ast/bool_literal.h" #include "src/ast/call_statement.h" #include "src/ast/fallthrough_statement.h" @@ -32,7 +33,6 @@ #include "src/sem/access_control_type.h" #include "src/sem/alias_type.h" #include "src/sem/array.h" -#include "src/sem/array_type.h" #include "src/sem/bool_type.h" #include "src/sem/call.h" #include "src/sem/depth_texture_type.h" @@ -88,8 +88,10 @@ } for (auto* const ty : program_->AST().ConstructedTypes()) { - if (!EmitConstructedType(TypeOf(ty))) { - return false; + if (!ty->Is<ast::Alias>()) { + if (!EmitConstructedType(TypeOf(ty))) { + return false; + } } } if (!program_->AST().ConstructedTypes().empty()) { @@ -889,7 +891,7 @@ bool GeneratorImpl::EmitTypeConstructor(ast::TypeConstructorExpression* expr) { auto* type = TypeOf(expr); - if (type->IsAnyOf<sem::ArrayType, sem::Struct>()) { + if (type->IsAnyOf<sem::Array, sem::Struct>()) { out_ << "{"; } else { if (!EmitType(type, "")) { @@ -918,7 +920,7 @@ } } - if (type->IsAnyOf<sem::ArrayType, sem::Struct>()) { + if (type->IsAnyOf<sem::Array, sem::Struct>()) { out_ << "}"; } else { out_ << ")"; @@ -942,9 +944,9 @@ return EmitZeroValue(vec->type()); } else if (auto* mat = type->As<sem::Matrix>()) { return EmitZeroValue(mat->type()); - } else if (auto* arr = type->As<sem::ArrayType>()) { + } else if (auto* arr = type->As<sem::Array>()) { out_ << "{"; - if (!EmitZeroValue(arr->type())) { + if (!EmitZeroValue(arr->ElemType())) { return false; } out_ << "}"; @@ -1331,7 +1333,7 @@ return false; } // 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()); } } @@ -1918,16 +1920,16 @@ if (auto* alias = type->As<sem::Alias>()) { out_ << program_->Symbols().NameFor(alias->symbol()); - } else if (auto* ary = type->As<sem::ArrayType>()) { - sem::Type* base_type = ary; + } else if (auto* ary = type->As<sem::Array>()) { + const sem::Type* base_type = ary; std::vector<uint32_t> sizes; - while (auto* arr = base_type->As<sem::ArrayType>()) { - if (arr->IsRuntimeArray()) { + while (auto* arr = base_type->As<sem::Array>()) { + if (arr->IsRuntimeSized()) { sizes.push_back(1); } else { - sizes.push_back(arr->size()); + sizes.push_back(arr->Count()); } - base_type = arr->type(); + base_type = arr->ElemType(); } if (!EmitType(base_type, "")) { return false; @@ -2122,7 +2124,7 @@ auto* ty = mem->Type()->UnwrapAliasIfNeeded(); // Array member name will be output with the type - if (!ty->Is<sem::ArrayType>()) { + if (!ty->Is<sem::Array>()) { out_ << " " << name; } @@ -2223,7 +2225,7 @@ if (!EmitType(var->Type(), program_->Symbols().NameFor(decl->symbol()))) { return false; } - if (!var->Type()->Is<sem::ArrayType>()) { + if (!var->Type()->Is<sem::Array>()) { out_ << " " << program_->Symbols().NameFor(decl->symbol()); } @@ -2266,7 +2268,7 @@ if (!EmitType(type, program_->Symbols().NameFor(var->symbol()))) { return false; } - if (!type->Is<sem::ArrayType>()) { + if (!type->Is<sem::Array>()) { out_ << " " << program_->Symbols().NameFor(var->symbol()); } @@ -2284,7 +2286,7 @@ } GeneratorImpl::SizeAndAlign GeneratorImpl::MslPackedTypeSizeAndAlign( - sem::Type* ty) { + const sem::Type* ty) { ty = ty->UnwrapAliasIfNeeded(); if (ty->IsAnyOf<sem::U32, sem::I32, sem::F32>()) { @@ -2327,14 +2329,9 @@ } } - if (auto* arr = ty->As<sem::ArrayType>()) { - auto* sem = program_->Sem().Get(arr); - if (!sem) { - TINT_ICE(diagnostics_) << "Array missing semantic info"; - return {}; - } - auto el_size_align = MslPackedTypeSizeAndAlign(arr->type()); - if (sem->Stride() != el_size_align.size) { + if (auto* arr = ty->As<sem::Array>()) { + auto el_size_align = MslPackedTypeSizeAndAlign(arr->ElemType()); + if (arr->Stride() != el_size_align.size) { // 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 // stride. @@ -2342,7 +2339,7 @@ << "Arrays with custom strides not yet implemented"; 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}; }
diff --git a/src/writer/msl/generator_impl.h b/src/writer/msl/generator_impl.h index 22e5d75..b4fa982 100644 --- a/src/writer/msl/generator_impl.h +++ b/src/writer/msl/generator_impl.h
@@ -287,7 +287,7 @@ /// @returns the MSL packed type size and alignment in bytes for the given /// type. - SizeAndAlign MslPackedTypeSizeAndAlign(sem::Type* ty); + SizeAndAlign MslPackedTypeSizeAndAlign(const sem::Type* ty); ScopeStack<const sem::Variable*> global_variables_; Symbol current_ep_sym_;
diff --git a/src/writer/msl/generator_impl_type_test.cc b/src/writer/msl/generator_impl_type_test.cc index 7e28db8..fd35026 100644 --- a/src/writer/msl/generator_impl_type_test.cc +++ b/src/writer/msl/generator_impl_type_test.cc
@@ -67,62 +67,68 @@ } 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(); - 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]"); } TEST_F(MslGeneratorImplTest, EmitType_ArrayOfArray) { - auto a = ty.array<bool, 4>(); - auto b = ty.array(a, 5); + auto* a = ty.array<bool, 4>(); + auto* b = ty.array(a, 5); + Global("G", b, ast::StorageClass::kPrivate); 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]"); } // TODO(dsinclair): Is this possible? What order should it output in? TEST_F(MslGeneratorImplTest, DISABLED_EmitType_ArrayOfArrayOfRuntimeArray) { - auto a = ty.array<bool, 4>(); - auto b = ty.array(a, 5); - auto c = ty.array(b, 0); + auto* a = ty.array<bool, 4>(); + auto* b = ty.array(a, 5); + auto* c = ty.array(b, 0); + Global("G", c, ast::StorageClass::kPrivate); 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]"); } TEST_F(MslGeneratorImplTest, EmitType_ArrayOfArrayOfArray) { - auto a = ty.array<bool, 4>(); - auto b = ty.array(a, 5); - auto c = ty.array(b, 6); + auto* a = ty.array<bool, 4>(); + auto* b = ty.array(a, 5); + auto* c = ty.array(b, 6); + Global("G", c, ast::StorageClass::kPrivate); 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]"); } 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(); - ASSERT_TRUE(gen.EmitType(arr, "")) << gen.error(); + ASSERT_TRUE(gen.EmitType(program->TypeOf(arr), "")) << gen.error(); EXPECT_EQ(gen.result(), "bool[4]"); } 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(); - 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]"); } @@ -411,13 +417,13 @@ }); // 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) - auto array_y = ty.array(inner, 4); + auto* array_y = ty.array(inner, 4); // array_z: size(4), align(4) - auto array_z = ty.array<f32>(); + auto* array_z = ty.array<f32>(); auto* s = Structure("S",
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc index 27fed86..dee0885 100644 --- a/src/writer/spirv/builder.cc +++ b/src/writer/spirv/builder.cc
@@ -111,9 +111,9 @@ /// one or more levels of an arrays inside of `type`. /// @param type the given type, which must not be null /// @returns the nested matrix type, or nullptr if none -sem::Matrix* GetNestedMatrixType(sem::Type* type) { - while (auto* arr = type->As<sem::ArrayType>()) { - type = arr->type(); +const sem::Matrix* GetNestedMatrixType(const sem::Type* type) { + while (auto* arr = type->As<sem::Array>()) { + type = arr->ElemType(); } return type->As<sem::Matrix>(); } @@ -251,7 +251,7 @@ } /// @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>()) { return v->type(); } @@ -1067,9 +1067,9 @@ // how the Resolver currently determines the type of these expression. This // should be fixed when proper support for ptr/ref types is implemented. 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 && - (!ary_res_type->type()->is_scalar() || + (!ary_res_type->ElemType()->is_scalar() || !array->idx_expr()->Is<ast::ScalarConstructorExpression>())) { // Wrap the source type in a pointer to function storage. auto ptr = @@ -1167,7 +1167,7 @@ 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>()) { return id; } @@ -1287,13 +1287,13 @@ continue; } - sem::Type* subtype = result_type->UnwrapAll(); + const sem::Type* subtype = result_type->UnwrapAll(); if (auto* vec = subtype->As<sem::Vector>()) { subtype = vec->type()->UnwrapAll(); } else if (auto* mat = subtype->As<sem::Matrix>()) { subtype = mat->type()->UnwrapAll(); - } else if (auto* arr = subtype->As<sem::ArrayType>()) { - subtype = arr->type()->UnwrapAll(); + } else if (auto* arr = subtype->As<sem::Array>()) { + subtype = arr->ElemType()->UnwrapAll(); } else if (auto* str = subtype->As<sem::Struct>()) { subtype = str->Members()[i]->Type()->UnwrapAll(); } @@ -1373,7 +1373,7 @@ // 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. 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; ops.push_back(Operand::Int(id)); @@ -2574,7 +2574,7 @@ }); } -uint32_t Builder::GenerateSampledImage(sem::Type* texture_type, +uint32_t Builder::GenerateSampledImage(const sem::Type* texture_type, Operand texture_operand, Operand sampler_operand) { uint32_t sampled_image_type_id = 0; @@ -2996,7 +2996,7 @@ result)) { return 0; } - } else if (auto* arr = type->As<sem::ArrayType>()) { + } else if (auto* arr = type->As<sem::Array>()) { if (!GenerateArrayType(arr, result)) { return 0; } @@ -3126,18 +3126,17 @@ return true; } -bool Builder::GenerateArrayType(const sem::ArrayType* ary, - const Operand& result) { - auto elem_type = GenerateTypeIfNeeded(ary->type()); +bool Builder::GenerateArrayType(const sem::Array* ary, const Operand& result) { + auto elem_type = GenerateTypeIfNeeded(ary->ElemType()); if (elem_type == 0) { return false; } auto result_id = result.to_i(); - if (ary->IsRuntimeArray()) { + if (ary->IsRuntimeSized()) { push_type(spv::Op::OpTypeRuntimeArray, {result, Operand::Int(elem_type)}); } else { - auto len_id = GenerateConstantIfNeeded(ScalarConstant::U32(ary->size())); + auto len_id = GenerateConstantIfNeeded(ScalarConstant::U32(ary->Count())); if (len_id == 0) { return false; } @@ -3146,14 +3145,9 @@ {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, {Operand::Int(result_id), Operand::Int(SpvDecorationArrayStride), - Operand::Int(sem_arr->Stride())}); + Operand::Int(ary->Stride())}); return true; }
diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h index 82d27a3..7163e27 100644 --- a/src/writer/spirv/builder.h +++ b/src/writer/spirv/builder.h
@@ -65,7 +65,7 @@ uint32_t source_id; /// The type of the current chain source. This type matches the deduced /// 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 /// chain indices if the source is pointer. std::vector<uint32_t> access_chain_indices; @@ -263,7 +263,7 @@ /// @param type the type to generate for /// @param struct_id the struct id /// @param member_idx the member index - void GenerateMemberAccessControlIfNeeded(sem::Type* type, + void GenerateMemberAccessControlIfNeeded(const sem::Type* type, uint32_t struct_id, uint32_t member_idx); /// Generates a function variable @@ -372,7 +372,7 @@ /// @param texture_operand the texture operand /// @param sampler_operand the sampler operand /// @returns the expression ID - uint32_t GenerateSampledImage(sem::Type* texture_type, + uint32_t GenerateSampledImage(const sem::Type* texture_type, Operand texture_operand, Operand sampler_operand); /// Generates a cast or object copy for the expression result, @@ -413,7 +413,7 @@ /// @param type the type to load /// @param id the variable id to load /// @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 /// currently outside a function. /// @param to the ID to store too @@ -433,7 +433,7 @@ /// @param ary the array to generate /// @param result the result operand /// @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 /// @param mat the matrix to generate /// @param result the result operand @@ -488,7 +488,7 @@ /// @returns the resolved type of the ast::Expression `expr` /// @param expr the expression - sem::Type* TypeOf(ast::Expression* expr) const { + const sem::Type* TypeOf(ast::Expression* expr) const { return builder_.TypeOf(expr); }
diff --git a/src/writer/spirv/builder_accessor_expression_test.cc b/src/writer/spirv/builder_accessor_expression_test.cc index f1d4a89..00919e0 100644 --- a/src/writer/spirv/builder_accessor_expression_test.cc +++ b/src/writer/spirv/builder_accessor_expression_test.cc
@@ -135,7 +135,7 @@ } 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[3][2]; @@ -173,7 +173,7 @@ } 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>; // a[2].xy; @@ -696,10 +696,10 @@ auto* c_type = Structure("C", {Member("baz", ty.vec3<f32>())}); 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_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* expr = MemberAccessor( MemberAccessor(
diff --git a/src/writer/spirv/builder_type_test.cc b/src/writer/spirv/builder_type_test.cc index 3fdf1ad..3de7d99 100644 --- a/src/writer/spirv/builder_type_test.cc +++ b/src/writer/spirv/builder_type_test.cc
@@ -58,7 +58,7 @@ } 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)}, {create<ast::StructBlockDecoration>()}); auto ac = ty.access(ast::AccessControl::kReadOnly, str); @@ -66,7 +66,7 @@ spirv::Builder& b = Build(); - auto id = b.GenerateTypeIfNeeded(ary); + auto id = b.GenerateTypeIfNeeded(program->TypeOf(ary)); ASSERT_FALSE(b.has_error()) << b.error(); EXPECT_EQ(1u, id); @@ -76,7 +76,7 @@ } 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)}, {create<ast::StructBlockDecoration>()}); auto ac = ty.access(ast::AccessControl::kReadOnly, str); @@ -84,8 +84,8 @@ spirv::Builder& b = Build(); - EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u); - EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u); + EXPECT_EQ(b.GenerateTypeIfNeeded(program->TypeOf(ary)), 1u); + EXPECT_EQ(b.GenerateTypeIfNeeded(program->TypeOf(ary)), 1u); ASSERT_FALSE(b.has_error()) << b.error(); EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1 @@ -94,12 +94,12 @@ } 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); spirv::Builder& b = Build(); - auto id = b.GenerateTypeIfNeeded(ary); + auto id = b.GenerateTypeIfNeeded(program->TypeOf(ary)); ASSERT_FALSE(b.has_error()) << b.error(); EXPECT_EQ(1u, id); @@ -111,12 +111,12 @@ } 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); spirv::Builder& b = Build(); - auto id = b.GenerateTypeIfNeeded(ary); + auto id = b.GenerateTypeIfNeeded(program->TypeOf(ary)); ASSERT_FALSE(b.has_error()) << b.error(); EXPECT_EQ(1u, id); @@ -131,13 +131,13 @@ } 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); spirv::Builder& b = Build(); - EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u); - EXPECT_EQ(b.GenerateTypeIfNeeded(ary), 1u); + EXPECT_EQ(b.GenerateTypeIfNeeded(program->TypeOf(ary)), 1u); + EXPECT_EQ(b.GenerateTypeIfNeeded(program->TypeOf(ary)), 1u); ASSERT_FALSE(b.has_error()) << b.error(); EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1 @@ -445,9 +445,9 @@ // 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 // in levels of arrays. - 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 rtarr_mat4x4 = ty.array(ty.mat4x4<f32>(), 0); // Runtime 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* rtarr_mat4x4 = ty.array(ty.mat4x4<f32>(), 0); // Runtime array auto* s = Structure("S",
diff --git a/src/writer/wgsl/generator_impl_type_test.cc b/src/writer/wgsl/generator_impl_type_test.cc index 1afdba6..c311247 100644 --- a/src/writer/wgsl/generator_impl_type_test.cc +++ b/src/writer/wgsl/generator_impl_type_test.cc
@@ -37,7 +37,7 @@ } 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)); GeneratorImpl& gen = Build(); @@ -73,7 +73,7 @@ } 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)); GeneratorImpl& gen = Build(); @@ -83,7 +83,7 @@ } 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)); GeneratorImpl& gen = Build();
diff --git a/test/BUILD.gn b/test/BUILD.gn index a985eb9..740ce6b 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn
@@ -270,7 +270,6 @@ "../src/scope_stack_test.cc", "../src/sem/access_control_type_test.cc", "../src/sem/alias_type_test.cc", - "../src/sem/array_type_test.cc", "../src/sem/bool_type_test.cc", "../src/sem/depth_texture_type_test.cc", "../src/sem/external_texture_type_test.cc", @@ -281,6 +280,7 @@ "../src/sem/pointer_type_test.cc", "../src/sem/sampled_texture_type_test.cc", "../src/sem/sampler_type_test.cc", + "../src/sem/sem_array_test.cc", "../src/sem/sem_struct_test.cc", "../src/sem/storage_texture_type_test.cc", "../src/sem/texture_type_test.cc",
diff --git a/test/bug_tint_782.wgsl b/test/bug_tint_782.wgsl new file mode 100644 index 0000000..dbfcb00 --- /dev/null +++ b/test/bug_tint_782.wgsl
@@ -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; +}