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

Code before:
    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();
      }
      bool explicit_as = target.kind == ConversionTarget::ExplicitAs;
      auto type_of_expr_id = context.insts().Get(expr_id).type_id();
      if (target.type_id == SemIR::TypeType::SingletonTypeId ||
          sem_ir.types().Is<SemIR::FacetType>(target.type_id)) {
        if (context.types().Is<SemIR::FacetType>(type_of_expr_id)) {
          CARBON_DIAGNOSTIC(
              ConversionFailureFacetToFacet, Error,
              "cannot{0:| implicitly} convert type {1} that implements {2} to "
              "type implementing {3}{0: with `as`|}",
              BoolAsSelect, InstIdAsType, TypeOfInstId, SemIR::TypeId);
          return context.emitter().Build(loc_id, ConversionFailureFacetToFacet,
                                         explicit_as, expr_id, expr_id,
                                         target.type_id);
        } else if (auto facet_access_type =
                       context.insts().TryGetAs<SemIR::FacetAccessType>(
                           expr_id)) {
          CARBON_DIAGNOSTIC(
              ConversionFailureFacetAccessTypeToFacet, Error,
              "cannot{0:| implicitly} convert type {1} that implements {2} to "
              "type implementing {3}{0: with `as`|}",
              BoolAsSelect, InstIdAsType, TypeOfInstId, SemIR::TypeId);
          return context.emitter().Build(
              loc_id, ConversionFailureFacetAccessTypeToFacet, explicit_as,
              expr_id, facet_access_type->facet_value_inst_id, target.type_id);
        } else if (type_of_expr_id == SemIR::TypeType::SingletonTypeId) {
          CARBON_DIAGNOSTIC(
              ConversionFailureTypeToFacet, Error,
              "cannot{0:| implicitly} convert type {1} to type implementing "
              "{2}{0: with `as`|}; type {1} does not implement the required "
              "interface(s)",
              BoolAsSelect, InstIdAsType, SemIR::TypeId);
          return context.emitter().Build(loc_id, ConversionFailureTypeToFacet,
                                         explicit_as, expr_id, target.type_id);

Review comment (suggestion):
Usually when we talk about "convert[ing] X to Y", X describes a source expression, and Y describes a target type, so we're talking about something like `X as Y` or `let v: Y = X;`. But here, "type implementing <facet>" is a type, but it's not the target type of the conversion: converting an expression `X` to type `T` where `T:! Y` would be a "conversion to a type (T) implementing Y". So I find this phrasing inconsistent. At the same time, I do like that we say "type implementing Y" in this diagnostic, because I think that gives the reader a good clue about what's going on.

I suggest that we change the word "to" to "into" in these diagnostics, so that we use "convert X to Y" to mean a conversion of source expression X to target type Y, and we use "convert X into Y" to mean a conversion of source expression X to a target expression described by Y. So for example "convert expression X to type Y" and "convert expression X into expression of type Y" would mean the same thing.

tl;dr:
```suggestion
              "cannot{0:| implicitly} convert type {1} that implements {2} into "
```
here and in the next 3 diagnostics.

Code after:
    // TODO: We currently encounter this for use of namespaces and functions.
    // We should provide a better diagnostic for inappropriate use of
    // namespace names, and allow use of functions as values.
    if (target.diagnose) {
      CARBON_DIAGNOSTIC(UseOfNonExprAsValue, Error,
                        "expression cannot be used as a value");
      context.emitter().Emit(expr_id, UseOfNonExprAsValue);
    }
    return SemIR::ErrorInst::SingletonInstId;
  }

  // We can only perform initialization for complete, non-abstract types. Note
  // that `RequireConcreteType` returns true for facet types, since their
  // representation is fixed. This allows us to support using the `Self` of an
  // interface inside its definition.
  if (!RequireConcreteType(
          context, target.type_id, loc_id,
          [&] {
            CARBON_CHECK(!target.is_initializer(),
                         "Initialization of incomplete types is expected to be "
                         "caught elsewhere.");
            if (!target.diagnose) {
              return context.emitter().BuildSuppressed();
            }
            CARBON_DIAGNOSTIC(IncompleteTypeInValueConversion, Error,
                              "forming value of incomplete type {0}",
                              SemIR::TypeId);
            CARBON_DIAGNOSTIC(IncompleteTypeInConversion, Error,
                              "invalid use of incomplete type {0}",
                              SemIR::TypeId);
            return context.emitter().Build(
                loc_id,
                target.kind == ConversionTarget::Value
                    ? IncompleteTypeInValueConversion
                    : IncompleteTypeInConversion,
                target.type_id);
          },
          [&] {
            if (!target.diagnose || !target.is_initializer()) {
              return context.emitter().BuildSuppressed();
            }
            CARBON_DIAGNOSTIC(AbstractTypeInInit, Error,
                              "initialization of abstract type {0}",
                              SemIR::TypeId);
            return context.emitter().Build(loc_id, AbstractTypeInInit,
                                           target.type_id);
          })) {
    return SemIR::ErrorInst::SingletonInstId;
  }

  // Check whether any builtin conversion applies.