tint/resolver: Evaluate constant index accessors

If the object and index are both constant expressions, resolve the
index as a constant.

Note: Expectations have been updated for indexing on 'let's. Once
'const' is introduced, 'let' will no longer be treated as a constant
expression, and these expectations will be reverted.

Bug: tint:1580
Change-Id: I42793d36c1a5f82890ccaa5dbbb71b4346493bef
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/94329
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index 0b52ec9..961de41 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -171,17 +171,25 @@
                              const ast::ExpressionList& params,
                              uint32_t* id);
 
-    //////////////////////////////////////////////////////////////////////////////
-    // AST and Type traversal methods
-    //////////////////////////////////////////////////////////////////////////////
+    /// Expression traverses the graph of expressions starting at `expr`, building a postordered
+    /// list (leaf-first) of all the expression nodes. Each of the expressions are then resolved by
+    /// dispatching to the appropriate expression handlers below.
+    /// @returns the resolved semantic node for the expression `expr`, or nullptr on failure.
+    sem::Expression* Expression(const ast::Expression* expr);
 
+    ////////////////////////////////////////////////////////////////////////////////////////////////
     // Expression resolving methods
+    //
     // Returns the semantic node pointer on success, nullptr on failure.
+    //
+    // These methods are invoked by Expression(), in postorder (child-first). These methods should
+    // not attempt to resolve their children. This design avoids recursion, which is a common cause
+    // of stack-overflows.
+    ////////////////////////////////////////////////////////////////////////////////////////////////
     sem::Expression* IndexAccessor(const ast::IndexAccessorExpression*);
     sem::Expression* Binary(const ast::BinaryExpression*);
     sem::Expression* Bitcast(const ast::BitcastExpression*);
     sem::Call* Call(const ast::CallExpression*);
-    sem::Expression* Expression(const ast::Expression*);
     sem::Function* Function(const ast::Function*);
     sem::Call* FunctionCall(const ast::CallExpression*,
                             sem::Function* target,
@@ -195,6 +203,29 @@
     sem::Expression* MemberAccessor(const ast::MemberAccessorExpression*);
     sem::Expression* UnaryOp(const ast::UnaryOpExpression*);
 
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    /// Constant value evaluation methods
+    ///
+    /// These methods are called from the expression resolving methods, and so child-expression
+    /// nodes are guaranteed to have been already resolved and any constant values calculated.
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    sem::Constant EvaluateConstantValue(const ast::Expression* expr, const sem::Type* type);
+    sem::Constant EvaluateConstantValue(const ast::LiteralExpression* literal,
+                                        const sem::Type* type);
+    sem::Constant EvaluateConstantValue(const ast::CallExpression* call, const sem::Type* type);
+    sem::Constant EvaluateConstantValue(const ast::IndexAccessorExpression* call,
+                                        const sem::Type* type);
+
+    /// The result type of a ConstantEvaluation method. Holds the constant value and a boolean,
+    /// which is true on success, false on an error.
+    using ConstantResult = utils::Result<sem::Constant>;
+
+    /// Convert the `value` to `target_type`
+    /// @return the converted value
+    ConstantResult ConvertValue(const sem::Constant& value,
+                                const sem::Type* target_type,
+                                const Source& source);
+
     /// If `expr` is not of an abstract-numeric type, then Materialize() will just return `expr`.
     /// If `expr` is of an abstract-numeric type:
     /// * Materialize will create and return a sem::Materialize node wrapping `expr`.
@@ -379,23 +410,6 @@
     /// Adds the given note message to the diagnostics
     void AddNote(const std::string& msg, const Source& source) const;
 
-    //////////////////////////////////////////////////////////////////////////////
-    /// Constant value evaluation methods
-    //////////////////////////////////////////////////////////////////////////////
-    /// The result type of a ConstantEvaluation method. Holds the constant value and a boolean,
-    /// which is true on success, false on an error.
-    using ConstantResult = utils::Result<sem::Constant>;
-
-    /// Convert the `value` to `target_type`
-    /// @return the converted value
-    ConstantResult ConvertValue(const sem::Constant& value,
-                                const sem::Type* target_type,
-                                const Source& source);
-    sem::Constant EvaluateConstantValue(const ast::Expression* expr, const sem::Type* type);
-    sem::Constant EvaluateConstantValue(const ast::LiteralExpression* literal,
-                                        const sem::Type* type);
-    sem::Constant EvaluateConstantValue(const ast::CallExpression* call, const sem::Type* type);
-
     /// @returns true if the symbol is the name of a builtin function.
     bool IsBuiltin(Symbol) const;
 
diff --git a/src/tint/resolver/resolver_constants.cc b/src/tint/resolver/resolver_constants.cc
index ad89f3e..cf93c6e 100644
--- a/src/tint/resolver/resolver_constants.cc
+++ b/src/tint/resolver/resolver_constants.cc
@@ -155,7 +155,8 @@
     return Switch(
         expr,  //
         [&](const ast::LiteralExpression* e) { return EvaluateConstantValue(e, type); },
-        [&](const ast::CallExpression* e) { return EvaluateConstantValue(e, type); });
+        [&](const ast::CallExpression* e) { return EvaluateConstantValue(e, type); },
+        [&](const ast::IndexAccessorExpression* e) { return EvaluateConstantValue(e, type); });
 }
 
 sem::Constant Resolver::EvaluateConstantValue(const ast::LiteralExpression* literal,
@@ -255,6 +256,54 @@
         elements.value());
 }
 
