blob: 96326a5c01dc33948f9c1253bc1ffed03d35d2b6 [file] [log] [blame]
// 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.
#ifndef SRC_TINT_LANG_WGSL_RESOLVER_RESOLVER_HELPER_TEST_H_
#define SRC_TINT_LANG_WGSL_RESOLVER_RESOLVER_HELPER_TEST_H_
#include <functional>
#include <memory>
#include <ostream>
#include <string>
#include <tuple>
#include <utility>
#include <variant>
#include "gtest/gtest.h"
#include "src/tint/lang/core/type/abstract_float.h"
#include "src/tint/lang/core/type/abstract_int.h"
#include "src/tint/lang/wgsl/program/program_builder.h"
#include "src/tint/lang/wgsl/resolver/resolver.h"
#include "src/tint/lang/wgsl/sem/array.h"
#include "src/tint/lang/wgsl/sem/statement.h"
#include "src/tint/lang/wgsl/sem/value_expression.h"
#include "src/tint/lang/wgsl/sem/variable.h"
#include "src/tint/utils/containers/vector.h"
#include "src/tint/utils/traits/traits.h"
namespace tint::resolver {
/// Helper class for testing
class TestHelper : public ProgramBuilder {
public:
/// Constructor
TestHelper();
/// Destructor
~TestHelper();
/// @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::VariableUser>(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, VectorRef<const ast::Expression*> expected_users) {
auto var_users = Sem().Get(var)->Users();
if (var_users.Length() != expected_users.Length()) {
return false;
}
for (size_t i = 0; i < var_users.Length(); 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(ast::Type type) { return type->identifier->symbol.Name(); }
/// @param type a type
/// @returns the name for `type` that closely resembles how it would be
/// declared in WGSL.
std::string FriendlyName(const core::type::Type* type) { return type->FriendlyName(); }
private:
std::unique_ptr<Resolver> resolver_;
};
class ResolverTest : public TestHelper, public testing::Test {};
template <typename T>
class ResolverTestWithParam : public TestHelper, public testing::TestWithParam<T> {};
namespace builder {
template <typename TO, int ID = 0>
struct alias {};
template <typename TO>
using alias1 = alias<TO, 1>;
template <typename TO>
using alias2 = alias<TO, 2>;
template <typename TO>
using alias3 = alias<TO, 3>;
/// Scalar represents a scalar value
struct Scalar {
/// The possible types of a scalar value.
using Value =
std::variant<core::i32, core::u32, core::f32, core::f16, core::AInt, core::AFloat, bool>;
/// Constructor
/// @param val the value used to construct this Scalar
template <typename T>
Scalar(T&& val) : value(std::forward<T>(val)) {} // NOLINT
/// @returns the Value
operator Value&() { return value; }
/// Equality operator
/// @param other the other Scalar
/// @return true if this Scalar is equal to @p other
bool operator==(const Scalar& other) const { return value == other.value; }
/// Inequality operator
/// @param other the other Scalar
/// @return true if this Scalar is not equal to @p other
bool operator!=(const Scalar& other) const { return value != other.value; }
/// The scalar value
Value value;
};
/// @param out the stream to write to
/// @param s the Scalar
/// @returns @p out so calls can be chained
template <typename STREAM, typename = traits::EnableIfIsOStream<STREAM>>
STREAM& operator<<(STREAM& out, const Scalar& s) {
std::visit([&](auto&& v) { out << v; }, s.value);
return out;
}
/// @return current variant value in @p s cast to type `T`
template <typename T>
T As(const Scalar& s) {
return std::visit([](auto&& v) { return static_cast<T>(v); }, s.value);
}
using ast_type_func_ptr = ast::Type (*)(ProgramBuilder& b);
using ast_expr_func_ptr = const ast::Expression* (*)(ProgramBuilder& b, VectorRef<Scalar> args);
using ast_expr_from_double_func_ptr = const ast::Expression* (*)(ProgramBuilder& b, double v);
using sem_type_func_ptr = const core::type::Type* (*)(ProgramBuilder& b);
using type_name_func_ptr = std::string (*)();
struct UnspecializedElementType {};
/// Base template for DataType, specialized below.
template <typename T>
struct DataType {
/// The element type
using ElementType = UnspecializedElementType;
};
/// Helper that represents no-type. Returns nullptr for all static methods.
template <>
struct DataType<void> {
/// The element type
using ElementType = void;
/// @return nullptr
static inline ast::Type AST(ProgramBuilder&) { return {}; }
/// @return nullptr
static inline const core::type::Type* Sem(ProgramBuilder&) { return nullptr; }
};
/// Helper for building bool types and expressions
template <>
struct DataType<bool> {
/// 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 ast::Type AST(ProgramBuilder& b) { return b.ty.bool_(); }
/// @param b the ProgramBuilder
/// @return the semantic bool type
static inline const core::type::Type* Sem(ProgramBuilder& b) {
return b.create<core::type::Bool>();
}
/// @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, VectorRef<Scalar> args) {
return b.Expr(std::get<bool>(args[0].value));
}
/// @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, Vector<Scalar, 1>{static_cast<ElementType>(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<core::i32> {
/// The element type
using ElementType = core::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 ast::Type AST(ProgramBuilder& b) { return b.ty.i32(); }
/// @param b the ProgramBuilder
/// @return the semantic i32 type
static inline const core::type::Type* Sem(ProgramBuilder& b) {
return b.create<core::type::I32>();
}
/// @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, VectorRef<Scalar> args) {
return b.Expr(std::get<core::i32>(args[0].value));
}
/// @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, Vector<Scalar, 1>{static_cast<ElementType>(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<core::u32> {
/// The element type
using ElementType = core::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 ast::Type AST(ProgramBuilder& b) { return b.ty.u32(); }
/// @param b the ProgramBuilder
/// @return the semantic u32 type
static inline const core::type::Type* Sem(ProgramBuilder& b) {
return b.create<core::type::U32>();
}
/// @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, VectorRef<Scalar> args) {
return b.Expr(std::get<core::u32>(args[0].value));
}
/// @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, Vector<Scalar, 1>{static_cast<ElementType>(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<core::f32> {
/// The element type
using ElementType = core::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 ast::Type AST(ProgramBuilder& b) { return b.ty.f32(); }
/// @param b the ProgramBuilder
/// @return the semantic f32 type
static inline const core::type::Type* Sem(ProgramBuilder& b) {
return b.create<core::type::F32>();
}
/// @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, VectorRef<Scalar> args) {
return b.Expr(std::get<core::f32>(args[0].value));
}
/// @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, Vector<Scalar, 1>{static_cast<core::f32>(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<core::f16> {
/// The element type
using ElementType = core::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 ast::Type AST(ProgramBuilder& b) { return b.ty.f16(); }
/// @param b the ProgramBuilder
/// @return the semantic f16 type
static inline const core::type::Type* Sem(ProgramBuilder& b) {
return b.create<core::type::F16>();
}
/// @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, VectorRef<Scalar> args) {
return b.Expr(std::get<core::f16>(args[0].value));
}
/// @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, Vector<Scalar, 1>{static_cast<ElementType>(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<core::AFloat> {
/// The element type
using ElementType = core::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 ast::Type AST(ProgramBuilder&) { return {}; }
/// @param b the ProgramBuilder
/// @return the semantic abstract-float type
static inline const core::type::Type* Sem(ProgramBuilder& b) {
return b.create<core::type::AbstractFloat>();
}
/// @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, VectorRef<Scalar> args) {
return b.Expr(std::get<core::AFloat>(args[0].value));
}
/// @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, Vector<Scalar, 1>{static_cast<ElementType>(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<core::AInt> {
/// The element type
using ElementType = core::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 ast::Type AST(ProgramBuilder&) { return {}; }
/// @param b the ProgramBuilder
/// @return the semantic abstract-int type
static inline const core::type::Type* Sem(ProgramBuilder& b) {
return b.create<core::type::AbstractInt>();
}
/// @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, VectorRef<Scalar> args) {
return b.Expr(std::get<core::AInt>(args[0].value));
}
/// @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, Vector<Scalar, 1>{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() { return "abstract-int"; }
};
/// Helper for building vector types and expressions
template <uint32_t N, typename T>
struct DataType<core::fluent_types::vec<N, T>> {
/// 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 ast::Type AST(ProgramBuilder& b) {
if (ast::IsInferOrAbstract<T>) {
return b.ty.vec<core::fluent_types::Infer, N>();
} else {
return b.ty.vec(DataType<T>::AST(b), N);
}
}
/// @param b the ProgramBuilder
/// @return the semantic vector type
static inline const core::type::Type* Sem(ProgramBuilder& b) {
return b.create<core::type::Vector>(DataType<T>::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, VectorRef<Scalar> args) {
return b.Call(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, VectorRef<Scalar> args) {
const bool one_value = args.Length() == 1;
Vector<const ast::Expression*, N> r;
for (size_t i = 0; i < N; ++i) {
r.Push(DataType<T>::Expr(b, Vector<Scalar, 1>{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, Vector<Scalar, 1>{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() {
return "vec" + std::to_string(N) + "<" + DataType<T>::Name() + ">";
}
};
/// Helper for building matrix types and expressions
template <uint32_t N, uint32_t M, typename T>
struct DataType<core::fluent_types::mat<N, M, T>> {
/// 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 ast::Type AST(ProgramBuilder& b) {
if (ast::IsInferOrAbstract<T>) {
return b.ty.mat<core::fluent_types::Infer, N, M>();
} else {
return b.ty.mat(DataType<T>::AST(b), N, M);
}
}
/// @param b the ProgramBuilder
/// @return the semantic matrix type
static inline const core::type::Type* Sem(ProgramBuilder& b) {
auto* column_type = b.create<core::type::Vector>(DataType<T>::Sem(b), M);
return b.create<core::type::Matrix>(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, VectorRef<Scalar> args) {
return b.Call(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, VectorRef<Scalar> args) {
const bool one_value = args.Length() == 1;
size_t next = 0;
Vector<const ast::Expression*, N> r;
for (uint32_t i = 0; i < N; ++i) {
if (one_value) {
r.Push(
DataType<core::fluent_types::vec<M, T>>::Expr(b, Vector<Scalar, 1>{args[0]}));
} else {
Vector<Scalar, M> v;
for (size_t j = 0; j < M; ++j) {
v.Push(args[next++]);
}
r.Push(DataType<core::fluent_types::vec<M, T>>::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, Vector<Scalar, 1>{static_cast<ElementType>(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<T>::Name() +
">";
}
};
/// Helper for building alias types and expressions
template <typename T, int ID>
struct DataType<alias<T, ID>> {
/// The element type
using ElementType = typename DataType<T>::ElementType;
/// true if the aliased type is a composite type
static constexpr bool is_composite = DataType<T>::is_composite;
/// @param b the ProgramBuilder
/// @return a new AST alias type
static inline ast::Type AST(ProgramBuilder& b) {
auto name = b.Symbols().Register("alias_" + std::to_string(ID));
if (!b.AST().LookupType(name)) {
auto type = DataType<T>::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 core::type::Type* Sem(ProgramBuilder& b) { return DataType<T>::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 <bool IS_COMPOSITE = is_composite>
static inline tint::traits::EnableIf<!IS_COMPOSITE, const ast::Expression*> Expr(
ProgramBuilder& b,
VectorRef<Scalar> args) {
// Cast
return b.Call(AST(b), DataType<T>::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 <bool IS_COMPOSITE = is_composite>
static inline tint::traits::EnableIf<IS_COMPOSITE, const ast::Expression*> Expr(
ProgramBuilder& b,
VectorRef<Scalar> args) {
// Construct
return b.Call(AST(b), DataType<T>::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, Vector<Scalar, 1>{static_cast<ElementType>(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 <typename T>
struct DataType<
core::fluent_types::ptr<core::AddressSpace::kPrivate, T, core::Access::kUndefined>> {
/// The element type
using ElementType = typename DataType<T>::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 ast::Type AST(ProgramBuilder& b) {
return b.ty.ptr<core::AddressSpace::kPrivate, T>();
}
/// @param b the ProgramBuilder
/// @return the semantic aliased type
static inline const core::type::Type* Sem(ProgramBuilder& b) {
return b.create<core::type::Pointer>(core::AddressSpace::kPrivate, DataType<T>::Sem(b),
core::Access::kReadWrite);
}
/// @param b the ProgramBuilder
/// @return a new AST expression of the pointer type
static inline const ast::Expression* Expr(ProgramBuilder& b, VectorRef<Scalar> /*unused*/) {
auto sym = b.Symbols().New("global_for_ptr");
b.GlobalVar(sym, DataType<T>::AST(b), core::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, Vector<Scalar, 1>{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() { return "ptr<" + DataType<T>::Name() + ">"; }
};
/// Helper for building array types and expressions
template <typename T, uint32_t N>
struct DataType<core::fluent_types::array<T, N>> {
/// The element type
using ElementType = typename DataType<T>::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 ast::Type AST(ProgramBuilder& b) {
if (auto ast = DataType<T>::AST(b)) {
return b.ty.array(ast, core::u32(N));
}
return b.ty.array<core::fluent_types::Infer>();
}
/// @param b the ProgramBuilder
/// @return the semantic array type
static inline const core::type::Type* Sem(ProgramBuilder& b) {
auto* el = DataType<T>::Sem(b);
const core::type::ArrayCount* count = nullptr;
if (N == 0) {
count = b.create<core::type::RuntimeArrayCount>();
} else {
count = b.create<core::type::ConstantArrayCount>(N);
}
return b.create<sem::Array>(
/* 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, VectorRef<Scalar> args) {
return b.Call(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, VectorRef<Scalar> args) {
const bool one_value = args.Length() == 1;
Vector<const ast::Expression*, N> r;
for (uint32_t i = 0; i < N; i++) {
r.Push(DataType<T>::Expr(b, Vector<Scalar, 1>{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, Vector<Scalar, 1>{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() {
return "array<" + DataType<T>::Name() + ", " + std::to_string(N) + ">";
}
};
/// Helper for building atomic types and expressions
template <typename T>
struct DataType<core::fluent_types::atomic<T>> {
/// The element type
using ElementType = typename DataType<T>::ElementType;
/// true as atomics are a composite type
static constexpr bool is_composite = true;
/// @param b the ProgramBuilder
/// @return a new AST atomic type
static inline ast::Type AST(ProgramBuilder& b) { return b.ty.atomic(DataType<T>::AST(b)); }
/// @param b the ProgramBuilder
/// @return the semantic atomic type
static inline const core::type::Type* Sem(ProgramBuilder& b) {
return b.Types().atomic(DataType<T>::Sem(b));
}
/// @returns the WGSL name for the type
static inline std::string Name() { return "atomic<" + DataType<T>::Name() + ">"; }
};
/// 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() : "<unknown>");
}
/// Returns a CreatePtrs struct instance with all creation pointer types for
/// type `T`
template <typename T>
constexpr CreatePtrs CreatePtrsFor() {
return {DataType<T>::AST, DataType<T>::Expr, DataType<T>::ExprFromDouble, DataType<T>::Sem,
DataType<T>::Name};
}
/// True if DataType<T> is specialized for T, false otherwise.
template <typename T>
const bool IsDataTypeSpecializedFor =
!std::is_same_v<typename DataType<T>::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 <typename T>
static Value Create(VectorRef<Scalar> args) {
static_assert(IsDataTypeSpecializedFor<T>, "No DataType<T> specialization exists");
using EL_TY = typename builder::DataType<T>::ElementType;
return Value{
std::move(args), //
CreatePtrsFor<T>(), //
core::IsAbstract<EL_TY>, //
core::IsIntegral<EL_TY>, //
core::FriendlyName<EL_TY>(),
};
}
/// 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) {
o << a;
if (&a != &args.Back()) {
o << ", ";
}
}
o << ")";
return o;
}
/// The arguments used to construct the value
Vector<Scalar, 4> 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 = "<invalid>";
};
/// 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 <typename T>
constexpr bool IsValue = std::is_same_v<T, Value>;
/// Creates a Value of DataType<T> from a scalar `v`
template <typename T>
Value Val(T v) {
static_assert(tint::traits::IsTypeIn<T, Scalar::Value>, "v must be a Number of bool");
return Value::Create<T>(Vector<Scalar, 1>{v});
}
/// Creates a Value of DataType<vec<N, T>> from N scalar `args`
template <typename... Ts>
Value Vec(Ts... args) {
using FirstT = std::tuple_element_t<0, std::tuple<Ts...>>;
static_assert(sizeof...(args) >= 2 && sizeof...(args) <= 4, "Invalid vector size");
static_assert(std::conjunction_v<std::is_same<FirstT, Ts>...>,
"Vector args must all be the same type");
constexpr size_t N = sizeof...(args);
Vector<Scalar, sizeof...(args)> v{args...};
return Value::Create<core::fluent_types::vec<N, FirstT>>(std::move(v));
}
/// Creates a Value of DataType<array<N, T>> from N scalar `args`
template <typename... Ts>
Value Array(Ts... args) {
using FirstT = std::tuple_element_t<0, std::tuple<Ts...>>;
static_assert(std::conjunction_v<std::is_same<FirstT, Ts>...>,
"Array args must all be the same type");
constexpr size_t N = sizeof...(args);
Vector<Scalar, sizeof...(args)> v{args...};
return Value::Create<core::fluent_types::array<FirstT, N>>(std::move(v));
}
/// Creates a Value of DataType<mat<C,R,T> from C*R scalar `args`
template <size_t C, size_t R, typename T>
Value Mat(const T (&m_in)[C][R]) {
Vector<Scalar, C * R> 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<core::fluent_types::mat<C, R, T>>(std::move(m));
}
/// Creates a Value of DataType<mat<2,R,T> from column vectors `c0` and `c1`
template <typename T, size_t R>
Value Mat(const T (&c0)[R], const T (&c1)[R]) {
constexpr size_t C = 2;
Vector<Scalar, C * R> m;
for (auto v : c0) {
m.Push(v);
}
for (auto v : c1) {
m.Push(v);
}
return Value::Create<core::fluent_types::mat<C, R, T>>(std::move(m));
}
/// Creates a Value of DataType<mat<3,R,T> from column vectors `c0`, `c1`, and `c2`
template <typename T, size_t R>
Value Mat(const T (&c0)[R], const T (&c1)[R], const T (&c2)[R]) {
constexpr size_t C = 3;
Vector<Scalar, C * R> 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<core::fluent_types::mat<C, R, T>>(std::move(m));
}
/// Creates a Value of DataType<mat<4,R,T> from column vectors `c0`, `c1`, `c2`, and `c3`
template <typename T, size_t R>
Value Mat(const T (&c0)[R], const T (&c1)[R], const T (&c2)[R], const T (&c3)[R]) {
constexpr size_t C = 4;
Vector<Scalar, C * R> 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<core::fluent_types::mat<C, R, T>>(std::move(m));
}
} // namespace builder
} // namespace tint::resolver
#endif // SRC_TINT_LANG_WGSL_RESOLVER_RESOLVER_HELPER_TEST_H_