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

Code before:
  // Check whether any builtin conversion applies.
  expr_id = PerformBuiltinConversion(context, loc_id, expr_id, target);
  if (expr_id == SemIR::ErrorInst::SingletonInstId) {
    return expr_id;
  }

  // If this is not a builtin conversion, try an `ImplicitAs` conversion.
  if (sem_ir.insts().Get(expr_id).type_id() != target.type_id) {
    SemIR::InstId interface_args[] = {
        context.types().GetInstId(target.type_id)};
    Operator op = {
        .interface_name = target.kind == ConversionTarget::ExplicitAs
                              ? llvm::StringLiteral("As")
                              : llvm::StringLiteral("ImplicitAs"),
        .interface_args_ref = interface_args,
        .op_name = "Convert",
    };
    expr_id = BuildUnaryOperator(context, loc_id, op, expr_id, [&] {
      if (!target.diagnose) {
        return context.emitter().BuildSuppressed();
      }
      if (target.type_id == SemIR::TypeType::SingletonTypeId ||
          sem_ir.types().Is<SemIR::FacetType>(target.type_id)) {
        return DiagnoseConversionFailureToConstraintValue(context, loc_id,
                                                          expr_id, target);
      } else {
        // TODO: Should this message change to say "object of type" when
        // converting from a reference expression?
        CARBON_DIAGNOSTIC(ConversionFailure, Error,
                          "cannot{0:| implicitly} convert value of type {1} to "
                          "{2}{0: with `as`|}",
                          BoolAsSelect, TypeOfInstId, SemIR::TypeId);
        return context.emitter().Build(
            loc_id, ConversionFailure,
            target.kind == ConversionTarget::ExplicitAs, expr_id,
            target.type_id);
      }
    });

    // Pull a value directly out of the initializer if possible and wanted.
    if (expr_id != SemIR::ErrorInst::SingletonInstId &&
        CanUseValueOfInitializer(sem_ir, target.type_id, target.kind)) {
      expr_id = AddInst<SemIR::ValueOfInitializer>(
          context, loc_id, {.type_id = target.type_id, .init_id = expr_id});
    }
  }

  // Track that we performed a type conversion, if we did so.
  if (orig_expr_id != expr_id) {
    expr_id = AddInst<SemIR::Converted>(context, loc_id,
                                        {.type_id = target.type_id,

Review comment (refactor):
Add "// TODO" to move this into `PerformBuiltinConversion` (ideally with a reference to https://github.com/carbon-language/carbon-lang/issues/5122).

Code after:
  // Check whether any builtin conversion applies.
  expr_id = PerformBuiltinConversion(context, loc_id, expr_id, target);
  if (expr_id == SemIR::ErrorInst::SingletonInstId) {
    return expr_id;
  }

  // If this is not a builtin conversion, try an `ImplicitAs` conversion.
  if (sem_ir.insts().Get(expr_id).type_id() != target.type_id) {
    SemIR::InstId interface_args[] = {
        context.types().GetInstId(target.type_id)};
    Operator op = {
        .interface_name = target.kind == ConversionTarget::ExplicitAs
                              ? llvm::StringLiteral("As")
                              : llvm::StringLiteral("ImplicitAs"),
        .interface_args_ref = interface_args,
        .op_name = "Convert",
    };
    expr_id = BuildUnaryOperator(context, loc_id, op, expr_id, [&] {
      if (!target.diagnose) {
        return context.emitter().BuildSuppressed();
      }
      if (target.type_id == SemIR::TypeType::SingletonTypeId ||
          sem_ir.types().Is<SemIR::FacetType>(target.type_id)) {
        // TODO: Move this to PerformBuiltinConversion(). See
        // https://github.com/carbon-language/carbon-lang/issues/5122.
        return DiagnoseConversionFailureToConstraintValue(context, loc_id,
                                                          expr_id, target);
      } else {
        // TODO: Should this message change to say "object of type" when
        // converting from a reference expression?
        CARBON_DIAGNOSTIC(ConversionFailure, Error,
                          "cannot{0:| implicitly} convert value of type {1} to "
                          "{2}{0: with `as`|}",
                          BoolAsSelect, TypeOfInstId, SemIR::TypeId);
        return context.emitter().Build(
            loc_id, ConversionFailure,
            target.kind == ConversionTarget::ExplicitAs, expr_id,
            target.type_id);
      }
    });

    // Pull a value directly out of the initializer if possible and wanted.
    if (expr_id != SemIR::ErrorInst::SingletonInstId &&
        CanUseValueOfInitializer(sem_ir, target.type_id, target.kind)) {
      expr_id = AddInst<SemIR::ValueOfInitializer>(
          context, loc_id, {.type_id = target.type_id, .init_id = expr_id});
    }
  }

  // Track that we performed a type conversion, if we did so.
  if (orig_expr_id != expr_id) {