+sem::Constant Resolver::EvaluateConstantValue(const ast::IndexAccessorExpression* accessor,
+                                              const sem::Type* el_ty) {
+    auto* obj_sem = builder_->Sem().Get(accessor->object);
+    if (!obj_sem) {
+        return {};
+    }
+
+    auto obj_val = obj_sem->ConstantValue();
+    if (!obj_val) {
+        return {};
+    }
+
+    auto* idx_sem = builder_->Sem().Get(accessor->index);
+    if (!idx_sem) {
+        return {};
+    }
+
+    auto idx_val = idx_sem->ConstantValue();
+    if (!idx_val || idx_val.ElementCount() != 1) {
+        return {};
+    }
+
+    AInt idx = idx_val.Element<AInt>(0);
+
+    // The immediate child element count.
+    uint32_t el_count = 0;
+    sem::Type::ElementOf(obj_val.Type(), &el_count);
+
+    // The total number of most-nested elements per child element type.
+    uint32_t step = 0;
+    sem::Type::DeepestElementOf(el_ty, &step);
+
+    if (idx < 0 || idx >= el_count) {
+        auto clamped = std::min<AInt::type>(std::max<AInt::type>(idx, 0), el_count - 1);
+        AddWarning("index " + std::to_string(idx) + " out of bounds [0.." +
+                       std::to_string(el_count - 1) + "]. Clamping index to " +
+                       std::to_string(clamped),
+                   accessor->index->source);
+        idx = clamped;
+    }
+
+    return sem::Constant{el_ty, obj_val.WithElements([&](auto&& v) {
+                             using VEC = std::decay_t<decltype(v)>;
+                             return sem::Constant::Elements(
+                                 VEC(v.begin() + (idx * step), v.begin() + (idx + 1) * step));
+                         })};
+}
+
 utils::Result<sem::Constant> Resolver::ConvertValue(const sem::Constant& value,
                                                     const sem::Type* ty,
                                                     const Source& source) {
diff --git a/src/tint/resolver/resolver_constants_test.cc b/src/tint/resolver/resolver_constants_test.cc
index c937fa1..d1f43b7 100644
--- a/src/tint/resolver/resolver_constants_test.cc
+++ b/src/tint/resolver/resolver_constants_test.cc
@@ -608,5 +608,113 @@
     EXPECT_EQ(sem->ConstantValue().Element<AFloat>(5).value, 6._a);
 }
 
