diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn index 662f90d14f..b07045c72a 100644 --- a/src/tint/BUILD.gn +++ b/src/tint/BUILD.gn @@ -2051,6 +2051,12 @@ if (tint_build_unittests) { configs += [ "//build/config/compiler:no_chromium_code" ] } + if (is_win && is_debug) { + # TODO(crbug.com/tint/1749): both msvc and clang builds stack overflow on debug builds. + # Increase the initial stack size to 4 MB (default is 1MB). + ldflags = [ "/STACK:4194304" ] + } + testonly = true } } diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc index f934aca54f..76bb3dd4e0 100644 --- a/src/tint/resolver/resolver.cc +++ b/src/tint/resolver/resolver.cc @@ -99,6 +99,7 @@ namespace { constexpr int64_t kMaxArrayElementCount = 65536; constexpr uint32_t kMaxStatementDepth = 127; +constexpr size_t kMaxNestDepthOfCompositeType = 255; } // namespace @@ -1697,7 +1698,7 @@ const type::Type* Resolver::ConcreteType(const type::Type* ty, target_el_ty = target_arr_ty->ElemType(); } if (auto* el_ty = ConcreteType(a->ElemType(), target_el_ty, source)) { - return Array(source, source, el_ty, a->Count(), /* explicit_stride */ 0); + return Array(source, source, source, el_ty, a->Count(), /* explicit_stride */ 0); } return nullptr; }, @@ -2129,7 +2130,8 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) { } return nullptr; } - auto* arr = Array(expr->source, expr->source, el_ty, el_count, /* explicit_stride */ 0); + auto* arr = Array(expr->source, expr->source, expr->source, el_ty, el_count, + /* explicit_stride */ 0); if (!arr) { return nullptr; } @@ -2446,7 +2448,8 @@ type::Type* Resolver::BuiltinType(builtin::Builtin builtin_ty, const ast::Identi return nullptr; } - auto* out = Array(ast_el_ty->source, // + auto* out = Array(tmpl_ident->source, // + ast_el_ty->source, // ast_count ? ast_count->source : ident->source, // el_ty, el_count, explicit_stride); if (!out) { @@ -2726,6 +2729,19 @@ type::Type* Resolver::BuiltinType(builtin::Builtin builtin_ty, const ast::Identi return nullptr; } +size_t Resolver::NestDepth(const type::Type* ty) const { + return Switch( + ty, // + [](const type::Vector*) { return size_t{1}; }, + [](const type::Matrix*) { return size_t{2}; }, + [&](Default) { + if (auto d = nest_depth_.Get(ty)) { + return *d; + } + return size_t{0}; + }); +} + void Resolver::CollectTextureSamplerPairs( const sem::Builtin* builtin, utils::VectorRef args) const { @@ -3528,7 +3544,8 @@ bool Resolver::ArrayAttributes(utils::VectorRef attribute return true; } -type::Array* Resolver::Array(const Source& el_source, +type::Array* Resolver::Array(const Source& array_source, + const Source& el_source, const Source& count_source, const type::Type* el_ty, const type::ArrayCount* el_count, @@ -3555,6 +3572,17 @@ type::Array* Resolver::Array(const Source& el_source, el_ty, el_count, el_align, static_cast(size), static_cast(stride), static_cast(implicit_stride)); + // Maximum nesting depth of composite types + // https://gpuweb.github.io/gpuweb/wgsl/#limits + const size_t nest_depth = 1 + NestDepth(el_ty); + if (nest_depth > kMaxNestDepthOfCompositeType) { + AddError("array has nesting depth of " + std::to_string(nest_depth) + ", maximum is " + + std::to_string(kMaxNestDepthOfCompositeType), + array_source); + return nullptr; + } + nest_depth_.Add(out, nest_depth); + if (!validator_.Array(out, el_source)) { return nullptr; } @@ -3574,15 +3602,18 @@ type::Type* Resolver::Alias(const ast::Alias* alias) { } sem::Struct* Resolver::Structure(const ast::Struct* str) { + auto struct_name = [&] { // + return builder_->Symbols().NameFor(str->name->symbol); + }; + if (validator_.IsValidationEnabled(str->attributes, ast::DisabledValidation::kIgnoreStructMemberLimit)) { // Maximum number of members in a structure type // https://gpuweb.github.io/gpuweb/wgsl/#limits const size_t kMaxNumStructMembers = 16383; if (str->members.Length() > kMaxNumStructMembers) { - AddError("struct '" + builder_->Symbols().NameFor(str->name->symbol) + "' has " + - std::to_string(str->members.Length()) + " members, maximum is " + - std::to_string(kMaxNumStructMembers), + AddError("struct '" + struct_name() + "' has " + std::to_string(str->members.Length()) + + " members, maximum is " + std::to_string(kMaxNumStructMembers), str->source); return nullptr; } @@ -3608,6 +3639,7 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) { uint64_t struct_align = 1; utils::Hashmap member_map; + size_t members_nest_depth = 0; for (auto* member : str->members) { Mark(member); Mark(member->name); @@ -3624,6 +3656,8 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) { return nullptr; } + members_nest_depth = std::max(members_nest_depth, NestDepth(type)); + // validator_.Validate member type if (!validator_.IsPlain(type)) { AddError(sem_.TypeNameOf(type) + " cannot be used as the type of a structure member", @@ -3821,6 +3855,18 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) { return nullptr; } + // Maximum nesting depth of composite types + // https://gpuweb.github.io/gpuweb/wgsl/#limits + const size_t nest_depth = 1 + members_nest_depth; + if (nest_depth > kMaxNestDepthOfCompositeType) { + AddError("struct '" + struct_name() + "' has nesting depth of " + + std::to_string(nest_depth) + ", maximum is " + + std::to_string(kMaxNestDepthOfCompositeType), + str->source); + return nullptr; + } + nest_depth_.Add(out, nest_depth); + return out; } diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h index 72f9e8fd76..fff7506642 100644 --- a/src/tint/resolver/resolver.h +++ b/src/tint/resolver/resolver.h @@ -349,6 +349,7 @@ class Resolver { /// Builds and returns the semantic information for an array. /// @returns the semantic Array information, or nullptr if an error is raised. + /// @param array_source the source of the array /// @param el_source the source of the array element, or the array if the array does not have a /// locally-declared element AST node. /// @param count_source the source of the array count, or the array if the array does not have a @@ -356,7 +357,8 @@ class Resolver { /// @param el_ty the Array element type /// @param el_count the number of elements in the array. /// @param explicit_stride the explicit byte stride of the array. Zero means implicit stride. - type::Array* Array(const Source& el_source, + type::Array* Array(const Source& array_source, + const Source& el_source, const Source& count_source, const type::Type* el_ty, const type::ArrayCount* el_count, @@ -496,6 +498,10 @@ class Resolver { /// @note: Will raise an ICE if @p symbol is not a builtin type. type::Type* BuiltinType(builtin::Builtin builtin_ty, const ast::Identifier* ident); + /// @returns the nesting depth of @ty as defined in + /// https://gpuweb.github.io/gpuweb/wgsl/#composite-types + size_t NestDepth(const type::Type* ty) const; + // ArrayConstructorSig represents a unique array constructor signature. // It is a tuple of the array type, number of arguments provided and earliest evaluation stage. using ArrayConstructorSig = @@ -566,6 +572,7 @@ class Resolver { logical_binary_lhs_to_parent_; utils::Hashset skip_const_eval_; IdentifierResolveHint identifier_resolve_hint_; + utils::Hashmap nest_depth_; }; } // namespace tint::resolver diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc index 25155fff46..af8759a7f8 100644 --- a/src/tint/resolver/resolver_test.cc +++ b/src/tint/resolver/resolver_test.cc @@ -2434,5 +2434,195 @@ TEST_F(ResolverTest, MaxNumStructMembers_WithIgnoreStructMemberLimit_Valid) { EXPECT_TRUE(r()->Resolve()) << r()->error(); } +size_t kMaxNestDepthOfCompositeType = 255; + +TEST_F(ResolverTest, MaxNestDepthOfCompositeType_Structs_Valid) { + auto* s = Structure("S", utils::Vector{Member("m", ty.i32())}); + size_t depth = 1; // Depth of struct + size_t iterations = kMaxNestDepthOfCompositeType - depth; + for (size_t i = 0; i < iterations; ++i) { + s = Structure("S" + std::to_string(i), utils::Vector{Member("m", ty.Of(s))}); + } + EXPECT_TRUE(r()->Resolve()) << r()->error(); +} + +TEST_F(ResolverTest, MaxNestDepthOfCompositeType_Structs_Invalid) { + auto* s = Structure("S", utils::Vector{Member("m", ty.i32())}); + size_t depth = 1; // Depth of struct + size_t iterations = kMaxNestDepthOfCompositeType - depth + 1; + for (size_t i = 0; i < iterations; ++i) { + auto source = i == iterations - 1 ? Source{{12, 34}} : Source{{0, i}}; + s = Structure(source, "S" + std::to_string(i), utils::Vector{Member("m", ty.Of(s))}); + } + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), "12:34 error: struct 'S254' has nesting depth of 256, maximum is 255"); +} + +TEST_F(ResolverTest, MaxNestDepthOfCompositeType_StructsWithVector_Valid) { + auto* s = Structure("S", utils::Vector{Member("m", ty.vec3())}); + size_t depth = 2; // Despth of struct + vector + size_t iterations = kMaxNestDepthOfCompositeType - depth; + for (size_t i = 0; i < iterations; ++i) { + s = Structure("S" + std::to_string(i), utils::Vector{Member("m", ty.Of(s))}); + } + EXPECT_TRUE(r()->Resolve()) << r()->error(); +} + +TEST_F(ResolverTest, MaxNestDepthOfCompositeType_StructsWithVector_Invalid) { + auto* s = Structure("S", utils::Vector{Member("m", ty.vec3())}); + size_t depth = 2; // Despth of struct + vector + size_t iterations = kMaxNestDepthOfCompositeType - depth + 1; + for (size_t i = 0; i < iterations; ++i) { + auto source = i == iterations - 1 ? Source{{12, 34}} : Source{{0, i}}; + s = Structure(source, "S" + std::to_string(i), utils::Vector{Member("m", ty.Of(s))}); + } + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), "12:34 error: struct 'S253' has nesting depth of 256, maximum is 255"); +} + +TEST_F(ResolverTest, MaxNestDepthOfCompositeType_StructsWithMatrix_Valid) { + auto* s = Structure("S", utils::Vector{Member("m", ty.mat3x3())}); + size_t depth = 3; // Depth of struct + matrix + size_t iterations = kMaxNestDepthOfCompositeType - depth; + for (size_t i = 0; i < iterations; ++i) { + s = Structure("S" + std::to_string(i), utils::Vector{Member("m", ty.Of(s))}); + } + EXPECT_TRUE(r()->Resolve()) << r()->error(); +} + +TEST_F(ResolverTest, MaxNestDepthOfCompositeType_StructsWithMatrix_Invalid) { + auto* s = Structure("S", utils::Vector{Member("m", ty.mat3x3())}); + size_t depth = 3; // Depth of struct + matrix + size_t iterations = kMaxNestDepthOfCompositeType - depth + 1; + for (size_t i = 0; i < iterations; ++i) { + auto source = i == iterations - 1 ? Source{{12, 34}} : Source{{0, i}}; + s = Structure(source, "S" + std::to_string(i), utils::Vector{Member("m", ty.Of(s))}); + } + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), "12:34 error: struct 'S252' has nesting depth of 256, maximum is 255"); +} + +TEST_F(ResolverTest, MaxNestDepthOfCompositeType_Arrays_Valid) { + auto a = ty.array(ty.i32(), 10_u); + size_t depth = 1; // Depth of array + size_t iterations = kMaxNestDepthOfCompositeType - depth; + for (size_t i = 0; i < iterations; ++i) { + a = ty.array(a, 1_u); + } + Alias("a", a); + EXPECT_TRUE(r()->Resolve()) << r()->error(); +} + +TEST_F(ResolverTest, MaxNestDepthOfCompositeType_Arrays_Invalid) { + auto a = ty.array(Source{{99, 88}}, ty.i32(), 10_u); + size_t depth = 1; // Depth of array + size_t iterations = kMaxNestDepthOfCompositeType - depth + 1; + for (size_t i = 0; i < iterations; ++i) { + auto source = (i == iterations - 1) ? Source{{12, 34}} : Source{{0, i}}; + a = ty.array(source, a, 1_u); + } + Alias("a", a); + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), "12:34 error: array has nesting depth of 256, maximum is 255"); +} + +TEST_F(ResolverTest, MaxNestDepthOfCompositeType_ArraysOfVector_Valid) { + auto a = ty.array(ty.vec3(), 10_u); + size_t depth = 2; // Depth of array + vector + size_t iterations = kMaxNestDepthOfCompositeType - depth; + for (size_t i = 0; i < iterations; ++i) { + a = ty.array(a, 1_u); + } + Alias("a", a); + EXPECT_TRUE(r()->Resolve()) << r()->error(); +} + +TEST_F(ResolverTest, MaxNestDepthOfCompositeType_ArraysOfVector_Invalid) { + auto a = ty.array(Source{{99, 88}}, ty.vec3(), 10_u); + size_t depth = 2; // Depth of array + vector + size_t iterations = kMaxNestDepthOfCompositeType - depth + 1; + for (size_t i = 0; i < iterations; ++i) { + auto source = (i == iterations - 1) ? Source{{12, 34}} : Source{{0, i}}; + a = ty.array(source, a, 1_u); + } + Alias("a", a); + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), "12:34 error: array has nesting depth of 256, maximum is 255"); +} + +TEST_F(ResolverTest, MaxNestDepthOfCompositeType_ArraysOfMatrix_Valid) { + auto a = ty.array(ty.mat3x3(), 10_u); + size_t depth = 3; // Depth of array + matrix + size_t iterations = kMaxNestDepthOfCompositeType - depth; + for (size_t i = 0; i < iterations; ++i) { + a = ty.array(a, 1_u); + } + Alias("a", a); + EXPECT_TRUE(r()->Resolve()) << r()->error(); +} + +TEST_F(ResolverTest, MaxNestDepthOfCompositeType_ArraysOfMatrix_Invalid) { + auto a = ty.array(ty.mat3x3(), 10_u); + size_t depth = 3; // Depth of array + matrix + size_t iterations = kMaxNestDepthOfCompositeType - depth + 1; + for (size_t i = 0; i < iterations; ++i) { + auto source = (i == iterations - 1) ? Source{{12, 34}} : Source{{0, i}}; + a = ty.array(source, a, 1_u); + } + Alias("a", a); + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), "12:34 error: array has nesting depth of 256, maximum is 255"); +} + +TEST_F(ResolverTest, MaxNestDepthOfCompositeType_StructsOfArray_Valid) { + auto a = ty.array(ty.mat3x3(), 10_u); + auto* s = Structure("S", utils::Vector{Member("m", a)}); + size_t depth = 4; // Depth of struct + array + matrix + size_t iterations = kMaxNestDepthOfCompositeType - depth; + for (size_t i = 0; i < iterations; ++i) { + s = Structure("S" + std::to_string(i), utils::Vector{Member("m", ty.Of(s))}); + } + EXPECT_TRUE(r()->Resolve()) << r()->error(); +} + +TEST_F(ResolverTest, MaxNestDepthOfCompositeType_StructsOfArray_Invalid) { + auto a = ty.array(ty.mat3x3(), 10_u); + auto* s = Structure("S", utils::Vector{Member("m", a)}); + size_t depth = 4; // Depth of struct + array + matrix + size_t iterations = kMaxNestDepthOfCompositeType - depth + 1; + for (size_t i = 0; i < iterations; ++i) { + auto source = i == iterations - 1 ? Source{{12, 34}} : Source{{0, i}}; + s = Structure(source, "S" + std::to_string(i), utils::Vector{Member("m", ty.Of(s))}); + } + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), "12:34 error: struct 'S251' has nesting depth of 256, maximum is 255"); +} + +TEST_F(ResolverTest, MaxNestDepthOfCompositeType_ArraysOfStruct_Valid) { + auto* s = Structure("S", utils::Vector{Member("m", ty.mat3x3())}); + auto a = ty.array(ty.Of(s), 10_u); + size_t depth = 4; // Depth of array + struct + matrix + size_t iterations = kMaxNestDepthOfCompositeType - depth; + for (size_t i = 0; i < iterations; ++i) { + a = ty.array(a, 1_u); + } + Alias("a", a); + EXPECT_TRUE(r()->Resolve()) << r()->error(); +} + +TEST_F(ResolverTest, MaxNestDepthOfCompositeType_ArraysOfStruct_Invalid) { + auto* s = Structure("S", utils::Vector{Member("m", ty.mat3x3())}); + auto a = ty.array(ty.Of(s), 10_u); + size_t depth = 4; // Depth of array + struct + matrix + size_t iterations = kMaxNestDepthOfCompositeType - depth + 1; + for (size_t i = 0; i < iterations; ++i) { + auto source = (i == iterations - 1) ? Source{{12, 34}} : Source{{0, i}}; + a = ty.array(source, a, 1_u); + } + Alias("a", a); + EXPECT_FALSE(r()->Resolve()); + EXPECT_EQ(r()->error(), "12:34 error: array has nesting depth of 256, maximum is 255"); +} + } // namespace } // namespace tint::resolver