Просмотр исходного кода

Use `TupleAccess` instead of `TupleIndex` (#4318)

This change removes the `TupleIndex` instruction, and instead
consolidate it with the `TupleAccess` instruction, per this
[discussion](https://discord.com/channels/655572317891461132/655578254970716160/1271195835975204946).
This change, in turn removes `AnyAggregateIndex`.
Brymer Meneses 1 год назад
Родитель
Сommit
7f930d0f58
34 измененных файлов с 75 добавлено и 125 удалено
  1. 5 9
      toolchain/check/eval.cpp
  2. 2 2
      toolchain/check/handle_name.cpp
  3. 23 20
      toolchain/check/member_access.cpp
  4. 3 3
      toolchain/check/member_access.h
  5. 1 1
      toolchain/check/testdata/eval/aggregate.carbon
  6. 1 1
      toolchain/check/testdata/expr_category/in_place_tuple_init.carbon
  7. 1 1
      toolchain/check/testdata/function/call/more_param_ir.carbon
  8. 1 1
      toolchain/check/testdata/function/definition/import.carbon
  9. 1 1
      toolchain/check/testdata/let/convert.carbon
  10. 2 2
      toolchain/check/testdata/operators/builtin/assignment.carbon
  11. 2 2
      toolchain/check/testdata/pointer/address_of_lvalue.carbon
  12. 1 1
      toolchain/check/testdata/pointer/fail_address_of_value.carbon
  13. 1 1
      toolchain/check/testdata/tuple/access/element_access.carbon
  14. 0 1
      toolchain/check/testdata/tuple/access/fail_access_error.carbon
  15. 0 1
      toolchain/check/testdata/tuple/access/fail_empty_access.carbon
  16. 2 4
      toolchain/check/testdata/tuple/access/fail_large_index.carbon
  17. 1 2
      toolchain/check/testdata/tuple/access/fail_negative_indexing.carbon
  18. 1 2
      toolchain/check/testdata/tuple/access/fail_non_deterministic_type.carbon
  19. 0 1
      toolchain/check/testdata/tuple/access/fail_non_int_indexing.carbon
  20. 1 2
      toolchain/check/testdata/tuple/access/fail_out_of_bound_access.carbon
  21. 0 1
      toolchain/check/testdata/tuple/access/fail_out_of_bound_not_literal.carbon
  22. 1 1
      toolchain/check/testdata/tuple/access/index_not_literal.carbon
  23. 1 1
      toolchain/check/testdata/tuple/access/return_value_access.carbon
  24. 1 2
      toolchain/check/testdata/var/no_prelude/fail_init_type_mismatch.carbon
  25. 0 10
      toolchain/lower/handle_aggregates.cpp
  26. 3 3
      toolchain/lower/testdata/class/value_access.carbon
  27. 8 8
      toolchain/lower/testdata/function/call/tuple_param_with_return_slot.carbon
  28. 5 5
      toolchain/lower/testdata/let/tuple.carbon
  29. 4 4
      toolchain/lower/testdata/tuple/access/element_access.carbon
  30. 2 2
      toolchain/lower/testdata/tuple/access/return_value_access.carbon
  31. 0 6
      toolchain/sem_ir/file.cpp
  32. 1 0
      toolchain/sem_ir/ids.h
  33. 0 1
      toolchain/sem_ir/inst_kind.def
  34. 0 23
      toolchain/sem_ir/typed_insts.h

+ 5 - 9
toolchain/check/eval.cpp

@@ -480,9 +480,9 @@ static auto PerformAggregateAccess(EvalContext& eval_context, SemIR::Inst inst)
 
 // Performs an index into a homogeneous aggregate, retrieving the specified
 // element.
-static auto PerformAggregateIndex(EvalContext& eval_context, SemIR::Inst inst)
+static auto PerformArrayIndex(EvalContext& eval_context, SemIR::Inst inst)
     -> SemIR::ConstantId {
-  auto index_inst = inst.As<SemIR::AnyAggregateIndex>();
+  auto index_inst = inst.As<SemIR::ArrayIndex>();
   Phase phase = Phase::Template;
   auto index_id = GetConstantValue(eval_context, index_inst.index_id, &phase);
 
@@ -500,7 +500,7 @@ static auto PerformAggregateIndex(EvalContext& eval_context, SemIR::Inst inst)
   // regardless of whether the array itself is constant.
   const auto& index_val = eval_context.ints().Get(index->int_id);
   auto aggregate_type_id = eval_context.GetConstantValueAsType(
-      eval_context.insts().Get(index_inst.aggregate_id).type_id());
+      eval_context.insts().Get(index_inst.array_id).type_id());
   if (auto array_type =
           eval_context.types().TryGetAs<SemIR::ArrayType>(aggregate_type_id)) {
     if (auto bound = eval_context.insts().TryGetAs<SemIR::IntLiteral>(
@@ -523,7 +523,7 @@ static auto PerformAggregateIndex(EvalContext& eval_context, SemIR::Inst inst)
   }
 
   auto aggregate_id =
-      GetConstantValue(eval_context, index_inst.aggregate_id, &phase);
+      GetConstantValue(eval_context, index_inst.array_id, &phase);
   if (!aggregate_id.is_valid()) {
     return MakeNonConstantResult(phase);
   }
@@ -536,9 +536,6 @@ static auto PerformAggregateIndex(EvalContext& eval_context, SemIR::Inst inst)
   }
 
   auto elements = eval_context.inst_blocks().Get(aggregate->elements_id);
-  // We checked this for the array case above.
-  CARBON_CHECK(index_val.ult(elements.size()),
-               "Index out of bounds in tuple indexing");
   return eval_context.GetConstantValue(elements[index_val.getZExtValue()]);
 }
 
@@ -1336,8 +1333,7 @@ auto TryEvalInstInContext(EvalContext& eval_context, SemIR::InstId inst_id,
     case SemIR::TupleAccess::Kind:
       return PerformAggregateAccess(eval_context, inst);
     case SemIR::ArrayIndex::Kind:
-    case SemIR::TupleIndex::Kind:
-      return PerformAggregateIndex(eval_context, inst);
+      return PerformArrayIndex(eval_context, inst);
 
     case CARBON_KIND(SemIR::Call call): {
       return MakeConstantForCall(eval_context, inst_id, call);

+ 2 - 2
toolchain/check/handle_name.cpp

@@ -29,7 +29,7 @@ auto HandleParseNode(Context& context, Parse::MemberAccessExprId node_id)
     auto tuple_inst_id = context.node_stack().PopExpr();
 
     auto tuple_value_inst_id =
-        PerformTupleIndex(context, node_id, tuple_inst_id, index_inst_id);
+        PerformTupleAccess(context, node_id, tuple_inst_id, index_inst_id);
 
     context.node_stack().Push(node_id, tuple_value_inst_id);
   } else {
@@ -70,7 +70,7 @@ auto HandleParseNode(Context& context, Parse::PointerMemberAccessExprId node_id)
     auto tuple_inst_id = PerformPointerDereference(
         context, node_id, tuple_pointer_inst_id, diagnose_not_pointer);
     auto tuple_value_inst_id =
-        PerformTupleIndex(context, node_id, tuple_inst_id, index_inst_id);
+        PerformTupleAccess(context, node_id, tuple_inst_id, index_inst_id);
 
     context.node_stack().Push(node_id, tuple_value_inst_id);
   } else {

+ 23 - 20
toolchain/check/member_access.cpp

@@ -498,7 +498,7 @@ auto PerformCompoundMemberAccess(
                           member_id, missing_impl_diagnoser);
   } else if (context.insts().Is<SemIR::TupleType>(
                  context.constant_values().GetInstId(base_type_const_id))) {
-    return PerformTupleIndex(context, loc_id, base_id, member_expr_id);
+    return PerformTupleAccess(context, loc_id, base_id, member_expr_id);
   }
 
   // Perform instance binding if we found an instance member.
@@ -518,9 +518,9 @@ auto PerformCompoundMemberAccess(
   return member_id;
 }
 
-auto PerformTupleIndex(Context& context, SemIR::LocId loc_id,
-                       SemIR::InstId tuple_inst_id, SemIR::InstId index_inst_id)
-    -> SemIR::InstId {
+auto PerformTupleAccess(Context& context, SemIR::LocId loc_id,
+                        SemIR::InstId tuple_inst_id,
+                        SemIR::InstId index_inst_id) -> SemIR::InstId {
   tuple_inst_id = ConvertToValueOrRefExpr(context, tuple_inst_id);
   auto tuple_inst = context.insts().Get(tuple_inst_id);
   auto tuple_type_id = tuple_inst.type_id();
@@ -542,29 +542,32 @@ auto PerformTupleIndex(Context& context, SemIR::LocId loc_id,
       context.GetBuiltinType(SemIR::BuiltinInstKind::IntType));
   auto index_const_id = context.constant_values().Get(index_inst_id);
   if (index_const_id == SemIR::ConstantId::Error) {
-    index_inst_id = SemIR::InstId::BuiltinError;
+    return SemIR::InstId::BuiltinError;
   } else if (!index_const_id.is_template()) {
     // TODO: Decide what to do if the index is a symbolic constant.
     CARBON_DIAGNOSTIC(TupleIndexNotConstant, Error,
                       "Tuple index must be a constant.");
     context.emitter().Emit(loc_id, TupleIndexNotConstant);
-    index_inst_id = SemIR::InstId::BuiltinError;
-  } else {
-    auto index_literal = context.insts().GetAs<SemIR::IntLiteral>(
-        context.constant_values().GetInstId(index_const_id));
-    auto type_block = context.type_blocks().Get(tuple_type->elements_id);
-    if (const auto* index_val = ValidateTupleIndex(
-            context, loc_id, tuple_inst, index_literal, type_block.size())) {
-      element_type_id = type_block[index_val->getZExtValue()];
-    } else {
-      index_inst_id = SemIR::InstId::BuiltinError;
-    }
+    return SemIR::InstId::BuiltinError;
   }
 
-  return context.AddInst<SemIR::TupleIndex>(loc_id,
-                                            {.type_id = element_type_id,
-                                             .tuple_id = tuple_inst_id,
-                                             .index_id = index_inst_id});
+  auto index_literal = context.insts().GetAs<SemIR::IntLiteral>(
+      context.constant_values().GetInstId(index_const_id));
+  auto type_block = context.type_blocks().Get(tuple_type->elements_id);
+  const auto* index_val = ValidateTupleIndex(context, loc_id, tuple_inst,
+                                             index_literal, type_block.size());
+  if (!index_val) {
+    return SemIR::InstId::BuiltinError;
+  }
+
+  // TODO: Handle the case when `index_val->getZExtValue()` has too many bits.
+  element_type_id = type_block[index_val->getZExtValue()];
+  auto tuple_index = SemIR::ElementIndex(index_val->getZExtValue());
+
+  return context.AddInst<SemIR::TupleAccess>(loc_id,
+                                             {.type_id = element_type_id,
+                                              .tuple_id = tuple_inst_id,
+                                              .index = tuple_index});
 }
 
 }  // namespace Carbon::Check

+ 3 - 3
toolchain/check/member_access.h

@@ -28,9 +28,9 @@ auto PerformCompoundMemberAccess(
 
 // Creates SemIR to perform a tuple index with base expression `tuple_inst_id`
 // and index expression `index_inst_id`. Returns the result of the access.
-auto PerformTupleIndex(Context& context, SemIR::LocId loc_id,
-                       SemIR::InstId tuple_inst_id, SemIR::InstId index_inst_id)
-    -> SemIR::InstId;
+auto PerformTupleAccess(Context& context, SemIR::LocId loc_id,
+                        SemIR::InstId tuple_inst_id,
+                        SemIR::InstId index_inst_id) -> SemIR::InstId;
 
 }  // namespace Carbon::Check
 

+ 1 - 1
toolchain/check/testdata/eval/aggregate.carbon

@@ -175,7 +175,7 @@ var struct_access: [i32; 1] = (0,) as [i32; {.a = 3, .b = 1}.b];
 // CHECK:STDOUT:   %.loc15_56: i32 = int_literal 2 [template = constants.%.6]
 // CHECK:STDOUT:   %tuple: %.20 = tuple_value (%.loc15_44, %.loc15_47, %.loc15_50, %.loc15_53) [template = constants.%tuple.2]
 // CHECK:STDOUT:   %.loc15_54.2: %.20 = converted %.loc15_54.1, %tuple [template = constants.%tuple.2]
-// CHECK:STDOUT:   %.loc15_55: i32 = tuple_index %.loc15_54.2, %.loc15_56 [template = constants.%.5]
+// CHECK:STDOUT:   %.loc15_55: i32 = tuple_access %.loc15_54.2, element2 [template = constants.%.5]
 // CHECK:STDOUT:   %.loc15_38.1: type = value_of_initializer %int.make_type_32.loc15 [template = i32]
 // CHECK:STDOUT:   %.loc15_38.2: type = converted %int.make_type_32.loc15, %.loc15_38.1 [template = i32]
 // CHECK:STDOUT:   %.loc15_57: type = array_type %.loc15_55, i32 [template = constants.%.13]

+ 1 - 1
toolchain/check/testdata/expr_category/in_place_tuple_init.carbon

@@ -129,7 +129,7 @@ fn H() -> i32 {
 // CHECK:STDOUT:   %G.call: init %.3 = call %G.ref() to %.loc20_11.1
 // CHECK:STDOUT:   %.loc20_14: i32 = int_literal 0 [template = constants.%.5]
 // CHECK:STDOUT:   %.loc20_11.2: ref %.3 = temporary %.loc20_11.1, %G.call
-// CHECK:STDOUT:   %.loc20_13.1: ref i32 = tuple_index %.loc20_11.2, %.loc20_14
+// CHECK:STDOUT:   %.loc20_13.1: ref i32 = tuple_access %.loc20_11.2, element0
 // CHECK:STDOUT:   %.loc20_13.2: i32 = bind_value %.loc20_13.1
 // CHECK:STDOUT:   return %.loc20_13.2
 // CHECK:STDOUT: }

+ 1 - 1
toolchain/check/testdata/function/call/more_param_ir.carbon

@@ -95,7 +95,7 @@ fn Main() {
 // CHECK:STDOUT:   %Foo.ref: %Foo.type = name_ref Foo, file.%Foo.decl [template = constants.%Foo]
 // CHECK:STDOUT:   %x.ref: ref %.3 = name_ref x, %x
 // CHECK:STDOUT:   %.loc16_9: i32 = int_literal 0 [template = constants.%.5]
-// CHECK:STDOUT:   %.loc16_8.1: ref i32 = tuple_index %x.ref, %.loc16_9
+// CHECK:STDOUT:   %.loc16_8.1: ref i32 = tuple_access %x.ref, element0
 // CHECK:STDOUT:   %.loc16_12: i32 = int_literal 6 [template = constants.%.6]
 // CHECK:STDOUT:   %.loc16_8.2: i32 = bind_value %.loc16_8.1
 // CHECK:STDOUT:   %Foo.call: init %.1 = call %Foo.ref(%.loc16_8.2, %.loc16_12)

+ 1 - 1
toolchain/check/testdata/function/definition/import.carbon

@@ -209,7 +209,7 @@ fn D() {}
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %c.ref: %.3 = name_ref c, %c
 // CHECK:STDOUT:   %.loc6_47: i32 = int_literal 0 [template = constants.%.5]
-// CHECK:STDOUT:   %.loc6_46: i32 = tuple_index %c.ref, %.loc6_47
+// CHECK:STDOUT:   %.loc6_46: i32 = tuple_access %c.ref, element0
 // CHECK:STDOUT:   %.loc6_48: %.4 = struct_literal (%.loc6_46)
 // CHECK:STDOUT:   %struct: %.4 = struct_value (%.loc6_46)
 // CHECK:STDOUT:   %.loc6_49: %.4 = converted %.loc6_48, %struct

+ 1 - 1
toolchain/check/testdata/let/convert.carbon

@@ -114,7 +114,7 @@ fn F() -> i32 {
 // CHECK:STDOUT:   %w: %.3 = bind_name w, %.loc14_29
 // CHECK:STDOUT:   %w.ref: %.3 = name_ref w, %w
 // CHECK:STDOUT:   %.loc15_12: i32 = int_literal 1 [template = constants.%.5]
-// CHECK:STDOUT:   %.loc15_11: i32 = tuple_index %w.ref, %.loc15_12
+// CHECK:STDOUT:   %.loc15_11: i32 = tuple_access %w.ref, element1
 // CHECK:STDOUT:   return %.loc15_11
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 2 - 2
toolchain/check/testdata/operators/builtin/assignment.carbon

@@ -112,12 +112,12 @@ fn Main() {
 // CHECK:STDOUT:   assign %b.var, %.loc15_29
 // CHECK:STDOUT:   %b.ref.loc16: ref %.5 = name_ref b, %b
 // CHECK:STDOUT:   %.loc16_5: i32 = int_literal 0 [template = constants.%.9]
-// CHECK:STDOUT:   %.loc16_4: ref i32 = tuple_index %b.ref.loc16, %.loc16_5
+// CHECK:STDOUT:   %.loc16_4: ref i32 = tuple_access %b.ref.loc16, element0
 // CHECK:STDOUT:   %.loc16_9: i32 = int_literal 3 [template = constants.%.10]
 // CHECK:STDOUT:   assign %.loc16_4, %.loc16_9
 // CHECK:STDOUT:   %b.ref.loc17: ref %.5 = name_ref b, %b
 // CHECK:STDOUT:   %.loc17_5: i32 = int_literal 1 [template = constants.%.7]
-// CHECK:STDOUT:   %.loc17_4: ref i32 = tuple_index %b.ref.loc17, %.loc17_5
+// CHECK:STDOUT:   %.loc17_4: ref i32 = tuple_access %b.ref.loc17, element1
 // CHECK:STDOUT:   %.loc17_9: i32 = int_literal 4 [template = constants.%.11]
 // CHECK:STDOUT:   assign %.loc17_4, %.loc17_9
 // CHECK:STDOUT:   %int.make_type_32.loc19_15: init type = call constants.%Int32() [template = i32]

+ 2 - 2
toolchain/check/testdata/pointer/address_of_lvalue.carbon

@@ -147,7 +147,7 @@ fn F() {
 // CHECK:STDOUT:   %t0: ref %.6 = bind_name t0, %t0.var
 // CHECK:STDOUT:   %t.ref.loc19: ref %.8 = name_ref t, %t
 // CHECK:STDOUT:   %.loc19_21: i32 = int_literal 0 [template = constants.%.10]
-// CHECK:STDOUT:   %.loc19_20: ref i32 = tuple_index %t.ref.loc19, %.loc19_21
+// CHECK:STDOUT:   %.loc19_20: ref i32 = tuple_access %t.ref.loc19, element0
 // CHECK:STDOUT:   %.loc19_18: %.6 = addr_of %.loc19_20
 // CHECK:STDOUT:   assign %t0.var, %.loc19_18
 // CHECK:STDOUT:   %int.make_type_32.loc20: init type = call constants.%Int32() [template = i32]
@@ -158,7 +158,7 @@ fn F() {
 // CHECK:STDOUT:   %t1: ref %.6 = bind_name t1, %t1.var
 // CHECK:STDOUT:   %t.ref.loc20: ref %.8 = name_ref t, %t
 // CHECK:STDOUT:   %.loc20_21: i32 = int_literal 1 [template = constants.%.4]
-// CHECK:STDOUT:   %.loc20_20: ref i32 = tuple_index %t.ref.loc20, %.loc20_21
+// CHECK:STDOUT:   %.loc20_20: ref i32 = tuple_access %t.ref.loc20, element1
 // CHECK:STDOUT:   %.loc20_18: %.6 = addr_of %.loc20_20
 // CHECK:STDOUT:   assign %t1.var, %.loc20_18
 // CHECK:STDOUT:   return

+ 1 - 1
toolchain/check/testdata/pointer/fail_address_of_value.carbon

@@ -278,7 +278,7 @@ fn AddressOfParam(param: i32) {
 // CHECK:STDOUT:   %.loc92_12: i32 = int_literal 0 [template = constants.%.3]
 // CHECK:STDOUT:   %tuple: %.13 = tuple_value (%.loc92_6, %.loc92_9) [template = constants.%tuple]
 // CHECK:STDOUT:   %.loc92_10.2: %.13 = converted %.loc92_10.1, %tuple [template = constants.%tuple]
-// CHECK:STDOUT:   %.loc92_11: i32 = tuple_index %.loc92_10.2, %.loc92_12 [template = constants.%.11]
+// CHECK:STDOUT:   %.loc92_11: i32 = tuple_access %.loc92_10.2, element0 [template = constants.%.11]
 // CHECK:STDOUT:   %.loc92_3: %.4 = addr_of <error> [template = <error>]
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 1 - 1
toolchain/check/testdata/tuple/access/element_access.carbon

@@ -86,7 +86,7 @@ var c: i32 = b.0;
 // CHECK:STDOUT:   assign file.%b.var, %.loc12_18
 // CHECK:STDOUT:   %b.ref: ref %.3 = name_ref b, file.%b
 // CHECK:STDOUT:   %.loc13_16: i32 = int_literal 0 [template = constants.%.5]
-// CHECK:STDOUT:   %.loc13_15.1: ref i32 = tuple_index %b.ref, %.loc13_16
+// CHECK:STDOUT:   %.loc13_15.1: ref i32 = tuple_access %b.ref, element0
 // CHECK:STDOUT:   %.loc13_15.2: i32 = bind_value %.loc13_15.1
 // CHECK:STDOUT:   assign file.%c.var, %.loc13_15.2
 // CHECK:STDOUT:   return

+ 0 - 1
toolchain/check/testdata/tuple/access/fail_access_error.carbon

@@ -83,7 +83,6 @@ var b: i32 = a.(oops);
 // CHECK:STDOUT:   assign file.%a.var, %.loc11_28
 // CHECK:STDOUT:   %a.ref: ref %.3 = name_ref a, file.%a
 // CHECK:STDOUT:   %oops.ref: <error> = name_ref oops, <error> [template = <error>]
-// CHECK:STDOUT:   %.loc15: ref <error> = tuple_index %a.ref, <error> [template = <error>]
 // CHECK:STDOUT:   assign file.%b.var, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 0 - 1
toolchain/check/testdata/tuple/access/fail_empty_access.carbon

@@ -64,7 +64,6 @@ fn Run() {
 // CHECK:STDOUT:   %.loc17_7: i32 = int_literal 0 [template = constants.%.2]
 // CHECK:STDOUT:   %.loc17_4.1: ref %.1 = temporary_storage
 // CHECK:STDOUT:   %.loc17_4.2: ref %.1 = temporary %.loc17_4.1, %F.call
-// CHECK:STDOUT:   %.loc17_6: ref <error> = tuple_index %.loc17_4.2, <error> [template = <error>]
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 2 - 4
toolchain/check/testdata/tuple/access/fail_large_index.carbon

@@ -100,12 +100,10 @@ var d: i32 = b.(0x7FFF_FFFF);
 // CHECK:STDOUT:   %.loc12_18: init %.3 = converted %a.ref, %.loc12_17.3
 // CHECK:STDOUT:   assign file.%b.var, %.loc12_18
 // CHECK:STDOUT:   %b.ref.loc17: ref %.3 = name_ref b, file.%b
-// CHECK:STDOUT:   %.loc17_16: i32 = int_literal 1 [template = constants.%.5]
-// CHECK:STDOUT:   %.loc17_15: ref <error> = tuple_index %b.ref.loc17, <error> [template = <error>]
+// CHECK:STDOUT:   %.loc17: i32 = int_literal 1 [template = constants.%.5]
 // CHECK:STDOUT:   assign file.%c.var, <error>
 // CHECK:STDOUT:   %b.ref.loc21: ref %.3 = name_ref b, file.%b
-// CHECK:STDOUT:   %.loc21_17: i32 = int_literal 2147483647 [template = constants.%.6]
-// CHECK:STDOUT:   %.loc21_15: ref <error> = tuple_index %b.ref.loc21, <error> [template = <error>]
+// CHECK:STDOUT:   %.loc21: i32 = int_literal 2147483647 [template = constants.%.6]
 // CHECK:STDOUT:   assign file.%d.var, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 1 - 2
toolchain/check/testdata/tuple/access/fail_negative_indexing.carbon

@@ -108,9 +108,8 @@ var b: i32 = a.(-10);
 // CHECK:STDOUT:   %.loc11_28: init %.3 = converted %.loc11_27.1, %.loc11_27.6 [template = constants.%tuple]
 // CHECK:STDOUT:   assign file.%a.var, %.loc11_28
 // CHECK:STDOUT:   %a.ref: ref %.3 = name_ref a, file.%a
-// CHECK:STDOUT:   %.loc15_18: i32 = int_literal 10 [template = constants.%.7]
+// CHECK:STDOUT:   %.loc15: i32 = int_literal 10 [template = constants.%.7]
 // CHECK:STDOUT:   %Op.ref: %.9 = name_ref Op, imports.%import_ref.4 [template = constants.%.10]
-// CHECK:STDOUT:   %.loc15_15: ref <error> = tuple_index %a.ref, <error> [template = <error>]
 // CHECK:STDOUT:   assign file.%b.var, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 1 - 2
toolchain/check/testdata/tuple/access/fail_non_deterministic_type.carbon

@@ -93,8 +93,7 @@ var c: i32 = a.(b);
 // CHECK:STDOUT:   assign file.%b.var, %.loc12
 // CHECK:STDOUT:   %a.ref: ref %.3 = name_ref a, file.%a
 // CHECK:STDOUT:   %b.ref: ref i32 = name_ref b, file.%b
-// CHECK:STDOUT:   %.loc16_17: i32 = bind_value %b.ref
-// CHECK:STDOUT:   %.loc16_15: ref <error> = tuple_index %a.ref, <error> [template = <error>]
+// CHECK:STDOUT:   %.loc16: i32 = bind_value %b.ref
 // CHECK:STDOUT:   assign file.%c.var, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 0 - 1
toolchain/check/testdata/tuple/access/fail_non_int_indexing.carbon

@@ -141,7 +141,6 @@ var b: i32 = a.(2.6);
 // CHECK:STDOUT:   %.loc18_17.3: %.12 = specific_constant imports.%import_ref.4, @ImplicitAs(i32) [template = constants.%.13]
 // CHECK:STDOUT:   %Convert.ref: %.12 = name_ref Convert, %.loc18_17.3 [template = constants.%.13]
 // CHECK:STDOUT:   %.loc18_17.4: i32 = converted %.loc18_17.1, <error> [template = <error>]
-// CHECK:STDOUT:   %.loc18_15: ref <error> = tuple_index %a.ref, <error> [template = <error>]
 // CHECK:STDOUT:   assign file.%b.var, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 1 - 2
toolchain/check/testdata/tuple/access/fail_out_of_bound_access.carbon

@@ -83,8 +83,7 @@ var b: i32 = a.2;
 // CHECK:STDOUT:   %.loc11_28: init %.3 = converted %.loc11_27.1, %.loc11_27.6 [template = constants.%tuple]
 // CHECK:STDOUT:   assign file.%a.var, %.loc11_28
 // CHECK:STDOUT:   %a.ref: ref %.3 = name_ref a, file.%a
-// CHECK:STDOUT:   %.loc15_16: i32 = int_literal 2 [template = constants.%.7]
-// CHECK:STDOUT:   %.loc15_15: ref <error> = tuple_index %a.ref, <error> [template = <error>]
+// CHECK:STDOUT:   %.loc15: i32 = int_literal 2 [template = constants.%.7]
 // CHECK:STDOUT:   assign file.%b.var, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 0 - 1
toolchain/check/testdata/tuple/access/fail_out_of_bound_not_literal.carbon

@@ -90,7 +90,6 @@ var b: i32 = a.({.index = 2}.index);
 // CHECK:STDOUT:   %struct: %.8 = struct_value (%.loc15_27) [template = constants.%struct]
 // CHECK:STDOUT:   %.loc15_28.2: %.8 = converted %.loc15_28.1, %struct [template = constants.%struct]
 // CHECK:STDOUT:   %.loc15_29: i32 = struct_access %.loc15_28.2, element0 [template = constants.%.7]
-// CHECK:STDOUT:   %.loc15_15: ref <error> = tuple_index %a.ref, <error> [template = <error>]
 // CHECK:STDOUT:   assign file.%b.var, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 1 - 1
toolchain/check/testdata/tuple/access/index_not_literal.carbon

@@ -87,7 +87,7 @@ var b: i32 = a.({.index = 1}.index);
 // CHECK:STDOUT:   %struct: %.8 = struct_value (%.loc12_27) [template = constants.%struct]
 // CHECK:STDOUT:   %.loc12_28.2: %.8 = converted %.loc12_28.1, %struct [template = constants.%struct]
 // CHECK:STDOUT:   %.loc12_29: i32 = struct_access %.loc12_28.2, element0 [template = constants.%.7]
-// CHECK:STDOUT:   %.loc12_15.1: ref i32 = tuple_index %a.ref, %.loc12_29
+// CHECK:STDOUT:   %.loc12_15.1: ref i32 = tuple_access %a.ref, element1
 // CHECK:STDOUT:   %.loc12_15.2: i32 = bind_value %.loc12_15.1
 // CHECK:STDOUT:   assign file.%b.var, %.loc12_15.2
 // CHECK:STDOUT:   return

+ 1 - 1
toolchain/check/testdata/tuple/access/return_value_access.carbon

@@ -86,7 +86,7 @@ fn Run() -> i32 {
 // CHECK:STDOUT:   %.loc14_14: i32 = int_literal 0 [template = constants.%.4]
 // CHECK:STDOUT:   %.loc14_11.1: ref %.3 = temporary_storage
 // CHECK:STDOUT:   %.loc14_11.2: ref %.3 = temporary %.loc14_11.1, %F.call
-// CHECK:STDOUT:   %.loc14_13.1: ref i32 = tuple_index %.loc14_11.2, %.loc14_14
+// CHECK:STDOUT:   %.loc14_13.1: ref i32 = tuple_access %.loc14_11.2, element0
 // CHECK:STDOUT:   %.loc14_13.2: i32 = bind_value %.loc14_13.1
 // CHECK:STDOUT:   return %.loc14_13.2
 // CHECK:STDOUT: }

+ 1 - 2
toolchain/check/testdata/var/no_prelude/fail_init_type_mismatch.carbon

@@ -41,8 +41,7 @@ fn Main() {
 // CHECK:STDOUT:   %.loc15_16.1: %.1 = tuple_literal ()
 // CHECK:STDOUT:   %tuple: %.1 = tuple_value () [template = constants.%tuple]
 // CHECK:STDOUT:   %.loc15_16.2: %.1 = converted %.loc15_16.1, %tuple [template = constants.%tuple]
-// CHECK:STDOUT:   %.loc15_17.1: <error> = tuple_index %.loc15_16.2, <error> [template = <error>]
-// CHECK:STDOUT:   %.loc15_17.2: %.2 = converted %.loc15_16.1, <error> [template = <error>]
+// CHECK:STDOUT:   %.loc15_17: %.2 = converted %.loc15_16.1, <error> [template = <error>]
 // CHECK:STDOUT:   assign %x.var, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 0 - 10
toolchain/lower/handle_aggregates.cpp

@@ -248,16 +248,6 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
                                        inst.type_id, "tuple.elem"));
 }
 
-auto HandleInst(FunctionContext& context, SemIR::InstId inst_id,
-                SemIR::TupleIndex inst) -> void {
-  auto index_inst =
-      context.sem_ir().insts().GetAs<SemIR::IntLiteral>(inst.index_id);
-  auto index = context.sem_ir().ints().Get(index_inst.int_id).getZExtValue();
-  context.SetLocal(inst_id, GetAggregateElement(context, inst.tuple_id,
-                                                SemIR::ElementIndex(index),
-                                                inst.type_id, "tuple.index"));
-}
-
 auto HandleInst(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/,
                 SemIR::TupleLiteral /*inst*/) -> void {
   // A TupleLiteral should always be converted to a TupleInit or TupleValue if

+ 3 - 3
toolchain/lower/testdata/class/value_access.carbon

@@ -38,9 +38,9 @@ fn F(c: C) -> i32 {
 // CHECK:STDOUT:   store i32 %.loc19_11.5, ptr %tuple2, align 4, !dbg !7
 // CHECK:STDOUT:   %tuple3 = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple, i32 0, i32 2, !dbg !7
 // CHECK:STDOUT:   store i32 %.loc19_11.7, ptr %tuple3, align 4, !dbg !7
-// CHECK:STDOUT:   %.loc19_13.tuple.index = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple, i32 0, i32 1, !dbg !7
-// CHECK:STDOUT:   %.loc19_13.tuple.index.load = load i32, ptr %.loc19_13.tuple.index, align 4, !dbg !7
-// CHECK:STDOUT:   ret i32 %.loc19_13.tuple.index.load, !dbg !8
+// CHECK:STDOUT:   %.loc19_13.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %tuple, i32 0, i32 1, !dbg !7
+// CHECK:STDOUT:   %.loc19_13.tuple.elem.load = load i32, ptr %.loc19_13.tuple.elem, align 4, !dbg !7
+// CHECK:STDOUT:   ret i32 %.loc19_13.tuple.elem.load, !dbg !8
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1}

+ 8 - 8
toolchain/lower/testdata/function/call/tuple_param_with_return_slot.carbon

@@ -23,17 +23,17 @@ fn Main() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @_CF.Main(ptr sret({ i32, i32, i32 }) %return, { i32 } %b, ptr %c) !dbg !4 {
 // CHECK:STDOUT: entry:
-// CHECK:STDOUT:   %.loc12_12.tuple.index = extractvalue { i32 } %b, 0, !dbg !7
-// CHECK:STDOUT:   %.loc12_17.tuple.index = getelementptr inbounds nuw { i32, i32 }, ptr %c, i32 0, i32 0, !dbg !8
-// CHECK:STDOUT:   %.loc12_17.tuple.index.load = load i32, ptr %.loc12_17.tuple.index, align 4, !dbg !8
-// CHECK:STDOUT:   %.loc12_22.tuple.index = getelementptr inbounds nuw { i32, i32 }, ptr %c, i32 0, i32 1, !dbg !9
-// CHECK:STDOUT:   %.loc12_22.tuple.index.load = load i32, ptr %.loc12_22.tuple.index, align 4, !dbg !9
+// CHECK:STDOUT:   %.loc12_12.tuple.elem = extractvalue { i32 } %b, 0, !dbg !7
+// CHECK:STDOUT:   %.loc12_17.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %c, i32 0, i32 0, !dbg !8
+// CHECK:STDOUT:   %.loc12_17.tuple.elem.load = load i32, ptr %.loc12_17.tuple.elem, align 4, !dbg !8
+// CHECK:STDOUT:   %.loc12_22.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %c, i32 0, i32 1, !dbg !9
+// CHECK:STDOUT:   %.loc12_22.tuple.elem.load = load i32, ptr %.loc12_22.tuple.elem, align 4, !dbg !9
 // CHECK:STDOUT:   %.loc12_24.2.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %return, i32 0, i32 0, !dbg !10
-// CHECK:STDOUT:   store i32 %.loc12_12.tuple.index, ptr %.loc12_24.2.tuple.elem, align 4, !dbg !10
+// CHECK:STDOUT:   store i32 %.loc12_12.tuple.elem, ptr %.loc12_24.2.tuple.elem, align 4, !dbg !10
 // CHECK:STDOUT:   %.loc12_24.4.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %return, i32 0, i32 1, !dbg !10
-// CHECK:STDOUT:   store i32 %.loc12_17.tuple.index.load, ptr %.loc12_24.4.tuple.elem, align 4, !dbg !10
+// CHECK:STDOUT:   store i32 %.loc12_17.tuple.elem.load, ptr %.loc12_24.4.tuple.elem, align 4, !dbg !10
 // CHECK:STDOUT:   %.loc12_24.6.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %return, i32 0, i32 2, !dbg !10
-// CHECK:STDOUT:   store i32 %.loc12_22.tuple.index.load, ptr %.loc12_24.6.tuple.elem, align 4, !dbg !10
+// CHECK:STDOUT:   store i32 %.loc12_22.tuple.elem.load, ptr %.loc12_24.6.tuple.elem, align 4, !dbg !10
 // CHECK:STDOUT:   ret void, !dbg !11
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 5 - 5
toolchain/lower/testdata/let/tuple.carbon

@@ -59,11 +59,11 @@ fn F() -> i32 {
 // CHECK:STDOUT:   store ptr %tuple.loc14_43, ptr %tuple.loc14_476, align 8, !dbg !15
 // CHECK:STDOUT:   %tuple.loc14_477 = getelementptr inbounds nuw { ptr, ptr }, ptr %tuple.loc14_47, i32 0, i32 1, !dbg !15
 // CHECK:STDOUT:   store ptr %tuple.loc14_46, ptr %tuple.loc14_477, align 8, !dbg !15
-// CHECK:STDOUT:   %.loc15_11.tuple.index = getelementptr inbounds nuw { ptr, ptr }, ptr %tuple.loc14_47, i32 0, i32 1, !dbg !16
-// CHECK:STDOUT:   %.loc15_11.tuple.index.load = load ptr, ptr %.loc15_11.tuple.index, align 8, !dbg !16
-// CHECK:STDOUT:   %.loc15_13.tuple.index = getelementptr inbounds nuw { i32, i32 }, ptr %.loc15_11.tuple.index.load, i32 0, i32 1, !dbg !16
-// CHECK:STDOUT:   %.loc15_13.tuple.index.load = load i32, ptr %.loc15_13.tuple.index, align 4, !dbg !16
-// CHECK:STDOUT:   ret i32 %.loc15_13.tuple.index.load, !dbg !17
+// CHECK:STDOUT:   %.loc15_11.tuple.elem = getelementptr inbounds nuw { ptr, ptr }, ptr %tuple.loc14_47, i32 0, i32 1, !dbg !16
+// CHECK:STDOUT:   %.loc15_11.tuple.elem.load = load ptr, ptr %.loc15_11.tuple.elem, align 8, !dbg !16
+// CHECK:STDOUT:   %.loc15_13.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %.loc15_11.tuple.elem.load, i32 0, i32 1, !dbg !16
+// CHECK:STDOUT:   %.loc15_13.tuple.elem.load = load i32, ptr %.loc15_13.tuple.elem, align 4, !dbg !16
+// CHECK:STDOUT:   ret i32 %.loc15_13.tuple.elem.load, !dbg !17
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)

+ 4 - 4
toolchain/lower/testdata/tuple/access/element_access.carbon

@@ -28,12 +28,12 @@ fn Run() -> i32 {
 // CHECK:STDOUT:   %.loc12_36.6.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 2, !dbg !8
 // CHECK:STDOUT:   call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a.var, ptr align 4 @tuple.loc12_37, i64 12, i1 false), !dbg !9
 // CHECK:STDOUT:   %b.var = alloca i32, align 4, !dbg !10
-// CHECK:STDOUT:   %.loc13_17.1.tuple.index = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 0, !dbg !11
-// CHECK:STDOUT:   %.loc13_17.2 = load i32, ptr %.loc13_17.1.tuple.index, align 4, !dbg !11
+// CHECK:STDOUT:   %.loc13_17.1.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 0, !dbg !11
+// CHECK:STDOUT:   %.loc13_17.2 = load i32, ptr %.loc13_17.1.tuple.elem, align 4, !dbg !11
 // CHECK:STDOUT:   store i32 %.loc13_17.2, ptr %b.var, align 4, !dbg !12
 // CHECK:STDOUT:   %c.var = alloca i32, align 4, !dbg !13
-// CHECK:STDOUT:   %.loc14_17.1.tuple.index = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 2, !dbg !14
-// CHECK:STDOUT:   %.loc14_17.2 = load i32, ptr %.loc14_17.1.tuple.index, align 4, !dbg !14
+// CHECK:STDOUT:   %.loc14_17.1.tuple.elem = getelementptr inbounds nuw { i32, i32, i32 }, ptr %a.var, i32 0, i32 2, !dbg !14
+// CHECK:STDOUT:   %.loc14_17.2 = load i32, ptr %.loc14_17.1.tuple.elem, align 4, !dbg !14
 // CHECK:STDOUT:   store i32 %.loc14_17.2, ptr %c.var, align 4, !dbg !15
 // CHECK:STDOUT:   ret i32 0, !dbg !16
 // CHECK:STDOUT: }

+ 2 - 2
toolchain/lower/testdata/tuple/access/return_value_access.carbon

@@ -32,8 +32,8 @@ fn Run() {
 // CHECK:STDOUT:   %t.var = alloca i32, align 4, !dbg !10
 // CHECK:STDOUT:   %.loc14_17.1.temp = alloca { i32, i32 }, align 8, !dbg !11
 // CHECK:STDOUT:   call void @_CF.Main(ptr %.loc14_17.1.temp), !dbg !11
-// CHECK:STDOUT:   %.loc14_19.1.tuple.index = getelementptr inbounds nuw { i32, i32 }, ptr %.loc14_17.1.temp, i32 0, i32 1, !dbg !11
-// CHECK:STDOUT:   %.loc14_19.2 = load i32, ptr %.loc14_19.1.tuple.index, align 4, !dbg !11
+// CHECK:STDOUT:   %.loc14_19.1.tuple.elem = getelementptr inbounds nuw { i32, i32 }, ptr %.loc14_17.1.temp, i32 0, i32 1, !dbg !11
+// CHECK:STDOUT:   %.loc14_19.2 = load i32, ptr %.loc14_19.1.tuple.elem, align 4, !dbg !11
 // CHECK:STDOUT:   store i32 %.loc14_19.2, ptr %t.var, align 4, !dbg !12
 // CHECK:STDOUT:   ret void, !dbg !13
 // CHECK:STDOUT: }

+ 0 - 6
toolchain/sem_ir/file.cpp

@@ -462,7 +462,6 @@ static auto StringifyTypeExprImpl(const SemIR::File& outer_sem_ir,
       case Temporary::Kind:
       case TemporaryStorage::Kind:
       case TupleAccess::Kind:
-      case TupleIndex::Kind:
       case TupleLiteral::Kind:
       case TupleInit::Kind:
       case TupleValue::Kind:
@@ -631,11 +630,6 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory {
         continue;
       }
 
-      case CARBON_KIND(TupleIndex inst): {
-        inst_id = inst.tuple_id;
-        continue;
-      }
-
       case CARBON_KIND(SpliceBlock inst): {
         inst_id = inst.result_id;
         continue;

+ 1 - 0
toolchain/sem_ir/ids.h

@@ -710,6 +710,7 @@ constexpr TypeBlockId TypeBlockId::Invalid = TypeBlockId(InvalidIndex);
 // An index for element access, for structs, tuples, and classes.
 struct ElementIndex : public IndexBase, public Printable<ElementIndex> {
   using IndexBase::IndexBase;
+
   auto Print(llvm::raw_ostream& out) const -> void {
     out << "element";
     IndexBase::Print(out);

+ 0 - 1
toolchain/sem_ir/inst_kind.def

@@ -86,7 +86,6 @@ CARBON_SEM_IR_INST_KIND(StructValue)
 CARBON_SEM_IR_INST_KIND(TemporaryStorage)
 CARBON_SEM_IR_INST_KIND(Temporary)
 CARBON_SEM_IR_INST_KIND(TupleAccess)
-CARBON_SEM_IR_INST_KIND(TupleIndex)
 CARBON_SEM_IR_INST_KIND(TupleInit)
 CARBON_SEM_IR_INST_KIND(TupleLiteral)
 CARBON_SEM_IR_INST_KIND(TupleType)

+ 0 - 23
toolchain/sem_ir/typed_insts.h

@@ -99,18 +99,6 @@ struct AnyAggregateAccess {
   ElementIndex index;
 };
 
-// Common representation for aggregate index nodes, which access an element
-// determined by evaluating an expression.
-struct AnyAggregateIndex {
-  static constexpr InstKind Kinds[] = {InstKind::ArrayIndex,
-                                       InstKind::TupleIndex};
-
-  InstKind kind;
-  TypeId type_id;
-  InstId aggregate_id;
-  InstId index_id;
-};
-
 // Common representation for all kinds of aggregate initialization.
 struct AnyAggregateInit {
   static constexpr InstKind Kinds[] = {InstKind::ArrayInit, InstKind::ClassInit,
@@ -961,17 +949,6 @@ struct TupleAccess {
   ElementIndex index;
 };
 
-// Access to a tuple member by index, such as `tuple[index]`.
-struct TupleIndex {
-  // TODO: Make Parse::NodeId more specific.
-  static constexpr auto Kind =
-      InstKind::TupleIndex.Define<Parse::NodeId>({.ir_name = "tuple_index"});
-
-  TypeId type_id;
-  InstId tuple_id;
-  InstId index_id;
-};
-
 // Initializes the destination tuple with the given elements.
 struct TupleInit {
   static constexpr auto Kind =