+TEST_F(ResolverConstantsTest, Vec3_Index) {
+    auto* expr = IndexAccessor(vec3<i32>(1_i, 2_i, 3_i), 2_i);
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    EXPECT_NE(sem, nullptr);
+    ASSERT_TRUE(sem->Type()->Is<sem::I32>());
+    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
+    ASSERT_EQ(sem->ConstantValue().ElementCount(), 1u);
+    EXPECT_EQ(sem->ConstantValue().Element<i32>(0).value, 3_i);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_Index_OOB_High) {
+    auto* expr = IndexAccessor(vec3<i32>(1_i, 2_i, 3_i), Expr(Source{{12, 34}}, 3_i));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+    EXPECT_EQ(r()->error(), "12:34 warning: index 3 out of bounds [0..2]. Clamping index to 2");
+
+    auto* sem = Sem().Get(expr);
+    EXPECT_NE(sem, nullptr);
+    ASSERT_TRUE(sem->Type()->Is<sem::I32>());
+    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
+    ASSERT_EQ(sem->ConstantValue().ElementCount(), 1u);
+    EXPECT_EQ(sem->ConstantValue().Element<i32>(0).value, 3_i);
+}
+
+TEST_F(ResolverConstantsTest, Vec3_Index_OOB_Low) {
+    auto* expr = IndexAccessor(vec3<i32>(1_i, 2_i, 3_i), Expr(Source{{12, 34}}, -3_i));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+    EXPECT_EQ(r()->error(), "12:34 warning: index -3 out of bounds [0..2]. Clamping index to 0");
+
+    auto* sem = Sem().Get(expr);
+    EXPECT_NE(sem, nullptr);
+    ASSERT_TRUE(sem->Type()->Is<sem::I32>());
+    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
+    ASSERT_EQ(sem->ConstantValue().ElementCount(), 1u);
+    EXPECT_EQ(sem->ConstantValue().Element<i32>(0).value, 1_i);
+}
+
+TEST_F(ResolverConstantsTest, Mat3x2_Index) {
+    auto* expr = IndexAccessor(
+        mat3x2<f32>(vec2<f32>(1._a, 2._a), vec2<f32>(3._a, 4._a), vec2<f32>(5._a, 6._a)), 2_i);
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+    auto* sem = Sem().Get(expr);
+    EXPECT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_EQ(vec->Width(), 2u);
+    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
+    ASSERT_EQ(sem->ConstantValue().ElementCount(), 2u);
+    EXPECT_EQ(sem->ConstantValue().Element<f32>(0).value, 5._a);
+    EXPECT_EQ(sem->ConstantValue().Element<f32>(1).value, 6._a);
+}
+
+TEST_F(ResolverConstantsTest, Mat3x2_Index_OOB_High) {
+    auto* expr = IndexAccessor(
+        mat3x2<f32>(vec2<f32>(1._a, 2._a), vec2<f32>(3._a, 4._a), vec2<f32>(5._a, 6._a)),
+        Expr(Source{{12, 34}}, 3_i));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+    EXPECT_EQ(r()->error(), "12:34 warning: index 3 out of bounds [0..2]. Clamping index to 2");
+
+    auto* sem = Sem().Get(expr);
+    EXPECT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_EQ(vec->Width(), 2u);
+    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
+    ASSERT_EQ(sem->ConstantValue().ElementCount(), 2u);
+    EXPECT_EQ(sem->ConstantValue().Element<f32>(0).value, 5._a);
+    EXPECT_EQ(sem->ConstantValue().Element<f32>(1).value, 6._a);
+}
+
+TEST_F(ResolverConstantsTest, Mat3x2_Index_OOB_Low) {
+    auto* expr = IndexAccessor(
+        mat3x2<f32>(vec2<f32>(1._a, 2._a), vec2<f32>(3._a, 4._a), vec2<f32>(5._a, 6._a)),
+        Expr(Source{{12, 34}}, -3_i));
+    WrapInFunction(expr);
+
+    EXPECT_TRUE(r()->Resolve()) << r()->error();
+    EXPECT_EQ(r()->error(), "12:34 warning: index -3 out of bounds [0..2]. Clamping index to 0");
+
+    auto* sem = Sem().Get(expr);
+    EXPECT_NE(sem, nullptr);
+    auto* vec = sem->Type()->As<sem::Vector>();
+    ASSERT_NE(vec, nullptr);
+    EXPECT_EQ(vec->Width(), 2u);
+    EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+    EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
+    ASSERT_EQ(sem->ConstantValue().ElementCount(), 2u);
+    EXPECT_EQ(sem->ConstantValue().Element<f32>(0).value, 1._a);
+    EXPECT_EQ(sem->ConstantValue().Element<f32>(1).value, 2._a);
+}
+
 }  // namespace
 }  // namespace tint::resolver
