Re-allow dynamic indexing of 'let' arrays and matrices

Spec change: https://github.com/gpuweb/gpuweb/pull/2427
Reverses: tint:867

This reverts and fixes commits:
 b6fdcc54df6e012578e69550788e2b4b2b611c32
 10442eff7db4271d53eed553795e655068488276

Added a bunch of end-to-end tests.

Fixed: tint:1352
Change-Id: I34968243bbec1cab838c8ba50a6f027146bbfd06
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/75401
Reviewed-by: David Neto <dneto@google.com>
Reviewed-by: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/test/expressions/index/let/let/literal/array.wgsl b/test/expressions/index/let/let/literal/array.wgsl
new file mode 100644
index 0000000..414d397
--- /dev/null
+++ b/test/expressions/index/let/let/literal/array.wgsl
@@ -0,0 +1,5 @@
+fn f() -> i32 {
+  let a = array<i32, 8>(1, 2, 3, 4, 5, 6, 7, 8);
+  let i = 1;
+  return a[i];
+}
diff --git a/test/expressions/index/let/let/literal/array.wgsl.expected.hlsl b/test/expressions/index/let/let/literal/array.wgsl.expected.hlsl
new file mode 100644
index 0000000..7174cfa
--- /dev/null
+++ b/test/expressions/index/let/let/literal/array.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+int f() {
+  const int a[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+  return a[1];
+}
diff --git a/test/expressions/index/let/let/literal/array.wgsl.expected.msl b/test/expressions/index/let/let/literal/array.wgsl.expected.msl
new file mode 100644
index 0000000..57bf97d
--- /dev/null
+++ b/test/expressions/index/let/let/literal/array.wgsl.expected.msl
@@ -0,0 +1,13 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct tint_array_wrapper {
+  int arr[8];
+};
+
+int f() {
+  tint_array_wrapper const a = {.arr={1, 2, 3, 4, 5, 6, 7, 8}};
+  int const i = 1;
+  return a.arr[i];
+}
+
diff --git a/test/expressions/index/let/let/literal/array.wgsl.expected.spvasm b/test/expressions/index/let/let/literal/array.wgsl.expected.spvasm
new file mode 100644
index 0000000..1dc9cda
--- /dev/null
+++ b/test/expressions/index/let/let/literal/array.wgsl.expected.spvasm
@@ -0,0 +1,37 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 22
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpDecorate %_arr_int_uint_8 ArrayStride 4
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %int
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_int_uint_8 = OpTypeArray %int %uint_8
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+      %int_4 = OpConstant %int 4
+      %int_5 = OpConstant %int 5
+      %int_6 = OpConstant %int 6
+      %int_7 = OpConstant %int 7
+      %int_8 = OpConstant %int 8
+         %20 = OpConstantComposite %_arr_int_uint_8 %int_1 %int_2 %int_3 %int_4 %int_5 %int_6 %int_7 %int_8
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %int None %5
+          %8 = OpLabel
+         %21 = OpCompositeExtract %int %20 1
+               OpReturnValue %21
+               OpFunctionEnd
diff --git a/test/expressions/index/let/let/literal/array.wgsl.expected.wgsl b/test/expressions/index/let/let/literal/array.wgsl.expected.wgsl
new file mode 100644
index 0000000..414d397
--- /dev/null
+++ b/test/expressions/index/let/let/literal/array.wgsl.expected.wgsl
@@ -0,0 +1,5 @@
+fn f() -> i32 {
+  let a = array<i32, 8>(1, 2, 3, 4, 5, 6, 7, 8);
+  let i = 1;
+  return a[i];
+}
diff --git a/test/expressions/index/let/let/literal/matrix.wgsl b/test/expressions/index/let/let/literal/matrix.wgsl
new file mode 100644
index 0000000..33b717d
--- /dev/null
+++ b/test/expressions/index/let/let/literal/matrix.wgsl
@@ -0,0 +1,5 @@
+fn f() -> vec3<f32> {
+  let m = mat3x3<f32>(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  let i = 1;
+  return m[i];
+}
diff --git a/test/expressions/index/let/let/literal/matrix.wgsl.expected.hlsl b/test/expressions/index/let/let/literal/matrix.wgsl.expected.hlsl
new file mode 100644
index 0000000..f209d03
--- /dev/null
+++ b/test/expressions/index/let/let/literal/matrix.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float3 f() {
+  const float3x3 m = float3x3(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f);
+  return m[1];
+}
diff --git a/test/expressions/index/let/let/literal/matrix.wgsl.expected.msl b/test/expressions/index/let/let/literal/matrix.wgsl.expected.msl
new file mode 100644
index 0000000..ca9238e
--- /dev/null
+++ b/test/expressions/index/let/let/literal/matrix.wgsl.expected.msl
@@ -0,0 +1,9 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float3 f() {
+  float3x3 const m = float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f));
+  int const i = 1;
+  return m[i];
+}
+
diff --git a/test/expressions/index/let/let/literal/matrix.wgsl.expected.spvasm b/test/expressions/index/let/let/literal/matrix.wgsl.expected.spvasm
new file mode 100644
index 0000000..0e46829
--- /dev/null
+++ b/test/expressions/index/let/let/literal/matrix.wgsl.expected.spvasm
@@ -0,0 +1,41 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 27
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+          %5 = OpTypeFunction %v3float
+%mat3v3float = OpTypeMatrix %v3float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+         %14 = OpConstantComposite %v3float %float_1 %float_2 %float_3
+    %float_4 = OpConstant %float 4
+    %float_5 = OpConstant %float 5
+    %float_6 = OpConstant %float 6
+         %18 = OpConstantComposite %v3float %float_4 %float_5 %float_6
+    %float_7 = OpConstant %float 7
+    %float_8 = OpConstant %float 8
+    %float_9 = OpConstant %float 9
+         %22 = OpConstantComposite %v3float %float_7 %float_8 %float_9
+         %23 = OpConstantComposite %mat3v3float %14 %18 %22
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %v3float None %5
+          %9 = OpLabel
+         %26 = OpCompositeExtract %v3float %23 1
+               OpReturnValue %26
+               OpFunctionEnd
diff --git a/test/expressions/index/let/let/literal/matrix.wgsl.expected.wgsl b/test/expressions/index/let/let/literal/matrix.wgsl.expected.wgsl
new file mode 100644
index 0000000..33b717d
--- /dev/null
+++ b/test/expressions/index/let/let/literal/matrix.wgsl.expected.wgsl
@@ -0,0 +1,5 @@
+fn f() -> vec3<f32> {
+  let m = mat3x3<f32>(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  let i = 1;
+  return m[i];
+}
diff --git a/test/expressions/index/let/let/literal/vector.wgsl b/test/expressions/index/let/let/literal/vector.wgsl
new file mode 100644
index 0000000..d113acb
--- /dev/null
+++ b/test/expressions/index/let/let/literal/vector.wgsl
@@ -0,0 +1,5 @@
+fn f() -> f32 {
+  let v = vec3<f32>(1.0, 2.0, 3.0);
+  let i = 1;
+  return v[i];
+}
diff --git a/test/expressions/index/let/let/literal/vector.wgsl.expected.hlsl b/test/expressions/index/let/let/literal/vector.wgsl.expected.hlsl
new file mode 100644
index 0000000..ede6118
--- /dev/null
+++ b/test/expressions/index/let/let/literal/vector.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float f() {
+  const float3 v = float3(1.0f, 2.0f, 3.0f);
+  return v[1];
+}
diff --git a/test/expressions/index/let/let/literal/vector.wgsl.expected.msl b/test/expressions/index/let/let/literal/vector.wgsl.expected.msl
new file mode 100644
index 0000000..587f5cf
--- /dev/null
+++ b/test/expressions/index/let/let/literal/vector.wgsl.expected.msl
@@ -0,0 +1,9 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float f() {
+  float3 const v = float3(1.0f, 2.0f, 3.0f);
+  int const i = 1;
+  return v[i];
+}
+
diff --git a/test/expressions/index/let/let/literal/vector.wgsl.expected.spvasm b/test/expressions/index/let/let/literal/vector.wgsl.expected.spvasm
new file mode 100644
index 0000000..d274e46
--- /dev/null
+++ b/test/expressions/index/let/let/literal/vector.wgsl.expected.spvasm
@@ -0,0 +1,31 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 17
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+          %5 = OpTypeFunction %float
+    %v3float = OpTypeVector %float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+         %13 = OpConstantComposite %v3float %float_1 %float_2 %float_3
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %float None %5
+          %8 = OpLabel
+         %16 = OpCompositeExtract %float %13 1
+               OpReturnValue %16
+               OpFunctionEnd
diff --git a/test/expressions/index/let/let/literal/vector.wgsl.expected.wgsl b/test/expressions/index/let/let/literal/vector.wgsl.expected.wgsl
new file mode 100644
index 0000000..d113acb
--- /dev/null
+++ b/test/expressions/index/let/let/literal/vector.wgsl.expected.wgsl
@@ -0,0 +1,5 @@
+fn f() -> f32 {
+  let v = vec3<f32>(1.0, 2.0, 3.0);
+  let i = 1;
+  return v[i];
+}
diff --git a/test/expressions/index/let/let/param/array.wgsl b/test/expressions/index/let/let/param/array.wgsl
new file mode 100644
index 0000000..b4f524b
--- /dev/null
+++ b/test/expressions/index/let/let/param/array.wgsl
@@ -0,0 +1,5 @@
+fn f(x : i32) -> i32 {
+  let a = array<i32, 8>(1, 2, 3, 4, 5, 6, 7, 8);
+  let i = x;
+  return a[i];
+}
diff --git a/test/expressions/index/let/let/param/array.wgsl.expected.hlsl b/test/expressions/index/let/let/param/array.wgsl.expected.hlsl
new file mode 100644
index 0000000..520cdf8
--- /dev/null
+++ b/test/expressions/index/let/let/param/array.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+int f(int x) {
+  const int a[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+  return a[x];
+}
diff --git a/test/expressions/index/let/let/param/array.wgsl.expected.msl b/test/expressions/index/let/let/param/array.wgsl.expected.msl
new file mode 100644
index 0000000..7b941e4
--- /dev/null
+++ b/test/expressions/index/let/let/param/array.wgsl.expected.msl
@@ -0,0 +1,13 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct tint_array_wrapper {
+  int arr[8];
+};
+
+int f(int x) {
+  tint_array_wrapper const a = {.arr={1, 2, 3, 4, 5, 6, 7, 8}};
+  int const i = x;
+  return a.arr[i];
+}
+
diff --git a/test/expressions/index/let/let/param/array.wgsl.expected.spvasm b/test/expressions/index/let/let/param/array.wgsl.expected.spvasm
new file mode 100644
index 0000000..57ee30c
--- /dev/null
+++ b/test/expressions/index/let/let/param/array.wgsl.expected.spvasm
@@ -0,0 +1,46 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 28
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %x "x"
+               OpName %var_for_index "var_for_index"
+               OpDecorate %_arr_int_uint_8 ArrayStride 4
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %int %int
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_int_uint_8 = OpTypeArray %int %uint_8
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+      %int_4 = OpConstant %int 4
+      %int_5 = OpConstant %int 5
+      %int_6 = OpConstant %int 6
+      %int_7 = OpConstant %int 7
+      %int_8 = OpConstant %int 8
+         %21 = OpConstantComposite %_arr_int_uint_8 %int_1 %int_2 %int_3 %int_4 %int_5 %int_6 %int_7 %int_8
+%_ptr_Function__arr_int_uint_8 = OpTypePointer Function %_arr_int_uint_8
+         %24 = OpConstantNull %_arr_int_uint_8
+%_ptr_Function_int = OpTypePointer Function %int
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %int None %5
+          %x = OpFunctionParameter %int
+          %9 = OpLabel
+%var_for_index = OpVariable %_ptr_Function__arr_int_uint_8 Function %24
+               OpStore %var_for_index %21
+         %26 = OpAccessChain %_ptr_Function_int %var_for_index %x
+         %27 = OpLoad %int %26
+               OpReturnValue %27
+               OpFunctionEnd
diff --git a/test/expressions/index/let/let/param/array.wgsl.expected.wgsl b/test/expressions/index/let/let/param/array.wgsl.expected.wgsl
new file mode 100644
index 0000000..b4f524b
--- /dev/null
+++ b/test/expressions/index/let/let/param/array.wgsl.expected.wgsl
@@ -0,0 +1,5 @@
+fn f(x : i32) -> i32 {
+  let a = array<i32, 8>(1, 2, 3, 4, 5, 6, 7, 8);
+  let i = x;
+  return a[i];
+}
diff --git a/test/expressions/index/let/let/param/matrix.wgsl b/test/expressions/index/let/let/param/matrix.wgsl
new file mode 100644
index 0000000..f9561bf
--- /dev/null
+++ b/test/expressions/index/let/let/param/matrix.wgsl
@@ -0,0 +1,5 @@
+fn f(x : i32) -> vec3<f32> {
+  let m = mat3x3<f32>(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  let i = x;
+  return m[i];
+}
diff --git a/test/expressions/index/let/let/param/matrix.wgsl.expected.hlsl b/test/expressions/index/let/let/param/matrix.wgsl.expected.hlsl
new file mode 100644
index 0000000..9dd1ad7
--- /dev/null
+++ b/test/expressions/index/let/let/param/matrix.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float3 f(int x) {
+  const float3x3 m = float3x3(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f);
+  return m[x];
+}
diff --git a/test/expressions/index/let/let/param/matrix.wgsl.expected.msl b/test/expressions/index/let/let/param/matrix.wgsl.expected.msl
new file mode 100644
index 0000000..647d17a
--- /dev/null
+++ b/test/expressions/index/let/let/param/matrix.wgsl.expected.msl
@@ -0,0 +1,9 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float3 f(int x) {
+  float3x3 const m = float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f));
+  int const i = x;
+  return m[i];
+}
+
diff --git a/test/expressions/index/let/let/param/matrix.wgsl.expected.spvasm b/test/expressions/index/let/let/param/matrix.wgsl.expected.spvasm
new file mode 100644
index 0000000..0d54e21
--- /dev/null
+++ b/test/expressions/index/let/let/param/matrix.wgsl.expected.spvasm
@@ -0,0 +1,49 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %x "x"
+               OpName %var_for_index "var_for_index"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %v3float %int
+%mat3v3float = OpTypeMatrix %v3float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+         %16 = OpConstantComposite %v3float %float_1 %float_2 %float_3
+    %float_4 = OpConstant %float 4
+    %float_5 = OpConstant %float 5
+    %float_6 = OpConstant %float 6
+         %20 = OpConstantComposite %v3float %float_4 %float_5 %float_6
+    %float_7 = OpConstant %float 7
+    %float_8 = OpConstant %float 8
+    %float_9 = OpConstant %float 9
+         %24 = OpConstantComposite %v3float %float_7 %float_8 %float_9
+         %25 = OpConstantComposite %mat3v3float %16 %20 %24
+%_ptr_Function_mat3v3float = OpTypePointer Function %mat3v3float
+         %28 = OpConstantNull %mat3v3float
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %v3float None %5
+          %x = OpFunctionParameter %int
+         %11 = OpLabel
+%var_for_index = OpVariable %_ptr_Function_mat3v3float Function %28
+               OpStore %var_for_index %25
+         %30 = OpAccessChain %_ptr_Function_v3float %var_for_index %x
+         %31 = OpLoad %v3float %30
+               OpReturnValue %31
+               OpFunctionEnd
diff --git a/test/expressions/index/let/let/param/matrix.wgsl.expected.wgsl b/test/expressions/index/let/let/param/matrix.wgsl.expected.wgsl
new file mode 100644
index 0000000..f9561bf
--- /dev/null
+++ b/test/expressions/index/let/let/param/matrix.wgsl.expected.wgsl
@@ -0,0 +1,5 @@
+fn f(x : i32) -> vec3<f32> {
+  let m = mat3x3<f32>(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  let i = x;
+  return m[i];
+}
diff --git a/test/expressions/index/let/let/param/vector.wgsl b/test/expressions/index/let/let/param/vector.wgsl
new file mode 100644
index 0000000..b23729a
--- /dev/null
+++ b/test/expressions/index/let/let/param/vector.wgsl
@@ -0,0 +1,5 @@
+fn f(x : i32) -> f32 {
+  let v = vec3<f32>(1.0, 2.0, 3.0);
+  let i = x;
+  return v[i];
+}
diff --git a/test/expressions/index/let/let/param/vector.wgsl.expected.hlsl b/test/expressions/index/let/let/param/vector.wgsl.expected.hlsl
new file mode 100644
index 0000000..5629258
--- /dev/null
+++ b/test/expressions/index/let/let/param/vector.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float f(int x) {
+  const float3 v = float3(1.0f, 2.0f, 3.0f);
+  return v[x];
+}
diff --git a/test/expressions/index/let/let/param/vector.wgsl.expected.msl b/test/expressions/index/let/let/param/vector.wgsl.expected.msl
new file mode 100644
index 0000000..b0d8ca9
--- /dev/null
+++ b/test/expressions/index/let/let/param/vector.wgsl.expected.msl
@@ -0,0 +1,9 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float f(int x) {
+  float3 const v = float3(1.0f, 2.0f, 3.0f);
+  int const i = x;
+  return v[i];
+}
+
diff --git a/test/expressions/index/let/let/param/vector.wgsl.expected.spvasm b/test/expressions/index/let/let/param/vector.wgsl.expected.spvasm
new file mode 100644
index 0000000..d1bfae5
--- /dev/null
+++ b/test/expressions/index/let/let/param/vector.wgsl.expected.spvasm
@@ -0,0 +1,32 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 17
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %x "x"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %float %int
+    %v3float = OpTypeVector %float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+         %15 = OpConstantComposite %v3float %float_1 %float_2 %float_3
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %float None %5
+          %x = OpFunctionParameter %int
+         %10 = OpLabel
+         %16 = OpVectorExtractDynamic %float %15 %x
+               OpReturnValue %16
+               OpFunctionEnd
diff --git a/test/expressions/index/let/let/param/vector.wgsl.expected.wgsl b/test/expressions/index/let/let/param/vector.wgsl.expected.wgsl
new file mode 100644
index 0000000..b23729a
--- /dev/null
+++ b/test/expressions/index/let/let/param/vector.wgsl.expected.wgsl
@@ -0,0 +1,5 @@
+fn f(x : i32) -> f32 {
+  let v = vec3<f32>(1.0, 2.0, 3.0);
+  let i = x;
+  return v[i];
+}
diff --git a/test/expressions/index/let/literal/array.wgsl b/test/expressions/index/let/literal/array.wgsl
new file mode 100644
index 0000000..1435ee7
--- /dev/null
+++ b/test/expressions/index/let/literal/array.wgsl
@@ -0,0 +1,4 @@
+fn f() -> i32 {
+  let a = array<i32, 8>(1, 2, 3, 4, 5, 6, 7, 8);
+  return a[1];
+}
diff --git a/test/expressions/index/let/literal/array.wgsl.expected.hlsl b/test/expressions/index/let/literal/array.wgsl.expected.hlsl
new file mode 100644
index 0000000..7174cfa
--- /dev/null
+++ b/test/expressions/index/let/literal/array.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+int f() {
+  const int a[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+  return a[1];
+}
diff --git a/test/expressions/index/let/literal/array.wgsl.expected.msl b/test/expressions/index/let/literal/array.wgsl.expected.msl
new file mode 100644
index 0000000..d4d28b1
--- /dev/null
+++ b/test/expressions/index/let/literal/array.wgsl.expected.msl
@@ -0,0 +1,12 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct tint_array_wrapper {
+  int arr[8];
+};
+
+int f() {
+  tint_array_wrapper const a = {.arr={1, 2, 3, 4, 5, 6, 7, 8}};
+  return a.arr[1];
+}
+
diff --git a/test/expressions/index/let/literal/array.wgsl.expected.spvasm b/test/expressions/index/let/literal/array.wgsl.expected.spvasm
new file mode 100644
index 0000000..1dc9cda
--- /dev/null
+++ b/test/expressions/index/let/literal/array.wgsl.expected.spvasm
@@ -0,0 +1,37 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 22
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpDecorate %_arr_int_uint_8 ArrayStride 4
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %int
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_int_uint_8 = OpTypeArray %int %uint_8
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+      %int_4 = OpConstant %int 4
+      %int_5 = OpConstant %int 5
+      %int_6 = OpConstant %int 6
+      %int_7 = OpConstant %int 7
+      %int_8 = OpConstant %int 8
+         %20 = OpConstantComposite %_arr_int_uint_8 %int_1 %int_2 %int_3 %int_4 %int_5 %int_6 %int_7 %int_8
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %int None %5
+          %8 = OpLabel
+         %21 = OpCompositeExtract %int %20 1
+               OpReturnValue %21
+               OpFunctionEnd
diff --git a/test/expressions/index/let/literal/array.wgsl.expected.wgsl b/test/expressions/index/let/literal/array.wgsl.expected.wgsl
new file mode 100644
index 0000000..1435ee7
--- /dev/null
+++ b/test/expressions/index/let/literal/array.wgsl.expected.wgsl
@@ -0,0 +1,4 @@
+fn f() -> i32 {
+  let a = array<i32, 8>(1, 2, 3, 4, 5, 6, 7, 8);
+  return a[1];
+}
diff --git a/test/expressions/index/let/literal/matrix.wgsl b/test/expressions/index/let/literal/matrix.wgsl
new file mode 100644
index 0000000..a6f9e38
--- /dev/null
+++ b/test/expressions/index/let/literal/matrix.wgsl
@@ -0,0 +1,4 @@
+fn f() -> vec3<f32> {
+  let m = mat3x3<f32>(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  return m[1];
+}
diff --git a/test/expressions/index/let/literal/matrix.wgsl.expected.hlsl b/test/expressions/index/let/literal/matrix.wgsl.expected.hlsl
new file mode 100644
index 0000000..f209d03
--- /dev/null
+++ b/test/expressions/index/let/literal/matrix.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float3 f() {
+  const float3x3 m = float3x3(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f);
+  return m[1];
+}
diff --git a/test/expressions/index/let/literal/matrix.wgsl.expected.msl b/test/expressions/index/let/literal/matrix.wgsl.expected.msl
new file mode 100644
index 0000000..92a23c4
--- /dev/null
+++ b/test/expressions/index/let/literal/matrix.wgsl.expected.msl
@@ -0,0 +1,8 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float3 f() {
+  float3x3 const m = float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f));
+  return m[1];
+}
+
diff --git a/test/expressions/index/let/literal/matrix.wgsl.expected.spvasm b/test/expressions/index/let/literal/matrix.wgsl.expected.spvasm
new file mode 100644
index 0000000..0e46829
--- /dev/null
+++ b/test/expressions/index/let/literal/matrix.wgsl.expected.spvasm
@@ -0,0 +1,41 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 27
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+          %5 = OpTypeFunction %v3float
+%mat3v3float = OpTypeMatrix %v3float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+         %14 = OpConstantComposite %v3float %float_1 %float_2 %float_3
+    %float_4 = OpConstant %float 4
+    %float_5 = OpConstant %float 5
+    %float_6 = OpConstant %float 6
+         %18 = OpConstantComposite %v3float %float_4 %float_5 %float_6
+    %float_7 = OpConstant %float 7
+    %float_8 = OpConstant %float 8
+    %float_9 = OpConstant %float 9
+         %22 = OpConstantComposite %v3float %float_7 %float_8 %float_9
+         %23 = OpConstantComposite %mat3v3float %14 %18 %22
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %v3float None %5
+          %9 = OpLabel
+         %26 = OpCompositeExtract %v3float %23 1
+               OpReturnValue %26
+               OpFunctionEnd
diff --git a/test/expressions/index/let/literal/matrix.wgsl.expected.wgsl b/test/expressions/index/let/literal/matrix.wgsl.expected.wgsl
new file mode 100644
index 0000000..a6f9e38
--- /dev/null
+++ b/test/expressions/index/let/literal/matrix.wgsl.expected.wgsl
@@ -0,0 +1,4 @@
+fn f() -> vec3<f32> {
+  let m = mat3x3<f32>(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  return m[1];
+}
diff --git a/test/expressions/index/let/literal/vector.wgsl b/test/expressions/index/let/literal/vector.wgsl
new file mode 100644
index 0000000..7749978
--- /dev/null
+++ b/test/expressions/index/let/literal/vector.wgsl
@@ -0,0 +1,4 @@
+fn f() -> f32 {
+  let v = vec3<f32>(1.0, 2.0, 3.0);
+  return v[1];
+}
diff --git a/test/expressions/index/let/literal/vector.wgsl.expected.hlsl b/test/expressions/index/let/literal/vector.wgsl.expected.hlsl
new file mode 100644
index 0000000..ede6118
--- /dev/null
+++ b/test/expressions/index/let/literal/vector.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float f() {
+  const float3 v = float3(1.0f, 2.0f, 3.0f);
+  return v[1];
+}
diff --git a/test/expressions/index/let/literal/vector.wgsl.expected.msl b/test/expressions/index/let/literal/vector.wgsl.expected.msl
new file mode 100644
index 0000000..94dc06a
--- /dev/null
+++ b/test/expressions/index/let/literal/vector.wgsl.expected.msl
@@ -0,0 +1,8 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float f() {
+  float3 const v = float3(1.0f, 2.0f, 3.0f);
+  return v[1];
+}
+
diff --git a/test/expressions/index/let/literal/vector.wgsl.expected.spvasm b/test/expressions/index/let/literal/vector.wgsl.expected.spvasm
new file mode 100644
index 0000000..d274e46
--- /dev/null
+++ b/test/expressions/index/let/literal/vector.wgsl.expected.spvasm
@@ -0,0 +1,31 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 17
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+          %5 = OpTypeFunction %float
+    %v3float = OpTypeVector %float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+         %13 = OpConstantComposite %v3float %float_1 %float_2 %float_3
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %float None %5
+          %8 = OpLabel
+         %16 = OpCompositeExtract %float %13 1
+               OpReturnValue %16
+               OpFunctionEnd
diff --git a/test/expressions/index/let/literal/vector.wgsl.expected.wgsl b/test/expressions/index/let/literal/vector.wgsl.expected.wgsl
new file mode 100644
index 0000000..7749978
--- /dev/null
+++ b/test/expressions/index/let/literal/vector.wgsl.expected.wgsl
@@ -0,0 +1,4 @@
+fn f() -> f32 {
+  let v = vec3<f32>(1.0, 2.0, 3.0);
+  return v[1];
+}
diff --git a/test/expressions/index/let/param/array.wgsl b/test/expressions/index/let/param/array.wgsl
new file mode 100644
index 0000000..12182e7
--- /dev/null
+++ b/test/expressions/index/let/param/array.wgsl
@@ -0,0 +1,4 @@
+fn f(i : i32) -> i32 {
+  let a = array<i32, 8>(1, 2, 3, 4, 5, 6, 7, 8);
+  return a[i];
+}
diff --git a/test/expressions/index/let/param/array.wgsl.expected.hlsl b/test/expressions/index/let/param/array.wgsl.expected.hlsl
new file mode 100644
index 0000000..28830b2
--- /dev/null
+++ b/test/expressions/index/let/param/array.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+int f(int i) {
+  const int a[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+  return a[i];
+}
diff --git a/test/expressions/index/let/param/array.wgsl.expected.msl b/test/expressions/index/let/param/array.wgsl.expected.msl
new file mode 100644
index 0000000..eac5c42
--- /dev/null
+++ b/test/expressions/index/let/param/array.wgsl.expected.msl
@@ -0,0 +1,12 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct tint_array_wrapper {
+  int arr[8];
+};
+
+int f(int i) {
+  tint_array_wrapper const a = {.arr={1, 2, 3, 4, 5, 6, 7, 8}};
+  return a.arr[i];
+}
+
diff --git a/test/expressions/index/let/param/array.wgsl.expected.spvasm b/test/expressions/index/let/param/array.wgsl.expected.spvasm
new file mode 100644
index 0000000..43c8d10
--- /dev/null
+++ b/test/expressions/index/let/param/array.wgsl.expected.spvasm
@@ -0,0 +1,46 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 28
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %i "i"
+               OpName %var_for_index "var_for_index"
+               OpDecorate %_arr_int_uint_8 ArrayStride 4
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %int %int
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_int_uint_8 = OpTypeArray %int %uint_8
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+      %int_4 = OpConstant %int 4
+      %int_5 = OpConstant %int 5
+      %int_6 = OpConstant %int 6
+      %int_7 = OpConstant %int 7
+      %int_8 = OpConstant %int 8
+         %21 = OpConstantComposite %_arr_int_uint_8 %int_1 %int_2 %int_3 %int_4 %int_5 %int_6 %int_7 %int_8
+%_ptr_Function__arr_int_uint_8 = OpTypePointer Function %_arr_int_uint_8
+         %24 = OpConstantNull %_arr_int_uint_8
+%_ptr_Function_int = OpTypePointer Function %int
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %int None %5
+          %i = OpFunctionParameter %int
+          %9 = OpLabel
+%var_for_index = OpVariable %_ptr_Function__arr_int_uint_8 Function %24
+               OpStore %var_for_index %21
+         %26 = OpAccessChain %_ptr_Function_int %var_for_index %i
+         %27 = OpLoad %int %26
+               OpReturnValue %27
+               OpFunctionEnd
diff --git a/test/expressions/index/let/param/array.wgsl.expected.wgsl b/test/expressions/index/let/param/array.wgsl.expected.wgsl
new file mode 100644
index 0000000..12182e7
--- /dev/null
+++ b/test/expressions/index/let/param/array.wgsl.expected.wgsl
@@ -0,0 +1,4 @@
+fn f(i : i32) -> i32 {
+  let a = array<i32, 8>(1, 2, 3, 4, 5, 6, 7, 8);
+  return a[i];
+}
diff --git a/test/expressions/index/let/param/matrix.wgsl b/test/expressions/index/let/param/matrix.wgsl
new file mode 100644
index 0000000..dc0cff2
--- /dev/null
+++ b/test/expressions/index/let/param/matrix.wgsl
@@ -0,0 +1,4 @@
+fn f(i : i32) -> vec3<f32> {
+  let m = mat3x3<f32>(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  return m[i];
+}
diff --git a/test/expressions/index/let/param/matrix.wgsl.expected.hlsl b/test/expressions/index/let/param/matrix.wgsl.expected.hlsl
new file mode 100644
index 0000000..5609c2a
--- /dev/null
+++ b/test/expressions/index/let/param/matrix.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float3 f(int i) {
+  const float3x3 m = float3x3(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f);
+  return m[i];
+}
diff --git a/test/expressions/index/let/param/matrix.wgsl.expected.msl b/test/expressions/index/let/param/matrix.wgsl.expected.msl
new file mode 100644
index 0000000..49218fd
--- /dev/null
+++ b/test/expressions/index/let/param/matrix.wgsl.expected.msl
@@ -0,0 +1,8 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float3 f(int i) {
+  float3x3 const m = float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f));
+  return m[i];
+}
+
diff --git a/test/expressions/index/let/param/matrix.wgsl.expected.spvasm b/test/expressions/index/let/param/matrix.wgsl.expected.spvasm
new file mode 100644
index 0000000..9a5d8aa
--- /dev/null
+++ b/test/expressions/index/let/param/matrix.wgsl.expected.spvasm
@@ -0,0 +1,49 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %i "i"
+               OpName %var_for_index "var_for_index"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %v3float %int
+%mat3v3float = OpTypeMatrix %v3float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+         %16 = OpConstantComposite %v3float %float_1 %float_2 %float_3
+    %float_4 = OpConstant %float 4
+    %float_5 = OpConstant %float 5
+    %float_6 = OpConstant %float 6
+         %20 = OpConstantComposite %v3float %float_4 %float_5 %float_6
+    %float_7 = OpConstant %float 7
+    %float_8 = OpConstant %float 8
+    %float_9 = OpConstant %float 9
+         %24 = OpConstantComposite %v3float %float_7 %float_8 %float_9
+         %25 = OpConstantComposite %mat3v3float %16 %20 %24
+%_ptr_Function_mat3v3float = OpTypePointer Function %mat3v3float
+         %28 = OpConstantNull %mat3v3float
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %v3float None %5
+          %i = OpFunctionParameter %int
+         %11 = OpLabel
+%var_for_index = OpVariable %_ptr_Function_mat3v3float Function %28
+               OpStore %var_for_index %25
+         %30 = OpAccessChain %_ptr_Function_v3float %var_for_index %i
+         %31 = OpLoad %v3float %30
+               OpReturnValue %31
+               OpFunctionEnd
diff --git a/test/expressions/index/let/param/matrix.wgsl.expected.wgsl b/test/expressions/index/let/param/matrix.wgsl.expected.wgsl
new file mode 100644
index 0000000..dc0cff2
--- /dev/null
+++ b/test/expressions/index/let/param/matrix.wgsl.expected.wgsl
@@ -0,0 +1,4 @@
+fn f(i : i32) -> vec3<f32> {
+  let m = mat3x3<f32>(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  return m[i];
+}
diff --git a/test/expressions/index/let/param/vector.wgsl b/test/expressions/index/let/param/vector.wgsl
new file mode 100644
index 0000000..1aef165
--- /dev/null
+++ b/test/expressions/index/let/param/vector.wgsl
@@ -0,0 +1,4 @@
+fn f(i : i32) -> f32 {
+  let v = vec3<f32>(1.0, 2.0, 3.0);
+  return v[i];
+}
diff --git a/test/expressions/index/let/param/vector.wgsl.expected.hlsl b/test/expressions/index/let/param/vector.wgsl.expected.hlsl
new file mode 100644
index 0000000..16cccbf
--- /dev/null
+++ b/test/expressions/index/let/param/vector.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float f(int i) {
+  const float3 v = float3(1.0f, 2.0f, 3.0f);
+  return v[i];
+}
diff --git a/test/expressions/index/let/param/vector.wgsl.expected.msl b/test/expressions/index/let/param/vector.wgsl.expected.msl
new file mode 100644
index 0000000..4d45110
--- /dev/null
+++ b/test/expressions/index/let/param/vector.wgsl.expected.msl
@@ -0,0 +1,8 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float f(int i) {
+  float3 const v = float3(1.0f, 2.0f, 3.0f);
+  return v[i];
+}
+
diff --git a/test/expressions/index/let/param/vector.wgsl.expected.spvasm b/test/expressions/index/let/param/vector.wgsl.expected.spvasm
new file mode 100644
index 0000000..96196dd
--- /dev/null
+++ b/test/expressions/index/let/param/vector.wgsl.expected.spvasm
@@ -0,0 +1,32 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 17
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %i "i"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %float %int
+    %v3float = OpTypeVector %float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+         %15 = OpConstantComposite %v3float %float_1 %float_2 %float_3
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %float None %5
+          %i = OpFunctionParameter %int
+         %10 = OpLabel
+         %16 = OpVectorExtractDynamic %float %15 %i
+               OpReturnValue %16
+               OpFunctionEnd
diff --git a/test/expressions/index/let/param/vector.wgsl.expected.wgsl b/test/expressions/index/let/param/vector.wgsl.expected.wgsl
new file mode 100644
index 0000000..1aef165
--- /dev/null
+++ b/test/expressions/index/let/param/vector.wgsl.expected.wgsl
@@ -0,0 +1,4 @@
+fn f(i : i32) -> f32 {
+  let v = vec3<f32>(1.0, 2.0, 3.0);
+  return v[i];
+}
diff --git a/test/expressions/index/let/var/literal/array.wgsl b/test/expressions/index/let/var/literal/array.wgsl
new file mode 100644
index 0000000..c1781aa
--- /dev/null
+++ b/test/expressions/index/let/var/literal/array.wgsl
@@ -0,0 +1,5 @@
+fn f() -> i32 {
+  var a = array<i32, 8>(1, 2, 3, 4, 5, 6, 7, 8);
+  let i = 1;
+  return a[i];
+}
diff --git a/test/expressions/index/let/var/literal/array.wgsl.expected.hlsl b/test/expressions/index/let/var/literal/array.wgsl.expected.hlsl
new file mode 100644
index 0000000..4338718
--- /dev/null
+++ b/test/expressions/index/let/var/literal/array.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+int f() {
+  int a[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+  return a[1];
+}
diff --git a/test/expressions/index/let/var/literal/array.wgsl.expected.msl b/test/expressions/index/let/var/literal/array.wgsl.expected.msl
new file mode 100644
index 0000000..808c688
--- /dev/null
+++ b/test/expressions/index/let/var/literal/array.wgsl.expected.msl
@@ -0,0 +1,13 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct tint_array_wrapper {
+  int arr[8];
+};
+
+int f() {
+  tint_array_wrapper a = {.arr={1, 2, 3, 4, 5, 6, 7, 8}};
+  int const i = 1;
+  return a.arr[i];
+}
+
diff --git a/test/expressions/index/let/var/literal/array.wgsl.expected.spvasm b/test/expressions/index/let/var/literal/array.wgsl.expected.spvasm
new file mode 100644
index 0000000..565f53e
--- /dev/null
+++ b/test/expressions/index/let/var/literal/array.wgsl.expected.spvasm
@@ -0,0 +1,44 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 27
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %a "a"
+               OpDecorate %_arr_int_uint_8 ArrayStride 4
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %int
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_int_uint_8 = OpTypeArray %int %uint_8
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+      %int_4 = OpConstant %int 4
+      %int_5 = OpConstant %int 5
+      %int_6 = OpConstant %int 6
+      %int_7 = OpConstant %int 7
+      %int_8 = OpConstant %int 8
+         %20 = OpConstantComposite %_arr_int_uint_8 %int_1 %int_2 %int_3 %int_4 %int_5 %int_6 %int_7 %int_8
+%_ptr_Function__arr_int_uint_8 = OpTypePointer Function %_arr_int_uint_8
+         %23 = OpConstantNull %_arr_int_uint_8
+%_ptr_Function_int = OpTypePointer Function %int
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %int None %5
+          %8 = OpLabel
+          %a = OpVariable %_ptr_Function__arr_int_uint_8 Function %23
+               OpStore %a %20
+         %25 = OpAccessChain %_ptr_Function_int %a %int_1
+         %26 = OpLoad %int %25
+               OpReturnValue %26
+               OpFunctionEnd
diff --git a/test/expressions/index/let/var/literal/array.wgsl.expected.wgsl b/test/expressions/index/let/var/literal/array.wgsl.expected.wgsl
new file mode 100644
index 0000000..c1781aa
--- /dev/null
+++ b/test/expressions/index/let/var/literal/array.wgsl.expected.wgsl
@@ -0,0 +1,5 @@
+fn f() -> i32 {
+  var a = array<i32, 8>(1, 2, 3, 4, 5, 6, 7, 8);
+  let i = 1;
+  return a[i];
+}
diff --git a/test/expressions/index/let/var/literal/matrix.wgsl b/test/expressions/index/let/var/literal/matrix.wgsl
new file mode 100644
index 0000000..a20885c
--- /dev/null
+++ b/test/expressions/index/let/var/literal/matrix.wgsl
@@ -0,0 +1,5 @@
+fn f() -> vec3<f32> {
+  var m = mat3x3<f32>(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  let i = 1;
+  return m[i];
+}
diff --git a/test/expressions/index/let/var/literal/matrix.wgsl.expected.hlsl b/test/expressions/index/let/var/literal/matrix.wgsl.expected.hlsl
new file mode 100644
index 0000000..46a7995
--- /dev/null
+++ b/test/expressions/index/let/var/literal/matrix.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float3 f() {
+  float3x3 m = float3x3(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f);
+  return m[1];
+}
diff --git a/test/expressions/index/let/var/literal/matrix.wgsl.expected.msl b/test/expressions/index/let/var/literal/matrix.wgsl.expected.msl
new file mode 100644
index 0000000..0536bc1
--- /dev/null
+++ b/test/expressions/index/let/var/literal/matrix.wgsl.expected.msl
@@ -0,0 +1,9 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float3 f() {
+  float3x3 m = float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f));
+  int const i = 1;
+  return m[i];
+}
+
diff --git a/test/expressions/index/let/var/literal/matrix.wgsl.expected.spvasm b/test/expressions/index/let/var/literal/matrix.wgsl.expected.spvasm
new file mode 100644
index 0000000..be19a25
--- /dev/null
+++ b/test/expressions/index/let/var/literal/matrix.wgsl.expected.spvasm
@@ -0,0 +1,48 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %m "m"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+          %5 = OpTypeFunction %v3float
+%mat3v3float = OpTypeMatrix %v3float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+         %14 = OpConstantComposite %v3float %float_1 %float_2 %float_3
+    %float_4 = OpConstant %float 4
+    %float_5 = OpConstant %float 5
+    %float_6 = OpConstant %float 6
+         %18 = OpConstantComposite %v3float %float_4 %float_5 %float_6
+    %float_7 = OpConstant %float 7
+    %float_8 = OpConstant %float 8
+    %float_9 = OpConstant %float 9
+         %22 = OpConstantComposite %v3float %float_7 %float_8 %float_9
+         %23 = OpConstantComposite %mat3v3float %14 %18 %22
+%_ptr_Function_mat3v3float = OpTypePointer Function %mat3v3float
+         %26 = OpConstantNull %mat3v3float
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %v3float None %5
+          %9 = OpLabel
+          %m = OpVariable %_ptr_Function_mat3v3float Function %26
+               OpStore %m %23
+         %30 = OpAccessChain %_ptr_Function_v3float %m %int_1
+         %31 = OpLoad %v3float %30
+               OpReturnValue %31
+               OpFunctionEnd
diff --git a/test/expressions/index/let/var/literal/matrix.wgsl.expected.wgsl b/test/expressions/index/let/var/literal/matrix.wgsl.expected.wgsl
new file mode 100644
index 0000000..a20885c
--- /dev/null
+++ b/test/expressions/index/let/var/literal/matrix.wgsl.expected.wgsl
@@ -0,0 +1,5 @@
+fn f() -> vec3<f32> {
+  var m = mat3x3<f32>(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  let i = 1;
+  return m[i];
+}
diff --git a/test/expressions/index/let/var/literal/vector.wgsl b/test/expressions/index/let/var/literal/vector.wgsl
new file mode 100644
index 0000000..74962a8
--- /dev/null
+++ b/test/expressions/index/let/var/literal/vector.wgsl
@@ -0,0 +1,5 @@
+fn f() -> f32 {
+  var v = vec3<f32>(1.0, 2.0, 3.0);
+  let i = 1;
+  return v[i];
+}
diff --git a/test/expressions/index/let/var/literal/vector.wgsl.expected.hlsl b/test/expressions/index/let/var/literal/vector.wgsl.expected.hlsl
new file mode 100644
index 0000000..7297f63
--- /dev/null
+++ b/test/expressions/index/let/var/literal/vector.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float f() {
+  float3 v = float3(1.0f, 2.0f, 3.0f);
+  return v[1];
+}
diff --git a/test/expressions/index/let/var/literal/vector.wgsl.expected.msl b/test/expressions/index/let/var/literal/vector.wgsl.expected.msl
new file mode 100644
index 0000000..07ca4fb
--- /dev/null
+++ b/test/expressions/index/let/var/literal/vector.wgsl.expected.msl
@@ -0,0 +1,9 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float f() {
+  float3 v = float3(1.0f, 2.0f, 3.0f);
+  int const i = 1;
+  return v[i];
+}
+
diff --git a/test/expressions/index/let/var/literal/vector.wgsl.expected.spvasm b/test/expressions/index/let/var/literal/vector.wgsl.expected.spvasm
new file mode 100644
index 0000000..fe71fc3
--- /dev/null
+++ b/test/expressions/index/let/var/literal/vector.wgsl.expected.spvasm
@@ -0,0 +1,38 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 22
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %v "v"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+          %5 = OpTypeFunction %float
+    %v3float = OpTypeVector %float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+         %13 = OpConstantComposite %v3float %float_1 %float_2 %float_3
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+         %16 = OpConstantNull %v3float
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+%_ptr_Function_float = OpTypePointer Function %float
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %float None %5
+          %8 = OpLabel
+          %v = OpVariable %_ptr_Function_v3float Function %16
+               OpStore %v %13
+         %20 = OpAccessChain %_ptr_Function_float %v %int_1
+         %21 = OpLoad %float %20
+               OpReturnValue %21
+               OpFunctionEnd
diff --git a/test/expressions/index/let/var/literal/vector.wgsl.expected.wgsl b/test/expressions/index/let/var/literal/vector.wgsl.expected.wgsl
new file mode 100644
index 0000000..74962a8
--- /dev/null
+++ b/test/expressions/index/let/var/literal/vector.wgsl.expected.wgsl
@@ -0,0 +1,5 @@
+fn f() -> f32 {
+  var v = vec3<f32>(1.0, 2.0, 3.0);
+  let i = 1;
+  return v[i];
+}
diff --git a/test/expressions/index/var/let/literal/array.wgsl b/test/expressions/index/var/let/literal/array.wgsl
new file mode 100644
index 0000000..c1781aa
--- /dev/null
+++ b/test/expressions/index/var/let/literal/array.wgsl
@@ -0,0 +1,5 @@
+fn f() -> i32 {
+  var a = array<i32, 8>(1, 2, 3, 4, 5, 6, 7, 8);
+  let i = 1;
+  return a[i];
+}
diff --git a/test/expressions/index/var/let/literal/array.wgsl.expected.hlsl b/test/expressions/index/var/let/literal/array.wgsl.expected.hlsl
new file mode 100644
index 0000000..4338718
--- /dev/null
+++ b/test/expressions/index/var/let/literal/array.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+int f() {
+  int a[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+  return a[1];
+}
diff --git a/test/expressions/index/var/let/literal/array.wgsl.expected.msl b/test/expressions/index/var/let/literal/array.wgsl.expected.msl
new file mode 100644
index 0000000..808c688
--- /dev/null
+++ b/test/expressions/index/var/let/literal/array.wgsl.expected.msl
@@ -0,0 +1,13 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct tint_array_wrapper {
+  int arr[8];
+};
+
+int f() {
+  tint_array_wrapper a = {.arr={1, 2, 3, 4, 5, 6, 7, 8}};
+  int const i = 1;
+  return a.arr[i];
+}
+
diff --git a/test/expressions/index/var/let/literal/array.wgsl.expected.spvasm b/test/expressions/index/var/let/literal/array.wgsl.expected.spvasm
new file mode 100644
index 0000000..565f53e
--- /dev/null
+++ b/test/expressions/index/var/let/literal/array.wgsl.expected.spvasm
@@ -0,0 +1,44 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 27
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %a "a"
+               OpDecorate %_arr_int_uint_8 ArrayStride 4
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %int
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_int_uint_8 = OpTypeArray %int %uint_8
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+      %int_4 = OpConstant %int 4
+      %int_5 = OpConstant %int 5
+      %int_6 = OpConstant %int 6
+      %int_7 = OpConstant %int 7
+      %int_8 = OpConstant %int 8
+         %20 = OpConstantComposite %_arr_int_uint_8 %int_1 %int_2 %int_3 %int_4 %int_5 %int_6 %int_7 %int_8
+%_ptr_Function__arr_int_uint_8 = OpTypePointer Function %_arr_int_uint_8
+         %23 = OpConstantNull %_arr_int_uint_8
+%_ptr_Function_int = OpTypePointer Function %int
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %int None %5
+          %8 = OpLabel
+          %a = OpVariable %_ptr_Function__arr_int_uint_8 Function %23
+               OpStore %a %20
+         %25 = OpAccessChain %_ptr_Function_int %a %int_1
+         %26 = OpLoad %int %25
+               OpReturnValue %26
+               OpFunctionEnd
diff --git a/test/expressions/index/var/let/literal/array.wgsl.expected.wgsl b/test/expressions/index/var/let/literal/array.wgsl.expected.wgsl
new file mode 100644
index 0000000..c1781aa
--- /dev/null
+++ b/test/expressions/index/var/let/literal/array.wgsl.expected.wgsl
@@ -0,0 +1,5 @@
+fn f() -> i32 {
+  var a = array<i32, 8>(1, 2, 3, 4, 5, 6, 7, 8);
+  let i = 1;
+  return a[i];
+}
diff --git a/test/expressions/index/var/let/literal/matrix.wgsl b/test/expressions/index/var/let/literal/matrix.wgsl
new file mode 100644
index 0000000..a20885c
--- /dev/null
+++ b/test/expressions/index/var/let/literal/matrix.wgsl
@@ -0,0 +1,5 @@
+fn f() -> vec3<f32> {
+  var m = mat3x3<f32>(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  let i = 1;
+  return m[i];
+}
diff --git a/test/expressions/index/var/let/literal/matrix.wgsl.expected.hlsl b/test/expressions/index/var/let/literal/matrix.wgsl.expected.hlsl
new file mode 100644
index 0000000..46a7995
--- /dev/null
+++ b/test/expressions/index/var/let/literal/matrix.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float3 f() {
+  float3x3 m = float3x3(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f);
+  return m[1];
+}
diff --git a/test/expressions/index/var/let/literal/matrix.wgsl.expected.msl b/test/expressions/index/var/let/literal/matrix.wgsl.expected.msl
new file mode 100644
index 0000000..0536bc1
--- /dev/null
+++ b/test/expressions/index/var/let/literal/matrix.wgsl.expected.msl
@@ -0,0 +1,9 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float3 f() {
+  float3x3 m = float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f));
+  int const i = 1;
+  return m[i];
+}
+
diff --git a/test/expressions/index/var/let/literal/matrix.wgsl.expected.spvasm b/test/expressions/index/var/let/literal/matrix.wgsl.expected.spvasm
new file mode 100644
index 0000000..be19a25
--- /dev/null
+++ b/test/expressions/index/var/let/literal/matrix.wgsl.expected.spvasm
@@ -0,0 +1,48 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %m "m"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+          %5 = OpTypeFunction %v3float
+%mat3v3float = OpTypeMatrix %v3float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+         %14 = OpConstantComposite %v3float %float_1 %float_2 %float_3
+    %float_4 = OpConstant %float 4
+    %float_5 = OpConstant %float 5
+    %float_6 = OpConstant %float 6
+         %18 = OpConstantComposite %v3float %float_4 %float_5 %float_6
+    %float_7 = OpConstant %float 7
+    %float_8 = OpConstant %float 8
+    %float_9 = OpConstant %float 9
+         %22 = OpConstantComposite %v3float %float_7 %float_8 %float_9
+         %23 = OpConstantComposite %mat3v3float %14 %18 %22
+%_ptr_Function_mat3v3float = OpTypePointer Function %mat3v3float
+         %26 = OpConstantNull %mat3v3float
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %v3float None %5
+          %9 = OpLabel
+          %m = OpVariable %_ptr_Function_mat3v3float Function %26
+               OpStore %m %23
+         %30 = OpAccessChain %_ptr_Function_v3float %m %int_1
+         %31 = OpLoad %v3float %30
+               OpReturnValue %31
+               OpFunctionEnd
diff --git a/test/expressions/index/var/let/literal/matrix.wgsl.expected.wgsl b/test/expressions/index/var/let/literal/matrix.wgsl.expected.wgsl
new file mode 100644
index 0000000..a20885c
--- /dev/null
+++ b/test/expressions/index/var/let/literal/matrix.wgsl.expected.wgsl
@@ -0,0 +1,5 @@
+fn f() -> vec3<f32> {
+  var m = mat3x3<f32>(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  let i = 1;
+  return m[i];
+}
diff --git a/test/expressions/index/var/let/literal/vector.wgsl b/test/expressions/index/var/let/literal/vector.wgsl
new file mode 100644
index 0000000..74962a8
--- /dev/null
+++ b/test/expressions/index/var/let/literal/vector.wgsl
@@ -0,0 +1,5 @@
+fn f() -> f32 {
+  var v = vec3<f32>(1.0, 2.0, 3.0);
+  let i = 1;
+  return v[i];
+}
diff --git a/test/expressions/index/var/let/literal/vector.wgsl.expected.hlsl b/test/expressions/index/var/let/literal/vector.wgsl.expected.hlsl
new file mode 100644
index 0000000..7297f63
--- /dev/null
+++ b/test/expressions/index/var/let/literal/vector.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float f() {
+  float3 v = float3(1.0f, 2.0f, 3.0f);
+  return v[1];
+}
diff --git a/test/expressions/index/var/let/literal/vector.wgsl.expected.msl b/test/expressions/index/var/let/literal/vector.wgsl.expected.msl
new file mode 100644
index 0000000..07ca4fb
--- /dev/null
+++ b/test/expressions/index/var/let/literal/vector.wgsl.expected.msl
@@ -0,0 +1,9 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float f() {
+  float3 v = float3(1.0f, 2.0f, 3.0f);
+  int const i = 1;
+  return v[i];
+}
+
diff --git a/test/expressions/index/var/let/literal/vector.wgsl.expected.spvasm b/test/expressions/index/var/let/literal/vector.wgsl.expected.spvasm
new file mode 100644
index 0000000..fe71fc3
--- /dev/null
+++ b/test/expressions/index/var/let/literal/vector.wgsl.expected.spvasm
@@ -0,0 +1,38 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 22
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %v "v"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+          %5 = OpTypeFunction %float
+    %v3float = OpTypeVector %float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+         %13 = OpConstantComposite %v3float %float_1 %float_2 %float_3
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+         %16 = OpConstantNull %v3float
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+%_ptr_Function_float = OpTypePointer Function %float
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %float None %5
+          %8 = OpLabel
+          %v = OpVariable %_ptr_Function_v3float Function %16
+               OpStore %v %13
+         %20 = OpAccessChain %_ptr_Function_float %v %int_1
+         %21 = OpLoad %float %20
+               OpReturnValue %21
+               OpFunctionEnd
diff --git a/test/expressions/index/var/let/literal/vector.wgsl.expected.wgsl b/test/expressions/index/var/let/literal/vector.wgsl.expected.wgsl
new file mode 100644
index 0000000..74962a8
--- /dev/null
+++ b/test/expressions/index/var/let/literal/vector.wgsl.expected.wgsl
@@ -0,0 +1,5 @@
+fn f() -> f32 {
+  var v = vec3<f32>(1.0, 2.0, 3.0);
+  let i = 1;
+  return v[i];
+}
diff --git a/test/expressions/index/var/let/param/array.wgsl b/test/expressions/index/var/let/param/array.wgsl
new file mode 100644
index 0000000..ace4c30
--- /dev/null
+++ b/test/expressions/index/var/let/param/array.wgsl
@@ -0,0 +1,5 @@
+fn f(x : i32) -> i32 {
+  var a = array<i32, 8>(1, 2, 3, 4, 5, 6, 7, 8);
+  let i = x;
+  return a[i];
+}
diff --git a/test/expressions/index/var/let/param/array.wgsl.expected.hlsl b/test/expressions/index/var/let/param/array.wgsl.expected.hlsl
new file mode 100644
index 0000000..09ebc7d
--- /dev/null
+++ b/test/expressions/index/var/let/param/array.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+int f(int x) {
+  int a[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+  return a[x];
+}
diff --git a/test/expressions/index/var/let/param/array.wgsl.expected.msl b/test/expressions/index/var/let/param/array.wgsl.expected.msl
new file mode 100644
index 0000000..b1e207d
--- /dev/null
+++ b/test/expressions/index/var/let/param/array.wgsl.expected.msl
@@ -0,0 +1,13 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct tint_array_wrapper {
+  int arr[8];
+};
+
+int f(int x) {
+  tint_array_wrapper a = {.arr={1, 2, 3, 4, 5, 6, 7, 8}};
+  int const i = x;
+  return a.arr[i];
+}
+
diff --git a/test/expressions/index/var/let/param/array.wgsl.expected.spvasm b/test/expressions/index/var/let/param/array.wgsl.expected.spvasm
new file mode 100644
index 0000000..ca155d8
--- /dev/null
+++ b/test/expressions/index/var/let/param/array.wgsl.expected.spvasm
@@ -0,0 +1,46 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 28
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %x "x"
+               OpName %a "a"
+               OpDecorate %_arr_int_uint_8 ArrayStride 4
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %int %int
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_int_uint_8 = OpTypeArray %int %uint_8
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+      %int_4 = OpConstant %int 4
+      %int_5 = OpConstant %int 5
+      %int_6 = OpConstant %int 6
+      %int_7 = OpConstant %int 7
+      %int_8 = OpConstant %int 8
+         %21 = OpConstantComposite %_arr_int_uint_8 %int_1 %int_2 %int_3 %int_4 %int_5 %int_6 %int_7 %int_8
+%_ptr_Function__arr_int_uint_8 = OpTypePointer Function %_arr_int_uint_8
+         %24 = OpConstantNull %_arr_int_uint_8
+%_ptr_Function_int = OpTypePointer Function %int
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %int None %5
+          %x = OpFunctionParameter %int
+          %9 = OpLabel
+          %a = OpVariable %_ptr_Function__arr_int_uint_8 Function %24
+               OpStore %a %21
+         %26 = OpAccessChain %_ptr_Function_int %a %x
+         %27 = OpLoad %int %26
+               OpReturnValue %27
+               OpFunctionEnd
diff --git a/test/expressions/index/var/let/param/array.wgsl.expected.wgsl b/test/expressions/index/var/let/param/array.wgsl.expected.wgsl
new file mode 100644
index 0000000..ace4c30
--- /dev/null
+++ b/test/expressions/index/var/let/param/array.wgsl.expected.wgsl
@@ -0,0 +1,5 @@
+fn f(x : i32) -> i32 {
+  var a = array<i32, 8>(1, 2, 3, 4, 5, 6, 7, 8);
+  let i = x;
+  return a[i];
+}
diff --git a/test/expressions/index/var/let/param/matrix.wgsl b/test/expressions/index/var/let/param/matrix.wgsl
new file mode 100644
index 0000000..7b9043f
--- /dev/null
+++ b/test/expressions/index/var/let/param/matrix.wgsl
@@ -0,0 +1,5 @@
+fn f(x : i32) -> vec3<f32> {
+  var m = mat3x3<f32>(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  let i = x;
+  return m[i];
+}
diff --git a/test/expressions/index/var/let/param/matrix.wgsl.expected.hlsl b/test/expressions/index/var/let/param/matrix.wgsl.expected.hlsl
new file mode 100644
index 0000000..0920c91
--- /dev/null
+++ b/test/expressions/index/var/let/param/matrix.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float3 f(int x) {
+  float3x3 m = float3x3(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f);
+  return m[x];
+}
diff --git a/test/expressions/index/var/let/param/matrix.wgsl.expected.msl b/test/expressions/index/var/let/param/matrix.wgsl.expected.msl
new file mode 100644
index 0000000..f282e52
--- /dev/null
+++ b/test/expressions/index/var/let/param/matrix.wgsl.expected.msl
@@ -0,0 +1,9 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float3 f(int x) {
+  float3x3 m = float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f));
+  int const i = x;
+  return m[i];
+}
+
diff --git a/test/expressions/index/var/let/param/matrix.wgsl.expected.spvasm b/test/expressions/index/var/let/param/matrix.wgsl.expected.spvasm
new file mode 100644
index 0000000..6ba9971
--- /dev/null
+++ b/test/expressions/index/var/let/param/matrix.wgsl.expected.spvasm
@@ -0,0 +1,49 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %x "x"
+               OpName %m "m"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %v3float %int
+%mat3v3float = OpTypeMatrix %v3float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+         %16 = OpConstantComposite %v3float %float_1 %float_2 %float_3
+    %float_4 = OpConstant %float 4
+    %float_5 = OpConstant %float 5
+    %float_6 = OpConstant %float 6
+         %20 = OpConstantComposite %v3float %float_4 %float_5 %float_6
+    %float_7 = OpConstant %float 7
+    %float_8 = OpConstant %float 8
+    %float_9 = OpConstant %float 9
+         %24 = OpConstantComposite %v3float %float_7 %float_8 %float_9
+         %25 = OpConstantComposite %mat3v3float %16 %20 %24
+%_ptr_Function_mat3v3float = OpTypePointer Function %mat3v3float
+         %28 = OpConstantNull %mat3v3float
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %v3float None %5
+          %x = OpFunctionParameter %int
+         %11 = OpLabel
+          %m = OpVariable %_ptr_Function_mat3v3float Function %28
+               OpStore %m %25
+         %30 = OpAccessChain %_ptr_Function_v3float %m %x
+         %31 = OpLoad %v3float %30
+               OpReturnValue %31
+               OpFunctionEnd
diff --git a/test/expressions/index/var/let/param/matrix.wgsl.expected.wgsl b/test/expressions/index/var/let/param/matrix.wgsl.expected.wgsl
new file mode 100644
index 0000000..7b9043f
--- /dev/null
+++ b/test/expressions/index/var/let/param/matrix.wgsl.expected.wgsl
@@ -0,0 +1,5 @@
+fn f(x : i32) -> vec3<f32> {
+  var m = mat3x3<f32>(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  let i = x;
+  return m[i];
+}
diff --git a/test/expressions/index/var/let/param/vector.wgsl b/test/expressions/index/var/let/param/vector.wgsl
new file mode 100644
index 0000000..f3e969b
--- /dev/null
+++ b/test/expressions/index/var/let/param/vector.wgsl
@@ -0,0 +1,5 @@
+fn f(x : i32) -> f32 {
+  var v = vec3<f32>(1.0, 2.0, 3.0);
+  let i = x;
+  return v[i];
+}
diff --git a/test/expressions/index/var/let/param/vector.wgsl.expected.hlsl b/test/expressions/index/var/let/param/vector.wgsl.expected.hlsl
new file mode 100644
index 0000000..590b92e
--- /dev/null
+++ b/test/expressions/index/var/let/param/vector.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float f(int x) {
+  float3 v = float3(1.0f, 2.0f, 3.0f);
+  return v[x];
+}
diff --git a/test/expressions/index/var/let/param/vector.wgsl.expected.msl b/test/expressions/index/var/let/param/vector.wgsl.expected.msl
new file mode 100644
index 0000000..374fe2c
--- /dev/null
+++ b/test/expressions/index/var/let/param/vector.wgsl.expected.msl
@@ -0,0 +1,9 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float f(int x) {
+  float3 v = float3(1.0f, 2.0f, 3.0f);
+  int const i = x;
+  return v[i];
+}
+
diff --git a/test/expressions/index/var/let/param/vector.wgsl.expected.spvasm b/test/expressions/index/var/let/param/vector.wgsl.expected.spvasm
new file mode 100644
index 0000000..16da21e
--- /dev/null
+++ b/test/expressions/index/var/let/param/vector.wgsl.expected.spvasm
@@ -0,0 +1,39 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 22
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %x "x"
+               OpName %v "v"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %float %int
+    %v3float = OpTypeVector %float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+         %15 = OpConstantComposite %v3float %float_1 %float_2 %float_3
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+         %18 = OpConstantNull %v3float
+%_ptr_Function_float = OpTypePointer Function %float
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %float None %5
+          %x = OpFunctionParameter %int
+         %10 = OpLabel
+          %v = OpVariable %_ptr_Function_v3float Function %18
+               OpStore %v %15
+         %20 = OpAccessChain %_ptr_Function_float %v %x
+         %21 = OpLoad %float %20
+               OpReturnValue %21
+               OpFunctionEnd
diff --git a/test/expressions/index/var/let/param/vector.wgsl.expected.wgsl b/test/expressions/index/var/let/param/vector.wgsl.expected.wgsl
new file mode 100644
index 0000000..f3e969b
--- /dev/null
+++ b/test/expressions/index/var/let/param/vector.wgsl.expected.wgsl
@@ -0,0 +1,5 @@
+fn f(x : i32) -> f32 {
+  var v = vec3<f32>(1.0, 2.0, 3.0);
+  let i = x;
+  return v[i];
+}
diff --git a/test/expressions/index/var/literal/array.wgsl b/test/expressions/index/var/literal/array.wgsl
new file mode 100644
index 0000000..ea2f45d
--- /dev/null
+++ b/test/expressions/index/var/literal/array.wgsl
@@ -0,0 +1,4 @@
+fn f() -> i32 {
+  var a = array<i32, 8>(1, 2, 3, 4, 5, 6, 7, 8);
+  return a[1];
+}
diff --git a/test/expressions/index/var/literal/array.wgsl.expected.hlsl b/test/expressions/index/var/literal/array.wgsl.expected.hlsl
new file mode 100644
index 0000000..4338718
--- /dev/null
+++ b/test/expressions/index/var/literal/array.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+int f() {
+  int a[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+  return a[1];
+}
diff --git a/test/expressions/index/var/literal/array.wgsl.expected.msl b/test/expressions/index/var/literal/array.wgsl.expected.msl
new file mode 100644
index 0000000..ae2d9ca
--- /dev/null
+++ b/test/expressions/index/var/literal/array.wgsl.expected.msl
@@ -0,0 +1,12 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct tint_array_wrapper {
+  int arr[8];
+};
+
+int f() {
+  tint_array_wrapper a = {.arr={1, 2, 3, 4, 5, 6, 7, 8}};
+  return a.arr[1];
+}
+
diff --git a/test/expressions/index/var/literal/array.wgsl.expected.spvasm b/test/expressions/index/var/literal/array.wgsl.expected.spvasm
new file mode 100644
index 0000000..565f53e
--- /dev/null
+++ b/test/expressions/index/var/literal/array.wgsl.expected.spvasm
@@ -0,0 +1,44 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 27
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %a "a"
+               OpDecorate %_arr_int_uint_8 ArrayStride 4
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %int
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_int_uint_8 = OpTypeArray %int %uint_8
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+      %int_4 = OpConstant %int 4
+      %int_5 = OpConstant %int 5
+      %int_6 = OpConstant %int 6
+      %int_7 = OpConstant %int 7
+      %int_8 = OpConstant %int 8
+         %20 = OpConstantComposite %_arr_int_uint_8 %int_1 %int_2 %int_3 %int_4 %int_5 %int_6 %int_7 %int_8
+%_ptr_Function__arr_int_uint_8 = OpTypePointer Function %_arr_int_uint_8
+         %23 = OpConstantNull %_arr_int_uint_8
+%_ptr_Function_int = OpTypePointer Function %int
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %int None %5
+          %8 = OpLabel
+          %a = OpVariable %_ptr_Function__arr_int_uint_8 Function %23
+               OpStore %a %20
+         %25 = OpAccessChain %_ptr_Function_int %a %int_1
+         %26 = OpLoad %int %25
+               OpReturnValue %26
+               OpFunctionEnd
diff --git a/test/expressions/index/var/literal/array.wgsl.expected.wgsl b/test/expressions/index/var/literal/array.wgsl.expected.wgsl
new file mode 100644
index 0000000..ea2f45d
--- /dev/null
+++ b/test/expressions/index/var/literal/array.wgsl.expected.wgsl
@@ -0,0 +1,4 @@
+fn f() -> i32 {
+  var a = array<i32, 8>(1, 2, 3, 4, 5, 6, 7, 8);
+  return a[1];
+}
diff --git a/test/expressions/index/var/literal/matrix.wgsl b/test/expressions/index/var/literal/matrix.wgsl
new file mode 100644
index 0000000..8043ae4
--- /dev/null
+++ b/test/expressions/index/var/literal/matrix.wgsl
@@ -0,0 +1,4 @@
+fn f() -> vec3<f32> {
+  var m = mat3x3<f32>(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  return m[1];
+}
diff --git a/test/expressions/index/var/literal/matrix.wgsl.expected.hlsl b/test/expressions/index/var/literal/matrix.wgsl.expected.hlsl
new file mode 100644
index 0000000..46a7995
--- /dev/null
+++ b/test/expressions/index/var/literal/matrix.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float3 f() {
+  float3x3 m = float3x3(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f);
+  return m[1];
+}
diff --git a/test/expressions/index/var/literal/matrix.wgsl.expected.msl b/test/expressions/index/var/literal/matrix.wgsl.expected.msl
new file mode 100644
index 0000000..e37137b
--- /dev/null
+++ b/test/expressions/index/var/literal/matrix.wgsl.expected.msl
@@ -0,0 +1,8 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float3 f() {
+  float3x3 m = float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f));
+  return m[1];
+}
+
diff --git a/test/expressions/index/var/literal/matrix.wgsl.expected.spvasm b/test/expressions/index/var/literal/matrix.wgsl.expected.spvasm
new file mode 100644
index 0000000..be19a25
--- /dev/null
+++ b/test/expressions/index/var/literal/matrix.wgsl.expected.spvasm
@@ -0,0 +1,48 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %m "m"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+          %5 = OpTypeFunction %v3float
+%mat3v3float = OpTypeMatrix %v3float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+         %14 = OpConstantComposite %v3float %float_1 %float_2 %float_3
+    %float_4 = OpConstant %float 4
+    %float_5 = OpConstant %float 5
+    %float_6 = OpConstant %float 6
+         %18 = OpConstantComposite %v3float %float_4 %float_5 %float_6
+    %float_7 = OpConstant %float 7
+    %float_8 = OpConstant %float 8
+    %float_9 = OpConstant %float 9
+         %22 = OpConstantComposite %v3float %float_7 %float_8 %float_9
+         %23 = OpConstantComposite %mat3v3float %14 %18 %22
+%_ptr_Function_mat3v3float = OpTypePointer Function %mat3v3float
+         %26 = OpConstantNull %mat3v3float
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %v3float None %5
+          %9 = OpLabel
+          %m = OpVariable %_ptr_Function_mat3v3float Function %26
+               OpStore %m %23
+         %30 = OpAccessChain %_ptr_Function_v3float %m %int_1
+         %31 = OpLoad %v3float %30
+               OpReturnValue %31
+               OpFunctionEnd
diff --git a/test/expressions/index/var/literal/matrix.wgsl.expected.wgsl b/test/expressions/index/var/literal/matrix.wgsl.expected.wgsl
new file mode 100644
index 0000000..8043ae4
--- /dev/null
+++ b/test/expressions/index/var/literal/matrix.wgsl.expected.wgsl
@@ -0,0 +1,4 @@
+fn f() -> vec3<f32> {
+  var m = mat3x3<f32>(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  return m[1];
+}
diff --git a/test/expressions/index/var/literal/vector.wgsl b/test/expressions/index/var/literal/vector.wgsl
new file mode 100644
index 0000000..bd832ab
--- /dev/null
+++ b/test/expressions/index/var/literal/vector.wgsl
@@ -0,0 +1,4 @@
+fn f() -> f32 {
+  var v = vec3<f32>(1.0, 2.0, 3.0);
+  return v[1];
+}
diff --git a/test/expressions/index/var/literal/vector.wgsl.expected.hlsl b/test/expressions/index/var/literal/vector.wgsl.expected.hlsl
new file mode 100644
index 0000000..7297f63
--- /dev/null
+++ b/test/expressions/index/var/literal/vector.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float f() {
+  float3 v = float3(1.0f, 2.0f, 3.0f);
+  return v[1];
+}
diff --git a/test/expressions/index/var/literal/vector.wgsl.expected.msl b/test/expressions/index/var/literal/vector.wgsl.expected.msl
new file mode 100644
index 0000000..315ef51
--- /dev/null
+++ b/test/expressions/index/var/literal/vector.wgsl.expected.msl
@@ -0,0 +1,8 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float f() {
+  float3 v = float3(1.0f, 2.0f, 3.0f);
+  return v[1];
+}
+
diff --git a/test/expressions/index/var/literal/vector.wgsl.expected.spvasm b/test/expressions/index/var/literal/vector.wgsl.expected.spvasm
new file mode 100644
index 0000000..fe71fc3
--- /dev/null
+++ b/test/expressions/index/var/literal/vector.wgsl.expected.spvasm
@@ -0,0 +1,38 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 22
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %v "v"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+          %5 = OpTypeFunction %float
+    %v3float = OpTypeVector %float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+         %13 = OpConstantComposite %v3float %float_1 %float_2 %float_3
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+         %16 = OpConstantNull %v3float
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+%_ptr_Function_float = OpTypePointer Function %float
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %float None %5
+          %8 = OpLabel
+          %v = OpVariable %_ptr_Function_v3float Function %16
+               OpStore %v %13
+         %20 = OpAccessChain %_ptr_Function_float %v %int_1
+         %21 = OpLoad %float %20
+               OpReturnValue %21
+               OpFunctionEnd
diff --git a/test/expressions/index/var/literal/vector.wgsl.expected.wgsl b/test/expressions/index/var/literal/vector.wgsl.expected.wgsl
new file mode 100644
index 0000000..bd832ab
--- /dev/null
+++ b/test/expressions/index/var/literal/vector.wgsl.expected.wgsl
@@ -0,0 +1,4 @@
+fn f() -> f32 {
+  var v = vec3<f32>(1.0, 2.0, 3.0);
+  return v[1];
+}
diff --git a/test/expressions/index/var/param/array.wgsl b/test/expressions/index/var/param/array.wgsl
new file mode 100644
index 0000000..183d103
--- /dev/null
+++ b/test/expressions/index/var/param/array.wgsl
@@ -0,0 +1,4 @@
+fn f(i : i32) -> i32 {
+  var a = array<i32, 8>(1, 2, 3, 4, 5, 6, 7, 8);
+  return a[i];
+}
diff --git a/test/expressions/index/var/param/array.wgsl.expected.hlsl b/test/expressions/index/var/param/array.wgsl.expected.hlsl
new file mode 100644
index 0000000..f0e32a0
--- /dev/null
+++ b/test/expressions/index/var/param/array.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+int f(int i) {
+  int a[8] = {1, 2, 3, 4, 5, 6, 7, 8};
+  return a[i];
+}
diff --git a/test/expressions/index/var/param/array.wgsl.expected.msl b/test/expressions/index/var/param/array.wgsl.expected.msl
new file mode 100644
index 0000000..9d03b80
--- /dev/null
+++ b/test/expressions/index/var/param/array.wgsl.expected.msl
@@ -0,0 +1,12 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct tint_array_wrapper {
+  int arr[8];
+};
+
+int f(int i) {
+  tint_array_wrapper a = {.arr={1, 2, 3, 4, 5, 6, 7, 8}};
+  return a.arr[i];
+}
+
diff --git a/test/expressions/index/var/param/array.wgsl.expected.spvasm b/test/expressions/index/var/param/array.wgsl.expected.spvasm
new file mode 100644
index 0000000..69364cb
--- /dev/null
+++ b/test/expressions/index/var/param/array.wgsl.expected.spvasm
@@ -0,0 +1,46 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 28
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %i "i"
+               OpName %a "a"
+               OpDecorate %_arr_int_uint_8 ArrayStride 4
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %int %int
+       %uint = OpTypeInt 32 0
+     %uint_8 = OpConstant %uint 8
+%_arr_int_uint_8 = OpTypeArray %int %uint_8
+      %int_1 = OpConstant %int 1
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+      %int_4 = OpConstant %int 4
+      %int_5 = OpConstant %int 5
+      %int_6 = OpConstant %int 6
+      %int_7 = OpConstant %int 7
+      %int_8 = OpConstant %int 8
+         %21 = OpConstantComposite %_arr_int_uint_8 %int_1 %int_2 %int_3 %int_4 %int_5 %int_6 %int_7 %int_8
+%_ptr_Function__arr_int_uint_8 = OpTypePointer Function %_arr_int_uint_8
+         %24 = OpConstantNull %_arr_int_uint_8
+%_ptr_Function_int = OpTypePointer Function %int
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %int None %5
+          %i = OpFunctionParameter %int
+          %9 = OpLabel
+          %a = OpVariable %_ptr_Function__arr_int_uint_8 Function %24
+               OpStore %a %21
+         %26 = OpAccessChain %_ptr_Function_int %a %i
+         %27 = OpLoad %int %26
+               OpReturnValue %27
+               OpFunctionEnd
diff --git a/test/expressions/index/var/param/array.wgsl.expected.wgsl b/test/expressions/index/var/param/array.wgsl.expected.wgsl
new file mode 100644
index 0000000..183d103
--- /dev/null
+++ b/test/expressions/index/var/param/array.wgsl.expected.wgsl
@@ -0,0 +1,4 @@
+fn f(i : i32) -> i32 {
+  var a = array<i32, 8>(1, 2, 3, 4, 5, 6, 7, 8);
+  return a[i];
+}
diff --git a/test/expressions/index/var/param/matrix.wgsl b/test/expressions/index/var/param/matrix.wgsl
new file mode 100644
index 0000000..7e249d7
--- /dev/null
+++ b/test/expressions/index/var/param/matrix.wgsl
@@ -0,0 +1,4 @@
+fn f(i : i32) -> vec3<f32> {
+  var m = mat3x3<f32>(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  return m[i];
+}
diff --git a/test/expressions/index/var/param/matrix.wgsl.expected.hlsl b/test/expressions/index/var/param/matrix.wgsl.expected.hlsl
new file mode 100644
index 0000000..2b104d6
--- /dev/null
+++ b/test/expressions/index/var/param/matrix.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float3 f(int i) {
+  float3x3 m = float3x3(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f);
+  return m[i];
+}
diff --git a/test/expressions/index/var/param/matrix.wgsl.expected.msl b/test/expressions/index/var/param/matrix.wgsl.expected.msl
new file mode 100644
index 0000000..3826f12
--- /dev/null
+++ b/test/expressions/index/var/param/matrix.wgsl.expected.msl
@@ -0,0 +1,8 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float3 f(int i) {
+  float3x3 m = float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f));
+  return m[i];
+}
+
diff --git a/test/expressions/index/var/param/matrix.wgsl.expected.spvasm b/test/expressions/index/var/param/matrix.wgsl.expected.spvasm
new file mode 100644
index 0000000..43a587a
--- /dev/null
+++ b/test/expressions/index/var/param/matrix.wgsl.expected.spvasm
@@ -0,0 +1,49 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %i "i"
+               OpName %m "m"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %v3float %int
+%mat3v3float = OpTypeMatrix %v3float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+         %16 = OpConstantComposite %v3float %float_1 %float_2 %float_3
+    %float_4 = OpConstant %float 4
+    %float_5 = OpConstant %float 5
+    %float_6 = OpConstant %float 6
+         %20 = OpConstantComposite %v3float %float_4 %float_5 %float_6
+    %float_7 = OpConstant %float 7
+    %float_8 = OpConstant %float 8
+    %float_9 = OpConstant %float 9
+         %24 = OpConstantComposite %v3float %float_7 %float_8 %float_9
+         %25 = OpConstantComposite %mat3v3float %16 %20 %24
+%_ptr_Function_mat3v3float = OpTypePointer Function %mat3v3float
+         %28 = OpConstantNull %mat3v3float
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %v3float None %5
+          %i = OpFunctionParameter %int
+         %11 = OpLabel
+          %m = OpVariable %_ptr_Function_mat3v3float Function %28
+               OpStore %m %25
+         %30 = OpAccessChain %_ptr_Function_v3float %m %i
+         %31 = OpLoad %v3float %30
+               OpReturnValue %31
+               OpFunctionEnd
diff --git a/test/expressions/index/var/param/matrix.wgsl.expected.wgsl b/test/expressions/index/var/param/matrix.wgsl.expected.wgsl
new file mode 100644
index 0000000..7e249d7
--- /dev/null
+++ b/test/expressions/index/var/param/matrix.wgsl.expected.wgsl
@@ -0,0 +1,4 @@
+fn f(i : i32) -> vec3<f32> {
+  var m = mat3x3<f32>(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
+  return m[i];
+}
diff --git a/test/expressions/index/var/param/vector.wgsl b/test/expressions/index/var/param/vector.wgsl
new file mode 100644
index 0000000..8bf7c40
--- /dev/null
+++ b/test/expressions/index/var/param/vector.wgsl
@@ -0,0 +1,4 @@
+fn f(i : i32) -> f32 {
+  var v = vec3<f32>(1.0, 2.0, 3.0);
+  return v[i];
+}
diff --git a/test/expressions/index/var/param/vector.wgsl.expected.hlsl b/test/expressions/index/var/param/vector.wgsl.expected.hlsl
new file mode 100644
index 0000000..d046c84
--- /dev/null
+++ b/test/expressions/index/var/param/vector.wgsl.expected.hlsl
@@ -0,0 +1,9 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float f(int i) {
+  float3 v = float3(1.0f, 2.0f, 3.0f);
+  return v[i];
+}
diff --git a/test/expressions/index/var/param/vector.wgsl.expected.msl b/test/expressions/index/var/param/vector.wgsl.expected.msl
new file mode 100644
index 0000000..15f873e
--- /dev/null
+++ b/test/expressions/index/var/param/vector.wgsl.expected.msl
@@ -0,0 +1,8 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float f(int i) {
+  float3 v = float3(1.0f, 2.0f, 3.0f);
+  return v[i];
+}
+
diff --git a/test/expressions/index/var/param/vector.wgsl.expected.spvasm b/test/expressions/index/var/param/vector.wgsl.expected.spvasm
new file mode 100644
index 0000000..b4951c8
--- /dev/null
+++ b/test/expressions/index/var/param/vector.wgsl.expected.spvasm
@@ -0,0 +1,39 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 22
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %f "f"
+               OpName %i "i"
+               OpName %v "v"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %float %int
+    %v3float = OpTypeVector %float 3
+    %float_1 = OpConstant %float 1
+    %float_2 = OpConstant %float 2
+    %float_3 = OpConstant %float 3
+         %15 = OpConstantComposite %v3float %float_1 %float_2 %float_3
+%_ptr_Function_v3float = OpTypePointer Function %v3float
+         %18 = OpConstantNull %v3float
+%_ptr_Function_float = OpTypePointer Function %float
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %f = OpFunction %float None %5
+          %i = OpFunctionParameter %int
+         %10 = OpLabel
+          %v = OpVariable %_ptr_Function_v3float Function %18
+               OpStore %v %15
+         %20 = OpAccessChain %_ptr_Function_float %v %i
+         %21 = OpLoad %float %20
+               OpReturnValue %21
+               OpFunctionEnd
diff --git a/test/expressions/index/var/param/vector.wgsl.expected.wgsl b/test/expressions/index/var/param/vector.wgsl.expected.wgsl
new file mode 100644
index 0000000..8bf7c40
--- /dev/null
+++ b/test/expressions/index/var/param/vector.wgsl.expected.wgsl
@@ -0,0 +1,4 @@
+fn f(i : i32) -> f32 {
+  var v = vec3<f32>(1.0, 2.0, 3.0);
+  return v[i];
+}