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

Code before:
// the specified evaluation context. `*phase` is updated to describe the
// constant phase of the result. Returns whether `*phase` is a constant phase;
// if not, `inst` may not be fully updated and should not be used.
static auto ReplaceAllFieldsWithConstantValues(EvalContext& eval_context,
                                               SemIR::Inst* inst, Phase* phase)
    -> bool {
  auto arg0 =
      GetConstantValueForArg(eval_context, inst->arg0_and_kind(), phase);
  if (*phase == Phase::Runtime) {
    // We already know the final value will be runtime (phases can only increase
    // and runtime is the max), so we can early out.
    return false;
  }
  auto arg1 =
      GetConstantValueForArg(eval_context, inst->arg1_and_kind(), phase);
  if (!IsNonErrorConstant(*phase)) {
    return false;
  }
  inst->SetArgs(arg0, arg1);
  return true;
}

// Given an instruction and its ID, replaces its type with the corresponding
// value in this evaluation context. Updates `*phase` to describe the phase of
// the result, and returns whether `*phase` is a non-error constant phase.
static auto ReplaceTypeWithConstantValue(EvalContext& eval_context,
                                         SemIR::InstId inst_id,
                                         SemIR::Inst* inst, Phase* phase)
    -> bool {
  inst->SetType(GetTypeOfInst(eval_context, inst_id, *inst, phase));
  return IsNonErrorConstant(*phase);
}

template <typename InstT>
static auto ReplaceTypeWithConstantValue(EvalContext& eval_context,
                                         SemIR::InstId inst_id, InstT* inst,
                                         Phase* phase) -> bool {
  inst->type_id = GetTypeOfInst(eval_context, inst_id, *inst, phase);
  return IsNonErrorConstant(*phase);
}

template <typename... Types>
static auto KindHasGetConstantValueOverload(TypeEnum<Types...> e) -> bool {
  static constexpr std::array<bool, SemIR::IdKind::NumTypes> Values = {
      (HasGetConstantValueOverload<Types>)...};
  return Values[e.ToIndex()];
}

static auto ResolveSpecificDeclForSpecificId(EvalContext& eval_context,
                                             SemIR::SpecificId specific_id)
    -> void {

Review comment (suggestion):
The callers of this generally look like `ReplaceTypeWithConstantValue(...) && ReplaceAllFieldsWithConstantValues(...)` so we'll still inconsistently produce an error constant if the type is an error but one of the operands is runtime.

Code after:
// the specified evaluation context. `*phase` is updated to describe the
// constant phase of the result. Returns whether `*phase` is a constant phase;
// if not, `inst` may not be fully updated and should not be used.
static auto ReplaceAllFieldsWithConstantValues(EvalContext& eval_context,
                                               SemIR::Inst* inst, Phase* phase)
    -> bool {
  auto arg0 =
      GetConstantValueForArg(eval_context, inst->arg0_and_kind(), phase);
  if (!IsConstantOrError(*phase)) {
    return false;
  }
  auto arg1 =
      GetConstantValueForArg(eval_context, inst->arg1_and_kind(), phase);
  if (!IsConstantOrError(*phase)) {
    return false;
  }
  inst->SetArgs(arg0, arg1);
  return true;
}

// Given an instruction and its ID, replaces its type with the corresponding
// value in this evaluation context. Updates `*phase` to describe the phase of
// the result, and returns whether `*phase` is a constant phase.
static auto ReplaceTypeWithConstantValue(EvalContext& eval_context,
                                         SemIR::InstId inst_id,
                                         SemIR::Inst* inst, Phase* phase)
    -> bool {
  inst->SetType(GetTypeOfInst(eval_context, inst_id, *inst, phase));
  return IsConstantOrError(*phase);
}

template <typename InstT>
static auto ReplaceTypeWithConstantValue(EvalContext& eval_context,
                                         SemIR::InstId inst_id, InstT* inst,
                                         Phase* phase) -> bool {
  inst->type_id = GetTypeOfInst(eval_context, inst_id, *inst, phase);
  return IsConstantOrError(*phase);
}

template <typename... Types>
static auto KindHasGetConstantValueOverload(TypeEnum<Types...> e) -> bool {
  static constexpr std::array<bool, SemIR::IdKind::NumTypes> Values = {
      (HasGetConstantValueOverload<Types>)...};
  return Values[e.ToIndex()];
}

static auto ResolveSpecificDeclForSpecificId(EvalContext& eval_context,
                                             SemIR::SpecificId specific_id)
    -> void {
  if (!specific_id.has_value()) {
    return;