diff --git a/test/tint/access/let/matrix.spvasm.expected.glsl b/test/tint/access/let/matrix.spvasm.expected.glsl
index 8292f89..f4bf9cc 100644
--- a/test/tint/access/let/matrix.spvasm.expected.glsl
+++ b/test/tint/access/let/matrix.spvasm.expected.glsl
@@ -1,7 +1,7 @@
 #version 310 es
 
 void main_1() {
-  float x_24 = mat3(vec3(1.0f, 2.0f, 3.0f), vec3(4.0f, 5.0f, 6.0f), vec3(7.0f, 8.0f, 9.0f))[1u].y;
+  float x_24 = vec3(4.0f, 5.0f, 6.0f).y;
   return;
 }
 
diff --git a/test/tint/access/let/matrix.spvasm.expected.hlsl b/test/tint/access/let/matrix.spvasm.expected.hlsl
index 7f13ac2..164f020 100644
--- a/test/tint/access/let/matrix.spvasm.expected.hlsl
+++ b/test/tint/access/let/matrix.spvasm.expected.hlsl
@@ -1,5 +1,5 @@
 void main_1() {
-  const float x_24 = float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f))[1u].y;
+  const float x_24 = float3(4.0f, 5.0f, 6.0f).y;
   return;
 }
 
diff --git a/test/tint/access/let/matrix.spvasm.expected.msl b/test/tint/access/let/matrix.spvasm.expected.msl
index 74a4b93..ca54fa6 100644
--- a/test/tint/access/let/matrix.spvasm.expected.msl
+++ b/test/tint/access/let/matrix.spvasm.expected.msl
@@ -2,7 +2,7 @@
 
 using namespace metal;
 void main_1() {
-  float const x_24 = float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f))[1u][1];
+  float const x_24 = float3(4.0f, 5.0f, 6.0f)[1];
   return;
 }
 
diff --git a/test/tint/access/let/matrix.wgsl.expected.glsl b/test/tint/access/let/matrix.wgsl.expected.glsl
index 590ce6f..6f4717b 100644
--- a/test/tint/access/let/matrix.wgsl.expected.glsl
+++ b/test/tint/access/let/matrix.wgsl.expected.glsl
@@ -2,8 +2,8 @@
 
 void tint_symbol() {
   mat3 m = mat3(vec3(1.0f, 2.0f, 3.0f), vec3(4.0f, 5.0f, 6.0f), vec3(7.0f, 8.0f, 9.0f));
-  vec3 v = mat3(vec3(1.0f, 2.0f, 3.0f), vec3(4.0f, 5.0f, 6.0f), vec3(7.0f, 8.0f, 9.0f))[1];
-  float f = v[1];
+  vec3 v = vec3(4.0f, 5.0f, 6.0f);
+  float f = 5.0f;
 }
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
diff --git a/test/tint/access/let/matrix.wgsl.expected.hlsl b/test/tint/access/let/matrix.wgsl.expected.hlsl
index 8b6bb55..4dcc54e 100644
--- a/test/tint/access/let/matrix.wgsl.expected.hlsl
+++ b/test/tint/access/let/matrix.wgsl.expected.hlsl
@@ -1,7 +1,7 @@
 [numthreads(1, 1, 1)]
 void main() {
   const float3x3 m = float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f));
