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

Code before:
    ResolveSpecificDeclForField(eval_context, interface.specific_id);
  }
}

// An overload for id types that have a GetConstantValue() overload but for
// which that overload does not canonicalize any SpecificId in the value type.
template <typename T>
  requires SameAsOneOf<T, SemIR::DestInstId, SemIR::EntityNameId,
                       SemIR::InstBlockId, SemIR::InstId, SemIR::MetaInstId,
                       SemIR::StructTypeFieldsId, SemIR::TypeInstId>
static auto ResolveSpecificDeclForField(EvalContext& /*eval_context*/,
                                        T /*id*/) {}

using ResolveSpecificHandlerFnT = auto(EvalContext& context, int32_t arg)
    -> void;

// Returns a lookup table to get constants by Id::Kind. Requires a null IdKind
// as a parameter in order to get the type pack.
template <typename... Types>
static constexpr auto MakeResolveSpecificHandlerTable(
    TypeEnum<Types...>* /*id_kind*/)
    -> std::array<ResolveSpecificHandlerFnT*, SemIR::IdKind::NumValues> {
  std::array<ResolveSpecificHandlerFnT*, SemIR::IdKind::NumValues> table = {};
  ((table[SemIR::IdKind::template For<Types>.ToIndex()] =
        [](EvalContext& eval_context, int32_t arg) {
          if constexpr (HasGetConstantValueOverload<Types>) {
            auto id = SemIR::Inst::FromRaw<Types>(arg);
            ResolveSpecificDeclForField(eval_context, id);
          }
        }),
   ...);
  table[SemIR::IdKind::Invalid.ToIndex()] = [](EvalContext& /*context*/,
                                               int32_t /*arg*/) {
    CARBON_FATAL("Instruction has argument with invalid IdKind");
  };
  table[SemIR::IdKind::None.ToIndex()] = [](EvalContext& /*context*/,
                                            int32_t /*arg*/) {};
  return table;
}

static auto ResolveSpecificDeclForInst(EvalContext& eval_context,
                                       const SemIR::Inst& inst) -> void {
  static constexpr auto Table =
      MakeResolveSpecificHandlerTable(static_cast<SemIR::IdKind*>(nullptr));
  Table[inst.arg0_and_kind().kind().ToIndex()](eval_context,
                                               inst.arg0_and_kind().value());
  Table[inst.arg1_and_kind().kind().ToIndex()](eval_context,
                                               inst.arg1_and_kind().value());
}

auto AddImportedConstant(Context& context, SemIR::Inst inst)

Review comment (suggestion):
Had you considering treating this `HasGetConstantValueOverload` the same as the `SameAsOneOf` above, either generating an empty function for both or handling both situations here?

That said though, looking at the overall flow, I wonder if it'd be better to do a `switch`. We do that on `ArgAndKind` in a few spots where most values aren't handled, such as https://github.com/carbon-language/carbon-lang/blob/trunk/toolchain/check/subst.cpp#L84

Here, I think that could look like:

```
static auto ResolveSpecificArg(EvalContext& eval_context, SemIR::Inst::ArgAndKind arg) -> void {
  CARBON_KIND_SWITCH(arg) {
    case CARBON_KIND(SemIR::FacetTypeId facet_type_id): { ... }
    case CARBON_KIND(SemIR::SpecificId specific_id): { ... }
    case CARBON_KIND(SemIR::SpecificInterfaceId specific_interface_id): { ... }
    default:
      // Maybe just do nothing? But you could also write an appropriate CARBON_CHECK if you wanted.
      break;
  }
}
```

In part, I note the `switch` because I think it's a simpler flow to understand.

Code after:
        }
        for (const auto& interface : info.self_impls_constraints) {
          ResolveSpecificDeclForSpecificId(eval_context, interface.specific_id);
        }
        break;
      }
      case CARBON_KIND(SemIR::SpecificId specific_id): {
        ResolveSpecificDeclForSpecificId(eval_context, specific_id);
        break;
      }
      case CARBON_KIND(SemIR::SpecificInterfaceId specific_interface_id): {
        ResolveSpecificDeclForSpecificId(eval_context,
                                         eval_context.specific_interfaces()
                                             .Get(specific_interface_id)
                                             .specific_id);
        break;
      }

        // These id types have a GetConstantValue() overload but that overload
        // does not canonicalize any SpecificId in the value type.
      case SemIR::IdKind::For<SemIR::DestInstId>:
      case SemIR::IdKind::For<SemIR::EntityNameId>:
      case SemIR::IdKind::For<SemIR::InstBlockId>:
      case SemIR::IdKind::For<SemIR::InstId>:
      case SemIR::IdKind::For<SemIR::MetaInstId>:
      case SemIR::IdKind::For<SemIR::StructTypeFieldsId>:
      case SemIR::IdKind::For<SemIR::TypeInstId>:
        break;

      default:
        CARBON_CHECK(
            !KindHasGetConstantValueOverload<SemIR::IdKind>::Value(
                arg_and_kind.kind()),
            "Missing case for {0} which has a GetConstantValue() overload",
            arg_and_kind.kind());
        break;
    }
  }
}

auto AddImportedConstant(Context& context, SemIR::Inst inst)
    -> SemIR::ConstantId {
  EvalContext eval_context(&context, SemIR::LocId::None);
  CARBON_CHECK(inst.kind().has_type(), "Can't import untyped instructions: {0}",
               inst.kind());
  Phase phase = GetPhase(context.constant_values(),
                         context.types().GetConstantId(inst.type_id()));
  if (!ReplaceAllFieldsWithConstantValues(eval_context, &inst, &phase)) {
    return SemIR::ConstantId::NotConstant;
  }
  return MakeConstantResult(context, inst, phase);