// Copyright 2021 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_TINT_RESOLVER_RESOLVER_TEST_HELPER_H_ #define SRC_TINT_RESOLVER_RESOLVER_TEST_HELPER_H_ #include #include #include #include #include #include #include #include "gtest/gtest.h" #include "src/tint/program_builder.h" #include "src/tint/resolver/resolver.h" #include "src/tint/sem/statement.h" #include "src/tint/sem/value_expression.h" #include "src/tint/sem/variable.h" #include "src/tint/traits.h" #include "src/tint/type/abstract_float.h" #include "src/tint/type/abstract_int.h" #include "src/tint/utils/vector.h" namespace tint::resolver { /// Helper class for testing class TestHelper : public ProgramBuilder { public: /// Constructor TestHelper(); /// Destructor ~TestHelper() override; /// @return a pointer to the Resolver Resolver* r() const { return resolver_.get(); } /// @return a pointer to the validator const Validator* v() const { return resolver_->GetValidatorForTesting(); } /// Returns the statement that holds the given expression. /// @param expr the ast::Expression /// @return the ast::Statement of the ast::Expression, or nullptr if the /// expression is not owned by a statement. const ast::Statement* StmtOf(const ast::Expression* expr) { auto* sem_stmt = Sem().Get(expr)->Stmt(); return sem_stmt ? sem_stmt->Declaration() : nullptr; } /// Returns the BlockStatement that holds the given statement. /// @param stmt the ast::Statement /// @return the ast::BlockStatement that holds the ast::Statement, or nullptr /// if the statement is not owned by a BlockStatement. const ast::BlockStatement* BlockOf(const ast::Statement* stmt) { auto* sem_stmt = Sem().Get(stmt); return sem_stmt ? sem_stmt->Block()->Declaration() : nullptr; } /// Returns the BlockStatement that holds the given expression. /// @param expr the ast::Expression /// @return the ast::Statement of the ast::Expression, or nullptr if the /// expression is not indirectly owned by a BlockStatement. const ast::BlockStatement* BlockOf(const ast::Expression* expr) { auto* sem_stmt = Sem().Get(expr)->Stmt(); return sem_stmt ? sem_stmt->Block()->Declaration() : nullptr; } /// Returns the semantic variable for the given identifier expression. /// @param expr the identifier expression /// @return the resolved sem::Variable of the identifier, or nullptr if /// the expression did not resolve to a variable. const sem::Variable* VarOf(const ast::Expression* expr) { if (auto* sem = Sem().GetVal(expr)) { if (auto* var_user = As(sem->UnwrapLoad())) { return var_user->Variable(); } } return nullptr; } /// Checks that all the users of the given variable are as expected /// @param var the variable to check /// @param expected_users the expected users of the variable /// @return true if all users are as expected bool CheckVarUsers(const ast::Variable* var, utils::VectorRef expected_users) { auto& var_users = Sem().Get(var)->Users(); if (var_users.size() != expected_users.Length()) { return false; } for (size_t i = 0; i < var_users.size(); i++) { if (var_users[i]->Declaration() != expected_users[i]) { return false; } } return true; } /// @param type a type /// @returns the name for `type` that closely resembles how it would be /// declared in WGSL. std::string FriendlyName(const ast::Type* type) { return type->FriendlyName(Symbols()); } /// @param type a type /// @returns the name for `type` that closely resembles how it would be /// declared in WGSL. std::string FriendlyName(const type::Type* type) { return type->FriendlyName(Symbols()); } private: std::unique_ptr resolver_; }; class ResolverTest : public TestHelper, public testing::Test {}; template class ResolverTestWithParam : public TestHelper, public testing::TestWithParam {}; namespace builder { template struct vec {}; template using vec2 = vec<2, T>; template using vec3 = vec<3, T>; template using vec4 = vec<4, T>; template struct mat {}; template using mat2x2 = mat<2, 2, T>; template using mat2x3 = mat<2, 3, T>; template using mat2x4 = mat<2, 4, T>; template using mat3x2 = mat<3, 2, T>; template using mat3x3 = mat<3, 3, T>; template using mat3x4 = mat<3, 4, T>; template using mat4x2 = mat<4, 2, T>; template using mat4x3 = mat<4, 3, T>; template using mat4x4 = mat<4, 4, T>; template struct array {}; template struct alias {}; template using alias1 = alias; template using alias2 = alias; template using alias3 = alias; template struct ptr {}; /// A scalar value using Scalar = std::variant; /// Returns current variant value in `s` cast to type `T` template T As(const Scalar& s) { return std::visit([](auto&& v) { return static_cast(v); }, s); } using ast_type_func_ptr = const ast::Type* (*)(ProgramBuilder& b); using ast_expr_func_ptr = const ast::Expression* (*)(ProgramBuilder& b, utils::VectorRef args); using ast_expr_from_double_func_ptr = const ast::Expression* (*)(ProgramBuilder& b, double v); using sem_type_func_ptr = const type::Type* (*)(ProgramBuilder& b); using type_name_func_ptr = std::string (*)(); struct UnspecializedElementType {}; /// Base template for DataType, specialized below. template struct DataType { /// The element type using ElementType = UnspecializedElementType; }; /// Helper that represents no-type. Returns nullptr for all static methods. template <> struct DataType { /// The element type using ElementType = void; /// @return nullptr static inline const ast::Type* AST(ProgramBuilder&) { return nullptr; } /// @return nullptr static inline const type::Type* Sem(ProgramBuilder&) { return nullptr; } }; /// Helper for building bool types and expressions template <> struct DataType { /// The element type using ElementType = bool; /// false as bool is not a composite type static constexpr bool is_composite = false; /// @param b the ProgramBuilder /// @return a new AST bool type static inline const ast::Type* AST(ProgramBuilder& b) { return b.ty.bool_(); } /// @param b the ProgramBuilder /// @return the semantic bool type static inline const type::Type* Sem(ProgramBuilder& b) { return b.create(); } /// @param b the ProgramBuilder /// @param args args of size 1 with the boolean value to init with /// @return a new AST expression of the bool type static inline const ast::Expression* Expr(ProgramBuilder& b, utils::VectorRef args) { return b.Expr(std::get(args[0])); } /// @param b the ProgramBuilder /// @param v arg of type double that will be cast to bool. /// @return a new AST expression of the bool type static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) { return Expr(b, utils::Vector{static_cast(v)}); } /// @returns the WGSL name for the type static inline std::string Name() { return "bool"; } }; /// Helper for building i32 types and expressions template <> struct DataType { /// The element type using ElementType = i32; /// false as i32 is not a composite type static constexpr bool is_composite = false; /// @param b the ProgramBuilder /// @return a new AST i32 type static inline const ast::Type* AST(ProgramBuilder& b) { return b.ty.i32(); } /// @param b the ProgramBuilder /// @return the semantic i32 type static inline const type::Type* Sem(ProgramBuilder& b) { return b.create(); } /// @param b the ProgramBuilder /// @param args args of size 1 with the i32 value to init with /// @return a new AST i32 literal value expression static inline const ast::Expression* Expr(ProgramBuilder& b, utils::VectorRef args) { return b.Expr(std::get(args[0])); } /// @param b the ProgramBuilder /// @param v arg of type double that will be cast to i32. /// @return a new AST i32 literal value expression static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) { return Expr(b, utils::Vector{static_cast(v)}); } /// @returns the WGSL name for the type static inline std::string Name() { return "i32"; } }; /// Helper for building u32 types and expressions template <> struct DataType { /// The element type using ElementType = u32; /// false as u32 is not a composite type static constexpr bool is_composite = false; /// @param b the ProgramBuilder /// @return a new AST u32 type static inline const ast::Type* AST(ProgramBuilder& b) { return b.ty.u32(); } /// @param b the ProgramBuilder /// @return the semantic u32 type static inline const type::Type* Sem(ProgramBuilder& b) { return b.create(); } /// @param b the ProgramBuilder /// @param args args of size 1 with the u32 value to init with /// @return a new AST u32 literal value expression static inline const ast::Expression* Expr(ProgramBuilder& b, utils::VectorRef args) { return b.Expr(std::get(args[0])); } /// @param b the ProgramBuilder /// @param v arg of type double that will be cast to u32. /// @return a new AST u32 literal value expression static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) { return Expr(b, utils::Vector{static_cast(v)}); } /// @returns the WGSL name for the type static inline std::string Name() { return "u32"; } }; /// Helper for building f32 types and expressions template <> struct DataType { /// The element type using ElementType = f32; /// false as f32 is not a composite type static constexpr bool is_composite = false; /// @param b the ProgramBuilder /// @return a new AST f32 type static inline const ast::Type* AST(ProgramBuilder& b) { return b.ty.f32(); } /// @param b the ProgramBuilder /// @return the semantic f32 type static inline const type::Type* Sem(ProgramBuilder& b) { return b.create(); } /// @param b the ProgramBuilder /// @param args args of size 1 with the f32 value to init with /// @return a new AST f32 literal value expression static inline const ast::Expression* Expr(ProgramBuilder& b, utils::VectorRef args) { return b.Expr(std::get(args[0])); } /// @param b the ProgramBuilder /// @param v arg of type double that will be cast to f32. /// @return a new AST f32 literal value expression static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) { return Expr(b, utils::Vector{static_cast(v)}); } /// @returns the WGSL name for the type static inline std::string Name() { return "f32"; } }; /// Helper for building f16 types and expressions template <> struct DataType { /// The element type using ElementType = f16; /// false as f16 is not a composite type static constexpr bool is_composite = false; /// @param b the ProgramBuilder /// @return a new AST f16 type static inline const ast::Type* AST(ProgramBuilder& b) { return b.ty.f16(); } /// @param b the ProgramBuilder /// @return the semantic f16 type static inline const type::Type* Sem(ProgramBuilder& b) { return b.create(); } /// @param b the ProgramBuilder /// @param args args of size 1 with the f16 value to init with /// @return a new AST f16 literal value expression static inline const ast::Expression* Expr(ProgramBuilder& b, utils::VectorRef args) { return b.Expr(std::get(args[0])); } /// @param b the ProgramBuilder /// @param v arg of type double that will be cast to f16. /// @return a new AST f16 literal value expression static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) { return Expr(b, utils::Vector{static_cast(v)}); } /// @returns the WGSL name for the type static inline std::string Name() { return "f16"; } }; /// Helper for building abstract float types and expressions template <> struct DataType { /// The element type using ElementType = AFloat; /// false as AFloat is not a composite type static constexpr bool is_composite = false; /// @returns nullptr, as abstract floats are un-typeable static inline const ast::Type* AST(ProgramBuilder&) { return nullptr; } /// @param b the ProgramBuilder /// @return the semantic abstract-float type static inline const type::Type* Sem(ProgramBuilder& b) { return b.create(); } /// @param b the ProgramBuilder /// @param args args of size 1 with the abstract-float value to init with /// @return a new AST abstract-float literal value expression static inline const ast::Expression* Expr(ProgramBuilder& b, utils::VectorRef args) { return b.Expr(std::get(args[0])); } /// @param b the ProgramBuilder /// @param v arg of type double that will be cast to AFloat. /// @return a new AST abstract-float literal value expression static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) { return Expr(b, utils::Vector{static_cast(v)}); } /// @returns the WGSL name for the type static inline std::string Name() { return "abstract-float"; } }; /// Helper for building abstract integer types and expressions template <> struct DataType { /// The element type using ElementType = AInt; /// false as AFloat is not a composite type static constexpr bool is_composite = false; /// @returns nullptr, as abstract integers are un-typeable static inline const ast::Type* AST(ProgramBuilder&) { return nullptr; } /// @param b the ProgramBuilder /// @return the semantic abstract-int type static inline const type::Type* Sem(ProgramBuilder& b) { return b.create(); } /// @param b the ProgramBuilder /// @param args args of size 1 with the abstract-int value to init with /// @return a new AST abstract-int literal value expression static inline const ast::Expression* Expr(ProgramBuilder& b, utils::VectorRef args) { return b.Expr(std::get(args[0])); } /// @param b the ProgramBuilder /// @param v arg of type double that will be cast to AInt. /// @return a new AST abstract-int literal value expression static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) { return Expr(b, utils::Vector{static_cast(v)}); } /// @returns the WGSL name for the type static inline std::string Name() { return "abstract-int"; } }; /// Helper for building vector types and expressions template struct DataType> { /// The element type using ElementType = T; /// true as vectors are a composite type static constexpr bool is_composite = true; /// @param b the ProgramBuilder /// @return a new AST vector type static inline const ast::Type* AST(ProgramBuilder& b) { return b.ty.vec(DataType::AST(b), N); } /// @param b the ProgramBuilder /// @return the semantic vector type static inline const type::Type* Sem(ProgramBuilder& b) { return b.create(DataType::Sem(b), N); } /// @param b the ProgramBuilder /// @param args args of size 1 or N with values of type T to initialize with /// @return a new AST vector value expression static inline const ast::Expression* Expr(ProgramBuilder& b, utils::VectorRef args) { return b.Construct(AST(b), ExprArgs(b, std::move(args))); } /// @param b the ProgramBuilder /// @param args args of size 1 or N with values of type T to initialize with /// @return the list of expressions that are used to construct the vector static inline auto ExprArgs(ProgramBuilder& b, utils::VectorRef args) { const bool one_value = args.Length() == 1; utils::Vector r; for (size_t i = 0; i < N; ++i) { r.Push(DataType::Expr(b, utils::Vector{one_value ? args[0] : args[i]})); } return r; } /// @param b the ProgramBuilder /// @param v arg of type double that will be cast to ElementType /// @return a new AST vector value expression static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) { return Expr(b, utils::Vector{static_cast(v)}); } /// @returns the WGSL name for the type static inline std::string Name() { return "vec" + std::to_string(N) + "<" + DataType::Name() + ">"; } }; /// Helper for building matrix types and expressions template struct DataType> { /// The element type using ElementType = T; /// true as matrices are a composite type static constexpr bool is_composite = true; /// @param b the ProgramBuilder /// @return a new AST matrix type static inline const ast::Type* AST(ProgramBuilder& b) { return b.ty.mat(DataType::AST(b), N, M); } /// @param b the ProgramBuilder /// @return the semantic matrix type static inline const type::Type* Sem(ProgramBuilder& b) { auto* column_type = b.create(DataType::Sem(b), M); return b.create(column_type, N); } /// @param b the ProgramBuilder /// @param args args of size 1 or N*M with values of type T to initialize with /// @return a new AST matrix value expression static inline const ast::Expression* Expr(ProgramBuilder& b, utils::VectorRef args) { return b.Construct(AST(b), ExprArgs(b, std::move(args))); } /// @param b the ProgramBuilder /// @param args args of size 1 or N*M with values of type T to initialize with /// @return a new AST matrix value expression static inline auto ExprArgs(ProgramBuilder& b, utils::VectorRef args) { const bool one_value = args.Length() == 1; size_t next = 0; utils::Vector r; for (uint32_t i = 0; i < N; ++i) { if (one_value) { r.Push(DataType>::Expr(b, utils::Vector{args[0]})); } else { utils::Vector v; for (size_t j = 0; j < M; ++j) { v.Push(args[next++]); } r.Push(DataType>::Expr(b, std::move(v))); } } return r; } /// @param b the ProgramBuilder /// @param v arg of type double that will be cast to ElementType /// @return a new AST matrix value expression static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) { return Expr(b, utils::Vector{static_cast(v)}); } /// @returns the WGSL name for the type static inline std::string Name() { return "mat" + std::to_string(N) + "x" + std::to_string(M) + "<" + DataType::Name() + ">"; } }; /// Helper for building alias types and expressions template struct DataType> { /// The element type using ElementType = typename DataType::ElementType; /// true if the aliased type is a composite type static constexpr bool is_composite = DataType::is_composite; /// @param b the ProgramBuilder /// @return a new AST alias type static inline const ast::Type* AST(ProgramBuilder& b) { auto name = b.Symbols().Register("alias_" + std::to_string(ID)); if (!b.AST().LookupType(name)) { auto* type = DataType::AST(b); b.AST().AddTypeDecl(b.ty.alias(name, type)); } return b.ty(name); } /// @param b the ProgramBuilder /// @return the semantic aliased type static inline const type::Type* Sem(ProgramBuilder& b) { return DataType::Sem(b); } /// @param b the ProgramBuilder /// @param args the value nested elements will be initialized with /// @return a new AST expression of the alias type template static inline traits::EnableIf Expr( ProgramBuilder& b, utils::VectorRef args) { // Cast return b.Construct(AST(b), DataType::Expr(b, std::move(args))); } /// @param b the ProgramBuilder /// @param args the value nested elements will be initialized with /// @return a new AST expression of the alias type template static inline traits::EnableIf Expr( ProgramBuilder& b, utils::VectorRef args) { // Construct return b.Construct(AST(b), DataType::ExprArgs(b, std::move(args))); } /// @param b the ProgramBuilder /// @param v arg of type double that will be cast to ElementType /// @return a new AST expression of the alias type static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) { return Expr(b, utils::Vector{static_cast(v)}); } /// @returns the WGSL name for the type static inline std::string Name() { return "alias_" + std::to_string(ID); } }; /// Helper for building pointer types and expressions template struct DataType> { /// The element type using ElementType = typename DataType::ElementType; /// true if the pointer type is a composite type static constexpr bool is_composite = false; /// @param b the ProgramBuilder /// @return a new AST alias type static inline const ast::Type* AST(ProgramBuilder& b) { return b.create(DataType::AST(b), type::AddressSpace::kPrivate, type::Access::kUndefined); } /// @param b the ProgramBuilder /// @return the semantic aliased type static inline const type::Type* Sem(ProgramBuilder& b) { return b.create(DataType::Sem(b), type::AddressSpace::kPrivate, type::Access::kReadWrite); } /// @param b the ProgramBuilder /// @return a new AST expression of the pointer type static inline const ast::Expression* Expr(ProgramBuilder& b, utils::VectorRef /*unused*/) { auto sym = b.Symbols().New("global_for_ptr"); b.GlobalVar(sym, DataType::AST(b), type::AddressSpace::kPrivate); return b.AddressOf(sym); } /// @param b the ProgramBuilder /// @param v arg of type double that will be cast to ElementType /// @return a new AST expression of the pointer type static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) { return Expr(b, utils::Vector{static_cast(v)}); } /// @returns the WGSL name for the type static inline std::string Name() { return "ptr<" + DataType::Name() + ">"; } }; /// Helper for building array types and expressions template struct DataType> { /// The element type using ElementType = typename DataType::ElementType; /// true as arrays are a composite type static constexpr bool is_composite = true; /// @param b the ProgramBuilder /// @return a new AST array type static inline const ast::Type* AST(ProgramBuilder& b) { if (auto* ast = DataType::AST(b)) { return b.ty.array(ast, u32(N)); } return b.ty.array(nullptr, nullptr); } /// @param b the ProgramBuilder /// @return the semantic array type static inline const type::Type* Sem(ProgramBuilder& b) { auto* el = DataType::Sem(b); const type::ArrayCount* count = nullptr; if (N == 0) { count = b.create(); } else { count = b.create(N); } return b.create( /* element */ el, /* count */ count, /* align */ el->Align(), /* size */ N * el->Size(), /* stride */ el->Align(), /* implicit_stride */ el->Align()); } /// @param b the ProgramBuilder /// @param args args of size 1 or N with values of type T to initialize with /// with /// @return a new AST array value expression static inline const ast::Expression* Expr(ProgramBuilder& b, utils::VectorRef args) { return b.Construct(AST(b), ExprArgs(b, std::move(args))); } /// @param b the ProgramBuilder /// @param args args of size 1 or N with values of type T to initialize with /// @return the list of expressions that are used to construct the array static inline auto ExprArgs(ProgramBuilder& b, utils::VectorRef args) { const bool one_value = args.Length() == 1; utils::Vector r; for (uint32_t i = 0; i < N; i++) { r.Push(DataType::Expr(b, utils::Vector{one_value ? args[0] : args[i]})); } return r; } /// @param b the ProgramBuilder /// @param v arg of type double that will be cast to ElementType /// @return a new AST array value expression static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) { return Expr(b, utils::Vector{static_cast(v)}); } /// @returns the WGSL name for the type static inline std::string Name() { return "array<" + DataType::Name() + ", " + std::to_string(N) + ">"; } }; /// Struct of all creation pointer types struct CreatePtrs { /// ast node type create function ast_type_func_ptr ast; /// ast expression type create function ast_expr_func_ptr expr; /// ast expression type create function from double arg ast_expr_from_double_func_ptr expr_from_double; /// sem type create function sem_type_func_ptr sem; /// type name function type_name_func_ptr name; }; /// @param o the std::ostream to write to /// @param ptrs the CreatePtrs /// @return the std::ostream so calls can be chained inline std::ostream& operator<<(std::ostream& o, const CreatePtrs& ptrs) { return o << (ptrs.name ? ptrs.name() : ""); } /// Returns a CreatePtrs struct instance with all creation pointer types for /// type `T` template constexpr CreatePtrs CreatePtrsFor() { return {DataType::AST, DataType::Expr, DataType::ExprFromDouble, DataType::Sem, DataType::Name}; } /// True if DataType is specialized for T, false otherwise. template const bool IsDataTypeSpecializedFor = !std::is_same_v::ElementType, UnspecializedElementType>; /// Value is used to create Values with a Scalar vector initializer. struct Value { /// Creates a Value for type T initialized with `args` /// @param args the scalar args /// @returns Value template static Value Create(utils::VectorRef args) { static_assert(IsDataTypeSpecializedFor, "No DataType specialization exists"); using EL_TY = typename builder::DataType::ElementType; return Value{ std::move(args), // CreatePtrsFor(), // tint::IsAbstract, // tint::IsIntegral, // tint::FriendlyName(), }; } /// Creates an `ast::Expression` for the type T passing in previously stored args /// @param b the ProgramBuilder /// @returns an expression node const ast::Expression* Expr(ProgramBuilder& b) const { return (*create_ptrs.expr)(b, args); } /// Prints this value to the output stream /// @param o the output stream /// @returns input argument `o` std::ostream& Print(std::ostream& o) const { o << type_name << "("; for (auto& a : args) { std::visit([&](auto& v) { o << v; }, a); if (&a != &args.Back()) { o << ", "; } } o << ")"; return o; } /// The arguments used to construct the value utils::Vector args; /// CreatePtrs for value's type used to create an expression with `args` builder::CreatePtrs create_ptrs; /// True if the element type is abstract bool is_abstract = false; /// True if the element type is an integer bool is_integral = false; /// The name of the type. const char* type_name = ""; }; /// Prints Value to ostream inline std::ostream& operator<<(std::ostream& o, const Value& value) { return value.Print(o); } /// True if T is Value, false otherwise template constexpr bool IsValue = std::is_same_v; /// Creates a Value of DataType from a scalar `v` template Value Val(T v) { static_assert(traits::IsTypeIn, "v must be a Number of bool"); return Value::Create(utils::Vector{v}); } /// Creates a Value of DataType> from N scalar `args` template Value Vec(Ts... args) { using FirstT = std::tuple_element_t<0, std::tuple>; static_assert(sizeof...(args) >= 2 && sizeof...(args) <= 4, "Invalid vector size"); static_assert(std::conjunction_v...>, "Vector args must all be the same type"); constexpr size_t N = sizeof...(args); utils::Vector v{args...}; return Value::Create>(std::move(v)); } /// Creates a Value of DataType> from N scalar `args` template Value Array(Ts... args) { using FirstT = std::tuple_element_t<0, std::tuple>; static_assert(std::conjunction_v...>, "Array args must all be the same type"); constexpr size_t N = sizeof...(args); utils::Vector v{args...}; return Value::Create>(std::move(v)); } /// Creates a Value of DataType from C*R scalar `args` template Value Mat(const T (&m_in)[C][R]) { utils::Vector m; for (uint32_t i = 0; i < C; ++i) { for (size_t j = 0; j < R; ++j) { m.Push(m_in[i][j]); } } return Value::Create>(std::move(m)); } /// Creates a Value of DataType from column vectors `c0` and `c1` template Value Mat(const T (&c0)[R], const T (&c1)[R]) { constexpr size_t C = 2; utils::Vector m; for (auto v : c0) { m.Push(v); } for (auto v : c1) { m.Push(v); } return Value::Create>(std::move(m)); } /// Creates a Value of DataType from column vectors `c0`, `c1`, and `c2` template Value Mat(const T (&c0)[R], const T (&c1)[R], const T (&c2)[R]) { constexpr size_t C = 3; utils::Vector m; for (auto v : c0) { m.Push(v); } for (auto v : c1) { m.Push(v); } for (auto v : c2) { m.Push(v); } return Value::Create>(std::move(m)); } /// Creates a Value of DataType from column vectors `c0`, `c1`, `c2`, and `c3` template Value Mat(const T (&c0)[R], const T (&c1)[R], const T (&c2)[R], const T (&c3)[R]) { constexpr size_t C = 4; utils::Vector m; for (auto v : c0) { m.Push(v); } for (auto v : c1) { m.Push(v); } for (auto v : c2) { m.Push(v); } for (auto v : c3) { m.Push(v); } return Value::Create>(std::move(m)); } } // namespace builder } // namespace tint::resolver #endif // SRC_TINT_RESOLVER_RESOLVER_TEST_HELPER_H_