-  const float3 v = float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f))[1];
-  const float f = v[1];
+  const float3 v = float3(4.0f, 5.0f, 6.0f);
+  const float f = 5.0f;
   return;
 }
diff --git a/test/tint/access/let/matrix.wgsl.expected.msl b/test/tint/access/let/matrix.wgsl.expected.msl
index eecf61a..a1b3e45 100644
--- a/test/tint/access/let/matrix.wgsl.expected.msl
+++ b/test/tint/access/let/matrix.wgsl.expected.msl
@@ -3,8 +3,8 @@
 using namespace metal;
 kernel void tint_symbol() {
   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));
-  float3 const v = float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f))[1];
-  float const f = v[1];
+  float3 const v = float3(4.0f, 5.0f, 6.0f);
+  float const f = 5.0f;
   return;
 }
 
diff --git a/test/tint/access/let/matrix.wgsl.expected.spvasm b/test/tint/access/let/matrix.wgsl.expected.spvasm
index cbc5aca..47263bf 100644
--- a/test/tint/access/let/matrix.wgsl.expected.spvasm
+++ b/test/tint/access/let/matrix.wgsl.expected.spvasm
@@ -1,7 +1,7 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 25
+; Bound: 21
 ; Schema: 0
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -26,11 +26,7 @@
     %float_9 = OpConstant %float 9
          %19 = OpConstantComposite %v3float %float_7 %float_8 %float_9
          %20 = OpConstantComposite %mat3v3float %11 %15 %19
-        %int = OpTypeInt 32 1
-      %int_1 = OpConstant %int 1
        %main = OpFunction %void None %1
           %4 = OpLabel
-         %23 = OpCompositeExtract %v3float %20 1
-         %24 = OpCompositeExtract %float %23 1
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/bug/tint/764.wgsl.expected.glsl b/test/tint/bug/tint/764.wgsl.expected.glsl
index 1b62f39..347d829 100644
--- a/test/tint/bug/tint/764.wgsl.expected.glsl
+++ b/test/tint/bug/tint/764.wgsl.expected.glsl
@@ -6,7 +6,7 @@
 }
 void f() {
   mat4 m = mat4(vec4(1.0f), vec4(1.0f), vec4(1.0f), vec4(1.0f));
-  vec4 v1 = mat4(vec4(1.0f), vec4(1.0f), vec4(1.0f), vec4(1.0f))[0];
-  float a = v1[0];
+  vec4 v1 = vec4(1.0f);
+  float a = 1.0f;
 }
 
diff --git a/test/tint/bug/tint/764.wgsl.expected.hlsl b/test/tint/bug/tint/764.wgsl.expected.hlsl
index 236bed1..e4e19f7 100644
--- a/test/tint/bug/tint/764.wgsl.expected.hlsl
+++ b/test/tint/bug/tint/764.wgsl.expected.hlsl
@@ -5,6 +5,6 @@
 
 void f() {
   const float4x4 m = float4x4((1.0f).xxxx, (1.0f).xxxx, (1.0f).xxxx, (1.0f).xxxx);
-  const float4 v1 = float4x4((1.0f).xxxx, (1.0f).xxxx, (1.0f).xxxx, (1.0f).xxxx)[0];
-  const float a = v1[0];
+  const float4 v1 = (1.0f).xxxx;
+  const float a = 1.0f;
 }
diff --git a/test/tint/bug/tint/764.wgsl.expected.msl b/test/tint/bug/tint/764.wgsl.expected.msl
index f70d744..aa71892 100644
--- a/test/tint/bug/tint/764.wgsl.expected.msl
+++ b/test/tint/bug/tint/764.wgsl.expected.msl
@@ -3,7 +3,7 @@
 using namespace metal;
 void f() {
   float4x4 const m = float4x4(float4(1.0f), float4(1.0f), float4(1.0f), float4(1.0f));
-  float4 const v1 = float4x4(float4(1.0f), float4(1.0f), float4(1.0f), float4(1.0f))[0];
-  float const a = v1[0];
+  float4 const v1 = float4(1.0f);
+  float const a = 1.0f;
 }
 
