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

Code before:
      context, loc_id, overload_set_id, self_id, arg_ids);
  SemIR::Callee callee = GetCallee(context.sem_ir(), callee_id);
  CARBON_KIND_SWITCH(callee) {
    case CARBON_KIND(SemIR::CalleeError _): {
      return SemIR::ErrorInst::InstId;
    }
    case CARBON_KIND(SemIR::CalleeFunction fn): {
      CARBON_CHECK(!fn.self_id.has_value());
      if (self_id.has_value()) {
        // Preserve the `self` argument from the original callee.
        fn.self_id = self_id;
      }
      return PerformCallToFunction(context, loc_id, callee_id, fn, arg_ids);
    }
    case CARBON_KIND(SemIR::CalleeCppOverloadSet _): {
      CARBON_FATAL("overloads can't be recursive");
    }
    case CARBON_KIND(SemIR::CalleeNonFunction _): {
      CARBON_FATAL("overloads should produce functions");
    }
  }
}

// Synthesize a placeholder `void{}` template argument, that will never be a
// valid argument for any template parameter. This is used in order to get Clang
// to diagnose invalid template argument errors for us.
static auto MakePlaceholderTemplateArg(Context& context, SemIR::InstId arg_id)
    -> clang::TemplateArgumentLoc {
  auto arg_loc = GetCppLocation(context, SemIR::LocId(arg_id));
  auto void_type = context.ast_context().VoidTy;
  auto* arg = new (context.ast_context()) clang::CXXScalarValueInitExpr(
      void_type,
      context.ast_context().getTrivialTypeSourceInfo(void_type, arg_loc),
      arg_loc);
  return clang::TemplateArgumentLoc(
      clang::TemplateArgument(arg, /*IsCanonical=*/false), arg);
}

// Converts an argument in a call to a C++ template name into a corresponding
// clang template argument, given the template parameter it will be matched
// against.
static auto ConvertArgToTemplateArg(Context& context,
                                    const clang::NamedDecl* param_decl,
                                    SemIR::InstId arg_id)
    -> std::optional<clang::TemplateArgumentLoc> {
  if (isa<clang::TemplateTypeParmDecl>(param_decl)) {
    auto type = ExprAsType(context, SemIR::LocId(arg_id), arg_id);
    if (type.type_id == SemIR::ErrorInst::TypeId) {
      return std::nullopt;
    }
    auto clang_type = MapToCppType(context, type.type_id);

Review comment (suggestion):
Maybe add that this will map this `void{}` argument's source location to the Carbon argument's source location?

It's a bit of an implementation detail, but key to making the diagnostic effective.

Also, would it make sense to flag this as a candidate for an extension point in Clang so that even when the `void{}` is printed within the diagnostic text it can either call back into Carbon to print the expression or at least say something like `<Carbon placeholder expression>` instead of `void{}`? If so, maybe a TODO? Not looking to do more than the current code does in this PR, just to record that we could go further here.

Code after:
      context, loc_id, overload_set_id, self_id, arg_ids);
  SemIR::Callee callee = GetCallee(context.sem_ir(), callee_id);
  CARBON_KIND_SWITCH(callee) {
    case CARBON_KIND(SemIR::CalleeError _): {
      return SemIR::ErrorInst::InstId;
    }
    case CARBON_KIND(SemIR::CalleeFunction fn): {
      CARBON_CHECK(!fn.self_id.has_value());
      if (self_id.has_value()) {
        // Preserve the `self` argument from the original callee.
        fn.self_id = self_id;
      }
      return PerformCallToFunction(context, loc_id, callee_id, fn, arg_ids);
    }
    case CARBON_KIND(SemIR::CalleeCppOverloadSet _): {
      CARBON_FATAL("overloads can't be recursive");
    }
    case CARBON_KIND(SemIR::CalleeNonFunction _): {
      CARBON_FATAL("overloads should produce functions");
    }
  }
}

// Synthesize a placeholder `void{}` template argument, that will never be a
// valid argument for any template parameter. This is used in order to get Clang
// to diagnose invalid template argument errors for us. The location of the
// Carbon expression is used as the location of the C++ expression, so
// Clang's diagnostics will point into the Carbon code.
//
// TODO: If Clang ever tries to print the type of the expression or to
// pretty-print the expression itself, it would print the wrong thing. Currently
// this doesn't appear to happen, but in principle it could. Ideally we'd add an
// extension point to Clang to represent a "foreign expression" and use it here
// instead of creating a bogus placeholder expression.
static auto MakePlaceholderTemplateArg(Context& context, SemIR::InstId arg_id)
    -> clang::TemplateArgumentLoc {
  auto arg_loc = GetCppLocation(context, SemIR::LocId(arg_id));
  auto void_type = context.ast_context().VoidTy;
  auto* arg = new (context.ast_context()) clang::CXXScalarValueInitExpr(
      void_type,
      context.ast_context().getTrivialTypeSourceInfo(void_type, arg_loc),
      arg_loc);
  return clang::TemplateArgumentLoc(
      clang::TemplateArgument(arg, /*IsCanonical=*/false), arg);
}

// Converts an argument in a call to a C++ template name into a corresponding
// clang template argument, given the template parameter it will be matched
// against.
static auto ConvertArgToTemplateArg(Context& context,
                                    const clang::NamedDecl* param_decl,