| // Copyright 2021 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include <string> |
| #include <tuple> |
| #include <utility> |
| |
| #include "gmock/gmock.h" |
| #include "src/tint/lang/core/address_space.h" |
| #include "src/tint/lang/core/type/texture_dimension.h" |
| #include "src/tint/lang/wgsl/resolver/dependency_graph.h" |
| #include "src/tint/lang/wgsl/resolver/resolver_helper_test.h" |
| #include "src/tint/utils/containers/transform.h" |
| |
| namespace tint::resolver { |
| namespace { |
| |
| using ::testing::ElementsAre; |
| using namespace tint::core::fluent_types; // NOLINT |
| using namespace tint::core::number_suffixes; // NOLINT |
| |
| template <typename T> |
| class ResolverDependencyGraphTestWithParam : public ResolverTestWithParam<T> { |
| public: |
| DependencyGraph Build(std::string expected_error = "") { |
| DependencyGraph graph; |
| auto result = DependencyGraph::Build(this->AST(), this->Diagnostics(), graph); |
| if (expected_error.empty()) { |
| EXPECT_TRUE(result) << this->Diagnostics(); |
| } else { |
| EXPECT_FALSE(result); |
| EXPECT_EQ(expected_error, this->Diagnostics().Str()); |
| } |
| return graph; |
| } |
| }; |
| |
| using ResolverDependencyGraphTest = ResolverDependencyGraphTestWithParam<::testing::Test>; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Parameterized test helpers |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| /// SymbolDeclKind is used by parameterized tests to enumerate the different |
| /// kinds of symbol declarations. |
| enum class SymbolDeclKind { |
| GlobalVar, |
| GlobalConst, |
| Alias, |
| Struct, |
| Function, |
| Parameter, |
| LocalVar, |
| LocalLet, |
| NestedLocalVar, |
| NestedLocalLet, |
| }; |
| |
| static constexpr SymbolDeclKind kAllSymbolDeclKinds[] = { |
| SymbolDeclKind::GlobalVar, SymbolDeclKind::GlobalConst, SymbolDeclKind::Alias, |
| SymbolDeclKind::Struct, SymbolDeclKind::Function, SymbolDeclKind::Parameter, |
| SymbolDeclKind::LocalVar, SymbolDeclKind::LocalLet, SymbolDeclKind::NestedLocalVar, |
| SymbolDeclKind::NestedLocalLet, |
| }; |
| |
| static constexpr SymbolDeclKind kTypeDeclKinds[] = { |
| SymbolDeclKind::Alias, |
| SymbolDeclKind::Struct, |
| }; |
| |
| static constexpr SymbolDeclKind kValueDeclKinds[] = { |
| SymbolDeclKind::GlobalVar, SymbolDeclKind::GlobalConst, SymbolDeclKind::Parameter, |
| SymbolDeclKind::LocalVar, SymbolDeclKind::LocalLet, SymbolDeclKind::NestedLocalVar, |
| SymbolDeclKind::NestedLocalLet, |
| }; |
| |
| static constexpr SymbolDeclKind kGlobalDeclKinds[] = { |
| SymbolDeclKind::GlobalVar, SymbolDeclKind::GlobalConst, SymbolDeclKind::Alias, |
| SymbolDeclKind::Struct, SymbolDeclKind::Function, |
| }; |
| |
| static constexpr SymbolDeclKind kLocalDeclKinds[] = { |
| SymbolDeclKind::Parameter, SymbolDeclKind::LocalVar, SymbolDeclKind::LocalLet, |
| SymbolDeclKind::NestedLocalVar, SymbolDeclKind::NestedLocalLet, |
| }; |
| |
| static constexpr SymbolDeclKind kGlobalValueDeclKinds[] = { |
| SymbolDeclKind::GlobalVar, |
| SymbolDeclKind::GlobalConst, |
| }; |
| |
| static constexpr SymbolDeclKind kFuncDeclKinds[] = { |
| SymbolDeclKind::Function, |
| }; |
| |
| /// SymbolUseKind is used by parameterized tests to enumerate the different kinds of symbol uses. |
| enum class SymbolUseKind { |
| GlobalVarType, |
| GlobalVarArrayElemType, |
| GlobalVarArraySizeValue, |
| GlobalVarVectorElemType, |
| GlobalVarMatrixElemType, |
| GlobalVarSampledTexElemType, |
| GlobalVarMultisampledTexElemType, |
| GlobalVarValue, |
| GlobalConstType, |
| GlobalConstArrayElemType, |
| GlobalConstArraySizeValue, |
| GlobalConstVectorElemType, |
| GlobalConstMatrixElemType, |
| GlobalConstValue, |
| AliasType, |
| StructMemberType, |
| CallFunction, |
| ParameterType, |
| LocalVarType, |
| LocalVarArrayElemType, |
| LocalVarArraySizeValue, |
| LocalVarVectorElemType, |
| LocalVarMatrixElemType, |
| LocalVarValue, |
| LocalLetType, |
| LocalLetValue, |
| NestedLocalVarType, |
| NestedLocalVarValue, |
| NestedLocalLetType, |
| NestedLocalLetValue, |
| WorkgroupSizeValue, |
| }; |
| |
| static constexpr SymbolUseKind kAllUseKinds[] = { |
| SymbolUseKind::GlobalVarType, |
| SymbolUseKind::GlobalVarArrayElemType, |
| SymbolUseKind::GlobalVarArraySizeValue, |
| SymbolUseKind::GlobalVarVectorElemType, |
| SymbolUseKind::GlobalVarMatrixElemType, |
| SymbolUseKind::GlobalVarSampledTexElemType, |
| SymbolUseKind::GlobalVarMultisampledTexElemType, |
| SymbolUseKind::GlobalVarValue, |
| SymbolUseKind::GlobalConstType, |
| SymbolUseKind::GlobalConstArrayElemType, |
| SymbolUseKind::GlobalConstArraySizeValue, |
| SymbolUseKind::GlobalConstVectorElemType, |
| SymbolUseKind::GlobalConstMatrixElemType, |
| SymbolUseKind::GlobalConstValue, |
| SymbolUseKind::AliasType, |
| SymbolUseKind::StructMemberType, |
| SymbolUseKind::CallFunction, |
| SymbolUseKind::ParameterType, |
| SymbolUseKind::LocalVarType, |
| SymbolUseKind::LocalVarArrayElemType, |
| SymbolUseKind::LocalVarArraySizeValue, |
| SymbolUseKind::LocalVarVectorElemType, |
| SymbolUseKind::LocalVarMatrixElemType, |
| SymbolUseKind::LocalVarValue, |
| SymbolUseKind::LocalLetType, |
| SymbolUseKind::LocalLetValue, |
| SymbolUseKind::NestedLocalVarType, |
| SymbolUseKind::NestedLocalVarValue, |
| SymbolUseKind::NestedLocalLetType, |
| SymbolUseKind::NestedLocalLetValue, |
| SymbolUseKind::WorkgroupSizeValue, |
| }; |
| |
| static constexpr SymbolUseKind kTypeUseKinds[] = { |
| SymbolUseKind::GlobalVarType, |
| SymbolUseKind::GlobalVarArrayElemType, |
| SymbolUseKind::GlobalVarArraySizeValue, |
| SymbolUseKind::GlobalVarVectorElemType, |
| SymbolUseKind::GlobalVarMatrixElemType, |
| SymbolUseKind::GlobalVarSampledTexElemType, |
| SymbolUseKind::GlobalVarMultisampledTexElemType, |
| SymbolUseKind::GlobalConstType, |
| SymbolUseKind::GlobalConstArrayElemType, |
| SymbolUseKind::GlobalConstArraySizeValue, |
| SymbolUseKind::GlobalConstVectorElemType, |
| SymbolUseKind::GlobalConstMatrixElemType, |
| SymbolUseKind::AliasType, |
| SymbolUseKind::StructMemberType, |
| SymbolUseKind::ParameterType, |
| SymbolUseKind::LocalVarType, |
| SymbolUseKind::LocalVarArrayElemType, |
| SymbolUseKind::LocalVarArraySizeValue, |
| SymbolUseKind::LocalVarVectorElemType, |
| SymbolUseKind::LocalVarMatrixElemType, |
| SymbolUseKind::LocalLetType, |
| SymbolUseKind::NestedLocalVarType, |
| SymbolUseKind::NestedLocalLetType, |
| }; |
| |
| static constexpr SymbolUseKind kValueUseKinds[] = { |
| SymbolUseKind::GlobalVarValue, SymbolUseKind::GlobalConstValue, |
| SymbolUseKind::LocalVarValue, SymbolUseKind::LocalLetValue, |
| SymbolUseKind::NestedLocalVarValue, SymbolUseKind::NestedLocalLetValue, |
| SymbolUseKind::WorkgroupSizeValue, |
| }; |
| |
| static constexpr SymbolUseKind kFuncUseKinds[] = { |
| SymbolUseKind::CallFunction, |
| }; |
| |
| /// @returns the description of the symbol declaration kind. |
| /// @note: This differs from the strings used in diagnostic messages. |
| std::ostream& operator<<(std::ostream& out, SymbolDeclKind kind) { |
| switch (kind) { |
| case SymbolDeclKind::GlobalVar: |
| return out << "global var"; |
| case SymbolDeclKind::GlobalConst: |
| return out << "global const"; |
| case SymbolDeclKind::Alias: |
| return out << "alias"; |
| case SymbolDeclKind::Struct: |
| return out << "struct"; |
| case SymbolDeclKind::Function: |
| return out << "function"; |
| case SymbolDeclKind::Parameter: |
| return out << "parameter"; |
| case SymbolDeclKind::LocalVar: |
| return out << "local var"; |
| case SymbolDeclKind::LocalLet: |
| return out << "local let"; |
| case SymbolDeclKind::NestedLocalVar: |
| return out << "nested local var"; |
| case SymbolDeclKind::NestedLocalLet: |
| return out << "nested local let"; |
| } |
| return out << "<unknown>"; |
| } |
| |
| /// @returns the description of the symbol use kind. |
| /// @note: This differs from the strings used in diagnostic messages. |
| std::ostream& operator<<(std::ostream& out, SymbolUseKind kind) { |
| switch (kind) { |
| case SymbolUseKind::GlobalVarType: |
| return out << "global var type"; |
| case SymbolUseKind::GlobalVarValue: |
| return out << "global var value"; |
| case SymbolUseKind::GlobalVarArrayElemType: |
| return out << "global var array element type"; |
| case SymbolUseKind::GlobalVarArraySizeValue: |
| return out << "global var array size value"; |
| case SymbolUseKind::GlobalVarVectorElemType: |
| return out << "global var vector element type"; |
| case SymbolUseKind::GlobalVarMatrixElemType: |
| return out << "global var matrix element type"; |
| case SymbolUseKind::GlobalVarSampledTexElemType: |
| return out << "global var sampled_texture element type"; |
| case SymbolUseKind::GlobalVarMultisampledTexElemType: |
| return out << "global var multisampled_texture element type"; |
| case SymbolUseKind::GlobalConstType: |
| return out << "global const type"; |
| case SymbolUseKind::GlobalConstValue: |
| return out << "global const value"; |
| case SymbolUseKind::GlobalConstArrayElemType: |
| return out << "global const array element type"; |
| case SymbolUseKind::GlobalConstArraySizeValue: |
| return out << "global const array size value"; |
| case SymbolUseKind::GlobalConstVectorElemType: |
| return out << "global const vector element type"; |
| case SymbolUseKind::GlobalConstMatrixElemType: |
| return out << "global const matrix element type"; |
| case SymbolUseKind::AliasType: |
| return out << "alias type"; |
| case SymbolUseKind::StructMemberType: |
| return out << "struct member type"; |
| case SymbolUseKind::CallFunction: |
| return out << "call function"; |
| case SymbolUseKind::ParameterType: |
| return out << "parameter type"; |
| case SymbolUseKind::LocalVarType: |
| return out << "local var type"; |
| case SymbolUseKind::LocalVarArrayElemType: |
| return out << "local var array element type"; |
| case SymbolUseKind::LocalVarArraySizeValue: |
| return out << "local var array size value"; |
| case SymbolUseKind::LocalVarVectorElemType: |
| return out << "local var vector element type"; |
| case SymbolUseKind::LocalVarMatrixElemType: |
| return out << "local var matrix element type"; |
| case SymbolUseKind::LocalVarValue: |
| return out << "local var value"; |
| case SymbolUseKind::LocalLetType: |
| return out << "local let type"; |
| case SymbolUseKind::LocalLetValue: |
| return out << "local let value"; |
| case SymbolUseKind::NestedLocalVarType: |
| return out << "nested local var type"; |
| case SymbolUseKind::NestedLocalVarValue: |
| return out << "nested local var value"; |
| case SymbolUseKind::NestedLocalLetType: |
| return out << "nested local let type"; |
| case SymbolUseKind::NestedLocalLetValue: |
| return out << "nested local let value"; |
| case SymbolUseKind::WorkgroupSizeValue: |
| return out << "workgroup size value"; |
| } |
| return out << "<unknown>"; |
| } |
| |
| /// @returns the declaration scope depth for the symbol declaration kind. |
| /// Globals are at depth 0, parameters and locals are at depth 1, |
| /// nested locals are at depth 2. |
| int ScopeDepth(SymbolDeclKind kind) { |
| switch (kind) { |
| case SymbolDeclKind::GlobalVar: |
| case SymbolDeclKind::GlobalConst: |
| case SymbolDeclKind::Alias: |
| case SymbolDeclKind::Struct: |
| case SymbolDeclKind::Function: |
| return 0; |
| case SymbolDeclKind::Parameter: |
| case SymbolDeclKind::LocalVar: |
| case SymbolDeclKind::LocalLet: |
| return 1; |
| case SymbolDeclKind::NestedLocalVar: |
| case SymbolDeclKind::NestedLocalLet: |
| return 2; |
| } |
| return -1; |
| } |
| |
| /// @returns the use depth for the symbol use kind. |
| /// Globals are at depth 0, parameters and locals are at depth 1, |
| /// nested locals are at depth 2. |
| int ScopeDepth(SymbolUseKind kind) { |
| switch (kind) { |
| case SymbolUseKind::GlobalVarType: |
| case SymbolUseKind::GlobalVarValue: |
| case SymbolUseKind::GlobalVarArrayElemType: |
| case SymbolUseKind::GlobalVarArraySizeValue: |
| case SymbolUseKind::GlobalVarVectorElemType: |
| case SymbolUseKind::GlobalVarMatrixElemType: |
| case SymbolUseKind::GlobalVarSampledTexElemType: |
| case SymbolUseKind::GlobalVarMultisampledTexElemType: |
| case SymbolUseKind::GlobalConstType: |
| case SymbolUseKind::GlobalConstValue: |
| case SymbolUseKind::GlobalConstArrayElemType: |
| case SymbolUseKind::GlobalConstArraySizeValue: |
| case SymbolUseKind::GlobalConstVectorElemType: |
| case SymbolUseKind::GlobalConstMatrixElemType: |
| case SymbolUseKind::AliasType: |
| case SymbolUseKind::StructMemberType: |
| case SymbolUseKind::WorkgroupSizeValue: |
| return 0; |
| case SymbolUseKind::CallFunction: |
| case SymbolUseKind::ParameterType: |
| case SymbolUseKind::LocalVarType: |
| case SymbolUseKind::LocalVarArrayElemType: |
| case SymbolUseKind::LocalVarArraySizeValue: |
| case SymbolUseKind::LocalVarVectorElemType: |
| case SymbolUseKind::LocalVarMatrixElemType: |
| case SymbolUseKind::LocalVarValue: |
| case SymbolUseKind::LocalLetType: |
| case SymbolUseKind::LocalLetValue: |
| return 1; |
| case SymbolUseKind::NestedLocalVarType: |
| case SymbolUseKind::NestedLocalVarValue: |
| case SymbolUseKind::NestedLocalLetType: |
| case SymbolUseKind::NestedLocalLetValue: |
| return 2; |
| } |
| return -1; |
| } |
| |
| /// A helper for building programs that exercise symbol declaration tests. |
| struct SymbolTestHelper { |
| /// The program builder |
| ProgramBuilder* const builder; |
| /// Parameters to a function that may need to be built |
| Vector<const ast::Parameter*, 8> parameters; |
| /// Shallow function var / let declaration statements |
| Vector<const ast::Statement*, 8> statements; |
| /// Nested function local var / let declaration statements |
| Vector<const ast::Statement*, 8> nested_statements; |
| /// Function attributes |
| Vector<const ast::Attribute*, 8> func_attrs; |
| |
| /// Constructor |
| /// @param builder the program builder |
| explicit SymbolTestHelper(ProgramBuilder* builder); |
| |
| /// Destructor. |
| ~SymbolTestHelper(); |
| |
| /// Declares an identifier with the given kind |
| /// @param kind the kind of identifier declaration |
| /// @param symbol the symbol to use for the declaration |
| /// @param source the source of the declaration |
| /// @returns the identifier node |
| const ast::Node* Add(SymbolDeclKind kind, Symbol symbol, Source source); |
| |
| /// Declares a use of an identifier with the given kind |
| /// @param kind the kind of symbol use |
| /// @param symbol the declaration symbol to use |
| /// @param source the source of the use |
| /// @returns the use node |
| const ast::Identifier* Add(SymbolUseKind kind, Symbol symbol, Source source); |
| |
| /// Builds a function, if any parameter or local declarations have been added |
| void Build(); |
| }; |
| |
| SymbolTestHelper::SymbolTestHelper(ProgramBuilder* b) : builder(b) {} |
| |
| SymbolTestHelper::~SymbolTestHelper() {} |
| |
| const ast::Node* SymbolTestHelper::Add(SymbolDeclKind kind, Symbol symbol, Source source = {}) { |
| auto& b = *builder; |
| switch (kind) { |
| case SymbolDeclKind::GlobalVar: |
| return b.GlobalVar(source, symbol, b.ty.i32(), core::AddressSpace::kPrivate); |
| case SymbolDeclKind::GlobalConst: |
| return b.GlobalConst(source, symbol, b.ty.i32(), b.Expr(1_i)); |
| case SymbolDeclKind::Alias: |
| return b.Alias(source, symbol, b.ty.i32()); |
| case SymbolDeclKind::Struct: |
| return b.Structure(source, symbol, Vector{b.Member("m", b.ty.i32())}); |
| case SymbolDeclKind::Function: |
| return b.Func(source, symbol, tint::Empty, b.ty.void_(), tint::Empty); |
| case SymbolDeclKind::Parameter: { |
| auto* node = b.Param(source, symbol, b.ty.i32()); |
| parameters.Push(node); |
| return node; |
| } |
| case SymbolDeclKind::LocalVar: { |
| auto* node = b.Var(source, symbol, b.ty.i32()); |
| statements.Push(b.Decl(node)); |
| return node; |
| } |
| case SymbolDeclKind::LocalLet: { |
| auto* node = b.Let(source, symbol, b.ty.i32(), b.Expr(1_i)); |
| statements.Push(b.Decl(node)); |
| return node; |
| } |
| case SymbolDeclKind::NestedLocalVar: { |
| auto* node = b.Var(source, symbol, b.ty.i32()); |
| nested_statements.Push(b.Decl(node)); |
| return node; |
| } |
| case SymbolDeclKind::NestedLocalLet: { |
| auto* node = b.Let(source, symbol, b.ty.i32(), b.Expr(1_i)); |
| nested_statements.Push(b.Decl(node)); |
| return node; |
| } |
| } |
| return nullptr; |
| } |
| |
| const ast::Identifier* SymbolTestHelper::Add(SymbolUseKind kind, |
| Symbol symbol, |
| Source source = {}) { |
| auto& b = *builder; |
| switch (kind) { |
| case SymbolUseKind::GlobalVarType: { |
| auto node = b.ty(source, symbol); |
| b.GlobalVar(b.Sym(), node, core::AddressSpace::kPrivate); |
| return node->identifier; |
| } |
| case SymbolUseKind::GlobalVarArrayElemType: { |
| auto node = b.ty(source, symbol); |
| b.GlobalVar(b.Sym(), b.ty.array(node, 4_i), core::AddressSpace::kPrivate); |
| return node->identifier; |
| } |
| case SymbolUseKind::GlobalVarArraySizeValue: { |
| auto* node = b.Expr(source, symbol); |
| b.GlobalVar(b.Sym(), b.ty.array(b.ty.i32(), node), core::AddressSpace::kPrivate); |
| return node->identifier; |
| } |
| case SymbolUseKind::GlobalVarVectorElemType: { |
| auto node = b.ty(source, symbol); |
| b.GlobalVar(b.Sym(), b.ty.vec3(node), core::AddressSpace::kPrivate); |
| return node->identifier; |
| } |
| case SymbolUseKind::GlobalVarMatrixElemType: { |
| ast::Type node = b.ty(source, symbol); |
| b.GlobalVar(b.Sym(), b.ty.mat3x4(node), core::AddressSpace::kPrivate); |
| return node->identifier; |
| } |
| case SymbolUseKind::GlobalVarSampledTexElemType: { |
| ast::Type node = b.ty(source, symbol); |
| b.GlobalVar(b.Sym(), b.ty.sampled_texture(core::type::TextureDimension::k2d, node)); |
| return node->identifier; |
| } |
| case SymbolUseKind::GlobalVarMultisampledTexElemType: { |
| ast::Type node = b.ty(source, symbol); |
| b.GlobalVar(b.Sym(), |
| b.ty.multisampled_texture(core::type::TextureDimension::k2d, node)); |
| return node->identifier; |
| } |
| case SymbolUseKind::GlobalVarValue: { |
| auto* node = b.Expr(source, symbol); |
| b.GlobalVar(b.Sym(), b.ty.i32(), core::AddressSpace::kPrivate, node); |
| return node->identifier; |
| } |
| case SymbolUseKind::GlobalConstType: { |
| auto node = b.ty(source, symbol); |
| b.GlobalConst(b.Sym(), node, b.Expr(1_i)); |
| return node->identifier; |
| } |
| case SymbolUseKind::GlobalConstArrayElemType: { |
| auto node = b.ty(source, symbol); |
| b.GlobalConst(b.Sym(), b.ty.array(node, 4_i), b.Expr(1_i)); |
| return node->identifier; |
| } |
| case SymbolUseKind::GlobalConstArraySizeValue: { |
| auto* node = b.Expr(source, symbol); |
| b.GlobalConst(b.Sym(), b.ty.array(b.ty.i32(), node), b.Expr(1_i)); |
| return node->identifier; |
| } |
| case SymbolUseKind::GlobalConstVectorElemType: { |
| auto node = b.ty(source, symbol); |
| b.GlobalConst(b.Sym(), b.ty.vec3(node), b.Expr(1_i)); |
| return node->identifier; |
| } |
| case SymbolUseKind::GlobalConstMatrixElemType: { |
| auto node = b.ty(source, symbol); |
| b.GlobalConst(b.Sym(), b.ty.mat3x4(node), b.Expr(1_i)); |
| return node->identifier; |
| } |
| case SymbolUseKind::GlobalConstValue: { |
| auto* node = b.Expr(source, symbol); |
| b.GlobalConst(b.Sym(), b.ty.i32(), node); |
| return node->identifier; |
| } |
| case SymbolUseKind::AliasType: { |
| auto node = b.ty(source, symbol); |
| b.Alias(b.Sym(), node); |
| return node->identifier; |
| } |
| case SymbolUseKind::StructMemberType: { |
| auto node = b.ty(source, symbol); |
| b.Structure(b.Sym(), Vector{b.Member("m", node)}); |
| return node->identifier; |
| } |
| case SymbolUseKind::CallFunction: { |
| auto* node = b.Ident(source, symbol); |
| statements.Push(b.CallStmt(b.Call(node))); |
| return node; |
| } |
| case SymbolUseKind::ParameterType: { |
| auto node = b.ty(source, symbol); |
| parameters.Push(b.Param(b.Sym(), node)); |
| return node->identifier; |
| } |
| case SymbolUseKind::LocalVarType: { |
| auto node = b.ty(source, symbol); |
| statements.Push(b.Decl(b.Var(b.Sym(), node))); |
| return node->identifier; |
| } |
| case SymbolUseKind::LocalVarArrayElemType: { |
| auto node = b.ty(source, symbol); |
| statements.Push(b.Decl(b.Var(b.Sym(), b.ty.array(node, 4_u), b.Expr(1_i)))); |
| return node->identifier; |
| } |
| case SymbolUseKind::LocalVarArraySizeValue: { |
| auto* node = b.Expr(source, symbol); |
| statements.Push(b.Decl(b.Var(b.Sym(), b.ty.array(b.ty.i32(), node), b.Expr(1_i)))); |
| return node->identifier; |
| } |
| case SymbolUseKind::LocalVarVectorElemType: { |
| auto node = b.ty(source, symbol); |
| statements.Push(b.Decl(b.Var(b.Sym(), b.ty.vec3(node)))); |
| return node->identifier; |
| } |
| case SymbolUseKind::LocalVarMatrixElemType: { |
| auto node = b.ty(source, symbol); |
| statements.Push(b.Decl(b.Var(b.Sym(), b.ty.mat3x4(node)))); |
| return node->identifier; |
| } |
| case SymbolUseKind::LocalVarValue: { |
| auto* node = b.Expr(source, symbol); |
| statements.Push(b.Decl(b.Var(b.Sym(), b.ty.i32(), node))); |
| return node->identifier; |
| } |
| case SymbolUseKind::LocalLetType: { |
| auto node = b.ty(source, symbol); |
| statements.Push(b.Decl(b.Let(b.Sym(), node, b.Expr(1_i)))); |
| return node->identifier; |
| } |
| case SymbolUseKind::LocalLetValue: { |
| auto* node = b.Expr(source, symbol); |
| statements.Push(b.Decl(b.Let(b.Sym(), b.ty.i32(), node))); |
| return node->identifier; |
| } |
| case SymbolUseKind::NestedLocalVarType: { |
| auto node = b.ty(source, symbol); |
| nested_statements.Push(b.Decl(b.Var(b.Sym(), node))); |
| return node->identifier; |
| } |
| case SymbolUseKind::NestedLocalVarValue: { |
| auto* node = b.Expr(source, symbol); |
| nested_statements.Push(b.Decl(b.Var(b.Sym(), b.ty.i32(), node))); |
| return node->identifier; |
| } |
| case SymbolUseKind::NestedLocalLetType: { |
| auto node = b.ty(source, symbol); |
| nested_statements.Push(b.Decl(b.Let(b.Sym(), node, b.Expr(1_i)))); |
| return node->identifier; |
| } |
| case SymbolUseKind::NestedLocalLetValue: { |
| auto* node = b.Expr(source, symbol); |
| nested_statements.Push(b.Decl(b.Let(b.Sym(), b.ty.i32(), node))); |
| return node->identifier; |
| } |
| case SymbolUseKind::WorkgroupSizeValue: { |
| auto* node = b.Expr(source, symbol); |
| func_attrs.Push(b.WorkgroupSize(1_i, node, 2_i)); |
| return node->identifier; |
| } |
| } |
| return nullptr; |
| } |
| |
| void SymbolTestHelper::Build() { |
| auto& b = *builder; |
| if (!nested_statements.IsEmpty()) { |
| statements.Push(b.Block(nested_statements)); |
| nested_statements.Clear(); |
| } |
| if (!parameters.IsEmpty() || !statements.IsEmpty() || !func_attrs.IsEmpty()) { |
| b.Func("func", parameters, b.ty.void_(), statements, func_attrs); |
| parameters.Clear(); |
| statements.Clear(); |
| func_attrs.Clear(); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Used-before-declared tests |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace used_before_decl_tests { |
| |
| using ResolverDependencyGraphUsedBeforeDeclTest = ResolverDependencyGraphTest; |
| |
| TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, FuncCall) { |
| // fn A() { B(); } |
| // fn B() {} |
| |
| Func("A", tint::Empty, ty.void_(), Vector{CallStmt(Call(Ident(Source{{12, 34}}, "B")))}); |
| Func(Source{{56, 78}}, "B", tint::Empty, ty.void_(), Vector{Return()}); |
| |
| Build(); |
| } |
| |
| TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeConstructed) { |
| // fn F() { |
| // { _ = T(); } |
| // } |
| // type T = i32; |
| |
| Func("F", tint::Empty, ty.void_(), Vector{Block(Ignore(Call(Ident(Source{{12, 34}}, "T"))))}); |
| Alias(Source{{56, 78}}, "T", ty.i32()); |
| |
| Build(); |
| } |
| |
| TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeUsedByLocal) { |
| // fn F() { |
| // { var v : T; } |
| // } |
| // type T = i32; |
| |
| Func("F", tint::Empty, ty.void_(), Vector{Block(Decl(Var("v", ty(Source{{12, 34}}, "T"))))}); |
| Alias(Source{{56, 78}}, "T", ty.i32()); |
| |
| Build(); |
| } |
| |
| TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeUsedByParam) { |
| // fn F(p : T) {} |
| // type T = i32; |
| |
| Func("F", Vector{Param("p", ty(Source{{12, 34}}, "T"))}, ty.void_(), tint::Empty); |
| Alias(Source{{56, 78}}, "T", ty.i32()); |
| |
| Build(); |
| } |
| |
| TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeUsedAsReturnType) { |
| // fn F() -> T {} |
| // type T = i32; |
| |
| Func("F", tint::Empty, ty(Source{{12, 34}}, "T"), tint::Empty); |
| Alias(Source{{56, 78}}, "T", ty.i32()); |
| |
| Build(); |
| } |
| |
| TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeByStructMember) { |
| // struct S { m : T }; |
| // type T = i32; |
| |
| Structure("S", Vector{Member("m", ty(Source{{12, 34}}, "T"))}); |
| Alias(Source{{56, 78}}, "T", ty.i32()); |
| |
| Build(); |
| } |
| |
| TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, VarUsed) { |
| // fn F() { |
| // { G = 3.14f; } |
| // } |
| // var G: f32 = 2.1; |
| |
| Func("F", tint::Empty, ty.void_(), |
| Vector{ |
| Block(Assign(Expr(Source{{12, 34}}, "G"), 3.14_f)), |
| }); |
| |
| GlobalVar(Source{{56, 78}}, "G", ty.f32(), core::AddressSpace::kPrivate, Expr(2.1_f)); |
| |
| Build(); |
| } |
| |
| } // namespace used_before_decl_tests |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Undeclared symbol tests |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace undeclared_tests { |
| |
| using ResolverDependencyGraphUndeclaredSymbolTest = |
| ResolverDependencyGraphTestWithParam<SymbolUseKind>; |
| |
| TEST_P(ResolverDependencyGraphUndeclaredSymbolTest, Test) { |
| const Symbol symbol = Sym("SYMBOL"); |
| const auto use_kind = GetParam(); |
| |
| // Build a use of a non-existent symbol |
| SymbolTestHelper helper(this); |
| auto* ident = helper.Add(use_kind, symbol, Source{{56, 78}}); |
| helper.Build(); |
| |
| auto graph = Build(); |
| |
| auto resolved_identifier = graph.resolved_identifiers.Get(ident); |
| ASSERT_TRUE(resolved_identifier); |
| auto* unresolved = resolved_identifier->Unresolved(); |
| ASSERT_NE(unresolved, nullptr); |
| EXPECT_EQ(unresolved->name, "SYMBOL"); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(Types, |
| ResolverDependencyGraphUndeclaredSymbolTest, |
| testing::ValuesIn(kTypeUseKinds)); |
| |
| INSTANTIATE_TEST_SUITE_P(Values, |
| ResolverDependencyGraphUndeclaredSymbolTest, |
| testing::ValuesIn(kValueUseKinds)); |
| |
| INSTANTIATE_TEST_SUITE_P(Functions, |
| ResolverDependencyGraphUndeclaredSymbolTest, |
| testing::ValuesIn(kFuncUseKinds)); |
| |
| } // namespace undeclared_tests |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Self reference by decl |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace undeclared_tests { |
| |
| using ResolverDependencyGraphDeclSelfUse = ResolverDependencyGraphTest; |
| |
| TEST_F(ResolverDependencyGraphDeclSelfUse, GlobalVar) { |
| const Symbol symbol = Sym("SYMBOL"); |
| GlobalVar(symbol, ty.i32(), Mul(Expr(Source{{12, 34}}, symbol), 123_i)); |
| Build(R"(error: cyclic dependency found: 'SYMBOL' -> 'SYMBOL' |
| 12:34 note: var 'SYMBOL' references var 'SYMBOL' here)"); |
| } |
| |
| TEST_F(ResolverDependencyGraphDeclSelfUse, GlobalConst) { |
| const Symbol symbol = Sym("SYMBOL"); |
| GlobalConst(symbol, ty.i32(), Mul(Expr(Source{{12, 34}}, symbol), 123_i)); |
| Build(R"(error: cyclic dependency found: 'SYMBOL' -> 'SYMBOL' |
| 12:34 note: const 'SYMBOL' references const 'SYMBOL' here)"); |
| } |
| |
| TEST_F(ResolverDependencyGraphDeclSelfUse, LocalVar) { |
| const Symbol symbol = Sym("SYMBOL"); |
| auto* ident = Ident(Source{{12, 34}}, symbol); |
| WrapInFunction(Decl(Var(symbol, ty.i32(), Mul(Expr(ident), 123_i)))); |
| auto graph = Build(); |
| |
| auto resolved_identifier = graph.resolved_identifiers.Get(ident); |
| ASSERT_TRUE(resolved_identifier); |
| auto* unresolved = resolved_identifier->Unresolved(); |
| ASSERT_NE(unresolved, nullptr); |
| EXPECT_EQ(unresolved->name, "SYMBOL"); |
| } |
| |
| TEST_F(ResolverDependencyGraphDeclSelfUse, LocalLet) { |
| const Symbol symbol = Sym("SYMBOL"); |
| auto* ident = Ident(Source{{12, 34}}, symbol); |
| WrapInFunction(Decl(Let(symbol, ty.i32(), Mul(Expr(ident), 123_i)))); |
| auto graph = Build(); |
| |
| auto resolved_identifier = graph.resolved_identifiers.Get(ident); |
| ASSERT_TRUE(resolved_identifier); |
| auto* unresolved = resolved_identifier->Unresolved(); |
| ASSERT_NE(unresolved, nullptr); |
| EXPECT_EQ(unresolved->name, "SYMBOL"); |
| } |
| |
| } // namespace undeclared_tests |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Recursive dependency tests |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace recursive_tests { |
| |
| using ResolverDependencyGraphCyclicRefTest = ResolverDependencyGraphTest; |
| |
| TEST_F(ResolverDependencyGraphCyclicRefTest, DirectCall) { |
| // fn main() { main(); } |
| |
| Func(Source{{12, 34}}, "main", tint::Empty, ty.void_(), |
| Vector{CallStmt(Call(Ident(Source{{56, 78}}, "main")))}); |
| |
| Build(R"(12:34 error: cyclic dependency found: 'main' -> 'main' |
| 56:78 note: function 'main' references function 'main' here)"); |
| } |
| |
| TEST_F(ResolverDependencyGraphCyclicRefTest, IndirectCall) { |
| // 1: fn a() { b(); } |
| // 2: fn e() { } |
| // 3: fn d() { e(); b(); } |
| // 4: fn c() { d(); } |
| // 5: fn b() { c(); } |
| |
| Func(Source{{1, 1}}, "a", tint::Empty, ty.void_(), |
| Vector{CallStmt(Call(Ident(Source{{1, 10}}, "b")))}); |
| Func(Source{{2, 1}}, "e", tint::Empty, ty.void_(), tint::Empty); |
| Func(Source{{3, 1}}, "d", tint::Empty, ty.void_(), |
| Vector{ |
| CallStmt(Call(Ident(Source{{3, 10}}, "e"))), |
| CallStmt(Call(Ident(Source{{3, 10}}, "b"))), |
| }); |
| Func(Source{{4, 1}}, "c", tint::Empty, ty.void_(), |
| Vector{CallStmt(Call(Ident(Source{{4, 10}}, "d")))}); |
| Func(Source{{5, 1}}, "b", tint::Empty, ty.void_(), |
| Vector{CallStmt(Call(Ident(Source{{5, 10}}, "c")))}); |
| |
| Build(R"(5:1 error: cyclic dependency found: 'b' -> 'c' -> 'd' -> 'b' |
| 5:10 note: function 'b' references function 'c' here |
| 4:10 note: function 'c' references function 'd' here |
| 3:10 note: function 'd' references function 'b' here)"); |
| } |
| |
| TEST_F(ResolverDependencyGraphCyclicRefTest, Alias_Direct) { |
| // type T = T; |
| |
| Alias(Source{{12, 34}}, "T", ty(Source{{56, 78}}, "T")); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| R"(12:34 error: cyclic dependency found: 'T' -> 'T' |
| 56:78 note: alias 'T' references alias 'T' here)"); |
| } |
| |
| TEST_F(ResolverDependencyGraphCyclicRefTest, Alias_Indirect) { |
| // 1: type Y = Z; |
| // 2: type X = Y; |
| // 3: type Z = X; |
| |
| Alias(Source{{1, 1}}, "Y", ty(Source{{1, 10}}, "Z")); |
| Alias(Source{{2, 1}}, "X", ty(Source{{2, 10}}, "Y")); |
| Alias(Source{{3, 1}}, "Z", ty(Source{{3, 10}}, "X")); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| R"(1:1 error: cyclic dependency found: 'Y' -> 'Z' -> 'X' -> 'Y' |
| 1:10 note: alias 'Y' references alias 'Z' here |
| 3:10 note: alias 'Z' references alias 'X' here |
| 2:10 note: alias 'X' references alias 'Y' here)"); |
| } |
| |
| TEST_F(ResolverDependencyGraphCyclicRefTest, Struct_Direct) { |
| // struct S { |
| // a: S; |
| // }; |
| |
| Structure(Source{{12, 34}}, "S", Vector{Member("a", ty(Source{{56, 78}}, "S"))}); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| R"(12:34 error: cyclic dependency found: 'S' -> 'S' |
| 56:78 note: struct 'S' references struct 'S' here)"); |
| } |
| |
| TEST_F(ResolverDependencyGraphCyclicRefTest, Struct_Indirect) { |
| // 1: struct Y { z: Z; }; |
| // 2: struct X { y: Y; }; |
| // 3: struct Z { x: X; }; |
| |
| Structure(Source{{1, 1}}, "Y", Vector{Member("z", ty(Source{{1, 10}}, "Z"))}); |
| Structure(Source{{2, 1}}, "X", Vector{Member("y", ty(Source{{2, 10}}, "Y"))}); |
| Structure(Source{{3, 1}}, "Z", Vector{Member("x", ty(Source{{3, 10}}, "X"))}); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| R"(1:1 error: cyclic dependency found: 'Y' -> 'Z' -> 'X' -> 'Y' |
| 1:10 note: struct 'Y' references struct 'Z' here |
| 3:10 note: struct 'Z' references struct 'X' here |
| 2:10 note: struct 'X' references struct 'Y' here)"); |
| } |
| |
| TEST_F(ResolverDependencyGraphCyclicRefTest, GlobalVar_Direct) { |
| // var<private> V : i32 = V; |
| |
| GlobalVar(Source{{12, 34}}, "V", ty.i32(), Expr(Source{{56, 78}}, "V")); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| R"(12:34 error: cyclic dependency found: 'V' -> 'V' |
| 56:78 note: var 'V' references var 'V' here)"); |
| } |
| |
| TEST_F(ResolverDependencyGraphCyclicRefTest, GlobalConst_Direct) { |
| // let V : i32 = V; |
| |
| GlobalConst(Source{{12, 34}}, "V", ty.i32(), Expr(Source{{56, 78}}, "V")); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| R"(12:34 error: cyclic dependency found: 'V' -> 'V' |
| 56:78 note: const 'V' references const 'V' here)"); |
| } |
| |
| TEST_F(ResolverDependencyGraphCyclicRefTest, GlobalVar_Indirect) { |
| // 1: var<private> Y : i32 = Z; |
| // 2: var<private> X : i32 = Y; |
| // 3: var<private> Z : i32 = X; |
| |
| GlobalVar(Source{{1, 1}}, "Y", ty.i32(), Expr(Source{{1, 10}}, "Z")); |
| GlobalVar(Source{{2, 1}}, "X", ty.i32(), Expr(Source{{2, 10}}, "Y")); |
| GlobalVar(Source{{3, 1}}, "Z", ty.i32(), Expr(Source{{3, 10}}, "X")); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| R"(1:1 error: cyclic dependency found: 'Y' -> 'Z' -> 'X' -> 'Y' |
| 1:10 note: var 'Y' references var 'Z' here |
| 3:10 note: var 'Z' references var 'X' here |
| 2:10 note: var 'X' references var 'Y' here)"); |
| } |
| |
| TEST_F(ResolverDependencyGraphCyclicRefTest, GlobalConst_Indirect) { |
| // 1: const Y : i32 = Z; |
| // 2: const X : i32 = Y; |
| // 3: const Z : i32 = X; |
| |
| GlobalConst(Source{{1, 1}}, "Y", ty.i32(), Expr(Source{{1, 10}}, "Z")); |
| GlobalConst(Source{{2, 1}}, "X", ty.i32(), Expr(Source{{2, 10}}, "Y")); |
| GlobalConst(Source{{3, 1}}, "Z", ty.i32(), Expr(Source{{3, 10}}, "X")); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| R"(1:1 error: cyclic dependency found: 'Y' -> 'Z' -> 'X' -> 'Y' |
| 1:10 note: const 'Y' references const 'Z' here |
| 3:10 note: const 'Z' references const 'X' here |
| 2:10 note: const 'X' references const 'Y' here)"); |
| } |
| |
| TEST_F(ResolverDependencyGraphCyclicRefTest, Mixed_RecursiveDependencies) { |
| // 1: fn F() -> R { return Z; } |
| // 2: type A = S; |
| // 3: struct S { a : A }; |
| // 4: var Z = L; |
| // 5: type R = A; |
| // 6: const L : S = Z; |
| |
| Func(Source{{1, 1}}, "F", tint::Empty, ty(Source{{1, 5}}, "R"), |
| Vector{Return(Expr(Source{{1, 10}}, "Z"))}); |
| Alias(Source{{2, 1}}, "A", ty(Source{{2, 10}}, "S")); |
| Structure(Source{{3, 1}}, "S", Vector{Member("a", ty(Source{{3, 10}}, "A"))}); |
| GlobalVar(Source{{4, 1}}, "Z", Expr(Source{{4, 10}}, "L")); |
| Alias(Source{{5, 1}}, "R", ty(Source{{5, 10}}, "A")); |
| GlobalConst(Source{{6, 1}}, "L", ty(Source{{5, 5}}, "S"), Expr(Source{{5, 10}}, "Z")); |
| |
| EXPECT_FALSE(r()->Resolve()); |
| EXPECT_EQ(r()->error(), |
| R"(2:1 error: cyclic dependency found: 'A' -> 'S' -> 'A' |
| 2:10 note: alias 'A' references struct 'S' here |
| 3:10 note: struct 'S' references alias 'A' here |
| 4:1 error: cyclic dependency found: 'Z' -> 'L' -> 'Z' |
| 4:10 note: var 'Z' references const 'L' here |
| 5:10 note: const 'L' references var 'Z' here)"); |
| } |
| |
| } // namespace recursive_tests |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Symbol Redeclaration tests |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace redeclaration_tests { |
| |
| using ResolverDependencyGraphRedeclarationTest = |
| ResolverDependencyGraphTestWithParam<std::tuple<SymbolDeclKind, SymbolDeclKind>>; |
| |
| TEST_P(ResolverDependencyGraphRedeclarationTest, Test) { |
| const auto symbol = Sym("SYMBOL"); |
| |
| auto a_kind = std::get<0>(GetParam()); |
| auto b_kind = std::get<1>(GetParam()); |
| |
| auto a_source = Source{{12, 34}}; |
| auto b_source = Source{{56, 78}}; |
| |
| if (a_kind != SymbolDeclKind::Parameter && b_kind == SymbolDeclKind::Parameter) { |
| std::swap(a_source, b_source); // Parameters are declared before locals |
| } |
| |
| SymbolTestHelper helper(this); |
| helper.Add(a_kind, symbol, a_source); |
| helper.Add(b_kind, symbol, b_source); |
| helper.Build(); |
| |
| bool error = ScopeDepth(a_kind) == ScopeDepth(b_kind); |
| |
| Build(error ? R"(56:78 error: redeclaration of 'SYMBOL' |
| 12:34 note: 'SYMBOL' previously declared here)" |
| : ""); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(ResolverTest, |
| ResolverDependencyGraphRedeclarationTest, |
| testing::Combine(testing::ValuesIn(kAllSymbolDeclKinds), |
| testing::ValuesIn(kAllSymbolDeclKinds))); |
| |
| } // namespace redeclaration_tests |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Ordered global tests |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace ordered_globals { |
| |
| using ResolverDependencyGraphOrderedGlobalsTest = |
| ResolverDependencyGraphTestWithParam<std::tuple<SymbolDeclKind, SymbolUseKind>>; |
| |
| TEST_P(ResolverDependencyGraphOrderedGlobalsTest, InOrder) { |
| const Symbol symbol = Sym("SYMBOL"); |
| const auto decl_kind = std::get<0>(GetParam()); |
| const auto use_kind = std::get<1>(GetParam()); |
| |
| // Declaration before use |
| SymbolTestHelper helper(this); |
| helper.Add(decl_kind, symbol, Source{{12, 34}}); |
| helper.Add(use_kind, symbol, Source{{56, 78}}); |
| helper.Build(); |
| |
| ASSERT_EQ(AST().GlobalDeclarations().Length(), 2u); |
| |
| auto* decl = AST().GlobalDeclarations()[0]; |
| auto* use = AST().GlobalDeclarations()[1]; |
| EXPECT_THAT(Build().ordered_globals, ElementsAre(decl, use)); |
| } |
| |
| TEST_P(ResolverDependencyGraphOrderedGlobalsTest, OutOfOrder) { |
| const Symbol symbol = Sym("SYMBOL"); |
| const auto decl_kind = std::get<0>(GetParam()); |
| const auto use_kind = std::get<1>(GetParam()); |
| |
| // Use before declaration |
| SymbolTestHelper helper(this); |
| helper.Add(use_kind, symbol, Source{{56, 78}}); |
| helper.Build(); // If the use is in a function, then ensure this function is |
| // built before the symbol declaration |
| helper.Add(decl_kind, symbol, Source{{12, 34}}); |
| helper.Build(); |
| |
| ASSERT_EQ(AST().GlobalDeclarations().Length(), 2u); |
| |
| auto* use = AST().GlobalDeclarations()[0]; |
| auto* decl = AST().GlobalDeclarations()[1]; |
| EXPECT_THAT(Build().ordered_globals, ElementsAre(decl, use)); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(Types, |
| ResolverDependencyGraphOrderedGlobalsTest, |
| testing::Combine(testing::ValuesIn(kTypeDeclKinds), |
| testing::ValuesIn(kTypeUseKinds))); |
| |
| INSTANTIATE_TEST_SUITE_P(Values, |
| ResolverDependencyGraphOrderedGlobalsTest, |
| testing::Combine(testing::ValuesIn(kGlobalValueDeclKinds), |
| testing::ValuesIn(kValueUseKinds))); |
| |
| INSTANTIATE_TEST_SUITE_P(Functions, |
| ResolverDependencyGraphOrderedGlobalsTest, |
| testing::Combine(testing::ValuesIn(kFuncDeclKinds), |
| testing::ValuesIn(kFuncUseKinds))); |
| |
| TEST_F(ResolverDependencyGraphOrderedGlobalsTest, DirectiveFirst) { |
| // Test that directive nodes always go before any other global declaration. |
| // Although all directives in a valid WGSL program must go before any other global declaration, |
| // a transform may produce such a AST tree that has some declarations before directive nodes. |
| // DependencyGraph should deal with these cases. |
| auto* var_1 = GlobalVar("SYMBOL1", ty.i32()); |
| auto* enable = Enable(wgsl::Extension::kF16); |
| auto* var_2 = GlobalVar("SYMBOL2", ty.f32()); |
| auto* diagnostic = DiagnosticDirective(wgsl::DiagnosticSeverity::kWarning, "foo"); |
| auto* var_3 = GlobalVar("SYMBOL3", ty.u32()); |
| auto* req = Require(wgsl::LanguageFeature::kReadonlyAndReadwriteStorageTextures); |
| |
| EXPECT_THAT(AST().GlobalDeclarations(), |
| ElementsAre(var_1, enable, var_2, diagnostic, var_3, req)); |
| EXPECT_THAT(Build().ordered_globals, ElementsAre(enable, diagnostic, req, var_1, var_2, var_3)); |
| } |
| } // namespace ordered_globals |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Resolve to user-declaration tests |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace resolve_to_user_decl { |
| |
| using ResolverDependencyGraphResolveToUserDeclTest = |
| ResolverDependencyGraphTestWithParam<std::tuple<SymbolDeclKind, SymbolUseKind>>; |
| |
| TEST_P(ResolverDependencyGraphResolveToUserDeclTest, Test) { |
| const Symbol symbol = Sym("SYMBOL"); |
| const auto decl_kind = std::get<0>(GetParam()); |
| const auto use_kind = std::get<1>(GetParam()); |
| |
| // Build a symbol declaration and a use of that symbol |
| SymbolTestHelper helper(this); |
| auto* decl = helper.Add(decl_kind, symbol, Source{{12, 34}}); |
| auto* use = helper.Add(use_kind, symbol, Source{{56, 78}}); |
| helper.Build(); |
| |
| // If the declaration is visible to the use, then we expect the analysis to |
| // succeed. |
| bool expect_resolved = ScopeDepth(decl_kind) <= ScopeDepth(use_kind); |
| auto graph = Build(); |
| |
| auto resolved_identifier = graph.resolved_identifiers.Get(use); |
| ASSERT_TRUE(resolved_identifier); |
| |
| if (expect_resolved) { |
| // Check that the use resolves to the declaration |
| auto* resolved_node = resolved_identifier->Node(); |
| EXPECT_EQ(resolved_node, decl) |
| << "resolved: " << (resolved_node ? resolved_node->TypeInfo().name : "<null>") << "\n" |
| << "decl: " << decl->TypeInfo().name; |
| } else { |
| auto* unresolved = resolved_identifier->Unresolved(); |
| ASSERT_NE(unresolved, nullptr); |
| EXPECT_EQ(unresolved->name, "SYMBOL"); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(Types, |
| ResolverDependencyGraphResolveToUserDeclTest, |
| testing::Combine(testing::ValuesIn(kTypeDeclKinds), |
| testing::ValuesIn(kTypeUseKinds))); |
| |
| INSTANTIATE_TEST_SUITE_P(Values, |
| ResolverDependencyGraphResolveToUserDeclTest, |
| testing::Combine(testing::ValuesIn(kValueDeclKinds), |
| testing::ValuesIn(kValueUseKinds))); |
| |
| INSTANTIATE_TEST_SUITE_P(Functions, |
| ResolverDependencyGraphResolveToUserDeclTest, |
| testing::Combine(testing::ValuesIn(kFuncDeclKinds), |
| testing::ValuesIn(kFuncUseKinds))); |
| |
| } // namespace resolve_to_user_decl |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Resolve to builtin func tests |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace resolve_to_builtin_func { |
| |
| using ResolverDependencyGraphResolveToBuiltinFn = |
| ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, wgsl::BuiltinFn>>; |
| |
| TEST_P(ResolverDependencyGraphResolveToBuiltinFn, Resolve) { |
| const auto use = std::get<0>(GetParam()); |
| const auto builtin = std::get<1>(GetParam()); |
| const auto symbol = Symbols().New(tint::ToString(builtin)); |
| |
| SymbolTestHelper helper(this); |
| auto* ident = helper.Add(use, symbol); |
| helper.Build(); |
| |
| auto graph = Build(); |
| auto resolved = graph.resolved_identifiers.Get(ident); |
| ASSERT_TRUE(resolved); |
| EXPECT_EQ(resolved->BuiltinFn(), builtin) << resolved->String(); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(Types, |
| ResolverDependencyGraphResolveToBuiltinFn, |
| testing::Combine(testing::ValuesIn(kTypeUseKinds), |
| testing::ValuesIn(wgsl::kBuiltinFns))); |
| |
| INSTANTIATE_TEST_SUITE_P(Values, |
| ResolverDependencyGraphResolveToBuiltinFn, |
| testing::Combine(testing::ValuesIn(kValueUseKinds), |
| testing::ValuesIn(wgsl::kBuiltinFns))); |
| |
| INSTANTIATE_TEST_SUITE_P(Functions, |
| ResolverDependencyGraphResolveToBuiltinFn, |
| testing::Combine(testing::ValuesIn(kFuncUseKinds), |
| testing::ValuesIn(wgsl::kBuiltinFns))); |
| |
| } // namespace resolve_to_builtin_func |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Resolve to builtin type tests |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace resolve_to_builtin_type { |
| |
| using ResolverDependencyGraphResolveToBuiltinType = |
| ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, std::string_view>>; |
| |
| TEST_P(ResolverDependencyGraphResolveToBuiltinType, Resolve) { |
| const auto use = std::get<0>(GetParam()); |
| const auto name = std::get<1>(GetParam()); |
| const auto symbol = Symbols().New(name); |
| |
| SymbolTestHelper helper(this); |
| auto* ident = helper.Add(use, symbol); |
| helper.Build(); |
| |
| auto graph = Build(); |
| auto resolved = graph.resolved_identifiers.Get(ident); |
| ASSERT_TRUE(resolved); |
| EXPECT_EQ(resolved->BuiltinType(), core::ParseBuiltinType(name)) << resolved->String(); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(Types, |
| ResolverDependencyGraphResolveToBuiltinType, |
| testing::Combine(testing::ValuesIn(kTypeUseKinds), |
| testing::ValuesIn(core::kBuiltinTypeStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(Values, |
| ResolverDependencyGraphResolveToBuiltinType, |
| testing::Combine(testing::ValuesIn(kValueUseKinds), |
| testing::ValuesIn(core::kBuiltinTypeStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(Functions, |
| ResolverDependencyGraphResolveToBuiltinType, |
| testing::Combine(testing::ValuesIn(kFuncUseKinds), |
| testing::ValuesIn(core::kBuiltinTypeStrings))); |
| |
| } // namespace resolve_to_builtin_type |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Resolve to core::Access tests |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace resolve_to_access { |
| |
| using ResolverDependencyGraphResolveToAccess = |
| ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, std::string_view>>; |
| |
| TEST_P(ResolverDependencyGraphResolveToAccess, Resolve) { |
| const auto use = std::get<0>(GetParam()); |
| const auto name = std::get<1>(GetParam()); |
| const auto symbol = Symbols().New(name); |
| |
| SymbolTestHelper helper(this); |
| auto* ident = helper.Add(use, symbol); |
| helper.Build(); |
| |
| auto graph = Build(); |
| auto resolved = graph.resolved_identifiers.Get(ident); |
| ASSERT_TRUE(resolved); |
| EXPECT_EQ(resolved->Access(), core::ParseAccess(name)) << resolved->String(); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(Types, |
| ResolverDependencyGraphResolveToAccess, |
| testing::Combine(testing::ValuesIn(kTypeUseKinds), |
| testing::ValuesIn(core::kAccessStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(Values, |
| ResolverDependencyGraphResolveToAccess, |
| testing::Combine(testing::ValuesIn(kValueUseKinds), |
| testing::ValuesIn(core::kAccessStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(Functions, |
| ResolverDependencyGraphResolveToAccess, |
| testing::Combine(testing::ValuesIn(kFuncUseKinds), |
| testing::ValuesIn(core::kAccessStrings))); |
| |
| } // namespace resolve_to_access |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Resolve to core::AddressSpace tests |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace resolve_to_address_space { |
| |
| using ResolverDependencyGraphResolveToAddressSpace = |
| ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, std::string_view>>; |
| |
| TEST_P(ResolverDependencyGraphResolveToAddressSpace, Resolve) { |
| const auto use = std::get<0>(GetParam()); |
| const auto name = std::get<1>(GetParam()); |
| const auto symbol = Symbols().New(name); |
| |
| SymbolTestHelper helper(this); |
| auto* ident = helper.Add(use, symbol); |
| helper.Build(); |
| |
| auto graph = Build(); |
| auto resolved = graph.resolved_identifiers.Get(ident); |
| ASSERT_TRUE(resolved); |
| EXPECT_EQ(resolved->AddressSpace(), core::ParseAddressSpace(name)) << resolved->String(); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(Types, |
| ResolverDependencyGraphResolveToAddressSpace, |
| testing::Combine(testing::ValuesIn(kTypeUseKinds), |
| testing::ValuesIn(core::kAddressSpaceStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(Values, |
| ResolverDependencyGraphResolveToAddressSpace, |
| testing::Combine(testing::ValuesIn(kValueUseKinds), |
| testing::ValuesIn(core::kAddressSpaceStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(Functions, |
| ResolverDependencyGraphResolveToAddressSpace, |
| testing::Combine(testing::ValuesIn(kFuncUseKinds), |
| testing::ValuesIn(core::kAddressSpaceStrings))); |
| |
| } // namespace resolve_to_address_space |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Resolve to core::BuiltinValue tests |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace resolve_to_builtin_value { |
| |
| using ResolverDependencyGraphResolveToBuiltinValue = |
| ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, std::string_view>>; |
| |
| TEST_P(ResolverDependencyGraphResolveToBuiltinValue, Resolve) { |
| const auto use = std::get<0>(GetParam()); |
| const auto name = std::get<1>(GetParam()); |
| const auto symbol = Symbols().New(name); |
| |
| SymbolTestHelper helper(this); |
| auto* ident = helper.Add(use, symbol); |
| helper.Build(); |
| |
| auto graph = Build(); |
| auto resolved = graph.resolved_identifiers.Get(ident); |
| ASSERT_TRUE(resolved); |
| EXPECT_EQ(resolved->BuiltinValue(), core::ParseBuiltinValue(name)) << resolved->String(); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(Types, |
| ResolverDependencyGraphResolveToBuiltinValue, |
| testing::Combine(testing::ValuesIn(kTypeUseKinds), |
| testing::ValuesIn(core::kBuiltinValueStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(Values, |
| ResolverDependencyGraphResolveToBuiltinValue, |
| testing::Combine(testing::ValuesIn(kValueUseKinds), |
| testing::ValuesIn(core::kBuiltinValueStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(Functions, |
| ResolverDependencyGraphResolveToBuiltinValue, |
| testing::Combine(testing::ValuesIn(kFuncUseKinds), |
| testing::ValuesIn(core::kBuiltinValueStrings))); |
| |
| } // namespace resolve_to_builtin_value |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Resolve to core::InterpolationSampling tests |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace resolve_to_interpolation_sampling { |
| |
| using ResolverDependencyGraphResolveToInterpolationSampling = |
| ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, std::string_view>>; |
| |
| TEST_P(ResolverDependencyGraphResolveToInterpolationSampling, Resolve) { |
| const auto use = std::get<0>(GetParam()); |
| const auto name = std::get<1>(GetParam()); |
| const auto symbol = Symbols().New(name); |
| |
| SymbolTestHelper helper(this); |
| auto* ident = helper.Add(use, symbol); |
| helper.Build(); |
| |
| auto graph = Build(); |
| auto resolved = graph.resolved_identifiers.Get(ident); |
| ASSERT_TRUE(resolved); |
| EXPECT_EQ(resolved->InterpolationSampling(), core::ParseInterpolationSampling(name)) |
| << resolved->String(); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(Types, |
| ResolverDependencyGraphResolveToInterpolationSampling, |
| testing::Combine(testing::ValuesIn(kTypeUseKinds), |
| testing::ValuesIn(core::kInterpolationTypeStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(Values, |
| ResolverDependencyGraphResolveToInterpolationSampling, |
| testing::Combine(testing::ValuesIn(kValueUseKinds), |
| testing::ValuesIn(core::kInterpolationTypeStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(Functions, |
| ResolverDependencyGraphResolveToInterpolationSampling, |
| testing::Combine(testing::ValuesIn(kFuncUseKinds), |
| testing::ValuesIn(core::kInterpolationTypeStrings))); |
| |
| } // namespace resolve_to_interpolation_sampling |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Resolve to core::InterpolationType tests |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace resolve_to_interpolation_sampling { |
| |
| using ResolverDependencyGraphResolveToInterpolationType = |
| ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, std::string_view>>; |
| |
| TEST_P(ResolverDependencyGraphResolveToInterpolationType, Resolve) { |
| const auto use = std::get<0>(GetParam()); |
| const auto name = std::get<1>(GetParam()); |
| const auto symbol = Symbols().New(name); |
| |
| SymbolTestHelper helper(this); |
| auto* ident = helper.Add(use, symbol); |
| helper.Build(); |
| |
| auto graph = Build(); |
| auto resolved = graph.resolved_identifiers.Get(ident); |
| ASSERT_TRUE(resolved); |
| EXPECT_EQ(resolved->InterpolationType(), core::ParseInterpolationType(name)) |
| << resolved->String(); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(Types, |
| ResolverDependencyGraphResolveToInterpolationType, |
| testing::Combine(testing::ValuesIn(kTypeUseKinds), |
| testing::ValuesIn(core::kInterpolationSamplingStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(Values, |
| ResolverDependencyGraphResolveToInterpolationType, |
| testing::Combine(testing::ValuesIn(kValueUseKinds), |
| testing::ValuesIn(core::kInterpolationSamplingStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(Functions, |
| ResolverDependencyGraphResolveToInterpolationType, |
| testing::Combine(testing::ValuesIn(kFuncUseKinds), |
| testing::ValuesIn(core::kInterpolationSamplingStrings))); |
| |
| } // namespace resolve_to_interpolation_sampling |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Resolve to core::TexelFormat tests |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace resolve_to_texel_format { |
| |
| using ResolverDependencyGraphResolveToTexelFormat = |
| ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, std::string_view>>; |
| |
| TEST_P(ResolverDependencyGraphResolveToTexelFormat, Resolve) { |
| const auto use = std::get<0>(GetParam()); |
| const auto name = std::get<1>(GetParam()); |
| const auto symbol = Symbols().New(name); |
| |
| SymbolTestHelper helper(this); |
| auto* ident = helper.Add(use, symbol); |
| helper.Build(); |
| |
| auto graph = Build(); |
| auto resolved = graph.resolved_identifiers.Get(ident); |
| ASSERT_TRUE(resolved); |
| EXPECT_EQ(resolved->TexelFormat(), core::ParseTexelFormat(name)) << resolved->String(); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(Types, |
| ResolverDependencyGraphResolveToTexelFormat, |
| testing::Combine(testing::ValuesIn(kTypeUseKinds), |
| testing::ValuesIn(core::kTexelFormatStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(Values, |
| ResolverDependencyGraphResolveToTexelFormat, |
| testing::Combine(testing::ValuesIn(kValueUseKinds), |
| testing::ValuesIn(core::kTexelFormatStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(Functions, |
| ResolverDependencyGraphResolveToTexelFormat, |
| testing::Combine(testing::ValuesIn(kFuncUseKinds), |
| testing::ValuesIn(core::kTexelFormatStrings))); |
| |
| } // namespace resolve_to_texel_format |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // Shadowing tests |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace shadowing { |
| |
| using ResolverDependencyGraphShadowScopeTest = |
| ResolverDependencyGraphTestWithParam<std::tuple<SymbolDeclKind, SymbolDeclKind>>; |
| |
| TEST_P(ResolverDependencyGraphShadowScopeTest, Test) { |
| const Symbol symbol = Sym("SYMBOL"); |
| const auto outer_kind = std::get<0>(GetParam()); |
| const auto inner_kind = std::get<1>(GetParam()); |
| |
| // Build a symbol declaration and a use of that symbol |
| SymbolTestHelper helper(this); |
| auto* outer = helper.Add(outer_kind, symbol, Source{{12, 34}}); |
| helper.Add(inner_kind, symbol, Source{{56, 78}}); |
| auto* inner_var = helper.nested_statements.Length() |
| ? helper.nested_statements[0]->As<ast::VariableDeclStatement>()->variable |
| : helper.statements.Length() |
| ? helper.statements[0]->As<ast::VariableDeclStatement>()->variable |
| : helper.parameters[0]; |
| helper.Build(); |
| |
| auto shadows = Build().shadows; |
| auto shadow = shadows.Get(inner_var); |
| ASSERT_TRUE(shadow); |
| EXPECT_EQ(*shadow, outer); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(LocalShadowGlobal, |
| ResolverDependencyGraphShadowScopeTest, |
| testing::Combine(testing::ValuesIn(kGlobalDeclKinds), |
| testing::ValuesIn(kLocalDeclKinds))); |
| |
| INSTANTIATE_TEST_SUITE_P(NestedLocalShadowLocal, |
| ResolverDependencyGraphShadowScopeTest, |
| testing::Combine(testing::Values(SymbolDeclKind::Parameter, |
| SymbolDeclKind::LocalVar, |
| SymbolDeclKind::LocalLet), |
| testing::Values(SymbolDeclKind::NestedLocalVar, |
| SymbolDeclKind::NestedLocalLet))); |
| |
| using ResolverDependencyGraphShadowKindTest = |
| ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, std::string_view>>; |
| |
| TEST_P(ResolverDependencyGraphShadowKindTest, ShadowedByGlobalVar) { |
| const auto use = std::get<0>(GetParam()); |
| const std::string_view name = std::get<1>(GetParam()); |
| const auto symbol = Symbols().New(tint::ToString(name)); |
| |
| SymbolTestHelper helper(this); |
| auto* decl = GlobalVar( |
| symbol, // |
| name == "i32" ? ty.u32() : ty.i32(), |
| name == "private" ? core::AddressSpace::kWorkgroup : core::AddressSpace::kPrivate); |
| auto* ident = helper.Add(use, symbol); |
| helper.Build(); |
| |
| auto graph = Build(); |
| auto resolved = graph.resolved_identifiers.Get(ident); |
| ASSERT_TRUE(resolved); |
| EXPECT_EQ(resolved->Node(), decl) << resolved->String(); |
| } |
| |
| TEST_P(ResolverDependencyGraphShadowKindTest, ShadowedByStruct) { |
| const auto use = std::get<0>(GetParam()); |
| const std::string_view name = std::get<1>(GetParam()); |
| const auto symbol = Symbols().New(tint::ToString(name)); |
| |
| SymbolTestHelper helper(this); |
| auto* decl = Structure(symbol, Vector{ |
| Member("m", name == "i32" ? ty.u32() : ty.i32()), |
| }); |
| auto* ident = helper.Add(use, symbol); |
| helper.Build(); |
| |
| auto graph = Build(); |
| auto resolved = graph.resolved_identifiers.Get(ident); |
| ASSERT_TRUE(resolved); |
| EXPECT_EQ(resolved->Node(), decl) << resolved->String(); |
| } |
| |
| TEST_P(ResolverDependencyGraphShadowKindTest, ShadowedByFunc) { |
| const auto use = std::get<0>(GetParam()); |
| const auto name = std::get<1>(GetParam()); |
| const auto symbol = Symbols().New(tint::ToString(name)); |
| |
| SymbolTestHelper helper(this); |
| auto* decl = helper.Add(SymbolDeclKind::Function, symbol); |
| auto* ident = helper.Add(use, symbol); |
| helper.Build(); |
| |
| auto graph = Build(); |
| auto resolved = graph.resolved_identifiers.Get(ident); |
| ASSERT_TRUE(resolved); |
| EXPECT_EQ(resolved->Node(), decl) << resolved->String(); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(Access, |
| ResolverDependencyGraphShadowKindTest, |
| testing::Combine(testing::ValuesIn(kAllUseKinds), |
| testing::ValuesIn(core::kAccessStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(AddressSpace, |
| ResolverDependencyGraphShadowKindTest, |
| testing::Combine(testing::ValuesIn(kAllUseKinds), |
| testing::ValuesIn(core::kAddressSpaceStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(BuiltinType, |
| ResolverDependencyGraphShadowKindTest, |
| testing::Combine(testing::ValuesIn(kAllUseKinds), |
| testing::ValuesIn(core::kBuiltinTypeStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(BuiltinFn, |
| ResolverDependencyGraphShadowKindTest, |
| testing::Combine(testing::ValuesIn(kAllUseKinds), |
| testing::ValuesIn(core::kBuiltinTypeStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(InterpolationSampling, |
| ResolverDependencyGraphShadowKindTest, |
| testing::Combine(testing::ValuesIn(kAllUseKinds), |
| testing::ValuesIn(core::kInterpolationSamplingStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(InterpolationType, |
| ResolverDependencyGraphShadowKindTest, |
| testing::Combine(testing::ValuesIn(kAllUseKinds), |
| testing::ValuesIn(core::kInterpolationTypeStrings))); |
| |
| INSTANTIATE_TEST_SUITE_P(TexelFormat, |
| ResolverDependencyGraphShadowKindTest, |
| testing::Combine(testing::ValuesIn(kAllUseKinds), |
| testing::ValuesIn(core::kTexelFormatStrings))); |
| |
| } // namespace shadowing |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // AST traversal tests |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace ast_traversal { |
| |
| static const ast::Identifier* IdentifierOf(const ast::IdentifierExpression* expr) { |
| return expr->identifier; |
| } |
| |
| static const ast::Identifier* IdentifierOf(const ast::Identifier* ident) { |
| return ident; |
| } |
| |
| using ResolverDependencyGraphTraversalTest = ResolverDependencyGraphTest; |
| |
| TEST_F(ResolverDependencyGraphTraversalTest, SymbolsReached) { |
| const auto value_sym = Sym("VALUE"); |
| const auto type_sym = Sym("TYPE"); |
| const auto func_sym = Sym("FUNC"); |
| |
| const auto* value_decl = GlobalVar(value_sym, ty.i32(), core::AddressSpace::kPrivate); |
| const auto* type_decl = Alias(type_sym, ty.i32()); |
| const auto* func_decl = Func(func_sym, tint::Empty, ty.void_(), tint::Empty); |
| |
| struct SymbolUse { |
| const ast::Node* decl = nullptr; |
| const ast::Identifier* use = nullptr; |
| std::string where; |
| }; |
| |
| Vector<SymbolUse, 64> symbol_uses; |
| |
| auto add_use = [&](const ast::Node* decl, auto use, int line, std::string_view kind) { |
| symbol_uses.Push(SymbolUse{ |
| decl, IdentifierOf(use), |
| std::string(__FILE__) + ":" + std::to_string(line) + ": " + std::string(kind)}); |
| return use; |
| }; |
| |
| #define V add_use(value_decl, Expr(value_sym), __LINE__, "V()") |
| #define T add_use(type_decl, ty(type_sym), __LINE__, "T()") |
| #define F add_use(func_decl, Ident(func_sym), __LINE__, "F()") |
| |
| Alias(Sym(), T); |
| Structure(Sym(), // |
| Vector{Member(Sym(), T, |
| Vector{ |
| // |
| MemberAlign(V), MemberSize(V) // |
| })}); |
| GlobalVar(Sym(), T, V); |
| GlobalConst(Sym(), T, V); |
| Func(Sym(), |
| Vector{ |
| Param(Sym(), T, |
| Vector{ |
| Location(V), // Parameter attributes |
| Color(V), |
| Builtin(V), |
| Interpolate(V), |
| Interpolate(V, V), |
| }), |
| }, |
| T, // Return type |
| Vector{ |
| Decl(Var(Sym(), T, V)), // |
| Decl(Let(Sym(), T, V)), // |
| CallStmt(Call(F, V)), // |
| Block( // |
| Assign(V, V)), // |
| If(V, // |
| Block(Assign(V, V)), // |
| Else(If(V, // |
| Block(Assign(V, V))))), // |
| Ignore(Bitcast(T, V)), // |
| For(Decl(Var(Sym(), T, V)), // |
| Equal(V, V), // |
| Assign(V, V), // |
| Block( // |
| Assign(V, V))), // |
| While(Equal(V, V), // |
| Block( // |
| Assign(V, V))), // |
| Loop(Block(Assign(V, V)), // |
| Block(Assign(V, V), BreakIf(V))), // |
| Switch(V, // |
| Case(CaseSelector(1_i), // |
| Block(Assign(V, V))), // |
| DefaultCase(Block(Assign(V, V)))), // |
| Return(V), // |
| Break(), // |
| Discard(), // |
| }, |
| tint::Empty, // function attributes |
| Vector{Location(V)}); // return attributes |
| // Exercise type traversal |
| GlobalVar(Sym(), ty.atomic(T)); |
| GlobalVar(Sym(), ty.bool_()); |
| GlobalVar(Sym(), ty.i32()); |
| GlobalVar(Sym(), ty.u32()); |
| GlobalVar(Sym(), ty.f32()); |
| GlobalVar(Sym(), ty.array(T, V)); |
| GlobalVar(Sym(), ty.vec3(T)); |
| GlobalVar(Sym(), ty.mat3x2(T)); |
| GlobalVar(Sym(), ty.ptr<private_>(T)); |
| GlobalVar(Sym(), ty.sampled_texture(core::type::TextureDimension::k2d, T)); |
| GlobalVar(Sym(), ty.depth_texture(core::type::TextureDimension::k2d)); |
| GlobalVar(Sym(), ty.depth_multisampled_texture(core::type::TextureDimension::k2d)); |
| GlobalVar(Sym(), ty.external_texture()); |
| GlobalVar(Sym(), ty.multisampled_texture(core::type::TextureDimension::k2d, T)); |
| GlobalVar(Sym(), ty.storage_texture(core::type::TextureDimension::k2d, |
| core::TexelFormat::kR32Float, core::Access::kRead)); |
| GlobalVar(Sym(), ty.sampler(core::type::SamplerKind::kSampler)); |
| |
| GlobalVar(Sym(), ty.i32(), Vector{Binding(V), Group(V)}); |
| GlobalVar(Sym(), ty.input_attachment(T), Vector{Binding(V), Group(V), InputAttachmentIndex(V)}); |
| GlobalVar(Sym(), ty.i32(), Vector{Location(V)}); |
| Override(Sym(), ty.i32(), Vector{Id(V)}); |
| |
| Func(Sym(), tint::Empty, ty.void_(), tint::Empty); |
| #undef V |
| #undef T |
| #undef F |
| |
| auto graph = Build(); |
| for (auto use : symbol_uses) { |
| auto resolved_identifier = graph.resolved_identifiers.Get(use.use); |
| ASSERT_TRUE(resolved_identifier) << use.where; |
| EXPECT_EQ(*resolved_identifier, use.decl) << use.where; |
| } |
| } |
| |
| TEST_F(ResolverDependencyGraphTraversalTest, InferredType) { |
| // Check that the nullptr of the var / const / let type doesn't make things explode |
| GlobalVar("a", Expr(1_i)); |
| GlobalConst("b", Expr(1_i)); |
| WrapInFunction(Var("c", Expr(1_i)), // |
| Let("d", Expr(1_i))); |
| Build(); |
| } |
| |
| // Reproduces an unbalanced stack push / pop bug in |
| // DependencyAnalysis::SortGlobals(), found by clusterfuzz. |
| // See: crbug.com/chromium/1273451 |
| TEST_F(ResolverDependencyGraphTraversalTest, chromium_1273451) { |
| Structure("A", Vector{Member("a", ty.i32())}); |
| Structure("B", Vector{Member("b", ty.i32())}); |
| Func("f", Vector{Param("a", ty("A"))}, ty("B"), |
| Vector{ |
| Return(Call("B")), |
| }); |
| Build(); |
| } |
| |
| } // namespace ast_traversal |
| |
| } // namespace |
| } // namespace tint::resolver |