// Copyright 2022 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/tint/sem/load.h" #include "gmock/gmock.h" #include "src/tint/resolver/resolver.h" #include "src/tint/resolver/resolver_test_helper.h" #include "src/tint/sem/test_helper.h" #include "src/tint/type/reference.h" #include "src/tint/type/texture_dimension.h" using namespace tint::number_suffixes; // NOLINT namespace tint::resolver { namespace { using ResolverLoadTest = ResolverTest; TEST_F(ResolverLoadTest, VarInitializer) { // var ref = 1i; // var v = ref; auto* ident = Expr("ref"); WrapInFunction(Var("ref", Expr(1_i)), // Var("v", ident)); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* load = Sem().Get(ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } TEST_F(ResolverLoadTest, LetInitializer) { // var ref = 1i; // let l = ref; auto* ident = Expr("ref"); WrapInFunction(Var("ref", Expr(1_i)), // Let("l", ident)); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* load = Sem().Get(ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } TEST_F(ResolverLoadTest, Assignment) { // var ref = 1i; // var v : i32; // v = ref; auto* ident = Expr("ref"); WrapInFunction(Var("ref", Expr(1_i)), // Var("v", ty.i32()), // Assign("v", ident)); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* load = Sem().Get(ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } TEST_F(ResolverLoadTest, CompoundAssignment) { // var ref = 1i; // var v : i32; // v += ref; auto* ident = Expr("ref"); WrapInFunction(Var("ref", Expr(1_i)), // Var("v", ty.i32()), // CompoundAssign("v", ident, ast::BinaryOp::kAdd)); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* load = Sem().Get(ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } TEST_F(ResolverLoadTest, UnaryOp) { // var ref = 1i; // var v = -ref; auto* ident = Expr("ref"); WrapInFunction(Var("ref", Expr(1_i)), // Var("v", Negation(ident))); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* load = Sem().Get(ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } TEST_F(ResolverLoadTest, UnaryOp_NoLoad) { // var ref = 1i; // let v = &ref; auto* ident = Expr("ref"); WrapInFunction(Var("ref", Expr(1_i)), // Let("v", AddressOf(ident))); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* var_user = Sem().Get(ident); ASSERT_NE(var_user, nullptr); EXPECT_TRUE(var_user->Type()->Is()); EXPECT_TRUE(var_user->Type()->UnwrapRef()->Is()); } TEST_F(ResolverLoadTest, BinaryOp) { // var ref = 1i; // var v = ref * 1i; auto* ident = Expr("ref"); WrapInFunction(Var("ref", Expr(1_i)), // Var("v", Mul(ident, 1_i))); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* load = Sem().Get(ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } TEST_F(ResolverLoadTest, Index) { // var ref = 1i; // var v = array(1i, 2i, 3i)[ref]; auto* ident = Expr("ref"); WrapInFunction(Var("ref", Expr(1_i)), // IndexAccessor(array(1_i, 2_i, 3_i), ident)); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* load = Sem().Get(ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } TEST_F(ResolverLoadTest, MultiComponentSwizzle) { // var ref = vec4(1); // var v = ref.xyz; auto* ident = Expr("ref"); WrapInFunction(Var("ref", vec4(1_i)), // Var("v", MemberAccessor(ident, "xyz"))); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* load = Sem().Get(ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } TEST_F(ResolverLoadTest, Bitcast) { // var ref = 1f; // var v = bitcast(ref); auto* ident = Expr("ref"); WrapInFunction(Var("ref", Expr(1_f)), // Bitcast(ident)); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* load = Sem().Get(ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } TEST_F(ResolverLoadTest, BuiltinArg) { // var ref = 1f; // var v = abs(ref); auto* ident = Expr("ref"); WrapInFunction(Var("ref", Expr(1_f)), // Call("abs", ident)); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* load = Sem().Get(ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } TEST_F(ResolverLoadTest, FunctionArg) { // fn f(x : f32) {} // var ref = 1f; // f(ref); Func("f", utils::Vector{Param("x", ty.f32())}, ty.void_(), utils::Empty); auto* ident = Expr("ref"); WrapInFunction(Var("ref", Expr(1_f)), // CallStmt(Call("f", ident))); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* load = Sem().Get(ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } TEST_F(ResolverLoadTest, FunctionArg_Handles) { // @group(0) @binding(0) var t : texture_2d; // @group(0) @binding(1) var s : sampler; // fn f(tp : texture_2d, sp : sampler) -> vec4 { // return textureSampleLevel(tp, sp, vec2(), 0); // } // f(t, s); GlobalVar("t", ty.sampled_texture(type::TextureDimension::k2d, ty.f32()), utils::Vector{Group(0_a), Binding(0_a)}); GlobalVar("s", ty.sampler(type::SamplerKind::kSampler), utils::Vector{Group(0_a), Binding(1_a)}); Func("f", utils::Vector{ Param("tp", ty.sampled_texture(type::TextureDimension::k2d, ty.f32())), Param("sp", ty.sampler(type::SamplerKind::kSampler)), }, ty.vec4(), utils::Vector{ Return(Call("textureSampleLevel", "tp", "sp", vec2(), 0_a)), }); auto* t_ident = Expr("t"); auto* s_ident = Expr("s"); WrapInFunction(CallStmt(Call("f", t_ident, s_ident))); ASSERT_TRUE(r()->Resolve()) << r()->error(); { auto* load = Sem().Get(t_ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } { auto* load = Sem().Get(s_ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } } TEST_F(ResolverLoadTest, FunctionReturn) { // var ref = 1f; // return ref; auto* ident = Expr("ref"); Func("f", utils::Empty, ty.f32(), utils::Vector{ Decl(Var("ref", Expr(1_f))), Return(ident), }); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* load = Sem().Get(ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } TEST_F(ResolverLoadTest, IfCond) { // var ref = false; // if (ref) {} auto* ident = Expr("ref"); WrapInFunction(Var("ref", Expr(false)), // If(ident, Block())); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* load = Sem().Get(ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } TEST_F(ResolverLoadTest, Switch) { // var ref = 1i; // switch (ref) { // default: // } auto* ident = Expr("ref"); WrapInFunction(Var("ref", Expr(1_i)), // Switch(ident, DefaultCase())); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* load = Sem().Get(ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } TEST_F(ResolverLoadTest, BreakIfCond) { // var ref = false; // loop { // continuing { // break if (ref); // } // } auto* ident = Expr("ref"); WrapInFunction(Var("ref", Expr(false)), // Loop(Block(), Block(BreakIf(ident)))); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* load = Sem().Get(ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } TEST_F(ResolverLoadTest, ForCond) { // var ref = false; // for (; ref; ) {} auto* ident = Expr("ref"); WrapInFunction(Var("ref", Expr(false)), // For(nullptr, ident, nullptr, Block())); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* load = Sem().Get(ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } TEST_F(ResolverLoadTest, WhileCond) { // var ref = false; // while (ref) {} auto* ident = Expr("ref"); WrapInFunction(Var("ref", Expr(false)), // While(ident, Block())); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* load = Sem().Get(ident); ASSERT_NE(load, nullptr); EXPECT_TRUE(load->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->Is()); EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is()); } TEST_F(ResolverLoadTest, AddressOf) { // var ref = 1i; // let l = &ref; auto* ident = Expr("ref"); WrapInFunction(Var("ref", Expr(1_i)), // Let("l", AddressOf(ident))); ASSERT_TRUE(r()->Resolve()) << r()->error(); auto* no_load = Sem().GetVal(ident); ASSERT_NE(no_load, nullptr); EXPECT_TRUE(no_load->Type()->Is()); // No load } } // namespace } // namespace tint::resolver