diff --git a/test/tint/bug/tint/764.wgsl.expected.spvasm b/test/tint/bug/tint/764.wgsl.expected.spvasm
index 329c64e..f5a8869 100644
--- a/test/tint/bug/tint/764.wgsl.expected.spvasm
+++ b/test/tint/bug/tint/764.wgsl.expected.spvasm
@@ -1,7 +1,7 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 17
+; Bound: 13
 ; Schema: 0
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -17,15 +17,11 @@
     %float_1 = OpConstant %float 1
          %11 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
          %12 = OpConstantComposite %mat4v4float %11 %11 %11 %11
-        %int = OpTypeInt 32 1
-         %14 = OpConstantNull %int
 %unused_entry_point = OpFunction %void None %1
           %4 = OpLabel
                OpReturn
                OpFunctionEnd
           %f = OpFunction %void None %1
           %6 = OpLabel
-         %15 = OpCompositeExtract %v4float %12 0
-         %16 = OpCompositeExtract %float %15 0
                OpReturn
                OpFunctionEnd
diff --git a/test/tint/expressions/index/let/let/literal/matrix.wgsl.expected.glsl b/test/tint/expressions/index/let/let/literal/matrix.wgsl.expected.glsl
index b6b4777..a88b339 100644
--- a/test/tint/expressions/index/let/let/literal/matrix.wgsl.expected.glsl
+++ b/test/tint/expressions/index/let/let/literal/matrix.wgsl.expected.glsl
@@ -6,6 +6,6 @@
 }
 vec3 f() {
   mat3 m = mat3(vec3(1.0f, 2.0f, 3.0f), vec3(4.0f, 5.0f, 6.0f), vec3(7.0f, 8.0f, 9.0f));
-  return mat3(vec3(1.0f, 2.0f, 3.0f), vec3(4.0f, 5.0f, 6.0f), vec3(7.0f, 8.0f, 9.0f))[1];
+  return vec3(4.0f, 5.0f, 6.0f);
 }
 
diff --git a/test/tint/expressions/index/let/let/literal/matrix.wgsl.expected.hlsl b/test/tint/expressions/index/let/let/literal/matrix.wgsl.expected.hlsl
index 60abcc6..a730c4e 100644
--- a/test/tint/expressions/index/let/let/literal/matrix.wgsl.expected.hlsl
+++ b/test/tint/expressions/index/let/let/literal/matrix.wgsl.expected.hlsl
@@ -5,5 +5,5 @@
 
 float3 f() {
   const 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 float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f))[1];
+  return float3(4.0f, 5.0f, 6.0f);
 }
diff --git a/test/tint/expressions/index/let/let/literal/matrix.wgsl.expected.msl b/test/tint/expressions/index/let/let/literal/matrix.wgsl.expected.msl
index 606211b..6cc4826 100644
--- a/test/tint/expressions/index/let/let/literal/matrix.wgsl.expected.msl
+++ b/test/tint/expressions/index/let/let/literal/matrix.wgsl.expected.msl
@@ -4,6 +4,6 @@
 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 float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f))[1];
+  return float3(4.0f, 5.0f, 6.0f);
 }
 
diff --git a/test/tint/expressions/index/let/let/literal/matrix.wgsl.expected.spvasm b/test/tint/expressions/index/let/let/literal/matrix.wgsl.expected.spvasm
index 0e46829..9e8d909 100644
--- a/test/tint/expressions/index/let/let/literal/matrix.wgsl.expected.spvasm
+++ b/test/tint/expressions/index/let/let/literal/matrix.wgsl.expected.spvasm
@@ -1,7 +1,7 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 27
+; Bound: 26
 ; Schema: 0
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -36,6 +36,5 @@
                OpFunctionEnd
           %f = OpFunction %v3float None %5
           %9 = OpLabel
-         %26 = OpCompositeExtract %v3float %23 1
-               OpReturnValue %26
+               OpReturnValue %18
                OpFunctionEnd
