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

Code before:
}

template <typename... T>
struct KindHasGetConstantValueOverload;

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

// Resolves the specific declarations for a specific id in any field of the
// `inst` instruction. This must be done for any field type that has a
// GetConstantValue() overload which canonicalizes a specific (and thus
// potentially forming a new specific) as part of forming its constant value.
static auto ResolveSpecificDeclForInst(EvalContext& eval_context,
                                       const SemIR::Inst& inst) -> void {
  auto resolve_specific_decl = [&](SemIR::SpecificId specific_id) {
    if (!specific_id.has_value()) {
      return;
    }

    const auto& specific = eval_context.specifics().Get(specific_id);
    const auto& generic = eval_context.generics().Get(specific.generic_id);
    if (!generic.decl_block_id.has_value()) {
      // Impl witness table construction happens before its own generic is
      // finished, in order to make the table's instructions dependent
      // instructions of the Impl's generic. So we can not resolve the specific
      // declaration for such instructions at the time they are created, as the
      // generic's declaration eval block is not yet constructed (with those
      // dependent instructions in it).
      //
      // TODO: Find a way to avoid this early out by making the evaluation of
      // instructions in the witness table that require a specific happen after
      // the generic is complete. Or find a way to limit this just to the impl
      // witness table and ensure the declarations will be resolved.
      return;
    }
    ResolveSpecificDecl(eval_context.context(), eval_context.fallback_loc_id(),
                        specific_id);
  };

  for (auto arg_and_kind : {inst.arg0_and_kind(), inst.arg1_and_kind()}) {

Review comment (refactor):
Maybe consider keeping the helper instead of a lambda -- IIUC this isn't using `inst` as an argument, it might be a little clearer to pass `eval_context` explicitly. Also note this function is kind of long at 80 lines, so if more were added this would be something to refactor out.

Code after:

template <typename... T>
struct KindHasGetConstantValueOverload;

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

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

  const auto& specific = eval_context.specifics().Get(specific_id);
  const auto& generic = eval_context.generics().Get(specific.generic_id);
  if (specific_id == generic.self_specific_id) {
    // Impl witness table construction happens before its generic decl is
    // finish, in order to make the table's instructions dependent
    // instructions of the Impl's generic. But those instructions can refer to
    // the generic's self specific. We can not resolve the specific
    // declaration for the self specific until the generic is finished, but it
    // is explicitly resolved at that time in `FinishGenericDecl()`.
    return;
  }
  ResolveSpecificDecl(eval_context.context(), eval_context.fallback_loc_id(),
                      specific_id);
}

// Resolves the specific declarations for a specific id in any field of the
// `inst` instruction.
static auto ResolveSpecificDeclForInst(EvalContext& eval_context,
                                       const SemIR::Inst& inst) -> void {
  for (auto arg_and_kind : {inst.arg0_and_kind(), inst.arg1_and_kind()}) {
    // This switch must handle any field type that has a GetConstantValue()
    // overload which canonicalizes a specific (and thus potentially forms a new
    // specific) as part of forming its constant value.
    CARBON_KIND_SWITCH(arg_and_kind) {
      case CARBON_KIND(SemIR::FacetTypeId facet_type_id): {
        const auto& info =