Repository: carbon-language/carbon-lang
File: toolchain/check/handle_index.cpp (C++)

Code before:
#include "toolchain/check/handle.h"
#include "toolchain/check/inst.h"
#include "toolchain/check/literal.h"
#include "toolchain/check/name_lookup.h"
#include "toolchain/check/operator.h"
#include "toolchain/check/type.h"
#include "toolchain/diagnostics/diagnostic.h"
#include "toolchain/sem_ir/expr_info.h"
#include "toolchain/sem_ir/inst.h"
#include "toolchain/sem_ir/typed_insts.h"

namespace Carbon::Check {

auto HandleParseNode(Context& /*context*/, Parse::IndexExprStartId /*node_id*/)
    -> bool {
  // Leave the expression on the stack for IndexExpr.
  return true;
}

// Performs bounds checking for string indexing when the index is a constant.
static auto CheckStringIndexBounds(Context& context,
                                   SemIR::InstId operand_inst_id,
                                   SemIR::InstId index_inst_id,
                                   const llvm::APInt& index_int) -> void {
  if (index_int.isNegative()) {
    CARBON_DIAGNOSTIC(ArrayIndexNegative, Error, "index `{0}` is negative.",
                      TypedInt);
    context.emitter().Emit(
        SemIR::LocId(index_inst_id), ArrayIndexNegative,
        {.type = context.insts().Get(index_inst_id).type_id(),
         .value = index_int});
    return;
  }

  auto operand_const_id = context.constant_values().Get(operand_inst_id);
  if (!operand_const_id.is_constant()) {
    return;
  }

  auto operand_const_inst_id =
      context.constant_values().GetInstId(operand_const_id);
  auto str_struct =
      context.insts().TryGetAs<SemIR::StructValue>(operand_const_inst_id);
  if (!str_struct) {
    return;
  }

  auto elements = context.inst_blocks().Get(str_struct->elements_id);
  CARBON_CHECK(elements.size() == 2, "String struct should have 2 fields.");

  auto ptr_const_id = context.constant_values().Get(elements[0]);

Review comment (suggestion):
```suggestion
    CARBON_DIAGNOSTIC(StringIndexNegative, Error, "index `{0}` is negative.",
```

Code after:
#include "toolchain/check/handle.h"
#include "toolchain/check/inst.h"
#include "toolchain/check/literal.h"
#include "toolchain/check/name_lookup.h"
#include "toolchain/check/operator.h"
#include "toolchain/check/type.h"
#include "toolchain/diagnostics/diagnostic.h"
#include "toolchain/sem_ir/expr_info.h"
#include "toolchain/sem_ir/inst.h"
#include "toolchain/sem_ir/typed_insts.h"

namespace Carbon::Check {

auto HandleParseNode(Context& /*context*/, Parse::IndexExprStartId /*node_id*/)
    -> bool {
  // Leave the expression on the stack for IndexExpr.
  return true;
}

// Performs an index with base expression `operand_inst_id` and
// `operand_type_id` for types that are not an array. This checks if
// the base expression implements the `IndexWith` interface; if so, uses the
// `At` associative method, otherwise prints a diagnostic.
static auto PerformIndexWith(Context& context, Parse::NodeId node_id,
                             SemIR::InstId operand_inst_id,
                             SemIR::InstId index_inst_id) -> SemIR::InstId {
  SemIR::InstId args[] = {
      context.types().GetInstId(context.insts().Get(index_inst_id).type_id())};
  Operator op{.interface_name = "IndexWith",
              .interface_args_ref = args,
              .op_name = "At"};
  return BuildBinaryOperator(context, node_id, op, operand_inst_id,
                             index_inst_id);
}

auto HandleParseNode(Context& context, Parse::IndexExprId node_id) -> bool {
  auto index_inst_id = context.node_stack().PopExpr();
  auto operand_inst_id = context.node_stack().PopExpr();
  operand_inst_id = ConvertToValueOrRefExpr(context, operand_inst_id);
  auto operand_inst = context.insts().Get(operand_inst_id);
  auto operand_type_id = operand_inst.type_id();

  CARBON_KIND_SWITCH(context.types().GetAsInst(operand_type_id)) {
    case CARBON_KIND(SemIR::ArrayType array_type): {
      auto cast_index_id = ConvertToValueOfType(
          context, SemIR::LocId(index_inst_id), index_inst_id,
          // TODO: Replace this with impl lookup rather than hardcoding `i32`.
          MakeIntType(context, node_id, SemIR::IntKind::Signed,
                      context.ints().Add(32)));
      auto array_cat =
          SemIR::GetExprCategory(context.sem_ir(), operand_inst_id);