diff --git a/test/tint/expressions/index/let/let/literal/vector.wgsl.expected.glsl b/test/tint/expressions/index/let/let/literal/vector.wgsl.expected.glsl
index 4a858df..0d06163 100644
--- a/test/tint/expressions/index/let/let/literal/vector.wgsl.expected.glsl
+++ b/test/tint/expressions/index/let/let/literal/vector.wgsl.expected.glsl
@@ -6,6 +6,6 @@
 }
 float f() {
   vec3 v = vec3(1.0f, 2.0f, 3.0f);
-  return vec3(1.0f, 2.0f, 3.0f)[1];
+  return 2.0f;
 }
 
diff --git a/test/tint/expressions/index/let/let/literal/vector.wgsl.expected.hlsl b/test/tint/expressions/index/let/let/literal/vector.wgsl.expected.hlsl
index de48383..04d609c 100644
--- a/test/tint/expressions/index/let/let/literal/vector.wgsl.expected.hlsl
+++ b/test/tint/expressions/index/let/let/literal/vector.wgsl.expected.hlsl
@@ -5,5 +5,5 @@
 
 float f() {
   const float3 v = float3(1.0f, 2.0f, 3.0f);
-  return float3(1.0f, 2.0f, 3.0f)[1];
+  return 2.0f;
 }
diff --git a/test/tint/expressions/index/let/let/literal/vector.wgsl.expected.msl b/test/tint/expressions/index/let/let/literal/vector.wgsl.expected.msl
index 578bbe1..2218b65 100644
--- a/test/tint/expressions/index/let/let/literal/vector.wgsl.expected.msl
+++ b/test/tint/expressions/index/let/let/literal/vector.wgsl.expected.msl
@@ -4,6 +4,6 @@
 float f() {
   float3 const v = float3(1.0f, 2.0f, 3.0f);
   int const i = 1;
-  return float3(1.0f, 2.0f, 3.0f)[1];
+  return 2.0f;
 }
 
diff --git a/test/tint/expressions/index/let/let/literal/vector.wgsl.expected.spvasm b/test/tint/expressions/index/let/let/literal/vector.wgsl.expected.spvasm
index d274e46..91f8eea 100644
--- a/test/tint/expressions/index/let/let/literal/vector.wgsl.expected.spvasm
+++ b/test/tint/expressions/index/let/let/literal/vector.wgsl.expected.spvasm
@@ -1,7 +1,7 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 17
+; Bound: 16
 ; Schema: 0
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -26,6 +26,5 @@
                OpFunctionEnd
           %f = OpFunction %float None %5
           %8 = OpLabel
-         %16 = OpCompositeExtract %float %13 1
-               OpReturnValue %16
+               OpReturnValue %float_2
                OpFunctionEnd
diff --git a/test/tint/expressions/index/let/literal/matrix.wgsl.expected.glsl b/test/tint/expressions/index/let/literal/matrix.wgsl.expected.glsl
index b6b4777..a88b339 100644
--- a/test/tint/expressions/index/let/literal/matrix.wgsl.expected.glsl
+++ b/test/tint/expressions/index/let/literal/matrix.wgsl.expected.glsl
@@ -6,6 +6,6 @@
 }
 vec3 f() {
   mat3 m = mat3(vec3(1.0f, 2.0f, 3.0f), vec3(4.0f, 5.0f, 6.0f), vec3(7.0f, 8.0f, 9.0f));
-  return mat3(vec3(1.0f, 2.0f, 3.0f), vec3(4.0f, 5.0f, 6.0f), vec3(7.0f, 8.0f, 9.0f))[1];
+  return vec3(4.0f, 5.0f, 6.0f);
 }
 
diff --git a/test/tint/expressions/index/let/literal/matrix.wgsl.expected.hlsl b/test/tint/expressions/index/let/literal/matrix.wgsl.expected.hlsl
index 60abcc6..a730c4e 100644
--- a/test/tint/expressions/index/let/literal/matrix.wgsl.expected.hlsl
+++ b/test/tint/expressions/index/let/literal/matrix.wgsl.expected.hlsl
@@ -5,5 +5,5 @@
 
 float3 f() {
   const 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 float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f))[1];
