summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/flow_control/eval_order.spec.ts
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/flow_control/eval_order.spec.ts')
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/flow_control/eval_order.spec.ts1007
1 files changed, 1007 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/flow_control/eval_order.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/flow_control/eval_order.spec.ts
new file mode 100644
index 0000000000..5a07646a0d
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/shader/execution/flow_control/eval_order.spec.ts
@@ -0,0 +1,1007 @@
+export const description = `
+Flow control tests for expression evaluation order.
+`;
+
+import { makeTestGroup } from '../../../../common/framework/test_group.js';
+import { GPUTest } from '../../../gpu_test.js';
+
+import { runFlowControlTest } from './harness.js';
+
+export const g = makeTestGroup(GPUTest);
+
+g.test('binary_op')
+ .desc('Test that a binary operator evaluates the LHS then the RHS')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let l = lhs() + rhs();
+ ${f.expect_order(3)}
+`,
+ extra: `
+fn lhs() -> i32 {
+ ${f.expect_order(1)}
+ return 0;
+}
+fn rhs() -> i32 {
+ ${f.expect_order(2)}
+ return 0;
+}`,
+ }));
+ });
+
+g.test('binary_op_rhs_const')
+ .desc('Test that a binary operator evaluates the LHS, when the RHS is a constant expression')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let l = lhs() + rhs();
+ ${f.expect_order(2)}
+`,
+ extra: `
+fn lhs() -> i32 {
+ ${f.expect_order(1)}
+ return 0;
+}
+fn rhs() -> i32 {
+ return 0;
+}`,
+ }));
+ });
+
+g.test('binary_op_lhs_const')
+ .desc('Test that a binary operator evaluates the RHS, when the LHS is a constant expression')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let l = lhs() + rhs();
+ ${f.expect_order(2)}
+`,
+ extra: `
+fn lhs() -> i32 {
+ return 0;
+}
+fn rhs() -> i32 {
+ ${f.expect_order(1)}
+ return 0;
+}`,
+ }));
+ });
+
+g.test('binary_op_chain')
+ .desc('Test that a binary operator chain evaluates left-to-right')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let l = a() + b() - c() * d();
+ ${f.expect_order(5)}
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 1;
+}
+fn c() -> i32 {
+ ${f.expect_order(3)}
+ return 1;
+}
+fn d() -> i32 {
+ ${f.expect_order(4)}
+ return 1;
+}`,
+ }));
+ });
+
+g.test('binary_op_chain_R_C_C_C')
+ .desc(
+ 'Test evaluation order of a binary operator chain with a runtime-expression for the left-most expression'
+ )
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let l = f() + 1 + 2 + 3;
+ ${f.expect_order(2)}
+`,
+ extra: `
+fn f() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+`,
+ }));
+ });
+
+g.test('binary_op_chain_C_R_C_C')
+ .desc(
+ 'Test evaluation order of a binary operator chain with a runtime-expression for the second-left-most-const'
+ )
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let l = 1 + f() + 2 + 3;
+ ${f.expect_order(2)}
+ `,
+ extra: `
+fn f() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+ `,
+ }));
+ });
+
+g.test('binary_op_chain_C_C_R_C')
+ .desc(
+ 'Test evaluation order of a binary operator chain with a runtime-expression for the second-right-most-const'
+ )
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let l = 1 + 2 + f() + 3;
+ ${f.expect_order(2)}
+`,
+ extra: `
+fn f() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+ `,
+ }));
+ });
+
+g.test('binary_op_chain_C_C_C_R')
+ .desc(
+ 'Test evaluation order of a binary operator chain with a runtime-expression for the right-most expression'
+ )
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let l = 1 + 2 + 3 + f();
+ ${f.expect_order(2)}
+ `,
+ extra: `
+fn f() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+ `,
+ }));
+ });
+
+g.test('binary_op_parenthesized_expr')
+ .desc('Test that a parenthesized binary operator expression evaluates left-to-right')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let x = (a() + b()) - (c() * d());
+ ${f.expect_order(5)}
+ let y = a() + (b() - c()) * d();
+ ${f.expect_order(10)}
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(1, 6)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2, 7)}
+ return 1;
+}
+fn c() -> i32 {
+ ${f.expect_order(3, 8)}
+ return 1;
+}
+fn d() -> i32 {
+ ${f.expect_order(4, 9)}
+ return 1;
+}`,
+ }));
+ });
+
+g.test('array_index')
+ .desc('Test that array indices are evaluated left-to-right')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ var arr : array<array<array<i32, 8>, 8>, 8>;
+ ${f.expect_order(0)}
+ let x = arr[a()][b()][c()];
+ ${f.expect_order(4)}
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 1;
+}
+fn c() -> i32 {
+ ${f.expect_order(3)}
+ return 1;
+}`,
+ }));
+ });
+
+g.test('array_index_lhs_assignment')
+ .desc(
+ 'Test that array indices are evaluated left-to-right, when indexing the LHS of an assignment'
+ )
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ var arr : array<array<array<i32, 8>, 8>, 8>;
+ ${f.expect_order(0)}
+ arr[a()][b()][c()] = ~d();
+ ${f.expect_order(5)}
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 1;
+}
+fn c() -> i32 {
+ ${f.expect_order(3)}
+ return 1;
+}
+fn d() -> i32 {
+ ${f.expect_order(4)}
+ return 1;
+}`,
+ }));
+ });
+
+g.test('array_index_lhs_member_assignment')
+ .desc(
+ 'Test that array indices are evaluated left-to-right, when indexing with member-accessors in the LHS of an assignment'
+ )
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ var arr : array<array<S, 8>, 8>;
+ ${f.expect_order(0)}
+ arr[a()][b()].member[c()] = d();
+ ${f.expect_order(5)}
+`,
+ extra: `
+struct S {
+ member : array<i32, 8>,
+}
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 1;
+}
+fn c() -> i32 {
+ ${f.expect_order(3)}
+ return 1;
+}
+fn d() -> i32 {
+ ${f.expect_order(4)}
+ return 1;
+}`,
+ }));
+ });
+
+g.test('array_index_via_ptrs')
+ .desc('Test that array indices are evaluated in order, when used via pointers')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ var arr : array<array<array<i32, 8>, 8>, 8>;
+ ${f.expect_order(0)}
+ let p0 = &arr;
+ ${f.expect_order(1)}
+ let p1 = &(*p0)[a()];
+ ${f.expect_order(3)}
+ let p2 = &(*p1)[b()];
+ ${f.expect_order(5)}
+ let p3 = &(*p2)[c()];
+ ${f.expect_order(7)}
+ let p4 = *p3;
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(2)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(4)}
+ return 1;
+}
+fn c() -> i32 {
+ ${f.expect_order(6)}
+ return 1;
+}`,
+ }));
+ });
+
+g.test('array_index_via_struct_members')
+ .desc('Test that array indices are evaluated in order, when accessed via structure members')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ var x : X;
+ ${f.expect_order(0)}
+ let r = x.y[a()].z[b()].a[c()];
+ ${f.expect_order(4)}
+`,
+ extra: `
+struct X {
+ y : array<Y, 3>,
+};
+struct Y {
+ z : array<Z, 3>,
+};
+struct Z {
+ a : array<i32, 3>,
+};
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 1;
+}
+fn c() -> i32 {
+ ${f.expect_order(3)}
+ return 1;
+}`,
+ }));
+ });
+
+g.test('matrix_index')
+ .desc('Test that matrix indices are evaluated left-to-right')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ var mat : mat4x4<f32>;
+ ${f.expect_order(0)}
+ let x = mat[a()][b()];
+ ${f.expect_order(3)}
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 1;
+}`,
+ }));
+ });
+
+g.test('matrix_index_via_ptr')
+ .desc('Test that matrix indices are evaluated in order, when used via pointers')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ var mat : mat4x4<f32>;
+ ${f.expect_order(0)}
+ let p0 = &mat;
+ ${f.expect_order(1)}
+ let p1 = &(*p0)[a()];
+ ${f.expect_order(3)}
+ let v = (*p1)[b()];
+ ${f.expect_order(5)}
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(2)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(4)}
+ return 1;
+}`,
+ }));
+ });
+
+g.test('logical_and')
+ .desc(
+ 'Test that a chain of logical-AND expressions are evaluated left-to-right, stopping at the first false'
+ )
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let l = a() && b() && c();
+ ${f.expect_order(3)}
+`,
+ extra: `
+fn a() -> bool {
+ ${f.expect_order(1)}
+ return true;
+}
+fn b() -> bool {
+ ${f.expect_order(2)}
+ return false;
+}
+fn c() -> bool {
+ ${f.expect_not_reached()}
+ return true;
+}
+`,
+ }));
+ });
+
+g.test('logical_or')
+ .desc(
+ 'Test that a chain of logical-OR expressions are evaluated left-to-right, stopping at the first true'
+ )
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let l = a() || b() || c();
+ ${f.expect_order(3)}
+`,
+ extra: `
+fn a() -> bool {
+ ${f.expect_order(1)}
+ return false;
+}
+fn b() -> bool {
+ ${f.expect_order(2)}
+ return true;
+}
+fn c() -> bool {
+ ${f.expect_not_reached()}
+ return true;
+}
+`,
+ }));
+ });
+
+g.test('bitwise_and')
+ .desc(
+ 'Test that a chain of bitwise-AND expressions are evaluated left-to-right, with no short-circuiting'
+ )
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let l = a() & b() & c();
+ ${f.expect_order(4)}
+`,
+ extra: `
+fn a() -> bool {
+ ${f.expect_order(1)}
+ return true;
+}
+fn b() -> bool {
+ ${f.expect_order(2)}
+ return false;
+}
+fn c() -> bool {
+ ${f.expect_order(3)}
+ return true;
+}
+`,
+ }));
+ });
+
+g.test('bitwise_or')
+ .desc(
+ 'Test that a chain of bitwise-OR expressions are evaluated left-to-right, with no short-circuiting'
+ )
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let l = a() | b() | c();
+ ${f.expect_order(4)}
+`,
+ extra: `
+fn a() -> bool {
+ ${f.expect_order(1)}
+ return false;
+}
+fn b() -> bool {
+ ${f.expect_order(2)}
+ return true;
+}
+fn c() -> bool {
+ ${f.expect_order(3)}
+ return true;
+}
+`,
+ }));
+ });
+
+g.test('user_fn_args')
+ .desc('Test user function call arguments are evaluated left-to-right')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let l = f(a(), b(), c());
+ ${f.expect_order(5)}
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 2;
+}
+fn c() -> i32 {
+ ${f.expect_order(3)}
+ return 3;
+}
+fn f(x : i32, y : i32, z : i32) -> i32 {
+ ${f.expect_order(4)}
+ return x + y + z;
+}`,
+ }));
+ });
+
+g.test('nested_fn_args')
+ .desc('Test user nested call arguments are evaluated left-to-right')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let l = g(c(a(), b()), f(d(), e()));
+ ${f.expect_order(8)}
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 0;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 0;
+}
+fn c(x : i32, y : i32) -> i32 {
+ ${f.expect_order(3)}
+ return x + y;
+}
+fn d() -> i32 {
+ ${f.expect_order(4)}
+ return 0;
+}
+fn e() -> i32 {
+ ${f.expect_order(5)}
+ return 0;
+}
+fn f(x : i32, y : i32) -> i32 {
+ ${f.expect_order(6)}
+ return x + y;
+}
+fn g(x : i32, y : i32) -> i32 {
+ ${f.expect_order(7)}
+ return x + y;
+}`,
+ }));
+ });
+
+g.test('builtin_fn_args')
+ .desc('Test builtin function call arguments are evaluated left-to-right')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let l = mix(a(), b(), c());
+ ${f.expect_order(4)}
+`,
+ extra: `
+fn a() -> f32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> f32 {
+ ${f.expect_order(2)}
+ return 2;
+}
+fn c() -> f32 {
+ ${f.expect_order(3)}
+ return 3;
+}
+`,
+ }));
+ });
+
+g.test('nested_builtin_fn_args')
+ .desc('Test nested builtin function call arguments are evaluated left-to-right')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let l = mix(a(), mix(b(), c(), d()), e());
+ ${f.expect_order(6)}
+`,
+ extra: `
+fn a() -> f32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> f32 {
+ ${f.expect_order(2)}
+ return 2;
+}
+fn c() -> f32 {
+ ${f.expect_order(3)}
+ return 3;
+}
+fn d() -> f32 {
+ ${f.expect_order(4)}
+ return 3;
+}
+fn e() -> f32 {
+ ${f.expect_order(5)}
+ return 3;
+}
+`,
+ }));
+ });
+
+g.test('1d_array_constructor')
+ .desc('Test arguments of an array constructor are evaluated left-to-right')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let v = array(a(), b(), c(), d());
+ ${f.expect_order(5)}
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 2;
+}
+fn c() -> i32 {
+ ${f.expect_order(3)}
+ return 1;
+}
+fn d() -> i32 {
+ ${f.expect_order(4)}
+ return 2;
+}
+`,
+ }));
+ });
+
+g.test('2d_array_constructor')
+ .desc('Test arguments of a 2D array constructor are evaluated left-to-right')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let v = array(array(a(), b()), array(c(), d()));
+ ${f.expect_order(5)}
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 2;
+}
+fn c() -> i32 {
+ ${f.expect_order(3)}
+ return 1;
+}
+fn d() -> i32 {
+ ${f.expect_order(4)}
+ return 2;
+}
+`,
+ }));
+ });
+
+g.test('vec4_constructor')
+ .desc('Test arguments of a vector constructor are evaluated left-to-right')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let v = vec4(a(), b(), c(), d());
+ ${f.expect_order(5)}
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 2;
+}
+fn c() -> i32 {
+ ${f.expect_order(3)}
+ return 1;
+}
+fn d() -> i32 {
+ ${f.expect_order(4)}
+ return 2;
+}
+`,
+ }));
+ });
+
+g.test('nested_vec4_constructor')
+ .desc('Test arguments of a nested vector constructor are evaluated left-to-right')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let v = vec4(a(), vec2(b(), c()), d());
+ ${f.expect_order(5)}
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 2;
+}
+fn c() -> i32 {
+ ${f.expect_order(3)}
+ return 1;
+}
+fn d() -> i32 {
+ ${f.expect_order(4)}
+ return 2;
+}
+`,
+ }));
+ });
+
+g.test('struct_constructor')
+ .desc('Test arguments of a structure constructor are evaluated left-to-right')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let v = S(a(), b(), c(), d());
+ ${f.expect_order(5)}
+`,
+ extra: `
+struct S {
+ a : i32,
+ b : i32,
+ c : i32,
+ d : i32,
+}
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 2;
+}
+fn c() -> i32 {
+ ${f.expect_order(3)}
+ return 1;
+}
+fn d() -> i32 {
+ ${f.expect_order(4)}
+ return 2;
+}
+`,
+ }));
+ });
+
+g.test('nested_struct_constructor')
+ .desc('Test arguments of a nested structure constructor are evaluated left-to-right')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ ${f.expect_order(0)}
+ let v = Y(a(), X(b(), c()), d());
+ ${f.expect_order(5)}
+`,
+ extra: `
+struct Y {
+ a : i32,
+ x : X,
+ c : i32,
+}
+struct X {
+ b : i32,
+ c : i32,
+}
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 2;
+}
+fn c() -> i32 {
+ ${f.expect_order(3)}
+ return 1;
+}
+fn d() -> i32 {
+ ${f.expect_order(4)}
+ return 2;
+}
+`,
+ }));
+ });
+
+g.test('1d_array_assignment')
+ .desc('Test LHS of an array element assignment is evaluated before RHS')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ var arr : array<i32, 8>;
+ ${f.expect_order(0)}
+ arr[a()] = arr[b()];
+ ${f.expect_order(3)}
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 2;
+}
+`,
+ }));
+ });
+
+g.test('2d_array_assignment')
+ .desc('Test LHS of 2D-array element assignment is evaluated before RHS')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ var arr : array<array<i32, 8>, 8>;
+ ${f.expect_order(0)}
+ arr[a()][b()] = arr[c()][d()];
+ ${f.expect_order(5)}
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 2;
+}
+fn c() -> i32 {
+ ${f.expect_order(3)}
+ return 1;
+}
+fn d() -> i32 {
+ ${f.expect_order(4)}
+ return 2;
+}
+`,
+ }));
+ });
+
+g.test('1d_array_compound_assignment')
+ .desc('Test LHS of an array element compound assignment is evaluated before RHS')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ var arr : array<i32, 8>;
+ ${f.expect_order(0)}
+ arr[a()] += arr[b()];
+ ${f.expect_order(3)}
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 2;
+}
+`,
+ }));
+ });
+
+g.test('2d_array_compound_assignment')
+ .desc('Test LHS of a 2D-array element compound assignment is evaluated before RHS')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ var arr : array<array<i32, 8>, 8>;
+ ${f.expect_order(0)}
+ arr[a()][b()] += arr[c()][d()];
+ ${f.expect_order(5)}
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 2;
+}
+fn c() -> i32 {
+ ${f.expect_order(3)}
+ return 1;
+}
+fn d() -> i32 {
+ ${f.expect_order(4)}
+ return 2;
+}
+`,
+ }));
+ });
+
+g.test('1d_array_increment')
+ .desc('Test index of an array element increment is evaluated only once')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ var arr : array<i32, 8>;
+ ${f.expect_order(0)}
+ arr[a()]++;
+ ${f.expect_order(2)}
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+`,
+ }));
+ });
+
+g.test('2d_array_increment')
+ .desc('Test index of a 2D-array element increment is evaluated only once')
+ .fn(t => {
+ runFlowControlTest(t, f => ({
+ entrypoint: `
+ var arr : array<array<i32, 8>, 8>;
+ ${f.expect_order(0)}
+ arr[a()][b()]++;
+ ${f.expect_order(3)}
+`,
+ extra: `
+fn a() -> i32 {
+ ${f.expect_order(1)}
+ return 1;
+}
+fn b() -> i32 {
+ ${f.expect_order(2)}
+ return 1;
+}
+`,
+ }));
+ });