| // Copyright 2021 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.h" |
| |
| #include "src/tint/lang/wgsl/ast/transform/helper_test.h" |
| |
| namespace tint::ast::transform { |
| namespace { |
| |
| using PromoteInitializersToLetTest = TransformTest; |
| |
| TEST_F(PromoteInitializersToLetTest, EmptyModule) { |
| auto* src = ""; |
| auto* expect = ""; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, BasicConstArray) { |
| auto* src = R"( |
| fn f() { |
| const f0 = 1.0; |
| const f1 = 2.0; |
| const f2 = 3.0; |
| const f3 = 4.0; |
| var i = array<f32, 4u>(f0, f1, f2, f3)[2]; |
| } |
| )"; |
| |
| EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, BasicRuntimeArray) { |
| auto* src = R"( |
| fn f() { |
| var f0 = 1.0; |
| var f1 = 2.0; |
| var f2 = 3.0; |
| var f3 = 4.0; |
| var i = array<f32, 4u>(f0, f1, f2, f3)[2]; |
| } |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| var f0 = 1.0; |
| var f1 = 2.0; |
| var f2 = 3.0; |
| var f3 = 4.0; |
| let tint_symbol : array<f32, 4u> = array<f32, 4u>(f0, f1, f2, f3); |
| var i = tint_symbol[2]; |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, BasicConstStruct) { |
| auto* src = R"( |
| struct S { |
| a : i32, |
| b : f32, |
| c : vec3<f32>, |
| }; |
| |
| fn f() { |
| var x = S(1, 2.0, vec3<f32>()).b; |
| } |
| )"; |
| |
| EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, BasicRuntimeStruct) { |
| auto* src = R"( |
| struct S { |
| a : i32, |
| b : f32, |
| c : vec3<f32>, |
| }; |
| |
| fn f() { |
| let runtime_value = 1; |
| var x = S(runtime_value, 2.0, vec3<f32>()).b; |
| } |
| )"; |
| |
| auto* expect = R"( |
| struct S { |
| a : i32, |
| b : f32, |
| c : vec3<f32>, |
| } |
| |
| fn f() { |
| let runtime_value = 1; |
| let tint_symbol : S = S(runtime_value, 2.0, vec3<f32>()); |
| var x = tint_symbol.b; |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, BasicStruct_OutOfOrder) { |
| auto* src = R"( |
| fn f() { |
| let runtime_value = 1; |
| var x = S(runtime_value, 2.0, vec3<f32>()).b; |
| } |
| |
| struct S { |
| a : i32, |
| b : f32, |
| c : vec3<f32>, |
| }; |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| let runtime_value = 1; |
| let tint_symbol : S = S(runtime_value, 2.0, vec3<f32>()); |
| var x = tint_symbol.b; |
| } |
| |
| struct S { |
| a : i32, |
| b : f32, |
| c : vec3<f32>, |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, GlobalConstBasicArray) { |
| auto* src = R"( |
| const f0 = 1.0; |
| |
| const f1 = 2.0; |
| |
| const C = array<f32, 2u>(f0, f1); |
| |
| fn f() { |
| var f0 = 100.0; |
| var f1 = 100.0; |
| var i = C[1]; // Not hoisted, as the final const value is not an array |
| } |
| )"; |
| |
| EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, GlobalConstBasicArray_OutOfOrder) { |
| auto* src = R"( |
| fn f() { |
| var f0 = 100.0; |
| var f1 = 100.0; |
| var i = C[1]; |
| } |
| |
| const C = array<f32, 2u>(f0, f1); |
| |
| const f0 = 1.0; |
| |
| const f1 = 2.0; |
| )"; |
| |
| EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, GlobalConstArrayDynamicIndex) { |
| auto* src = R"( |
| const TRI_VERTICES = array( |
| vec4(0., 0., 0., 1.), |
| vec4(0., 1., 0., 1.), |
| vec4(1., 1., 0., 1.), |
| ); |
| |
| @vertex |
| fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> { |
| // note: TRI_VERTICES requires a materialize before the dynamic index. |
| return TRI_VERTICES[in_vertex_index]; |
| } |
| )"; |
| |
| auto* expect = R"( |
| const TRI_VERTICES = array(vec4(0.0, 0.0, 0.0, 1.0), vec4(0.0, 1.0, 0.0, 1.0), vec4(1.0, 1.0, 0.0, 1.0)); |
| |
| @vertex |
| fn vs_main(@builtin(vertex_index) in_vertex_index : u32) -> @builtin(position) vec4<f32> { |
| let tint_symbol : array<vec4<f32>, 3u> = TRI_VERTICES; |
| return tint_symbol[in_vertex_index]; |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, LocalConstBasicArray) { |
| auto* src = R"( |
| fn f() { |
| const f0 = 1.0; |
| const f1 = 2.0; |
| const C = array<f32, 2u>(f0, f1); |
| var i = C[1]; // Not hoisted, as the final const value is not an array |
| } |
| )"; |
| |
| EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, LocalConstBasicArrayRuntimeIndex) { |
| auto* src = R"( |
| fn f() { |
| const f0 = 1.0; |
| const f1 = 2.0; |
| const C = array<f32, 2u>(f0, f1); |
| let runtime_value = 1; |
| var i = C[runtime_value]; |
| } |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| const f0 = 1.0; |
| const f1 = 2.0; |
| const C = array<f32, 2u>(f0, f1); |
| let runtime_value = 1; |
| let tint_symbol : array<f32, 2u> = C; |
| var i = tint_symbol[runtime_value]; |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, ArrayInForLoopInit) { |
| auto* src = R"( |
| fn f() { |
| var insert_after = 1; |
| for(var i = array<f32, 4u>(0.0, 1.0, 2.0, 3.0)[insert_after]; ; ) { |
| break; |
| } |
| } |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| var insert_after = 1; |
| { |
| let tint_symbol : array<f32, 4u> = array<f32, 4u>(0.0, 1.0, 2.0, 3.0); |
| var i = tint_symbol[insert_after]; |
| loop { |
| { |
| break; |
| } |
| } |
| } |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, LocalConstArrayInForLoopInit) { |
| auto* src = R"( |
| fn f() { |
| const arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0); |
| let runtime_value = 1; |
| var insert_after = 1; |
| for(var i = arr[runtime_value]; ; ) { |
| break; |
| } |
| } |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| const arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0); |
| let runtime_value = 1; |
| var insert_after = 1; |
| { |
| let tint_symbol : array<f32, 4u> = arr; |
| var i = tint_symbol[runtime_value]; |
| loop { |
| { |
| break; |
| } |
| } |
| } |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInForLoopInit) { |
| auto* src = R"( |
| const arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0); |
| |
| fn f() { |
| let runtime_value = 1; |
| var insert_after = 1; |
| for(var i = arr[runtime_value]; ; ) { |
| break; |
| } |
| } |
| )"; |
| |
| auto* expect = R"( |
| const arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0); |
| |
| fn f() { |
| let runtime_value = 1; |
| var insert_after = 1; |
| { |
| let tint_symbol : array<f32, 4u> = arr; |
| var i = tint_symbol[runtime_value]; |
| loop { |
| { |
| break; |
| } |
| } |
| } |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, StructInForLoopInit) { |
| auto* src = R"( |
| struct S { |
| a : i32, |
| b : f32, |
| c : vec3<f32>, |
| }; |
| |
| fn get_b_runtime(s : S) -> f32 { |
| return s.b; |
| } |
| |
| fn f() { |
| var insert_after = 1; |
| for(var x = get_b_runtime(S(1, 2.0, vec3<f32>())); ; ) { |
| break; |
| } |
| } |
| )"; |
| |
| auto* expect = R"( |
| struct S { |
| a : i32, |
| b : f32, |
| c : vec3<f32>, |
| } |
| |
| fn get_b_runtime(s : S) -> f32 { |
| return s.b; |
| } |
| |
| fn f() { |
| var insert_after = 1; |
| { |
| let tint_symbol : S = S(1, 2.0, vec3<f32>()); |
| var x = get_b_runtime(tint_symbol); |
| loop { |
| { |
| break; |
| } |
| } |
| } |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, StructInForLoopInit_OutOfOrder) { |
| auto* src = R"( |
| fn f() { |
| var insert_after = 1; |
| for(var x = get_b_runtime(S(1, 2.0, vec3<f32>())); ; ) { |
| break; |
| } |
| } |
| |
| fn get_b_runtime(s : S) -> f32 { |
| return s.b; |
| } |
| |
| struct S { |
| a : i32, |
| b : f32, |
| c : vec3<f32>, |
| }; |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| var insert_after = 1; |
| { |
| let tint_symbol : S = S(1, 2.0, vec3<f32>()); |
| var x = get_b_runtime(tint_symbol); |
| loop { |
| { |
| break; |
| } |
| } |
| } |
| } |
| |
| fn get_b_runtime(s : S) -> f32 { |
| return s.b; |
| } |
| |
| struct S { |
| a : i32, |
| b : f32, |
| c : vec3<f32>, |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, ArrayInForLoopCond) { |
| auto* src = R"( |
| fn f() { |
| var f = 1.0; |
| for(; f == array<f32, 1u>(f)[0]; f = f + 1.0) { |
| var marker = 1; |
| } |
| } |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| var f = 1.0; |
| loop { |
| let tint_symbol : array<f32, 1u> = array<f32, 1u>(f); |
| if (!((f == tint_symbol[0]))) { |
| break; |
| } |
| { |
| var marker = 1; |
| } |
| |
| continuing { |
| f = (f + 1.0); |
| } |
| } |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, LocalConstArrayInForLoopCond) { |
| auto* src = R"( |
| fn f() { |
| let runtime_value = 0; |
| const f = 1.0; |
| const arr = array<f32, 1u>(f); |
| for(var i = f; i == arr[runtime_value]; i = i + 1.0) { |
| var marker = 1; |
| } |
| } |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| let runtime_value = 0; |
| const f = 1.0; |
| const arr = array<f32, 1u>(f); |
| { |
| var i = f; |
| loop { |
| let tint_symbol : array<f32, 1u> = arr; |
| if (!((i == tint_symbol[runtime_value]))) { |
| break; |
| } |
| { |
| var marker = 1; |
| } |
| |
| continuing { |
| i = (i + 1.0); |
| } |
| } |
| } |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInForLoopCond) { |
| auto* src = R"( |
| const f = 1.0; |
| |
| const arr = array<f32, 1u>(f); |
| |
| fn F() { |
| let runtime_value = 0; |
| for(var i = f; i == arr[runtime_value]; i = i + 1.0) { |
| var marker = 1; |
| } |
| } |
| )"; |
| |
| auto* expect = R"( |
| const f = 1.0; |
| |
| const arr = array<f32, 1u>(f); |
| |
| fn F() { |
| let runtime_value = 0; |
| { |
| var i = f; |
| loop { |
| let tint_symbol : array<f32, 1u> = arr; |
| if (!((i == tint_symbol[runtime_value]))) { |
| break; |
| } |
| { |
| var marker = 1; |
| } |
| |
| continuing { |
| i = (i + 1.0); |
| } |
| } |
| } |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, ArrayInForLoopCont) { |
| auto* src = R"( |
| fn f() { |
| let runtime_value = 0; |
| var f = 0.0; |
| for(; f < 10.0; f = f + array<f32, 1u>(1.0)[runtime_value]) { |
| var marker = 1; |
| } |
| } |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| let runtime_value = 0; |
| var f = 0.0; |
| loop { |
| if (!((f < 10.0))) { |
| break; |
| } |
| { |
| var marker = 1; |
| } |
| |
| continuing { |
| let tint_symbol : array<f32, 1u> = array<f32, 1u>(1.0); |
| f = (f + tint_symbol[runtime_value]); |
| } |
| } |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, LocalConstArrayInForLoopCont) { |
| auto* src = R"( |
| fn f() { |
| let runtime_value = 0; |
| const arr = array<f32, 1u>(1.0); |
| var f = 0.0; |
| for(; f < 10.0; f = f + arr[runtime_value]) { |
| var marker = 1; |
| } |
| } |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| let runtime_value = 0; |
| const arr = array<f32, 1u>(1.0); |
| var f = 0.0; |
| loop { |
| if (!((f < 10.0))) { |
| break; |
| } |
| { |
| var marker = 1; |
| } |
| |
| continuing { |
| let tint_symbol : array<f32, 1u> = arr; |
| f = (f + tint_symbol[runtime_value]); |
| } |
| } |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInForLoopCont) { |
| auto* src = R"( |
| const arr = array<f32, 1u>(1.0); |
| |
| fn f() { |
| let runtime_value = 0; |
| var f = 0.0; |
| for(; f < 10.0; f = f + arr[runtime_value]) { |
| var marker = 1; |
| } |
| } |
| )"; |
| |
| auto* expect = R"( |
| const arr = array<f32, 1u>(1.0); |
| |
| fn f() { |
| let runtime_value = 0; |
| var f = 0.0; |
| loop { |
| if (!((f < 10.0))) { |
| break; |
| } |
| { |
| var marker = 1; |
| } |
| |
| continuing { |
| let tint_symbol : array<f32, 1u> = arr; |
| f = (f + tint_symbol[runtime_value]); |
| } |
| } |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, ArrayInForLoopInitCondCont) { |
| auto* src = R"( |
| fn f() { |
| let runtime_value = 0; |
| for(var f = array<f32, 1u>(0.0)[runtime_value]; |
| f < array<f32, 1u>(1.0)[runtime_value]; |
| f = f + array<f32, 1u>(2.0)[runtime_value]) { |
| var marker = 1; |
| } |
| } |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| let runtime_value = 0; |
| { |
| let tint_symbol : array<f32, 1u> = array<f32, 1u>(0.0); |
| var f = tint_symbol[runtime_value]; |
| loop { |
| let tint_symbol_1 : array<f32, 1u> = array<f32, 1u>(1.0); |
| if (!((f < tint_symbol_1[runtime_value]))) { |
| break; |
| } |
| { |
| var marker = 1; |
| } |
| |
| continuing { |
| let tint_symbol_2 : array<f32, 1u> = array<f32, 1u>(2.0); |
| f = (f + tint_symbol_2[runtime_value]); |
| } |
| } |
| } |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, LocalConstArrayInForLoopInitCondCont) { |
| auto* src = R"( |
| fn f() { |
| let runtime_value = 0; |
| const arr_a = array<f32, 1u>(0.0); |
| const arr_b = array<f32, 1u>(1.0); |
| const arr_c = array<f32, 1u>(2.0); |
| for(var f = arr_a[runtime_value]; f < arr_b[runtime_value]; f = f + arr_c[runtime_value]) { |
| var marker = 1; |
| } |
| } |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| let runtime_value = 0; |
| const arr_a = array<f32, 1u>(0.0); |
| const arr_b = array<f32, 1u>(1.0); |
| const arr_c = array<f32, 1u>(2.0); |
| { |
| let tint_symbol : array<f32, 1u> = arr_a; |
| var f = tint_symbol[runtime_value]; |
| loop { |
| let tint_symbol_1 : array<f32, 1u> = arr_b; |
| if (!((f < tint_symbol_1[runtime_value]))) { |
| break; |
| } |
| { |
| var marker = 1; |
| } |
| |
| continuing { |
| let tint_symbol_2 : array<f32, 1u> = arr_c; |
| f = (f + tint_symbol_2[runtime_value]); |
| } |
| } |
| } |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, ArrayInElseIf) { |
| auto* src = R"( |
| fn f() { |
| var f = 1.0; |
| if (true) { |
| var marker = 0; |
| } else if (f == array<f32, 2u>(f, f)[0]) { |
| var marker = 1; |
| } |
| } |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| var f = 1.0; |
| if (true) { |
| var marker = 0; |
| } else { |
| let tint_symbol : array<f32, 2u> = array<f32, 2u>(f, f); |
| if ((f == tint_symbol[0])) { |
| var marker = 1; |
| } |
| } |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, ArrayInElseIfChain) { |
| auto* src = R"( |
| fn f() { |
| var f = 1.0; |
| if (true) { |
| var marker = 0; |
| } else if (true) { |
| var marker = 1; |
| } else if (f == array<f32, 2u>(f, f)[0]) { |
| var marker = 2; |
| } else if (f == array<f32, 2u>(f, f)[1]) { |
| var marker = 3; |
| } else if (true) { |
| var marker = 4; |
| } else { |
| var marker = 5; |
| } |
| } |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| var f = 1.0; |
| if (true) { |
| var marker = 0; |
| } else if (true) { |
| var marker = 1; |
| } else { |
| let tint_symbol : array<f32, 2u> = array<f32, 2u>(f, f); |
| if ((f == tint_symbol[0])) { |
| var marker = 2; |
| } else { |
| let tint_symbol_1 : array<f32, 2u> = array<f32, 2u>(f, f); |
| if ((f == tint_symbol_1[1])) { |
| var marker = 3; |
| } else if (true) { |
| var marker = 4; |
| } else { |
| var marker = 5; |
| } |
| } |
| } |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, LocalConstArrayInElseIfChain) { |
| auto* src = R"( |
| fn f() { |
| let runtime_value = 0; |
| const f = 1.0; |
| const arr = array<f32, 2u>(f, f); |
| if (true) { |
| var marker = 0; |
| } else if (true) { |
| var marker = 1; |
| } else if (f == arr[runtime_value]) { |
| var marker = 2; |
| } else if (f == arr[runtime_value + 1]) { |
| var marker = 3; |
| } else if (true) { |
| var marker = 4; |
| } else { |
| var marker = 5; |
| } |
| } |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| let runtime_value = 0; |
| const f = 1.0; |
| const arr = array<f32, 2u>(f, f); |
| if (true) { |
| var marker = 0; |
| } else if (true) { |
| var marker = 1; |
| } else { |
| let tint_symbol : array<f32, 2u> = arr; |
| if ((f == tint_symbol[runtime_value])) { |
| var marker = 2; |
| } else { |
| let tint_symbol_1 : array<f32, 2u> = arr; |
| if ((f == tint_symbol_1[(runtime_value + 1)])) { |
| var marker = 3; |
| } else if (true) { |
| var marker = 4; |
| } else { |
| var marker = 5; |
| } |
| } |
| } |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInElseIfChain) { |
| auto* src = R"( |
| const f = 1.0; |
| |
| const arr = array<f32, 2u>(f, f); |
| |
| fn F() { |
| let runtime_value = 0; |
| if (true) { |
| var marker = 0; |
| } else if (true) { |
| var marker = 1; |
| } else if (f == arr[runtime_value]) { |
| var marker = 2; |
| } else if (f == arr[runtime_value + 1]) { |
| var marker = 3; |
| } else if (true) { |
| var marker = 4; |
| } else { |
| var marker = 5; |
| } |
| } |
| )"; |
| |
| auto* expect = R"( |
| const f = 1.0; |
| |
| const arr = array<f32, 2u>(f, f); |
| |
| fn F() { |
| let runtime_value = 0; |
| if (true) { |
| var marker = 0; |
| } else if (true) { |
| var marker = 1; |
| } else { |
| let tint_symbol : array<f32, 2u> = arr; |
| if ((f == tint_symbol[runtime_value])) { |
| var marker = 2; |
| } else { |
| let tint_symbol_1 : array<f32, 2u> = arr; |
| if ((f == tint_symbol_1[(runtime_value + 1)])) { |
| var marker = 3; |
| } else if (true) { |
| var marker = 4; |
| } else { |
| var marker = 5; |
| } |
| } |
| } |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, ArrayInArrayArrayConstIndex) { |
| auto* src = R"( |
| fn f() { |
| var i = array<array<f32, 2u>, 2u>(array<f32, 2u>(1.0, 2.0), array<f32, 2u>(3.0, 4.0))[0][1]; |
| } |
| )"; |
| |
| EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, ArrayInArrayArrayRuntimeIndex) { |
| auto* src = R"( |
| fn f() { |
| let runtime_value = 1; |
| var i = array<array<f32, 2u>, 2u>(array<f32, 2u>(1.0, 2.0), array<f32, 2u>(3.0, 4.0))[runtime_value][runtime_value + 1]; |
| } |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| let runtime_value = 1; |
| let tint_symbol : array<array<f32, 2u>, 2u> = array<array<f32, 2u>, 2u>(array<f32, 2u>(1.0, 2.0), array<f32, 2u>(3.0, 4.0)); |
| var i = tint_symbol[runtime_value][(runtime_value + 1)]; |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, LocalConstArrayInArrayArrayConstIndex) { |
| auto* src = R"( |
| fn f() { |
| const arr_0 = array<f32, 2u>(1.0, 2.0); |
| const arr_1 = array<f32, 2u>(3.0, 4.0); |
| const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1); |
| var i = arr_2[0][1]; |
| } |
| )"; |
| |
| EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, LocalConstArrayInArrayArrayRuntimeIndex) { |
| auto* src = R"( |
| fn f() { |
| let runtime_value = 1; |
| const arr_0 = array<f32, 2u>(1.0, 2.0); |
| const arr_1 = array<f32, 2u>(3.0, 4.0); |
| const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1); |
| var i = arr_2[runtime_value][runtime_value + 1]; |
| } |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| let runtime_value = 1; |
| const arr_0 = array<f32, 2u>(1.0, 2.0); |
| const arr_1 = array<f32, 2u>(3.0, 4.0); |
| const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1); |
| let tint_symbol : array<array<f32, 2u>, 2u> = arr_2; |
| var i = tint_symbol[runtime_value][(runtime_value + 1)]; |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, GlobalConstArrayInArrayArray) { |
| auto* src = R"( |
| const arr_0 = array<f32, 2u>(1.0, 2.0); |
| |
| const arr_1 = array<f32, 2u>(3.0, 4.0); |
| |
| const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1); |
| |
| fn f() { |
| let runtime_value = 1; |
| var i = arr_2[runtime_value][runtime_value + 1]; |
| } |
| )"; |
| |
| auto* expect = R"( |
| const arr_0 = array<f32, 2u>(1.0, 2.0); |
| |
| const arr_1 = array<f32, 2u>(3.0, 4.0); |
| |
| const arr_2 = array<array<f32, 2u>, 2u>(arr_0, arr_1); |
| |
| fn f() { |
| let runtime_value = 1; |
| let tint_symbol : array<array<f32, 2u>, 2u> = arr_2; |
| var i = tint_symbol[runtime_value][(runtime_value + 1)]; |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, StructNested) { |
| auto* src = R"( |
| struct S1 { |
| a : i32, |
| }; |
| |
| struct S2 { |
| a : i32, |
| b : S1, |
| c : i32, |
| }; |
| |
| struct S3 { |
| a : S2, |
| }; |
| |
| fn get_a(s : S3) -> S2 { |
| return s.a; |
| } |
| |
| fn f() { |
| var x = get_a(S3(S2(1, S1(2), 3))).b.a; |
| } |
| )"; |
| |
| auto* expect = R"( |
| struct S1 { |
| a : i32, |
| } |
| |
| struct S2 { |
| a : i32, |
| b : S1, |
| c : i32, |
| } |
| |
| struct S3 { |
| a : S2, |
| } |
| |
| fn get_a(s : S3) -> S2 { |
| return s.a; |
| } |
| |
| fn f() { |
| let tint_symbol : S3 = S3(S2(1, S1(2), 3)); |
| var x = get_a(tint_symbol).b.a; |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, Mixed) { |
| auto* src = R"( |
| struct S1 { |
| a : i32, |
| }; |
| |
| struct S2 { |
| a : array<S1, 3u>, |
| }; |
| |
| fn get_a(s : S2) -> array<S1, 3u> { |
| return s.a; |
| } |
| |
| fn f() { |
| var x = get_a(S2(array<S1, 3u>(S1(1), S1(2), S1(3))))[1].a; |
| } |
| )"; |
| |
| auto* expect = R"( |
| struct S1 { |
| a : i32, |
| } |
| |
| struct S2 { |
| a : array<S1, 3u>, |
| } |
| |
| fn get_a(s : S2) -> array<S1, 3u> { |
| return s.a; |
| } |
| |
| fn f() { |
| let tint_symbol : S2 = S2(array<S1, 3u>(S1(1), S1(2), S1(3))); |
| var x = get_a(tint_symbol)[1].a; |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, Mixed_OutOfOrder) { |
| auto* src = R"( |
| fn f() { |
| var x = get_a(S2(array<S1, 3u>(S1(1), S1(2), S1(3))))[1].a; |
| } |
| |
| fn get_a(s : S2) -> array<S1, 3u> { |
| return s.a; |
| } |
| |
| struct S2 { |
| a : array<S1, 3u>, |
| }; |
| |
| struct S1 { |
| a : i32, |
| }; |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| let tint_symbol : S2 = S2(array<S1, 3u>(S1(1), S1(2), S1(3))); |
| var x = get_a(tint_symbol)[1].a; |
| } |
| |
| fn get_a(s : S2) -> array<S1, 3u> { |
| return s.a; |
| } |
| |
| struct S2 { |
| a : array<S1, 3u>, |
| } |
| |
| struct S1 { |
| a : i32, |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, NoChangeOnVarDecl) { |
| auto* src = R"( |
| alias F = f32; |
| |
| fn f() { |
| var local_arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0); |
| var local_str = F(3.0); |
| } |
| |
| const module_arr : array<f32, 4u> = array<f32, 4u>(0.0, 1.0, 2.0, 3.0); |
| |
| const module_str : F = F(2.0); |
| )"; |
| |
| auto* expect = src; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, NoChangeOnVarDecl_OutOfOrder) { |
| auto* src = R"( |
| fn f() { |
| var local_arr = array<f32, 4u>(0.0, 1.0, 2.0, 3.0); |
| var local_str = F(3.0); |
| } |
| |
| const module_str : F = F(2.0); |
| |
| alias F = f32; |
| |
| const module_arr : array<f32, 4u> = array<f32, 4u>(0.0, 1.0, 2.0, 3.0); |
| )"; |
| |
| auto* expect = src; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, ForLoopShadowing) { |
| auto* src = R"( |
| fn X() { |
| var i = 10; |
| for(var f = 0; f < 10; f = f + array<i32, 1u>(i)[0]) { |
| var i = 20; |
| } |
| } |
| |
| fn Y() { |
| var i = 10; |
| for(var f = 0; f < array<i32, 1u>(i)[0]; f = f + 1) { |
| var i = 20; |
| } |
| } |
| |
| fn Z() { |
| var i = 10; |
| for(var f = array<i32, 1u>(i)[0]; f < 10; f = f + 1) { |
| var i = 20; |
| } |
| } |
| )"; |
| |
| auto* expect = R"( |
| fn X() { |
| var i = 10; |
| { |
| var f = 0; |
| loop { |
| if (!((f < 10))) { |
| break; |
| } |
| { |
| var i = 20; |
| } |
| |
| continuing { |
| let tint_symbol : array<i32, 1u> = array<i32, 1u>(i); |
| f = (f + tint_symbol[0]); |
| } |
| } |
| } |
| } |
| |
| fn Y() { |
| var i = 10; |
| { |
| var f = 0; |
| loop { |
| let tint_symbol_1 : array<i32, 1u> = array<i32, 1u>(i); |
| if (!((f < tint_symbol_1[0]))) { |
| break; |
| } |
| { |
| var i = 20; |
| } |
| |
| continuing { |
| f = (f + 1); |
| } |
| } |
| } |
| } |
| |
| fn Z() { |
| var i = 10; |
| { |
| let tint_symbol_2 : array<i32, 1u> = array<i32, 1u>(i); |
| var f = tint_symbol_2[0]; |
| loop { |
| if (!((f < 10))) { |
| break; |
| } |
| { |
| var i = 20; |
| } |
| |
| continuing { |
| f = (f + 1); |
| } |
| } |
| } |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, AssignAbstractArray) { |
| // Test that hoisting an array with abstract type still materializes to the correct type. |
| auto* src = R"( |
| fn f() { |
| var arr : array<f32, 4>; |
| arr = array(1, 2, 3, 4); |
| } |
| )"; |
| |
| auto* expect = R"( |
| fn f() { |
| var arr : array<f32, 4>; |
| let tint_symbol : array<f32, 4u> = array(1, 2, 3, 4); |
| arr = tint_symbol; |
| } |
| )"; |
| |
| auto got = Run<PromoteInitializersToLet>(src); |
| |
| EXPECT_EQ(expect, str(got)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, AssignAbstractArray_ToPhony) { |
| // Test that we do not try to hoist an abstract array expression that is the RHS of a phony |
| // assignment, as its type will not be materialized. |
| auto* src = R"( |
| fn f() { |
| _ = array(1, 2, 3, 4); |
| } |
| )"; |
| |
| EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src)); |
| } |
| |
| TEST_F(PromoteInitializersToLetTest, Bug2241) { |
| auto* src = R"( |
| fn f () { |
| let v = false && array<i32, array(array(6))[0][0]>()[0] == 0; |
| } |
| )"; |
| EXPECT_FALSE(ShouldRun<PromoteInitializersToLet>(src)); |
| } |
| |
| } // namespace |
| } // namespace tint::ast::transform |