+  return float3(4.0f, 5.0f, 6.0f);
 }
diff --git a/test/tint/expressions/index/let/literal/matrix.wgsl.expected.msl b/test/tint/expressions/index/let/literal/matrix.wgsl.expected.msl
index f987c40..cd5b0bf 100644
--- a/test/tint/expressions/index/let/literal/matrix.wgsl.expected.msl
+++ b/test/tint/expressions/index/let/literal/matrix.wgsl.expected.msl
@@ -3,6 +3,6 @@
 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 float3x3(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), float3(7.0f, 8.0f, 9.0f))[1];
+  return float3(4.0f, 5.0f, 6.0f);
 }
 
diff --git a/test/tint/expressions/index/let/literal/matrix.wgsl.expected.spvasm b/test/tint/expressions/index/let/literal/matrix.wgsl.expected.spvasm
index 0e46829..362678c 100644
--- a/test/tint/expressions/index/let/literal/matrix.wgsl.expected.spvasm
+++ b/test/tint/expressions/index/let/literal/matrix.wgsl.expected.spvasm
@@ -1,7 +1,7 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 27
+; Bound: 24
 ; Schema: 0
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -28,14 +28,11 @@
     %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
+               OpReturnValue %18
                OpFunctionEnd
diff --git a/test/tint/expressions/index/let/literal/vector.wgsl.expected.glsl b/test/tint/expressions/index/let/literal/vector.wgsl.expected.glsl
index 4a858df..0d06163 100644
--- a/test/tint/expressions/index/let/literal/vector.wgsl.expected.glsl
+++ b/test/tint/expressions/index/let/literal/vector.wgsl.expected.glsl
@@ -6,6 +6,6 @@
 }
 float f() {
   vec3 v = vec3(1.0f, 2.0f, 3.0f);
-  return vec3(1.0f, 2.0f, 3.0f)[1];
+  return 2.0f;
 }
 
diff --git a/test/tint/expressions/index/let/literal/vector.wgsl.expected.hlsl b/test/tint/expressions/index/let/literal/vector.wgsl.expected.hlsl
index de48383..04d609c 100644
--- a/test/tint/expressions/index/let/literal/vector.wgsl.expected.hlsl
+++ b/test/tint/expressions/index/let/literal/vector.wgsl.expected.hlsl
@@ -5,5 +5,5 @@
 
 float f() {
   const float3 v = float3(1.0f, 2.0f, 3.0f);
-  return float3(1.0f, 2.0f, 3.0f)[1];
+  return 2.0f;
 }
diff --git a/test/tint/expressions/index/let/literal/vector.wgsl.expected.msl b/test/tint/expressions/index/let/literal/vector.wgsl.expected.msl
index 70f2313..cb5b4bb 100644
--- a/test/tint/expressions/index/let/literal/vector.wgsl.expected.msl
+++ b/test/tint/expressions/index/let/literal/vector.wgsl.expected.msl
@@ -3,6 +3,6 @@
 using namespace metal;
 float f() {
   float3 const v = float3(1.0f, 2.0f, 3.0f);
-  return float3(1.0f, 2.0f, 3.0f)[1];
+  return 2.0f;
 }
 
diff --git a/test/tint/expressions/index/let/literal/vector.wgsl.expected.spvasm b/test/tint/expressions/index/let/literal/vector.wgsl.expected.spvasm
index d274e46..0c4b98e 100644
--- a/test/tint/expressions/index/let/literal/vector.wgsl.expected.spvasm
+++ b/test/tint/expressions/index/let/literal/vector.wgsl.expected.spvasm
@@ -1,7 +1,7 @@
 ; SPIR-V
 ; Version: 1.3
 ; Generator: Google Tint Compiler; 0
-; Bound: 17
+; Bound: 14
 ; Schema: 0
                OpCapability Shader
                OpMemoryModel Logical GLSL450
@@ -18,14 +18,11 @@
     %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
+               OpReturnValue %float_2
                OpFunctionEnd