| // Copyright 2020 The Tint Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "src/transform/bound_array_accessors.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "gtest/gtest.h" |
| #include "src/ast/array_accessor_expression.h" |
| #include "src/ast/binary_expression.h" |
| #include "src/ast/block_statement.h" |
| #include "src/ast/builder.h" |
| #include "src/ast/call_expression.h" |
| #include "src/ast/function.h" |
| #include "src/ast/identifier_expression.h" |
| #include "src/ast/module.h" |
| #include "src/ast/scalar_constructor_expression.h" |
| #include "src/ast/sint_literal.h" |
| #include "src/ast/storage_class.h" |
| #include "src/ast/type/array_type.h" |
| #include "src/ast/type/f32_type.h" |
| #include "src/ast/type/i32_type.h" |
| #include "src/ast/type/matrix_type.h" |
| #include "src/ast/type/pointer_type.h" |
| #include "src/ast/type/u32_type.h" |
| #include "src/ast/type/vector_type.h" |
| #include "src/ast/type/void_type.h" |
| #include "src/ast/type_constructor_expression.h" |
| #include "src/ast/uint_literal.h" |
| #include "src/ast/variable.h" |
| #include "src/ast/variable_decl_statement.h" |
| #include "src/diagnostic/formatter.h" |
| #include "src/transform/manager.h" |
| #include "src/type_determiner.h" |
| |
| namespace tint { |
| namespace transform { |
| namespace { |
| |
| template <typename T = ast::Expression> |
| T* FindVariable(ast::Module* mod, std::string name) { |
| if (auto* func = mod->FindFunctionByName("func")) { |
| for (auto* stmt : *func->body()) { |
| if (auto* decl = stmt->As<ast::VariableDeclStatement>()) { |
| if (auto* var = decl->variable()) { |
| if (var->name() == name) { |
| return As<T>(var->constructor()); |
| } |
| } |
| } |
| } |
| } |
| return nullptr; |
| } |
| |
| class BoundArrayAccessorsTest : public testing::Test { |
| public: |
| ast::Module Transform(ast::Module in) { |
| TypeDeterminer td(&in); |
| if (!td.Determine()) { |
| error = "Type determination failed: " + td.error(); |
| return {}; |
| } |
| |
| Manager manager; |
| manager.append(std::make_unique<BoundArrayAccessors>()); |
| auto result = manager.Run(&in); |
| |
| if (result.diagnostics.contains_errors()) { |
| error = "manager().Run() errored:\n" + |
| diag::Formatter().format(result.diagnostics); |
| return {}; |
| } |
| |
| return std::move(result.module); |
| } |
| |
| std::string error; |
| }; |
| |
| struct ModuleBuilder : public ast::BuilderWithModule { |
| ModuleBuilder() : body_(create<ast::BlockStatement>()) { |
| mod->AddFunction(create<ast::Function>(Source{}, "func", |
| ast::VariableList{}, ty.void_, body_, |
| ast::FunctionDecorationList{})); |
| } |
| |
| ast::Module Module() { |
| Build(); |
| return std::move(*mod); |
| } |
| |
| protected: |
| virtual void Build() = 0; |
| void OnVariableBuilt(ast::Variable* var) override { |
| ASSERT_NE(body_, nullptr); |
| body_->append(create<ast::VariableDeclStatement>(var)); |
| } |
| ast::BlockStatement* body_ = nullptr; |
| }; |
| |
| TEST_F(BoundArrayAccessorsTest, Ptrs_Clamp) { |
| // var a : array<f32, 3>; |
| // const c : u32 = 1; |
| // const b : ptr<function, f32> = a[c] |
| // |
| // -> const b : ptr<function, i32> = a[min(u32(c), 2)] |
| struct Builder : ModuleBuilder { |
| void Build() override { |
| Var("a", ast::StorageClass::kFunction, ty.array<f32, 3>()); |
| Const("c", ast::StorageClass::kFunction, ty.u32); |
| Const("b", ast::StorageClass::kFunction, |
| ty.pointer<f32>(ast::StorageClass::kFunction), Index("a", "c"), {}); |
| } |
| }; |
| |
| ast::Module module = Transform(Builder{}.Module()); |
| ASSERT_EQ(error, ""); |
| |
| auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); |
| ASSERT_NE(b, nullptr); |
| |
| ASSERT_TRUE(b->idx_expr()->Is<ast::CallExpression>()); |
| |
| auto* idx = b->idx_expr()->As<ast::CallExpression>(); |
| ASSERT_TRUE(idx->func()->Is<ast::IdentifierExpression>()); |
| EXPECT_EQ(idx->func()->As<ast::IdentifierExpression>()->name(), "min"); |
| |
| ASSERT_EQ(idx->params().size(), 2u); |
| |
| ASSERT_TRUE(idx->params()[0]->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(idx->params()[0]->Is<ast::TypeConstructorExpression>()); |
| auto* tc = idx->params()[0]->As<ast::TypeConstructorExpression>(); |
| EXPECT_TRUE(tc->type()->Is<ast::type::U32>()); |
| ASSERT_EQ(tc->values().size(), 1u); |
| ASSERT_TRUE(tc->values()[0]->Is<ast::IdentifierExpression>()); |
| ASSERT_EQ(tc->values()[0]->As<ast::IdentifierExpression>()->name(), "c"); |
| |
| ASSERT_TRUE(idx->params()[1]->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(idx->params()[1]->Is<ast::ScalarConstructorExpression>()); |
| auto* scalar = idx->params()[1]->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); |
| |
| ASSERT_NE(b->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); |
| } |
| |
| TEST_F(BoundArrayAccessorsTest, Array_Idx_Nested_Scalar) { |
| // var a : array<f32, 3>; |
| // var b : array<f32, 5>; |
| // var i : u32; |
| // var c : f32 = a[b[i]]; |
| // |
| // -> var c : f32 = a[min(u32(b[min(u32(i), 4)]), 2)]; |
| struct Builder : ModuleBuilder { |
| void Build() override { |
| Var("a", ast::StorageClass::kFunction, ty.array<f32, 3>()); |
| Var("b", ast::StorageClass::kFunction, ty.array<f32, 5>()); |
| Var("i", ast::StorageClass::kFunction, ty.u32); |
| Const("c", ast::StorageClass::kFunction, ty.f32, |
| Index("a", Index("b", "i")), {}); |
| } |
| }; |
| |
| ast::Module module = Transform(Builder{}.Module()); |
| ASSERT_EQ(error, ""); |
| |
| auto* c = FindVariable<ast::ArrayAccessorExpression>(&module, "c"); |
| ASSERT_NE(c, nullptr); |
| |
| ASSERT_TRUE(c->Is<ast::ArrayAccessorExpression>()); |
| ASSERT_TRUE(c->idx_expr()->Is<ast::CallExpression>()); |
| |
| auto* idx = c->idx_expr()->As<ast::CallExpression>(); |
| ASSERT_TRUE(idx->func()->Is<ast::IdentifierExpression>()); |
| EXPECT_EQ(idx->func()->As<ast::IdentifierExpression>()->name(), "min"); |
| |
| ASSERT_EQ(idx->params().size(), 2u); |
| |
| ASSERT_TRUE(idx->params()[0]->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(idx->params()[0]->Is<ast::TypeConstructorExpression>()); |
| auto* tc = idx->params()[0]->As<ast::TypeConstructorExpression>(); |
| EXPECT_TRUE(tc->type()->Is<ast::type::U32>()); |
| ASSERT_EQ(tc->values().size(), 1u); |
| |
| auto* sub = tc->values()[0]; |
| ASSERT_TRUE(sub->Is<ast::ArrayAccessorExpression>()); |
| ASSERT_TRUE(sub->As<ast::ArrayAccessorExpression>() |
| ->idx_expr() |
| ->Is<ast::CallExpression>()); |
| |
| auto* sub_idx = sub->As<ast::ArrayAccessorExpression>() |
| ->idx_expr() |
| ->As<ast::CallExpression>(); |
| ASSERT_TRUE(sub_idx->func()->Is<ast::IdentifierExpression>()); |
| EXPECT_EQ(sub_idx->func()->As<ast::IdentifierExpression>()->name(), "min"); |
| |
| ASSERT_TRUE(sub_idx->params()[0]->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(sub_idx->params()[0]->Is<ast::TypeConstructorExpression>()); |
| tc = sub_idx->params()[0]->As<ast::TypeConstructorExpression>(); |
| EXPECT_TRUE(tc->type()->Is<ast::type::U32>()); |
| ASSERT_EQ(tc->values().size(), 1u); |
| ASSERT_TRUE(tc->values()[0]->Is<ast::IdentifierExpression>()); |
| ASSERT_EQ(tc->values()[0]->As<ast::IdentifierExpression>()->name(), "i"); |
| |
| ASSERT_TRUE(sub_idx->params()[1]->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(sub_idx->params()[1]->Is<ast::ScalarConstructorExpression>()); |
| auto* scalar = sub_idx->params()[1]->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 4u); |
| |
| ASSERT_TRUE(idx->params()[1]->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(idx->params()[1]->Is<ast::ScalarConstructorExpression>()); |
| scalar = idx->params()[1]->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); |
| |
| ASSERT_NE(c->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(c->idx_expr()->result_type()->Is<ast::type::U32>()); |
| } |
| |
| TEST_F(BoundArrayAccessorsTest, Array_Idx_Scalar) { |
| // var a : array<f32, 3> |
| // var b : f32 = a[1]; |
| // |
| // -> var b : f32 = a[1]; |
| struct Builder : ModuleBuilder { |
| void Build() override { |
| Var("a", ast::StorageClass::kFunction, ty.array(ty.f32, 3)); |
| Var("b", ast::StorageClass::kFunction, ty.f32, Index("a", 1u), {}); |
| } |
| }; |
| |
| ast::Module module = Transform(Builder{}.Module()); |
| ASSERT_EQ(error, ""); |
| |
| auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); |
| ASSERT_NE(b, nullptr); |
| |
| ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); |
| |
| auto* scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 1u); |
| |
| ASSERT_NE(b->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); |
| } |
| |
| TEST_F(BoundArrayAccessorsTest, Array_Idx_Expr) { |
| // var a : array<f32, 3> |
| // var c : u32; |
| // var b : f32 = a[c + 2 - 3] |
| // |
| // -> var b : f32 = a[min(u32(c + 2 - 3), 2)] |
| struct Builder : ModuleBuilder { |
| void Build() override { |
| Var("a", ast::StorageClass::kFunction, ty.array<f32, 3>()); |
| Var("c", ast::StorageClass::kFunction, ty.u32); |
| Var("b", ast::StorageClass::kFunction, ty.f32, |
| Index("a", Add("c", Sub(2u, 3u))), {}); |
| } |
| }; |
| |
| ast::Module module = Transform(Builder{}.Module()); |
| ASSERT_EQ(error, ""); |
| |
| auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); |
| ASSERT_NE(b, nullptr); |
| |
| ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::CallExpression>()); |
| |
| auto* idx = b->idx_expr()->As<ast::CallExpression>(); |
| ASSERT_TRUE(idx->func()->Is<ast::IdentifierExpression>()); |
| EXPECT_EQ(idx->func()->As<ast::IdentifierExpression>()->name(), "min"); |
| |
| ASSERT_EQ(idx->params().size(), 2u); |
| |
| ASSERT_TRUE(idx->params()[0]->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(idx->params()[0]->Is<ast::TypeConstructorExpression>()); |
| auto* tc = idx->params()[0]->As<ast::TypeConstructorExpression>(); |
| EXPECT_TRUE(tc->type()->Is<ast::type::U32>()); |
| ASSERT_EQ(tc->values().size(), 1u); |
| auto* add = tc->values()[0]->As<ast::BinaryExpression>(); |
| ASSERT_NE(add, nullptr); |
| ASSERT_EQ(add->op(), ast::BinaryOp::kAdd); |
| auto* add_lhs = add->lhs()->As<ast::IdentifierExpression>(); |
| ASSERT_NE(add_lhs, nullptr); |
| ASSERT_EQ(add_lhs->name(), "c"); |
| auto* add_rhs = add->rhs()->As<ast::BinaryExpression>(); |
| ASSERT_NE(add_rhs, nullptr); |
| ASSERT_TRUE(add_rhs->lhs()->Is<ast::ScalarConstructorExpression>()); |
| ASSERT_EQ(add_rhs->lhs() |
| ->As<ast::ScalarConstructorExpression>() |
| ->literal() |
| ->As<ast::UintLiteral>() |
| ->value(), |
| 2u); |
| ASSERT_TRUE(add_rhs->rhs()->Is<ast::ScalarConstructorExpression>()); |
| ASSERT_EQ(add_rhs->rhs() |
| ->As<ast::ScalarConstructorExpression>() |
| ->literal() |
| ->As<ast::UintLiteral>() |
| ->value(), |
| 3u); |
| |
| ASSERT_TRUE(idx->params()[1]->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(idx->params()[1]->Is<ast::ScalarConstructorExpression>()); |
| auto* scalar = idx->params()[1]->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); |
| |
| ASSERT_NE(b->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); |
| } |
| |
| TEST_F(BoundArrayAccessorsTest, Array_Idx_Negative) { |
| // var a : array<f32, 3> |
| // var b : f32 = a[-1] |
| // |
| // -> var b : f32 = a[0] |
| struct Builder : ModuleBuilder { |
| void Build() override { |
| Var("a", ast::StorageClass::kFunction, ty.array<f32, 3>()); |
| Var("b", ast::StorageClass::kFunction, ty.f32, Index("a", -1), {}); |
| } |
| }; |
| |
| ast::Module module = Transform(Builder{}.Module()); |
| ASSERT_EQ(error, ""); |
| |
| auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); |
| ASSERT_NE(b, nullptr); |
| |
| ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); |
| |
| auto* scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::SintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::SintLiteral>()->value(), 0); |
| |
| ASSERT_NE(b->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::I32>()); |
| } |
| |
| TEST_F(BoundArrayAccessorsTest, Array_Idx_OutOfBounds) { |
| // var a : array<f32, 3> |
| // var b : f32 = a[3] |
| // |
| // -> var b : f32 = a[2] |
| struct Builder : ModuleBuilder { |
| void Build() override { |
| Var("a", ast::StorageClass::kFunction, ty.array<f32, 3>()); |
| Var("b", ast::StorageClass::kFunction, ty.f32, Index("a", 3u), {}); |
| } |
| }; |
| |
| ast::Module module = Transform(Builder{}.Module()); |
| ASSERT_EQ(error, ""); |
| |
| auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); |
| ASSERT_NE(b, nullptr); |
| |
| ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); |
| |
| auto* scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); |
| |
| ASSERT_NE(b->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); |
| } |
| |
| TEST_F(BoundArrayAccessorsTest, Vector_Idx_Scalar) { |
| // var a : vec3<f32> |
| // var b : f32 = a[1]; |
| // |
| // -> var b : f32 = a[1] |
| struct Builder : ModuleBuilder { |
| void Build() override { |
| Var("a", ast::StorageClass::kFunction, ty.vec3<f32>()); |
| Var("b", ast::StorageClass::kFunction, ty.f32, Index("a", 1u), {}); |
| } |
| }; |
| |
| ast::Module module = Transform(Builder{}.Module()); |
| ASSERT_EQ(error, ""); |
| |
| auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); |
| ASSERT_NE(b, nullptr); |
| |
| ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); |
| |
| auto* scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 1u); |
| |
| ASSERT_NE(b->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); |
| } |
| |
| TEST_F(BoundArrayAccessorsTest, Vector_Idx_Expr) { |
| // var a : vec3<f32> |
| // var c : u32; |
| // var b : f32 = a[c + 2 - 3] |
| // |
| // -> var b : f32 = a[min(u32(c + 2 - 3), 2)] |
| struct Builder : ModuleBuilder { |
| void Build() override { |
| Var("a", ast::StorageClass::kFunction, ty.vec3<f32>()); |
| Var("c", ast::StorageClass::kFunction, ty.u32); |
| Var("b", ast::StorageClass::kFunction, ty.f32, |
| Index("a", Add("c", Sub(2u, 3u))), {}); |
| } |
| }; |
| |
| ast::Module module = Transform(Builder{}.Module()); |
| ASSERT_EQ(error, ""); |
| |
| auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); |
| ASSERT_NE(b, nullptr); |
| |
| ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::CallExpression>()); |
| |
| auto* idx = b->idx_expr()->As<ast::CallExpression>(); |
| ASSERT_TRUE(idx->func()->Is<ast::IdentifierExpression>()); |
| EXPECT_EQ(idx->func()->As<ast::IdentifierExpression>()->name(), "min"); |
| |
| ASSERT_EQ(idx->params().size(), 2u); |
| ASSERT_TRUE(idx->params()[0]->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(idx->params()[0]->Is<ast::TypeConstructorExpression>()); |
| auto* tc = idx->params()[0]->As<ast::TypeConstructorExpression>(); |
| EXPECT_TRUE(tc->type()->Is<ast::type::U32>()); |
| ASSERT_EQ(tc->values().size(), 1u); |
| auto* add = tc->values()[0]->As<ast::BinaryExpression>(); |
| ASSERT_NE(add, nullptr); |
| auto* add_lhs = add->lhs()->As<ast::IdentifierExpression>(); |
| ASSERT_NE(add_lhs, nullptr); |
| ASSERT_EQ(add_lhs->name(), "c"); |
| auto* add_rhs = add->rhs()->As<ast::BinaryExpression>(); |
| ASSERT_NE(add_rhs, nullptr); |
| ASSERT_TRUE(add_rhs->lhs()->Is<ast::ScalarConstructorExpression>()); |
| ASSERT_EQ(add_rhs->lhs() |
| ->As<ast::ScalarConstructorExpression>() |
| ->literal() |
| ->As<ast::UintLiteral>() |
| ->value(), |
| 2u); |
| ASSERT_TRUE(add_rhs->rhs()->Is<ast::ScalarConstructorExpression>()); |
| ASSERT_EQ(add_rhs->rhs() |
| ->As<ast::ScalarConstructorExpression>() |
| ->literal() |
| ->As<ast::UintLiteral>() |
| ->value(), |
| 3u); |
| |
| ASSERT_TRUE(idx->params()[1]->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(idx->params()[1]->Is<ast::ScalarConstructorExpression>()); |
| auto* scalar = idx->params()[1]->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); |
| |
| ASSERT_NE(b->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); |
| } |
| |
| TEST_F(BoundArrayAccessorsTest, Vector_Idx_Negative) { |
| // var a : vec3<f32> |
| // var b : f32 = a[-1] |
| // |
| // -> var b : f32 = a[0] |
| struct Builder : ModuleBuilder { |
| void Build() override { |
| Var("a", ast::StorageClass::kFunction, ty.vec3<f32>()); |
| Var("b", ast::StorageClass::kFunction, ty.f32, Index("a", -1), {}); |
| } |
| }; |
| |
| ast::Module module = Transform(Builder{}.Module()); |
| ASSERT_EQ(error, ""); |
| |
| auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); |
| ASSERT_NE(b, nullptr); |
| |
| ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); |
| |
| auto* scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::SintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::SintLiteral>()->value(), 0); |
| |
| ASSERT_NE(b->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::I32>()); |
| } |
| |
| TEST_F(BoundArrayAccessorsTest, Vector_Idx_OutOfBounds) { |
| // var a : vec3<f32> |
| // var b : f32 = a[3] |
| // |
| // -> var b : f32 = a[2] |
| struct Builder : ModuleBuilder { |
| void Build() override { |
| Var("a", ast::StorageClass::kFunction, ty.vec3<f32>()); |
| Var("b", ast::StorageClass::kFunction, ty.f32, Index("a", 3u), {}); |
| } |
| }; |
| |
| ast::Module module = Transform(Builder{}.Module()); |
| ASSERT_EQ(error, ""); |
| |
| auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); |
| ASSERT_NE(b, nullptr); |
| |
| ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); |
| |
| auto* scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); |
| |
| ASSERT_NE(b->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); |
| } |
| |
| TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Scalar) { |
| // var a : mat3x2<f32> |
| // var b : f32 = a[2][1]; |
| // |
| // -> var b : f32 = a[2][1] |
| struct Builder : ModuleBuilder { |
| void Build() override { |
| Var("a", ast::StorageClass::kFunction, ty.mat3x2<f32>()); |
| Var("b", ast::StorageClass::kFunction, ty.f32, Index(Index("a", 2u), 1u), |
| {}); |
| } |
| }; |
| |
| ast::Module module = Transform(Builder{}.Module()); |
| ASSERT_EQ(error, ""); |
| |
| auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); |
| ASSERT_NE(b, nullptr); |
| |
| ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); |
| |
| ASSERT_TRUE(b->array()->Is<ast::ArrayAccessorExpression>()); |
| auto* ary = b->array()->As<ast::ArrayAccessorExpression>(); |
| ASSERT_TRUE(ary->idx_expr()->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(ary->idx_expr()->Is<ast::ScalarConstructorExpression>()); |
| |
| auto* scalar = ary->idx_expr()->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); |
| |
| ASSERT_NE(ary->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(ary->idx_expr()->result_type()->Is<ast::type::U32>()); |
| |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); |
| |
| scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 1u); |
| |
| ASSERT_NE(b->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); |
| } |
| |
| TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Expr_Column) { |
| // var a : mat3x2<f32> |
| // var c : u32; |
| // var b : f32 = a[c + 2 - 3][1] |
| // |
| // -> var b : f32 = a[min(u32(c + 2 - 3), 2)][1] |
| struct Builder : ModuleBuilder { |
| void Build() override { |
| Var("a", ast::StorageClass::kFunction, ty.mat3x2<f32>()); |
| Var("c", ast::StorageClass::kFunction, ty.u32); |
| Var("b", ast::StorageClass::kFunction, ty.f32, |
| Index(Index("a", Add("c", Sub(2u, 3u))), 1u), {}); |
| } |
| }; |
| |
| ast::Module module = Transform(Builder{}.Module()); |
| ASSERT_EQ(error, ""); |
| |
| auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); |
| ASSERT_NE(b, nullptr); |
| |
| ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); |
| |
| ASSERT_TRUE(b->array()->Is<ast::ArrayAccessorExpression>()); |
| auto* ary = b->array()->As<ast::ArrayAccessorExpression>(); |
| |
| ASSERT_TRUE(ary->idx_expr()->Is<ast::CallExpression>()); |
| auto* idx = ary->idx_expr()->As<ast::CallExpression>(); |
| ASSERT_TRUE(idx->func()->Is<ast::IdentifierExpression>()); |
| EXPECT_EQ(idx->func()->As<ast::IdentifierExpression>()->name(), "min"); |
| |
| ASSERT_EQ(idx->params().size(), 2u); |
| |
| ASSERT_TRUE(idx->params()[0]->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(idx->params()[0]->Is<ast::TypeConstructorExpression>()); |
| auto* tc = idx->params()[0]->As<ast::TypeConstructorExpression>(); |
| EXPECT_TRUE(tc->type()->Is<ast::type::U32>()); |
| ASSERT_EQ(tc->values().size(), 1u); |
| auto* add = tc->values()[0]->As<ast::BinaryExpression>(); |
| ASSERT_NE(add, nullptr); |
| auto* add_lhs = add->lhs()->As<ast::IdentifierExpression>(); |
| ASSERT_NE(add_lhs, nullptr); |
| ASSERT_EQ(add_lhs->name(), "c"); |
| auto* add_rhs = add->rhs()->As<ast::BinaryExpression>(); |
| ASSERT_NE(add_rhs, nullptr); |
| ASSERT_TRUE(add_rhs->lhs()->Is<ast::ScalarConstructorExpression>()); |
| ASSERT_EQ(add_rhs->lhs() |
| ->As<ast::ScalarConstructorExpression>() |
| ->literal() |
| ->As<ast::UintLiteral>() |
| ->value(), |
| 2u); |
| ASSERT_TRUE(add_rhs->rhs()->Is<ast::ScalarConstructorExpression>()); |
| ASSERT_EQ(add_rhs->rhs() |
| ->As<ast::ScalarConstructorExpression>() |
| ->literal() |
| ->As<ast::UintLiteral>() |
| ->value(), |
| 3u); |
| |
| ASSERT_TRUE(idx->params()[1]->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(idx->params()[1]->Is<ast::ScalarConstructorExpression>()); |
| auto* scalar = idx->params()[1]->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); |
| |
| ASSERT_NE(ary->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(ary->idx_expr()->result_type()->Is<ast::type::U32>()); |
| |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); |
| |
| scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 1u); |
| |
| ASSERT_NE(b->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); |
| } |
| |
| TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Expr_Row) { |
| // var a : mat3x2<f32> |
| // var c : u32; |
| // var b : f32 = a[1][c + 2 - 3] |
| // |
| // -> var b : f32 = a[1][min(u32(c + 2 - 3), 1)] |
| struct Builder : ModuleBuilder { |
| void Build() override { |
| Var("a", ast::StorageClass::kFunction, ty.mat3x2<f32>()); |
| Var("c", ast::StorageClass::kFunction, ty.u32); |
| Var("b", ast::StorageClass::kFunction, ty.f32, |
| Index(Index("a", 1u), Add("c", Sub(2u, 3u))), {}); |
| } |
| }; |
| |
| ast::Module module = Transform(Builder{}.Module()); |
| ASSERT_EQ(error, ""); |
| |
| auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); |
| ASSERT_NE(b, nullptr); |
| |
| ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); |
| |
| ASSERT_TRUE(b->array()->Is<ast::ArrayAccessorExpression>()); |
| auto* ary = b->array()->As<ast::ArrayAccessorExpression>(); |
| |
| ASSERT_TRUE(ary->idx_expr()->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(ary->idx_expr()->Is<ast::ScalarConstructorExpression>()); |
| |
| auto* scalar = ary->idx_expr()->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 1u); |
| |
| ASSERT_TRUE(b->idx_expr()->Is<ast::CallExpression>()); |
| auto* idx = b->idx_expr()->As<ast::CallExpression>(); |
| ASSERT_TRUE(idx->func()->Is<ast::IdentifierExpression>()); |
| EXPECT_EQ(idx->func()->As<ast::IdentifierExpression>()->name(), "min"); |
| |
| ASSERT_EQ(idx->params().size(), 2u); |
| |
| ASSERT_TRUE(idx->params()[0]->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(idx->params()[0]->Is<ast::TypeConstructorExpression>()); |
| auto* tc = idx->params()[0]->As<ast::TypeConstructorExpression>(); |
| EXPECT_TRUE(tc->type()->Is<ast::type::U32>()); |
| ASSERT_EQ(tc->values().size(), 1u); |
| auto* add = tc->values()[0]->As<ast::BinaryExpression>(); |
| ASSERT_NE(add, nullptr); |
| auto* add_lhs = add->lhs()->As<ast::IdentifierExpression>(); |
| ASSERT_NE(add_lhs, nullptr); |
| ASSERT_EQ(add_lhs->name(), "c"); |
| auto* add_rhs = add->rhs()->As<ast::BinaryExpression>(); |
| ASSERT_NE(add_rhs, nullptr); |
| ASSERT_TRUE(add_rhs->lhs()->Is<ast::ScalarConstructorExpression>()); |
| ASSERT_EQ(add_rhs->lhs() |
| ->As<ast::ScalarConstructorExpression>() |
| ->literal() |
| ->As<ast::UintLiteral>() |
| ->value(), |
| 2u); |
| ASSERT_TRUE(add_rhs->rhs()->Is<ast::ScalarConstructorExpression>()); |
| ASSERT_EQ(add_rhs->rhs() |
| ->As<ast::ScalarConstructorExpression>() |
| ->literal() |
| ->As<ast::UintLiteral>() |
| ->value(), |
| 3u); |
| |
| ASSERT_TRUE(idx->params()[1]->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(idx->params()[1]->Is<ast::ScalarConstructorExpression>()); |
| scalar = idx->params()[1]->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 1u); |
| |
| ASSERT_NE(ary->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(ary->idx_expr()->result_type()->Is<ast::type::U32>()); |
| |
| ASSERT_NE(b->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); |
| } |
| |
| TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Negative_Column) { |
| // var a : mat3x2<f32> |
| // var b : f32 = a[-1][1] |
| // |
| // -> var b : f32 = a[0][1] |
| struct Builder : ModuleBuilder { |
| void Build() override { |
| Var("a", ast::StorageClass::kFunction, ty.mat3x2<f32>()); |
| Var("b", ast::StorageClass::kFunction, ty.f32, Index(Index("a", -1), 1), |
| {}); |
| } |
| }; |
| |
| ast::Module module = Transform(Builder{}.Module()); |
| ASSERT_EQ(error, ""); |
| |
| auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); |
| ASSERT_NE(b, nullptr); |
| |
| ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); |
| |
| ASSERT_TRUE(b->array()->Is<ast::ArrayAccessorExpression>()); |
| auto* ary = b->array()->As<ast::ArrayAccessorExpression>(); |
| ASSERT_TRUE(ary->idx_expr()->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(ary->idx_expr()->Is<ast::ScalarConstructorExpression>()); |
| |
| auto* scalar = ary->idx_expr()->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::SintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::SintLiteral>()->value(), 0); |
| |
| ASSERT_NE(ary->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(ary->idx_expr()->result_type()->Is<ast::type::I32>()); |
| |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); |
| |
| scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::SintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::SintLiteral>()->value(), 1); |
| |
| ASSERT_NE(b->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::I32>()); |
| } |
| |
| TEST_F(BoundArrayAccessorsTest, Matrix_Idx_Negative_Row) { |
| // var a : mat3x2<f32> |
| // var b : f32 = a[2][-1] |
| // |
| // -> var b : f32 = a[2][0] |
| struct Builder : ModuleBuilder { |
| void Build() override { |
| Var("a", ast::StorageClass::kFunction, ty.mat3x2<f32>()); |
| Var("b", ast::StorageClass::kFunction, ty.f32, Index(Index("a", 2), -1), |
| {}); |
| } |
| }; |
| |
| ast::Module module = Transform(Builder{}.Module()); |
| ASSERT_EQ(error, ""); |
| |
| auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); |
| ASSERT_NE(b, nullptr); |
| |
| ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); |
| |
| ASSERT_TRUE(b->array()->Is<ast::ArrayAccessorExpression>()); |
| auto* ary = b->array()->As<ast::ArrayAccessorExpression>(); |
| ASSERT_TRUE(ary->idx_expr()->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(ary->idx_expr()->Is<ast::ScalarConstructorExpression>()); |
| |
| auto* scalar = ary->idx_expr()->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::SintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::SintLiteral>()->value(), 2); |
| |
| ASSERT_NE(ary->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(ary->idx_expr()->result_type()->Is<ast::type::I32>()); |
| |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); |
| |
| scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::SintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::SintLiteral>()->value(), 0); |
| |
| ASSERT_NE(b->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::I32>()); |
| } |
| |
| TEST_F(BoundArrayAccessorsTest, Matrix_Idx_OutOfBounds_Column) { |
| // var a : mat3x2<f32> |
| // var b : f32 = a[5][1] |
| // |
| // -> var b : f32 = a[2][1] |
| struct Builder : ModuleBuilder { |
| void Build() override { |
| Var("a", ast::StorageClass::kFunction, ty.mat3x2<f32>()); |
| Var("b", ast::StorageClass::kFunction, ty.f32, Index(Index("a", 5u), 1u), |
| {}); |
| } |
| }; |
| |
| ast::Module module = Transform(Builder{}.Module()); |
| ASSERT_EQ(error, ""); |
| |
| auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); |
| ASSERT_NE(b, nullptr); |
| |
| ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); |
| |
| ASSERT_TRUE(b->array()->Is<ast::ArrayAccessorExpression>()); |
| auto* ary = b->array()->As<ast::ArrayAccessorExpression>(); |
| ASSERT_TRUE(ary->idx_expr()->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(ary->idx_expr()->Is<ast::ScalarConstructorExpression>()); |
| |
| auto* scalar = ary->idx_expr()->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); |
| |
| ASSERT_NE(ary->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(ary->idx_expr()->result_type()->Is<ast::type::U32>()); |
| |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); |
| |
| scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 1u); |
| |
| ASSERT_NE(b->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); |
| } |
| |
| TEST_F(BoundArrayAccessorsTest, Matrix_Idx_OutOfBounds_Row) { |
| // var a : mat3x2<f32> |
| // var b : f32 = a[2][5] |
| // |
| // -> var b : f32 = a[2][1] |
| struct Builder : ModuleBuilder { |
| void Build() override { |
| Var("a", ast::StorageClass::kFunction, ty.mat3x2<f32>()); |
| Var("b", ast::StorageClass::kFunction, ty.f32, Index(Index("a", 2u), 5u), |
| {}); |
| } |
| }; |
| |
| ast::Module module = Transform(Builder{}.Module()); |
| ASSERT_EQ(error, ""); |
| |
| auto* b = FindVariable<ast::ArrayAccessorExpression>(&module, "b"); |
| ASSERT_NE(b, nullptr); |
| |
| ASSERT_TRUE(b->Is<ast::ArrayAccessorExpression>()); |
| |
| ASSERT_TRUE(b->array()->Is<ast::ArrayAccessorExpression>()); |
| auto* ary = b->array()->As<ast::ArrayAccessorExpression>(); |
| ASSERT_TRUE(ary->idx_expr()->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(ary->idx_expr()->Is<ast::ScalarConstructorExpression>()); |
| |
| auto* scalar = ary->idx_expr()->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 2u); |
| |
| ASSERT_NE(ary->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(ary->idx_expr()->result_type()->Is<ast::type::U32>()); |
| |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ConstructorExpression>()); |
| ASSERT_TRUE(b->idx_expr()->Is<ast::ScalarConstructorExpression>()); |
| |
| scalar = b->idx_expr()->As<ast::ScalarConstructorExpression>(); |
| ASSERT_TRUE(scalar->literal()->Is<ast::UintLiteral>()); |
| EXPECT_EQ(scalar->literal()->As<ast::UintLiteral>()->value(), 1u); |
| |
| ASSERT_NE(b->idx_expr()->result_type(), nullptr); |
| ASSERT_TRUE(b->idx_expr()->result_type()->Is<ast::type::U32>()); |
| } |
| |
| // TODO(dsinclair): Implement when constant_id exists |
| TEST_F(BoundArrayAccessorsTest, DISABLED_Vector_Constant_Id_Clamps) { |
| // [[constant_id(1300)]] const idx : i32; |
| // var a : vec3<f32> |
| // var b : f32 = a[idx] |
| // |
| // ->var b : f32 = a[min(u32(idx), 2)] |
| } |
| |
| // TODO(dsinclair): Implement when constant_id exists |
| TEST_F(BoundArrayAccessorsTest, DISABLED_Array_Constant_Id_Clamps) { |
| // [[constant_id(1300)]] const idx : i32; |
| // var a : array<f32, 4> |
| // var b : f32 = a[idx] |
| // |
| // -> var b : f32 = a[min(u32(idx), 3)] |
| } |
| |
| // TODO(dsinclair): Implement when constant_id exists |
| TEST_F(BoundArrayAccessorsTest, DISABLED_Matrix_Column_Constant_Id_Clamps) { |
| // [[constant_id(1300)]] const idx : i32; |
| // var a : mat3x2<f32> |
| // var b : f32 = a[idx][1] |
| // |
| // -> var b : f32 = a[min(u32(idx), 2)][1] |
| } |
| |
| // TODO(dsinclair): Implement when constant_id exists |
| TEST_F(BoundArrayAccessorsTest, DISABLED_Matrix_Row_Constant_Id_Clamps) { |
| // [[constant_id(1300)]] const idx : i32; |
| // var a : mat3x2<f32> |
| // var b : f32 = a[1][idx] |
| // |
| // -> var b : f32 = a[1][min(u32(idx), 0, 1)] |
| } |
| |
| // TODO(dsinclair): Implement when we have arrayLength for Runtime Arrays |
| TEST_F(BoundArrayAccessorsTest, DISABLED_RuntimeArray_Clamps) { |
| // struct S { |
| // a : f32; |
| // b : array<f32>; |
| // } |
| // S s; |
| // var b : f32 = s.b[25] |
| // |
| // -> var b : f32 = s.b[min(u32(25), arrayLength(s.b))] |
| } |
| |
| // TODO(dsinclair): Clamp atomics when available. |
| TEST_F(BoundArrayAccessorsTest, DISABLED_Atomics_Clamp) { |
| FAIL(); |
| } |
| |
| // TODO(dsinclair): Clamp texture coord values. Depends on: |
| // https://github.com/gpuweb/gpuweb/issues/1107 |
| TEST_F(BoundArrayAccessorsTest, DISABLED_TextureCoord_Clamp) { |
| FAIL(); |
| } |
| |
| // TODO(dsinclair): Test for scoped variables when Lexical Scopes implemented |
| TEST_F(BoundArrayAccessorsTest, DISABLED_Scoped_Variable) { |
| // var a : array<f32, 3>; |
| // var i : u32; |
| // { |
| // var a : array<f32, 5>; |
| // var b : f32 = a[i]; |
| // } |
| // var c : f32 = a[i]; |
| // |
| // -> var b : f32 = a[min(u32(i), 4)]; |
| // var c : f32 = a[min(u32(i), 2)]; |
| FAIL(); |
| } |
| |
| } // namespace |
| } // namespace transform |
| } // namespace tint |