소스 검색

Improve diagnostics for overload resolution failure. (#6091)

Include notes listing the candidates and explaining why they didn't
work. Rather than duplicating the (substantial) logic for this, use the
Clang machinery to generate these diagnostics.

In order to support this, add a mechanism to map `SemIR::LocId`s to
`clang::SourceLocation`s. This works by creating source buffers in Clang
that refer into the Carbon source file so that `SourceLocation`s can
point into them.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
Richard Smith 7 달 전
부모
커밋
925250f8f9

+ 3 - 0
toolchain/check/BUILD

@@ -23,6 +23,7 @@ cc_library(
         "convert.cpp",
         "cpp/custom_type_mapping.cpp",
         "cpp/import.cpp",
+        "cpp/location.cpp",
         "cpp/operators.cpp",
         "cpp/overload_resolution.cpp",
         "cpp/thunk.cpp",
@@ -73,6 +74,7 @@ cc_library(
         "convert.h",
         "cpp/custom_type_mapping.h",
         "cpp/import.h",
+        "cpp/location.h",
         "cpp/operators.h",
         "cpp/overload_resolution.h",
         "cpp/thunk.h",
@@ -148,6 +150,7 @@ cc_library(
         "//toolchain/lex:tokenized_buffer",
         "//toolchain/parse:node_kind",
         "//toolchain/parse:tree",
+        "//toolchain/sem_ir:absolute_node_id",
         "//toolchain/sem_ir:clang_decl",
         "//toolchain/sem_ir:expr_info",
         "//toolchain/sem_ir:file",

+ 9 - 0
toolchain/check/context.h

@@ -157,6 +157,11 @@ class Context {
     return import_ir_constant_values_;
   }
 
+  auto cpp_carbon_file_locations()
+      -> llvm::SmallVector<clang::SourceLocation>& {
+    return cpp_carbon_file_locations_;
+  }
+
   auto definitions_required_by_decl() -> llvm::SmallVector<SemIR::InstId>& {
     return definitions_required_by_decl_;
   }
@@ -387,6 +392,10 @@ class Context {
   // Inline 0 elements because it's expected to require heap allocation.
   llvm::SmallVector<SemIR::ConstantValueStore, 0> import_ir_constant_values_;
 
+  // Per-Carbon-file start locations for corresponding Clang source buffers.
+  // Owned and managed by code in cpp/location.cpp.
+  llvm::SmallVector<clang::SourceLocation> cpp_carbon_file_locations_;
+
   // Declaration instructions of entities that should have definitions by the
   // end of the current source file.
   llvm::SmallVector<SemIR::InstId> definitions_required_by_decl_;

+ 7 - 0
toolchain/check/cpp/import.cpp

@@ -1667,6 +1667,13 @@ auto ImportCppFunctionDecl(Context& context, SemIR::LocId loc_id,
 
   SemIR::Function& function_info = context.functions().Get(*function_id);
   if (IsCppThunkRequired(context, function_info)) {
+    Diagnostics::AnnotationScope annotate_diagnostics(
+        &context.emitter(), [&](auto& builder) {
+          CARBON_DIAGNOSTIC(InCppThunk, Note,
+                            "in thunk for C++ function used here");
+          builder.Note(loc_id, InCppThunk);
+        });
+
     clang::FunctionDecl* thunk_clang_decl =
         BuildCppThunk(context, function_info);
     if (thunk_clang_decl) {

+ 82 - 0
toolchain/check/cpp/location.cpp

@@ -0,0 +1,82 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "toolchain/check/cpp/location.h"
+
+#include "toolchain/sem_ir/absolute_node_id.h"
+#include "toolchain/sem_ir/ids.h"
+
+namespace Carbon::Check {
+
+namespace {
+struct FileInfo {
+  const SemIR::File* sem_ir;
+  clang::SourceLocation start_loc;
+};
+}  // namespace
+
+// Map a CheckIRId into information about the corresponding file in both SemIR
+// and Clang's source manager.
+static auto GetFileInfo(Context& context, SemIR::CheckIRId ir_id) -> FileInfo {
+  const SemIR::File* sem_ir = &context.sem_ir();
+  int file_index = 0;
+
+  // If the file is imported, locate it in our imports map.
+  if (ir_id != context.sem_ir().check_ir_id()) {
+    auto import_id = context.check_ir_map().Get(ir_id);
+    CARBON_CHECK(import_id.has_value());
+    file_index = import_id.index + 1;
+
+    sem_ir = context.import_irs().Get(import_id).sem_ir;
+    CARBON_CHECK(sem_ir, "Node location in nonexistent IR");
+  }
+
+  // If we've seen this file before, reuse the same FileID.
+  auto& file_start_locs = context.cpp_carbon_file_locations();
+  if (static_cast<int>(file_start_locs.size()) <= file_index) {
+    // Never valid; prepare a slot for the caching below.
+    file_start_locs.resize(file_index + 1);
+  } else if (file_start_locs[file_index].isValid()) {
+    return {.sem_ir = sem_ir, .start_loc = file_start_locs[file_index]};
+  }
+
+  // We've not seen this file before. Create a corresponding virtual file in
+  // Clang's source manager.
+  // TODO: Consider recreating the complete import path instead of only the
+  // final entry.
+  const auto& source = sem_ir->parse_tree().tokens().source();
+  auto& src_mgr = context.ast_context().getSourceManager();
+  auto file_id = src_mgr.createFileID(
+      llvm::MemoryBufferRef(source.text(), source.filename()));
+  auto file_start_loc = src_mgr.getLocForStartOfFile(file_id);
+  file_start_locs[file_index] = file_start_loc;
+  return {.sem_ir = sem_ir, .start_loc = file_start_loc};
+}
+
+auto GetCppLocation(Context& context, SemIR::LocId loc_id)
+    -> clang::SourceLocation {
+  if (!context.sem_ir().clang_ast_unit()) {
+    return clang::SourceLocation();
+  }
+
+  // Break down the `LocId` into an import path. If that ends in a C++ location,
+  // we can just return that directly.
+  llvm::SmallVector<SemIR::AbsoluteNodeId> absolute_node_ids =
+      SemIR::GetAbsoluteNodeId(&context.sem_ir(), loc_id);
+  if (absolute_node_ids.back().check_ir_id() == SemIR::CheckIRId::Cpp) {
+    return context.sem_ir().clang_source_locs().Get(
+        absolute_node_ids.back().clang_source_loc_id());
+  }
+
+  // This is a location in Carbon code; get or create a corresponding file in
+  // Clang and build a corresponding location.
+  auto absolute_node_id = absolute_node_ids.back();
+  auto [ir, start_loc] = GetFileInfo(context, absolute_node_id.check_ir_id());
+  const auto& tree = ir->parse_tree();
+  auto offset =
+      tree.tokens().GetByteOffset(tree.node_token(absolute_node_id.node_id()));
+  return start_loc.getLocWithOffset(offset);
+}
+
+}  // namespace Carbon::Check

+ 19 - 0
toolchain/check/cpp/location.h

@@ -0,0 +1,19 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef CARBON_TOOLCHAIN_CHECK_CPP_LOCATION_H_
+#define CARBON_TOOLCHAIN_CHECK_CPP_LOCATION_H_
+
+#include "toolchain/check/context.h"
+#include "toolchain/sem_ir/ids.h"
+
+namespace Carbon::Check {
+
+// Maps a Carbon source location into an equivalent Clang source location.
+auto GetCppLocation(Context& context, SemIR::LocId loc_id)
+    -> clang::SourceLocation;
+
+}  // namespace Carbon::Check
+
+#endif  // CARBON_TOOLCHAIN_CHECK_CPP_LOCATION_H_

+ 35 - 34
toolchain/check/cpp/overload_resolution.cpp

@@ -4,15 +4,27 @@
 
 #include "toolchain/check/cpp/overload_resolution.h"
 
+#include "clang/Basic/DiagnosticSema.h"
 #include "clang/Sema/Overload.h"
 #include "clang/Sema/Sema.h"
 #include "toolchain/check/cpp/import.h"
+#include "toolchain/check/cpp/location.h"
 #include "toolchain/check/cpp/type_mapping.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/typed_insts.h"
 
 namespace Carbon::Check {
 
+// Map a Carbon name into a C++ name.
+static auto GetCppName(Context& context, SemIR::NameId name_id)
+    -> clang::DeclarationName {
+  // TODO: Some special names should probably use different formatting. In
+  // particular, NameId::CppOperator should probably map back to a
+  // CXXOperatorName.
+  auto name_str = context.names().GetFormatted(name_id);
+  return clang::DeclarationName(&context.ast_context().Idents.get(name_str));
+}
+
 // Adds the given overload candidates to the candidate set.
 static auto AddOverloadCandidataes(clang::Sema& sema,
                                    clang::OverloadCandidateSet& candidate_set,
@@ -67,13 +79,6 @@ auto PerformCppOverloadResolution(Context& context, SemIR::LocId loc_id,
                                   SemIR::InstId self_id,
                                   llvm::ArrayRef<SemIR::InstId> arg_ids)
     -> SemIR::InstId {
-  Diagnostics::AnnotationScope annotate_diagnostics(
-      &context.emitter(), [&](auto& builder) {
-        CARBON_DIAGNOSTIC(InCallToCppFunction, Note,
-                          "in call to Cpp function here");
-        builder.Note(loc_id, InCallToCppFunction);
-      });
-
   // Map Carbon call argument types to C++ types.
   clang::Expr* self_expr = nullptr;
   if (self_id.has_value()) {
@@ -82,67 +87,63 @@ auto PerformCppOverloadResolution(Context& context, SemIR::LocId loc_id,
       return SemIR::ErrorInst::InstId;
     }
   }
-  auto arg_exprs = InventClangArgs(context, arg_ids);
-  if (!arg_exprs.has_value()) {
+  auto maybe_arg_exprs = InventClangArgs(context, arg_ids);
+  if (!maybe_arg_exprs.has_value()) {
     return SemIR::ErrorInst::InstId;
   }
+  auto& arg_exprs = *maybe_arg_exprs;
 
   const SemIR::CppOverloadSet& overload_set =
       context.cpp_overload_sets().Get(overload_set_id);
 
+  clang::SourceLocation loc = GetCppLocation(context, loc_id);
+
   // Add candidate functions from the name lookup.
   clang::OverloadCandidateSet candidate_set(
-      // TODO: Add location accordingly.
-      clang::SourceLocation(),
-      clang::OverloadCandidateSet::CandidateSetKind::CSK_Normal);
+      loc, clang::OverloadCandidateSet::CandidateSetKind::CSK_Normal);
 
   clang::ASTUnit* ast = context.sem_ir().clang_ast_unit();
   CARBON_CHECK(ast);
   clang::Sema& sema = ast->getSema();
 
   AddOverloadCandidataes(sema, candidate_set, overload_set.candidate_functions,
-                         self_expr, *arg_exprs);
+                         self_expr, arg_exprs);
 
   // Find best viable function among the candidates.
   clang::OverloadCandidateSet::iterator best_viable_fn;
   clang::OverloadingResult overloading_result =
-      // TODO: Add location accordingly.
-      candidate_set.BestViableFunction(sema, clang::SourceLocation(),
-                                       best_viable_fn);
+      candidate_set.BestViableFunction(sema, loc, best_viable_fn);
 
   switch (overloading_result) {
     case clang::OverloadingResult::OR_Success: {
       // TODO: Handle the cases when Function is null.
       CARBON_CHECK(best_viable_fn->Function);
-      sema.MarkFunctionReferenced(clang::SourceLocation(),
-                                  best_viable_fn->Function);
+      sema.MarkFunctionReferenced(loc, best_viable_fn->Function);
       SemIR::InstId result =
           ImportCppFunctionDecl(context, loc_id, best_viable_fn->Function);
       return result;
     }
     case clang::OverloadingResult::OR_No_Viable_Function: {
-      // TODO: Add notes with the candidates.
-      CARBON_DIAGNOSTIC(CppOverloadingNoViableFunctionFound, Error,
-                        "no matching function for call to `{0}`",
-                        SemIR::NameId);
-      context.emitter().Emit(loc_id, CppOverloadingNoViableFunctionFound,
-                             overload_set.name_id);
+      candidate_set.NoteCandidates(
+          clang::PartialDiagnosticAt(
+              loc, sema.PDiag(clang::diag::err_ovl_no_viable_function_in_call)
+                       << GetCppName(context, overload_set.name_id)),
+          sema, clang::OCD_AllCandidates, arg_exprs);
       return SemIR::ErrorInst::InstId;
     }
     case clang::OverloadingResult::OR_Ambiguous: {
-      // TODO: Add notes with the candidates.
-      CARBON_DIAGNOSTIC(CppOverloadingAmbiguousCandidatesFound, Error,
-                        "call to `{0}` is ambiguous", SemIR::NameId);
-      context.emitter().Emit(loc_id, CppOverloadingAmbiguousCandidatesFound,
-                             overload_set.name_id);
+      candidate_set.NoteCandidates(
+          clang::PartialDiagnosticAt(
+              loc, sema.PDiag(clang::diag::err_ovl_ambiguous_call)
+                       << GetCppName(context, overload_set.name_id)),
+          sema, clang::OCD_AmbiguousCandidates, arg_exprs);
       return SemIR::ErrorInst::InstId;
     }
     case clang::OverloadingResult::OR_Deleted: {
-      // TODO: Add notes with the candidates.
-      CARBON_DIAGNOSTIC(CppOverloadingDeletedFunctionFound, Error,
-                        "call to deleted function `{0}`", SemIR::NameId);
-      context.emitter().Emit(loc_id, CppOverloadingDeletedFunctionFound,
-                             overload_set.name_id);
+      sema.DiagnoseUseOfDeletedFunction(
+          loc, clang::SourceRange(loc, loc),
+          GetCppName(context, overload_set.name_id), candidate_set,
+          best_viable_fn->Function, arg_exprs);
       return SemIR::ErrorInst::InstId;
     }
   }

+ 4 - 3
toolchain/check/cpp/type_mapping.cpp

@@ -16,6 +16,7 @@
 #include "toolchain/base/value_ids.h"
 #include "toolchain/check/context.h"
 #include "toolchain/check/convert.h"
+#include "toolchain/check/cpp/location.h"
 #include "toolchain/check/literal.h"
 #include "toolchain/sem_ir/class.h"
 #include "toolchain/sem_ir/expr_info.h"
@@ -272,9 +273,9 @@ auto InventClangArg(Context& context, SemIR::InstId arg_id) -> clang::Expr* {
 
   // TODO: Avoid heap allocating more of these on every call. Either cache them
   // somewhere or put them on the stack.
-  return new (context.ast_context()) clang::OpaqueValueExpr(
-      // TODO: Add location accordingly.
-      clang::SourceLocation(), arg_cpp_type.getNonReferenceType(), value_kind);
+  return new (context.ast_context())
+      clang::OpaqueValueExpr(GetCppLocation(context, SemIR::LocId(arg_id)),
+                             arg_cpp_type.getNonReferenceType(), value_kind);
 }
 
 auto InventClangArgs(Context& context, llvm::ArrayRef<SemIR::InstId> arg_ids)

+ 25 - 34
toolchain/check/testdata/interop/cpp/class/method.carbon

@@ -58,37 +58,29 @@ fn Value(v: Cpp.HasQualifiers) {
   v.plain();
 
   // TODO: This should remain invalid once we support `volatile`.
-  // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: object parameter type: volatile struct HasQualifiers` [SemanticsTodo]
-  // CHECK:STDERR:   v.volatile_this();
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE+4]]:3: error: semantics TODO: `Unsupported: object parameter type: volatile struct HasQualifiers` [SemanticsTodo]
   // CHECK:STDERR:   v.volatile_this();
   // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
   v.volatile_this();
 
-  // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE+7]]:3: error: no matching function for call to `ref_this` [CppOverloadingNoViableFunctionFound]
-  // CHECK:STDERR:   v.ref_this();
-  // CHECK:STDERR:   ^~~~~~~~~~~~
-  // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   v.ref_this();
-  // CHECK:STDERR:   ^~~~~~~~~~~~
+  // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE+8]]:14: error: no matching function for call to 'ref_this' [CppInteropParseError]
+  // CHECK:STDERR:    29 |   v.ref_this();
+  // CHECK:STDERR:       |              ^
+  // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE-20]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./object_param_qualifiers.h:7:8: note: candidate function not viable: expects an lvalue for object argument [CppInteropParseNote]
+  // CHECK:STDERR:     7 |   void ref_this() &;
+  // CHECK:STDERR:       |        ^
   // CHECK:STDERR:
   v.ref_this();
 
-  // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: object parameter type: struct HasQualifiers &&` [SemanticsTodo]
-  // CHECK:STDERR:   v.ref_ref_this();
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE+4]]:3: error: semantics TODO: `Unsupported: object parameter type: struct HasQualifiers &&` [SemanticsTodo]
   // CHECK:STDERR:   v.ref_ref_this();
   // CHECK:STDERR:   ^~~~~~~~~~~~~~~~
   // CHECK:STDERR:
   v.ref_ref_this();
 
-  // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: object parameter type: const struct HasQualifiers &&` [SemanticsTodo]
-  // CHECK:STDERR:   v.const_ref_ref_this();
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_bad_object_param_qualifiers_by_value.carbon:[[@LINE+4]]:3: error: semantics TODO: `Unsupported: object parameter type: const struct HasQualifiers &&` [SemanticsTodo]
   // CHECK:STDERR:   v.const_ref_ref_this();
   // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
@@ -103,10 +95,7 @@ import Cpp library "object_param_qualifiers.h";
 
 fn Ref(p: Cpp.HasQualifiers*) {
   // TODO: This should eventually be accepted if we support `volatile`.
-  // CHECK:STDERR: fail_todo_bad_object_param_qualifiers_by_ref.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: object parameter type: volatile struct HasQualifiers` [SemanticsTodo]
-  // CHECK:STDERR:   p->volatile_this();
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_todo_bad_object_param_qualifiers_by_ref.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_todo_bad_object_param_qualifiers_by_ref.carbon:[[@LINE+4]]:3: error: semantics TODO: `Unsupported: object parameter type: volatile struct HasQualifiers` [SemanticsTodo]
   // CHECK:STDERR:   p->volatile_this();
   // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
@@ -120,21 +109,23 @@ library "[[@TEST_NAME]]";
 import Cpp library "object_param_qualifiers.h";
 
 fn Ref(p: Cpp.HasQualifiers*) {
-  // CHECK:STDERR: fail_bad_object_param_qualifiers_ref_ref.carbon:[[@LINE+7]]:3: error: no matching function for call to `ref_ref_this` [CppOverloadingNoViableFunctionFound]
-  // CHECK:STDERR:   p->ref_ref_this();
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_bad_object_param_qualifiers_ref_ref.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   p->ref_ref_this();
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_bad_object_param_qualifiers_ref_ref.carbon:[[@LINE+8]]:19: error: no matching function for call to 'ref_ref_this' [CppInteropParseError]
+  // CHECK:STDERR:    15 |   p->ref_ref_this();
+  // CHECK:STDERR:       |                   ^
+  // CHECK:STDERR: fail_bad_object_param_qualifiers_ref_ref.carbon:[[@LINE-6]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./object_param_qualifiers.h:10:8: note: candidate function not viable: expects an rvalue for object argument [CppInteropParseNote]
+  // CHECK:STDERR:    10 |   void ref_ref_this() &&;
+  // CHECK:STDERR:       |        ^
   // CHECK:STDERR:
   p->ref_ref_this();
 
-  // CHECK:STDERR: fail_bad_object_param_qualifiers_ref_ref.carbon:[[@LINE+7]]:3: error: no matching function for call to `const_ref_ref_this` [CppOverloadingNoViableFunctionFound]
-  // CHECK:STDERR:   p->const_ref_ref_this();
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_bad_object_param_qualifiers_ref_ref.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   p->const_ref_ref_this();
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_bad_object_param_qualifiers_ref_ref.carbon:[[@LINE+8]]:25: error: no matching function for call to 'const_ref_ref_this' [CppInteropParseError]
+  // CHECK:STDERR:    25 |   p->const_ref_ref_this();
+  // CHECK:STDERR:       |                         ^
+  // CHECK:STDERR: fail_bad_object_param_qualifiers_ref_ref.carbon:[[@LINE-16]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./object_param_qualifiers.h:11:8: note: candidate function not viable: expects an rvalue for object argument [CppInteropParseNote]
+  // CHECK:STDERR:    11 |   void const_ref_ref_this() const&&;
+  // CHECK:STDERR:       |        ^
   // CHECK:STDERR:
   p->const_ref_ref_this();
 }

+ 1 - 4
toolchain/check/testdata/interop/cpp/fail_todo_arithmetic_types_unmapped.carbon

@@ -29,10 +29,7 @@ fn CallGetUL() -> u64 { return Cpp.GetUL(); }
 // TODO: Eventually, `unsigned long long` should map to
 // `Cpp.unsigned_long_long`, which should implicitly convert to `u64`.
 // We should switch to testing a different type when that case works.
-// CHECK:STDERR: fail_todo_use_u64_types.carbon:[[@LINE+7]]:33: error: semantics TODO: `Unsupported: return type: unsigned long long` [SemanticsTodo]
-// CHECK:STDERR: fn CallGetULL() -> u64 { return Cpp.GetULL(); }
-// CHECK:STDERR:                                 ^~~~~~~~~~~~
-// CHECK:STDERR: fail_todo_use_u64_types.carbon:[[@LINE+4]]:33: note: in call to Cpp function here [InCallToCppFunction]
+// CHECK:STDERR: fail_todo_use_u64_types.carbon:[[@LINE+4]]:33: error: semantics TODO: `Unsupported: return type: unsigned long long` [SemanticsTodo]
 // CHECK:STDERR: fn CallGetULL() -> u64 { return Cpp.GetULL(); }
 // CHECK:STDERR:                                 ^~~~~~~~~~~~
 // CHECK:STDERR:

+ 5 - 17
toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon

@@ -129,10 +129,7 @@ import Cpp library "wchar_t_param.h";
 
 fn F() {
   //@dump-sem-ir-begin
-  // CHECK:STDERR: fail_todo_import_wchar_t_param.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: wchar_t` [SemanticsTodo]
-  // CHECK:STDERR:   Cpp.foo('X');
-  // CHECK:STDERR:   ^~~~~~~~~~~~
-  // CHECK:STDERR: fail_todo_import_wchar_t_param.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_todo_import_wchar_t_param.carbon:[[@LINE+4]]:3: error: semantics TODO: `Unsupported: parameter type: wchar_t` [SemanticsTodo]
   // CHECK:STDERR:   Cpp.foo('X');
   // CHECK:STDERR:   ^~~~~~~~~~~~
   // CHECK:STDERR:
@@ -181,10 +178,7 @@ import Cpp library "char16_t_param.h";
 
 fn F() {
   //@dump-sem-ir-begin
-  // CHECK:STDERR: fail_todo_import_char16_t_param.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: char16_t` [SemanticsTodo]
-  // CHECK:STDERR:   Cpp.foo(1);
-  // CHECK:STDERR:   ^~~~~~~~~~
-  // CHECK:STDERR: fail_todo_import_char16_t_param.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_todo_import_char16_t_param.carbon:[[@LINE+4]]:3: error: semantics TODO: `Unsupported: parameter type: char16_t` [SemanticsTodo]
   // CHECK:STDERR:   Cpp.foo(1);
   // CHECK:STDERR:   ^~~~~~~~~~
   // CHECK:STDERR:
@@ -208,10 +202,7 @@ import Cpp library "char32_t_param.h";
 
 fn F() {
   //@dump-sem-ir-begin
-  // CHECK:STDERR: fail_todo_import_char32_t_param.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: char32_t` [SemanticsTodo]
-  // CHECK:STDERR:   Cpp.foo(1);
-  // CHECK:STDERR:   ^~~~~~~~~~
-  // CHECK:STDERR: fail_todo_import_char32_t_param.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_todo_import_char32_t_param.carbon:[[@LINE+4]]:3: error: semantics TODO: `Unsupported: parameter type: char32_t` [SemanticsTodo]
   // CHECK:STDERR:   Cpp.foo(1);
   // CHECK:STDERR:   ^~~~~~~~~~
   // CHECK:STDERR:
@@ -561,10 +552,7 @@ import Cpp library "bit_int_24_param.h";
 
 fn F() {
   //@dump-sem-ir-begin
-  // CHECK:STDERR: fail_todo_import_bit_int_24_param.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: bit_int_24` [SemanticsTodo]
-  // CHECK:STDERR:   Cpp.foo(1);
-  // CHECK:STDERR:   ^~~~~~~~~~
-  // CHECK:STDERR: fail_todo_import_bit_int_24_param.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_todo_import_bit_int_24_param.carbon:[[@LINE+4]]:3: error: semantics TODO: `Unsupported: parameter type: bit_int_24` [SemanticsTodo]
   // CHECK:STDERR:   Cpp.foo(1);
   // CHECK:STDERR:   ^~~~~~~~~~
   // CHECK:STDERR:
@@ -989,7 +977,7 @@ fn F() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %foo.ref: %.c5d = name_ref foo, imports.%.a21 [concrete = constants.%empty_struct]
-// CHECK:STDOUT:   %.loc15: Core.CharLiteral = char_value U+0058 [concrete = constants.%.d16]
+// CHECK:STDOUT:   %.loc12: Core.CharLiteral = char_value U+0058 [concrete = constants.%.d16]
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 2 - 2
toolchain/check/testdata/interop/cpp/function/class.carbon

@@ -153,7 +153,7 @@ import Cpp library "non_copyable_param_type.h";
 
 fn F() {
   //@dump-sem-ir-begin
-  // CHECK:STDERR: fail_import_non_copyable_param_type.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_import_non_copyable_param_type.carbon:[[@LINE+4]]:3: note: in thunk for C++ function used here [InCppThunk]
   // CHECK:STDERR:   Cpp.foo({} as Cpp.C);
   // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
@@ -440,7 +440,7 @@ library "[[@TEST_NAME]]";
 import Cpp library "decl_value_return_type.h";
 
 fn F() {
-  // CHECK:STDERR: fail_import_decl_value_return_type.carbon:[[@LINE+13]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_import_decl_value_return_type.carbon:[[@LINE+13]]:3: note: in thunk for C++ function used here [InCppThunk]
   // CHECK:STDERR:   Cpp.foo();
   // CHECK:STDERR:   ^~~~~~~~~
   // CHECK:STDERR:

+ 6 - 7
toolchain/check/testdata/interop/cpp/function/function.carbon

@@ -115,10 +115,7 @@ import Cpp library "variadic.h";
 
 fn F() {
   //@dump-sem-ir-begin
-  // CHECK:STDERR: fail_todo_import_variadic.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: Variadic function` [SemanticsTodo]
-  // CHECK:STDERR:   Cpp.foo(8);
-  // CHECK:STDERR:   ^~~~~~~~~~
-  // CHECK:STDERR: fail_todo_import_variadic.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_todo_import_variadic.carbon:[[@LINE+4]]:3: error: semantics TODO: `Unsupported: Variadic function` [SemanticsTodo]
   // CHECK:STDERR:   Cpp.foo(8);
   // CHECK:STDERR:   ^~~~~~~~~~
   // CHECK:STDERR:
@@ -139,16 +136,18 @@ static auto foo() -> void;
 library "[[@TEST_NAME]]";
 
 // TODO: Promote this warning to an error by default.
-// CHECK:STDERR: todo_fail_import_static.carbon:[[@LINE+5]]:10: in file included here [InCppInclude]
+// CHECK:STDERR: todo_fail_import_static.carbon:[[@LINE+4]]:10: in file included here [InCppInclude]
 // CHECK:STDERR: ./static.h:2:13: warning: function 'foo' has internal linkage but is not defined [CppInteropParseWarning]
 // CHECK:STDERR:     2 | static auto foo() -> void;
 // CHECK:STDERR:       |             ^
-// CHECK:STDERR:
 import Cpp library "static.h";
 
 fn F() {
   //@dump-sem-ir-begin
-  // TODO: Produce a note pointing here.
+  // CHECK:STDERR: todo_fail_import_static.carbon:[[@LINE+4]]:11: note: used here [CppInteropParseNote]
+  // CHECK:STDERR:    17 |   Cpp.foo();
+  // CHECK:STDERR:       |           ^
+  // CHECK:STDERR:
   Cpp.foo();
   //@dump-sem-ir-end
 }

+ 11 - 9
toolchain/check/testdata/interop/cpp/function/inline.carbon

@@ -43,16 +43,18 @@ inline void foo();
 library "[[@TEST_NAME]]";
 
 // TODO: Promote this warning to an error by default.
-// CHECK:STDERR: todo_fail_import_without_definition.carbon:[[@LINE+5]]:10: in file included here [InCppInclude]
+// CHECK:STDERR: todo_fail_import_without_definition.carbon:[[@LINE+4]]:10: in file included here [InCppInclude]
 // CHECK:STDERR: ./without_definition.h:2:13: warning: inline function 'foo' is not defined [CppInteropParseWarning]
 // CHECK:STDERR:     2 | inline void foo();
 // CHECK:STDERR:       |             ^
-// CHECK:STDERR:
 import Cpp library "without_definition.h";
 
 fn MyF() {
   //@dump-sem-ir-begin
-  // TODO: Produce a note pointing here.
+  // CHECK:STDERR: todo_fail_import_without_definition.carbon:[[@LINE+4]]:11: note: used here [CppInteropParseNote]
+  // CHECK:STDERR:    17 |   Cpp.foo();
+  // CHECK:STDERR:       |           ^
+  // CHECK:STDERR:
   Cpp.foo();
 
   // Don't error on repeated calls.
@@ -132,12 +134,12 @@ fn MyF() {
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @MyF() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %Cpp.ref.loc15: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:   %foo.ref.loc15: %.c5d = name_ref foo, imports.%.a21 [concrete = constants.%empty_struct]
-// CHECK:STDOUT:   %foo.call.loc15: init %empty_tuple.type = call imports.%foo.decl()
-// CHECK:STDOUT:   %Cpp.ref.loc18: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:   %foo.ref.loc18: %.c5d = name_ref foo, imports.%.a21 [concrete = constants.%empty_struct]
-// CHECK:STDOUT:   %foo.call.loc18: init %empty_tuple.type = call imports.%foo.decl()
+// CHECK:STDOUT:   %Cpp.ref.loc17: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %foo.ref.loc17: %.c5d = name_ref foo, imports.%.a21 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %foo.call.loc17: init %empty_tuple.type = call imports.%foo.decl()
+// CHECK:STDOUT:   %Cpp.ref.loc20: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %foo.ref.loc20: %.c5d = name_ref foo, imports.%.a21 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %foo.call.loc20: init %empty_tuple.type = call imports.%foo.decl()
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 48 - 50
toolchain/check/testdata/interop/cpp/function/operators.carbon

@@ -94,21 +94,22 @@ import Cpp library "postfix_inc_and_dec.h";
 
 fn F() {
   let postfix: Cpp.Postfix = Cpp.Postfix.Postfix();
-  // TODO: Make the error more descriptive to clarify we call prefix operators of a class with suffix operators.
-  // CHECK:STDERR: fail_postfix_calling_prefix.carbon:[[@LINE+7]]:3: error: no matching function for call to `<C++ operator>` [CppOverloadingNoViableFunctionFound]
-  // CHECK:STDERR:   ++postfix;
-  // CHECK:STDERR:   ^~~~~~~~~
-  // CHECK:STDERR: fail_postfix_calling_prefix.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   ++postfix;
-  // CHECK:STDERR:   ^~~~~~~~~
+  // CHECK:STDERR: fail_postfix_calling_prefix.carbon:[[@LINE+8]]:3: error: no matching function for call to '<C++ operator>' [CppInteropParseError]
+  // CHECK:STDERR:    16 |   ++postfix;
+  // CHECK:STDERR:       |   ^
+  // CHECK:STDERR: fail_postfix_calling_prefix.carbon:[[@LINE-7]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./postfix_inc_and_dec.h:3:6: note: candidate function not viable: requires 2 arguments, but 1 was provided [CppInteropParseNote]
+  // CHECK:STDERR:     3 | auto operator++(Postfix operand, int) -> Postfix;
+  // CHECK:STDERR:       |      ^          ~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
   ++postfix;
-  // CHECK:STDERR: fail_postfix_calling_prefix.carbon:[[@LINE+7]]:3: error: no matching function for call to `<C++ operator>` [CppOverloadingNoViableFunctionFound]
-  // CHECK:STDERR:   --postfix;
-  // CHECK:STDERR:   ^~~~~~~~~
-  // CHECK:STDERR: fail_postfix_calling_prefix.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   --postfix;
-  // CHECK:STDERR:   ^~~~~~~~~
+  // CHECK:STDERR: fail_postfix_calling_prefix.carbon:[[@LINE+8]]:3: error: no matching function for call to '<C++ operator>' [CppInteropParseError]
+  // CHECK:STDERR:    25 |   --postfix;
+  // CHECK:STDERR:       |   ^
+  // CHECK:STDERR: fail_postfix_calling_prefix.carbon:[[@LINE-16]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./postfix_inc_and_dec.h:4:6: note: candidate function not viable: requires 2 arguments, but 1 was provided [CppInteropParseNote]
+  // CHECK:STDERR:     4 | auto operator--(Postfix operand, int) -> Postfix;
+  // CHECK:STDERR:       |      ^          ~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
   --postfix;
 }
@@ -229,20 +230,22 @@ import Cpp library "binary_operators.h";
 
 fn F() {
   let c1: Cpp.C = Cpp.C.C();
-  // CHECK:STDERR: fail_call_with_wrong_type.carbon:[[@LINE+7]]:19: error: no matching function for call to `<C++ operator>` [CppOverloadingNoViableFunctionFound]
-  // CHECK:STDERR:   let c2: Cpp.C = c1 + 5;
-  // CHECK:STDERR:                   ^~~~~~
-  // CHECK:STDERR: fail_call_with_wrong_type.carbon:[[@LINE+4]]:19: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   let c2: Cpp.C = c1 + 5;
-  // CHECK:STDERR:                   ^~~~~~
+  // CHECK:STDERR: fail_call_with_wrong_type.carbon:[[@LINE+8]]:22: error: no matching function for call to '<C++ operator>' [CppInteropParseError]
+  // CHECK:STDERR:    16 |   let c2: Cpp.C = c1 + 5;
+  // CHECK:STDERR:       |                      ^
+  // CHECK:STDERR: fail_call_with_wrong_type.carbon:[[@LINE-7]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./binary_operators.h:5:6: note: candidate function not viable: no known conversion from 'int' to 'C' for 2nd argument [CppInteropParseNote]
+  // CHECK:STDERR:     5 | auto operator+(C lhs, C rhs) -> C;
+  // CHECK:STDERR:       |      ^                ~~~~~
   // CHECK:STDERR:
   let c2: Cpp.C = c1 + 5;
-  // CHECK:STDERR: fail_call_with_wrong_type.carbon:[[@LINE+7]]:19: error: no matching function for call to `<C++ operator>` [CppOverloadingNoViableFunctionFound]
-  // CHECK:STDERR:   let c3: Cpp.C = 6 + c1;
-  // CHECK:STDERR:                   ^~~~~~
-  // CHECK:STDERR: fail_call_with_wrong_type.carbon:[[@LINE+4]]:19: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   let c3: Cpp.C = 6 + c1;
-  // CHECK:STDERR:                   ^~~~~~
+  // CHECK:STDERR: fail_call_with_wrong_type.carbon:[[@LINE+8]]:21: error: no matching function for call to '<C++ operator>' [CppInteropParseError]
+  // CHECK:STDERR:    25 |   let c3: Cpp.C = 6 + c1;
+  // CHECK:STDERR:       |                     ^
+  // CHECK:STDERR: fail_call_with_wrong_type.carbon:[[@LINE-16]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./binary_operators.h:5:6: note: candidate function not viable: no known conversion from 'int' to 'C' for 1st argument [CppInteropParseNote]
+  // CHECK:STDERR:     5 | auto operator+(C lhs, C rhs) -> C;
+  // CHECK:STDERR:       |      ^         ~~~~~
   // CHECK:STDERR:
   let c3: Cpp.C = 6 + c1;
 }
@@ -602,12 +605,9 @@ fn F() {
   //@dump-sem-ir-begin
   let c1: Cpp.N.C = Cpp.N.C.C();
   let c2: Cpp.N.C = Cpp.N.C.C();
-  // CHECK:STDERR: fail_todo_import_operands_in_namespace_operator_in_global.carbon:[[@LINE+7]]:21: error: no matching function for call to `<C++ operator>` [CppOverloadingNoViableFunctionFound]
-  // CHECK:STDERR:   let c3: Cpp.N.C = c1 + c2;
-  // CHECK:STDERR:                     ^~~~~~~
-  // CHECK:STDERR: fail_todo_import_operands_in_namespace_operator_in_global.carbon:[[@LINE+4]]:21: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   let c3: Cpp.N.C = c1 + c2;
-  // CHECK:STDERR:                     ^~~~~~~
+  // CHECK:STDERR: fail_todo_import_operands_in_namespace_operator_in_global.carbon:[[@LINE+4]]:24: error: no matching function for call to '<C++ operator>' [CppInteropParseError]
+  // CHECK:STDERR:    14 |   let c3: Cpp.N.C = c1 + c2;
+  // CHECK:STDERR:       |                        ^
   // CHECK:STDERR:
   let c3: Cpp.N.C = c1 + c2;
   //@dump-sem-ir-end
@@ -687,12 +687,13 @@ fn F() {
   //@dump-sem-ir-begin
   let c1: Cpp.C = Cpp.C.C();
   let c2: Cpp.C = Cpp.C.C();
-  // CHECK:STDERR: fail_todo_import_member_add_with.carbon:[[@LINE+7]]:19: error: no matching function for call to `<C++ operator>` [CppOverloadingNoViableFunctionFound]
-  // CHECK:STDERR:   let c3: Cpp.C = c1 + c2;
-  // CHECK:STDERR:                   ^~~~~~~
-  // CHECK:STDERR: fail_todo_import_member_add_with.carbon:[[@LINE+4]]:19: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   let c3: Cpp.C = c1 + c2;
-  // CHECK:STDERR:                   ^~~~~~~
+  // CHECK:STDERR: fail_todo_import_member_add_with.carbon:[[@LINE+8]]:22: error: no matching function for call to '<C++ operator>' [CppInteropParseError]
+  // CHECK:STDERR:    18 |   let c3: Cpp.C = c1 + c2;
+  // CHECK:STDERR:       |                      ^
+  // CHECK:STDERR: fail_todo_import_member_add_with.carbon:[[@LINE-9]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./member_add_with.h:4:8: note: candidate function not viable: requires single argument 'rhs', but 2 arguments were provided [CppInteropParseNote]
+  // CHECK:STDERR:     4 |   auto operator+(C rhs) -> C;
+  // CHECK:STDERR:       |        ^         ~~~~~
   // CHECK:STDERR:
   let c3: Cpp.C = c1 + c2;
   //@dump-sem-ir-end
@@ -745,12 +746,9 @@ import Cpp library "not_found.h";
 fn F() {
   let c1: Cpp.C = Cpp.C.C();
   let c2: Cpp.C = Cpp.C.C();
-  // CHECK:STDERR: fail_import_not_found.carbon:[[@LINE+7]]:19: error: no matching function for call to `<C++ operator>` [CppOverloadingNoViableFunctionFound]
-  // CHECK:STDERR:   let c3: Cpp.C = c1 + c2;
-  // CHECK:STDERR:                   ^~~~~~~
-  // CHECK:STDERR: fail_import_not_found.carbon:[[@LINE+4]]:19: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   let c3: Cpp.C = c1 + c2;
-  // CHECK:STDERR:                   ^~~~~~~
+  // CHECK:STDERR: fail_import_not_found.carbon:[[@LINE+4]]:22: error: no matching function for call to '<C++ operator>' [CppInteropParseError]
+  // CHECK:STDERR:    13 |   let c3: Cpp.C = c1 + c2;
+  // CHECK:STDERR:       |                      ^
   // CHECK:STDERR:
   let c3: Cpp.C = c1 + c2;
 }
@@ -2385,10 +2383,10 @@ fn F() {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %c1.ref: %C = name_ref c1, %c1
 // CHECK:STDOUT:   %c2.ref: %C = name_ref c2, %c2
-// CHECK:STDOUT:   %.loc17: type = splice_block %C.ref.loc17 [concrete = constants.%C] {
-// CHECK:STDOUT:     %Cpp.ref.loc17: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:     %N.ref.loc17: <namespace> = name_ref N, imports.%N [concrete = imports.%N]
-// CHECK:STDOUT:     %C.ref.loc17: type = name_ref C, imports.%C.decl [concrete = constants.%C]
+// CHECK:STDOUT:   %.loc14: type = splice_block %C.ref.loc14 [concrete = constants.%C] {
+// CHECK:STDOUT:     %Cpp.ref.loc14: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:     %N.ref.loc14: <namespace> = name_ref N, imports.%N [concrete = imports.%N]
+// CHECK:STDOUT:     %C.ref.loc14: type = name_ref C, imports.%C.decl [concrete = constants.%C]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %c3: %C = bind_name c3, <error> [concrete = <error>]
 // CHECK:STDOUT:   %facet_value.loc9: %type_where = facet_value constants.%C, () [concrete = constants.%facet_value]
@@ -2739,9 +2737,9 @@ fn F() {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %c1.ref: %C = name_ref c1, %c1
 // CHECK:STDOUT:   %c2.ref: %C = name_ref c2, %c2
-// CHECK:STDOUT:   %.loc17: type = splice_block %C.ref.loc17 [concrete = constants.%C] {
-// CHECK:STDOUT:     %Cpp.ref.loc17: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:     %C.ref.loc17: type = name_ref C, imports.%C.decl [concrete = constants.%C]
+// CHECK:STDOUT:   %.loc18: type = splice_block %C.ref.loc18 [concrete = constants.%C] {
+// CHECK:STDOUT:     %Cpp.ref.loc18: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:     %C.ref.loc18: type = name_ref C, imports.%C.decl [concrete = constants.%C]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %c3: %C = bind_name c3, <error> [concrete = <error>]
 // CHECK:STDOUT:   %facet_value.loc9: %type_where = facet_value constants.%C, () [concrete = constants.%facet_value]

+ 46 - 38
toolchain/check/testdata/interop/cpp/function/overloads.carbon

@@ -183,12 +183,9 @@ library "[[@TEST_NAME]]";
 import Cpp library "struct_literal_call_arg.h";
 
 fn F() {
-  // CHECK:STDERR: fail_todo_import_struct_literal_call_arg.carbon:[[@LINE+7]]:11: error: call argument of type `{}` is not supported [CppCallArgTypeNotSupported]
+  // CHECK:STDERR: fail_todo_import_struct_literal_call_arg.carbon:[[@LINE+4]]:11: error: call argument of type `{}` is not supported [CppCallArgTypeNotSupported]
   // CHECK:STDERR:   Cpp.foo({});
   // CHECK:STDERR:           ^~
-  // CHECK:STDERR: fail_todo_import_struct_literal_call_arg.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   Cpp.foo({});
-  // CHECK:STDERR:   ^~~~~~~~~~~
   // CHECK:STDERR:
   Cpp.foo({});
 }
@@ -304,12 +301,13 @@ library "[[@TEST_NAME]]";
 import Cpp library "no_viable_function.h";
 
 fn F() {
-  // CHECK:STDERR: fail_import_no_viable_function.carbon:[[@LINE+7]]:3: error: no matching function for call to `foo` [CppOverloadingNoViableFunctionFound]
-  // CHECK:STDERR:   Cpp.foo(1 as i64);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_import_no_viable_function.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   Cpp.foo(1 as i64);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_import_no_viable_function.carbon:[[@LINE+8]]:19: error: no matching function for call to 'foo' [CppInteropParseError]
+  // CHECK:STDERR:    15 |   Cpp.foo(1 as i64);
+  // CHECK:STDERR:       |                   ^
+  // CHECK:STDERR: fail_import_no_viable_function.carbon:[[@LINE-6]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./no_viable_function.h:2:6: note: candidate function not viable: requires 2 arguments, but 1 was provided [CppInteropParseNote]
+  // CHECK:STDERR:     2 | auto foo(short a, int b) -> void;
+  // CHECK:STDERR:       |      ^   ~~~~~~~~~~~~~~
   // CHECK:STDERR:
   Cpp.foo(1 as i64);
 }
@@ -330,12 +328,17 @@ library "[[@TEST_NAME]]";
 import Cpp library "ambiguous_overload.h";
 
 fn F() {
-  // CHECK:STDERR: fail_import_ambiguous_overload.carbon:[[@LINE+7]]:3: error: call to `foo` is ambiguous [CppOverloadingAmbiguousCandidatesFound]
-  // CHECK:STDERR:   Cpp.foo(1 as i64);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_import_ambiguous_overload.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   Cpp.foo(1 as i64);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_import_ambiguous_overload.carbon:[[@LINE+12]]:19: error: call to 'foo' is ambiguous [CppInteropParseError]
+  // CHECK:STDERR:    19 |   Cpp.foo(1 as i64);
+  // CHECK:STDERR:       |                   ^
+  // CHECK:STDERR: fail_import_ambiguous_overload.carbon:[[@LINE-6]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./ambiguous_overload.h:2:6: note: candidate function [CppInteropParseNote]
+  // CHECK:STDERR:     2 | auto foo(short a) -> void;
+  // CHECK:STDERR:       |      ^
+  // CHECK:STDERR: fail_import_ambiguous_overload.carbon:[[@LINE-10]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./ambiguous_overload.h:3:6: note: candidate function [CppInteropParseNote]
+  // CHECK:STDERR:     3 | auto foo(int a) -> void;
+  // CHECK:STDERR:       |      ^
   // CHECK:STDERR:
   Cpp.foo(1 as i64);
 }
@@ -356,12 +359,17 @@ library "[[@TEST_NAME]]";
 import Cpp library "deleted_function.h";
 
 fn F() {
-  // CHECK:STDERR: fail_import_deleted_function.carbon:[[@LINE+7]]:3: error: call to deleted function `foo` [CppOverloadingDeletedFunctionFound]
-  // CHECK:STDERR:   Cpp.foo(1 as i32);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_import_deleted_function.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   Cpp.foo(1 as i32);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_import_deleted_function.carbon:[[@LINE+12]]:19: error: call to deleted function 'foo' [CppInteropParseError]
+  // CHECK:STDERR:    19 |   Cpp.foo(1 as i32);
+  // CHECK:STDERR:       |                   ^
+  // CHECK:STDERR: fail_import_deleted_function.carbon:[[@LINE-6]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./deleted_function.h:3:6: note: candidate function has been explicitly deleted [CppInteropParseNote]
+  // CHECK:STDERR:     3 | auto foo(int a) -> void = delete;
+  // CHECK:STDERR:       |      ^
+  // CHECK:STDERR: fail_import_deleted_function.carbon:[[@LINE-10]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./deleted_function.h:2:6: note: candidate function [CppInteropParseNote]
+  // CHECK:STDERR:     2 | auto foo(short a) -> void;
+  // CHECK:STDERR:       |      ^
   // CHECK:STDERR:
   Cpp.foo(1 as i32);
 }
@@ -1456,7 +1464,7 @@ fn F() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %foo.ref: %.c5d = name_ref foo, imports.%.a21 [concrete = constants.%empty_struct]
-// CHECK:STDOUT:   %.loc14: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc11: %empty_struct_type = struct_literal ()
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -1940,12 +1948,12 @@ fn F() {
 // CHECK:STDOUT:   %int_64: Core.IntLiteral = int_value 64 [concrete = constants.%int_64]
 // CHECK:STDOUT:   %i64: type = class_type @Int, @Int(constants.%int_64) [concrete = constants.%i64]
 // CHECK:STDOUT:   %impl.elem0: %.b12 = impl_witness_access constants.%As.impl_witness.4f1, element0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.fe2]
-// CHECK:STDOUT:   %bound_method.loc14_13.1: <bound method> = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound]
+// CHECK:STDOUT:   %bound_method.loc15_13.1: <bound method> = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound]
 // CHECK:STDOUT:   %specific_fn: <specific function> = specific_function %impl.elem0, @Core.IntLiteral.as.As.impl.Convert(constants.%int_64) [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.specific_fn]
-// CHECK:STDOUT:   %bound_method.loc14_13.2: <bound method> = bound_method %int_1, %specific_fn [concrete = constants.%bound_method]
-// CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.call: init %i64 = call %bound_method.loc14_13.2(%int_1) [concrete = constants.%int_1.41a]
-// CHECK:STDOUT:   %.loc14_13.1: %i64 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.41a]
-// CHECK:STDOUT:   %.loc14_13.2: %i64 = converted %int_1, %.loc14_13.1 [concrete = constants.%int_1.41a]
+// CHECK:STDOUT:   %bound_method.loc15_13.2: <bound method> = bound_method %int_1, %specific_fn [concrete = constants.%bound_method]
+// CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.call: init %i64 = call %bound_method.loc15_13.2(%int_1) [concrete = constants.%int_1.41a]
+// CHECK:STDOUT:   %.loc15_13.1: %i64 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.41a]
+// CHECK:STDOUT:   %.loc15_13.2: %i64 = converted %int_1, %.loc15_13.1 [concrete = constants.%int_1.41a]
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -2018,12 +2026,12 @@ fn F() {
 // CHECK:STDOUT:   %int_64: Core.IntLiteral = int_value 64 [concrete = constants.%int_64]
 // CHECK:STDOUT:   %i64: type = class_type @Int, @Int(constants.%int_64) [concrete = constants.%i64]
 // CHECK:STDOUT:   %impl.elem0: %.b12 = impl_witness_access constants.%As.impl_witness.4f1, element0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.fe2]
-// CHECK:STDOUT:   %bound_method.loc14_13.1: <bound method> = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound]
+// CHECK:STDOUT:   %bound_method.loc19_13.1: <bound method> = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound]
 // CHECK:STDOUT:   %specific_fn: <specific function> = specific_function %impl.elem0, @Core.IntLiteral.as.As.impl.Convert(constants.%int_64) [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.specific_fn]
-// CHECK:STDOUT:   %bound_method.loc14_13.2: <bound method> = bound_method %int_1, %specific_fn [concrete = constants.%bound_method]
-// CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.call: init %i64 = call %bound_method.loc14_13.2(%int_1) [concrete = constants.%int_1.41a]
-// CHECK:STDOUT:   %.loc14_13.1: %i64 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.41a]
-// CHECK:STDOUT:   %.loc14_13.2: %i64 = converted %int_1, %.loc14_13.1 [concrete = constants.%int_1.41a]
+// CHECK:STDOUT:   %bound_method.loc19_13.2: <bound method> = bound_method %int_1, %specific_fn [concrete = constants.%bound_method]
+// CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.call: init %i64 = call %bound_method.loc19_13.2(%int_1) [concrete = constants.%int_1.41a]
+// CHECK:STDOUT:   %.loc19_13.1: %i64 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.41a]
+// CHECK:STDOUT:   %.loc19_13.2: %i64 = converted %int_1, %.loc19_13.1 [concrete = constants.%int_1.41a]
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -2096,12 +2104,12 @@ fn F() {
 // CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
 // CHECK:STDOUT:   %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
 // CHECK:STDOUT:   %impl.elem0: %.351 = impl_witness_access constants.%As.impl_witness.080, element0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.414]
-// CHECK:STDOUT:   %bound_method.loc14_13.1: <bound method> = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound]
+// CHECK:STDOUT:   %bound_method.loc19_13.1: <bound method> = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound]
 // CHECK:STDOUT:   %specific_fn: <specific function> = specific_function %impl.elem0, @Core.IntLiteral.as.As.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.specific_fn]
-// CHECK:STDOUT:   %bound_method.loc14_13.2: <bound method> = bound_method %int_1, %specific_fn [concrete = constants.%bound_method]
-// CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.call: init %i32 = call %bound_method.loc14_13.2(%int_1) [concrete = constants.%int_1.5d2]
-// CHECK:STDOUT:   %.loc14_13.1: %i32 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.5d2]
-// CHECK:STDOUT:   %.loc14_13.2: %i32 = converted %int_1, %.loc14_13.1 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %bound_method.loc19_13.2: <bound method> = bound_method %int_1, %specific_fn [concrete = constants.%bound_method]
+// CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.call: init %i32 = call %bound_method.loc19_13.2(%int_1) [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc19_13.1: %i32 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc19_13.2: %i32 = converted %int_1, %.loc19_13.1 [concrete = constants.%int_1.5d2]
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 2 - 8
toolchain/check/testdata/interop/cpp/function/param_unsupported.carbon

@@ -26,10 +26,7 @@ import Cpp library "unsupported_primitive_type.h";
 
 fn F() {
   //@dump-sem-ir-begin
-  // CHECK:STDERR: fail_todo_import_unsupported_primitive_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: _BitInt(23)` [SemanticsTodo]
-  // CHECK:STDERR:   Cpp.foo(11);
-  // CHECK:STDERR:   ^~~~~~~~~~~
-  // CHECK:STDERR: fail_todo_import_unsupported_primitive_type.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_todo_import_unsupported_primitive_type.carbon:[[@LINE+4]]:3: error: semantics TODO: `Unsupported: parameter type: _BitInt(23)` [SemanticsTodo]
   // CHECK:STDERR:   Cpp.foo(11);
   // CHECK:STDERR:   ^~~~~~~~~~~
   // CHECK:STDERR:
@@ -53,10 +50,7 @@ import Cpp library "unsupported_primitive_type_among_params.h";
 
 fn F() {
   //@dump-sem-ir-begin
-  // CHECK:STDERR: fail_todo_import_unsupported_primitive_type_among_params.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: _BitInt(23)` [SemanticsTodo]
-  // CHECK:STDERR:   Cpp.foo(1, 20);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_todo_import_unsupported_primitive_type_among_params.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_todo_import_unsupported_primitive_type_among_params.carbon:[[@LINE+4]]:3: error: semantics TODO: `Unsupported: parameter type: _BitInt(23)` [SemanticsTodo]
   // CHECK:STDERR:   Cpp.foo(1, 20);
   // CHECK:STDERR:   ^~~~~~~~~~~~~~
   // CHECK:STDERR:

+ 23 - 32
toolchain/check/testdata/interop/cpp/function/pointer.carbon

@@ -176,10 +176,7 @@ import Cpp library "nullable_pointer_param.h";
 fn F() {
   //@dump-sem-ir-begin
   var s: Cpp.S = {};
-  // CHECK:STDERR: fail_todo_import_nullable_pointer_param.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: S *` [SemanticsTodo]
-  // CHECK:STDERR:   Cpp.foo(&s);
-  // CHECK:STDERR:   ^~~~~~~~~~~
-  // CHECK:STDERR: fail_todo_import_nullable_pointer_param.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_todo_import_nullable_pointer_param.carbon:[[@LINE+4]]:3: error: semantics TODO: `Unsupported: parameter type: S *` [SemanticsTodo]
   // CHECK:STDERR:   Cpp.foo(&s);
   // CHECK:STDERR:   ^~~~~~~~~~~
   // CHECK:STDERR:
@@ -236,19 +233,13 @@ import Cpp library "deduced_pointer_param.h";
 fn F() {
   //@dump-sem-ir-begin
   var s: Cpp.S = {};
-  // CHECK:STDERR: fail_import_deduced_pointer_param_as_pointer.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: struct S *` [SemanticsTodo]
-  // CHECK:STDERR:   Cpp.Direct(&s);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_import_deduced_pointer_param_as_pointer.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_import_deduced_pointer_param_as_pointer.carbon:[[@LINE+4]]:3: error: semantics TODO: `Unsupported: parameter type: struct S *` [SemanticsTodo]
   // CHECK:STDERR:   Cpp.Direct(&s);
   // CHECK:STDERR:   ^~~~~~~~~~~~~~
   // CHECK:STDERR:
   Cpp.Direct(&s);
 
-  // CHECK:STDERR: fail_import_deduced_pointer_param_as_pointer.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: return type: AddPointer<struct S>` [SemanticsTodo]
-  // CHECK:STDERR:   Cpp.Indirect({} as Cpp.S);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_import_deduced_pointer_param_as_pointer.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_import_deduced_pointer_param_as_pointer.carbon:[[@LINE+4]]:3: error: semantics TODO: `Unsupported: return type: AddPointer<struct S>` [SemanticsTodo]
   // CHECK:STDERR:   Cpp.Indirect({} as Cpp.S);
   // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
@@ -681,10 +672,10 @@ fn F() {
 // CHECK:STDOUT:     %S.ref: type = name_ref S, imports.%S.decl [concrete = constants.%S]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %s: ref %S = bind_name s, %s.var
-// CHECK:STDOUT:   %Cpp.ref.loc16: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Cpp.ref.loc13: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %foo.ref: %.c5d = name_ref foo, imports.%.a21 [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %s.ref: ref %S = name_ref s, %s
-// CHECK:STDOUT:   %addr.loc16: %ptr.5c7 = addr_of %s.ref
+// CHECK:STDOUT:   %addr.loc13: %ptr.5c7 = addr_of %s.ref
 // CHECK:STDOUT:   %facet_value: %type_where = facet_value constants.%S, () [concrete = constants.%facet_value]
 // CHECK:STDOUT:   %.loc8_3.2: %type_where = converted constants.%S, %facet_value [concrete = constants.%facet_value]
 // CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound: <bound method> = bound_method %s.var, constants.%AggregateT.as_type.as.Destroy.impl.Op.cc2
@@ -826,28 +817,28 @@ fn F() {
 // CHECK:STDOUT:     %S.ref.loc8: type = name_ref S, imports.%S.decl [concrete = constants.%S]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %s: ref %S = bind_name s, %s.var
-// CHECK:STDOUT:   %Cpp.ref.loc16: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Cpp.ref.loc13: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %Direct.ref: %.97c = name_ref Direct, imports.%.e4a [concrete = constants.%empty_struct.504]
 // CHECK:STDOUT:   %s.ref: ref %S = name_ref s, %s
-// CHECK:STDOUT:   %addr.loc16: %ptr.5c7 = addr_of %s.ref
-// CHECK:STDOUT:   %Cpp.ref.loc25_3: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %addr.loc13: %ptr.5c7 = addr_of %s.ref
+// CHECK:STDOUT:   %Cpp.ref.loc19_3: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %Indirect.ref: %.fed = name_ref Indirect, imports.%.9c4 [concrete = constants.%empty_struct.6e8]
-// CHECK:STDOUT:   %.loc25_17.1: %empty_struct_type = struct_literal ()
-// CHECK:STDOUT:   %Cpp.ref.loc25_22: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:   %S.ref.loc25: type = name_ref S, imports.%S.decl [concrete = constants.%S]
-// CHECK:STDOUT:   %.loc25_17.2: ref %S = temporary_storage
-// CHECK:STDOUT:   %.loc25_17.3: init %S = class_init (), %.loc25_17.2 [concrete = constants.%S.val]
-// CHECK:STDOUT:   %.loc25_17.4: ref %S = temporary %.loc25_17.2, %.loc25_17.3
-// CHECK:STDOUT:   %.loc25_19.1: ref %S = converted %.loc25_17.1, %.loc25_17.4
-// CHECK:STDOUT:   %.loc25_19.2: %S = bind_value %.loc25_19.1
-// CHECK:STDOUT:   %Indirect.call: init <error> = call imports.%Indirect.decl(%.loc25_19.2)
-// CHECK:STDOUT:   %facet_value.loc25: %type_where = facet_value constants.%S, () [concrete = constants.%facet_value]
-// CHECK:STDOUT:   %.loc25_17.5: %type_where = converted constants.%S, %facet_value.loc25 [concrete = constants.%facet_value]
-// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc25: <bound method> = bound_method %.loc25_17.4, constants.%AggregateT.as_type.as.Destroy.impl.Op.cc2
+// CHECK:STDOUT:   %.loc19_17.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %Cpp.ref.loc19_22: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %S.ref.loc19: type = name_ref S, imports.%S.decl [concrete = constants.%S]
+// CHECK:STDOUT:   %.loc19_17.2: ref %S = temporary_storage
+// CHECK:STDOUT:   %.loc19_17.3: init %S = class_init (), %.loc19_17.2 [concrete = constants.%S.val]
+// CHECK:STDOUT:   %.loc19_17.4: ref %S = temporary %.loc19_17.2, %.loc19_17.3
+// CHECK:STDOUT:   %.loc19_19.1: ref %S = converted %.loc19_17.1, %.loc19_17.4
+// CHECK:STDOUT:   %.loc19_19.2: %S = bind_value %.loc19_19.1
+// CHECK:STDOUT:   %Indirect.call: init <error> = call imports.%Indirect.decl(%.loc19_19.2)
+// CHECK:STDOUT:   %facet_value.loc19: %type_where = facet_value constants.%S, () [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %.loc19_17.5: %type_where = converted constants.%S, %facet_value.loc19 [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc19: <bound method> = bound_method %.loc19_17.4, constants.%AggregateT.as_type.as.Destroy.impl.Op.cc2
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc25: <bound method> = bound_method %.loc25_17.4, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.1
-// CHECK:STDOUT:   %addr.loc25: %ptr.5c7 = addr_of %.loc25_17.4
-// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc25: init %empty_tuple.type = call %bound_method.loc25(%addr.loc25)
+// CHECK:STDOUT:   %bound_method.loc19: <bound method> = bound_method %.loc19_17.4, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.1
+// CHECK:STDOUT:   %addr.loc19: %ptr.5c7 = addr_of %.loc19_17.4
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc19: init %empty_tuple.type = call %bound_method.loc19(%addr.loc19)
 // CHECK:STDOUT:   %facet_value.loc8: %type_where = facet_value constants.%S, () [concrete = constants.%facet_value]
 // CHECK:STDOUT:   %.loc8_3.2: %type_where = converted constants.%S, %facet_value.loc8 [concrete = constants.%facet_value]
 // CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc8: <bound method> = bound_method %s.var, constants.%AggregateT.as_type.as.Destroy.impl.Op.cc2

+ 111 - 104
toolchain/check/testdata/interop/cpp/function/reference.carbon

@@ -44,32 +44,35 @@ fn F() {
   //@dump-sem-ir-begin
   var v: Cpp.S;
   let s: Cpp.S = v;
-  // CHECK:STDERR: fail_lvalue_ref.carbon:[[@LINE+7]]:3: error: no matching function for call to `TakesLValue` [CppOverloadingNoViableFunctionFound]
-  // CHECK:STDERR:   Cpp.TakesLValue(s);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_lvalue_ref.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   Cpp.TakesLValue(s);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_lvalue_ref.carbon:[[@LINE+8]]:20: error: no matching function for call to 'TakesLValue' [CppInteropParseError]
+  // CHECK:STDERR:    18 |   Cpp.TakesLValue(s);
+  // CHECK:STDERR:       |                    ^
+  // CHECK:STDERR: fail_lvalue_ref.carbon:[[@LINE-9]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./lvalue_ref.h:5:6: note: candidate function not viable: expects an lvalue for 1st argument [CppInteropParseNote]
+  // CHECK:STDERR:     5 | auto TakesLValue(S&) -> void;
+  // CHECK:STDERR:       |      ^           ~~
   // CHECK:STDERR:
   Cpp.TakesLValue(s);
 
   var t: Cpp.T;
-  // CHECK:STDERR: fail_lvalue_ref.carbon:[[@LINE+7]]:3: error: no matching function for call to `TakesLValue` [CppOverloadingNoViableFunctionFound]
-  // CHECK:STDERR:   Cpp.TakesLValue(t);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_lvalue_ref.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   Cpp.TakesLValue(t);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_lvalue_ref.carbon:[[@LINE+8]]:20: error: no matching function for call to 'TakesLValue' [CppInteropParseError]
+  // CHECK:STDERR:    29 |   Cpp.TakesLValue(t);
+  // CHECK:STDERR:       |                    ^
+  // CHECK:STDERR: fail_lvalue_ref.carbon:[[@LINE-20]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./lvalue_ref.h:5:6: note: candidate function not viable: no known conversion from 'T' to 'S &' for 1st argument [CppInteropParseNote]
+  // CHECK:STDERR:     5 | auto TakesLValue(S&) -> void;
+  // CHECK:STDERR:       |      ^           ~~
   // CHECK:STDERR:
   Cpp.TakesLValue(t);
 
   var u: Cpp.S;
-  // CHECK:STDERR: fail_lvalue_ref.carbon:[[@LINE+7]]:3: error: no matching function for call to `TakesLValue` [CppOverloadingNoViableFunctionFound]
-  // CHECK:STDERR:   Cpp.TakesLValue(u as const Cpp.S);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_lvalue_ref.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   Cpp.TakesLValue(u as const Cpp.S);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_lvalue_ref.carbon:[[@LINE+8]]:35: error: no matching function for call to 'TakesLValue' [CppInteropParseError]
+  // CHECK:STDERR:    40 |   Cpp.TakesLValue(u as const Cpp.S);
+  // CHECK:STDERR:       |                                   ^
+  // CHECK:STDERR: fail_lvalue_ref.carbon:[[@LINE-31]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./lvalue_ref.h:5:6: note: candidate function not viable: 1st argument ('const S') would lose const qualifier [CppInteropParseNote]
+  // CHECK:STDERR:     5 | auto TakesLValue(S&) -> void;
+  // CHECK:STDERR:       |      ^           ~~
   // CHECK:STDERR:
   Cpp.TakesLValue(u as const Cpp.S);
   //@dump-sem-ir-end
@@ -124,31 +127,34 @@ import Cpp library "rvalue_ref.h";
 fn F() {
   //@dump-sem-ir-begin
   var s: Cpp.S;
-  // CHECK:STDERR: fail_rvalue_ref.carbon:[[@LINE+7]]:3: error: no matching function for call to `TakesRValue` [CppOverloadingNoViableFunctionFound]
-  // CHECK:STDERR:   Cpp.TakesRValue(s);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_rvalue_ref.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   Cpp.TakesRValue(s);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_rvalue_ref.carbon:[[@LINE+8]]:20: error: no matching function for call to 'TakesRValue' [CppInteropParseError]
+  // CHECK:STDERR:    17 |   Cpp.TakesRValue(s);
+  // CHECK:STDERR:       |                    ^
+  // CHECK:STDERR: fail_rvalue_ref.carbon:[[@LINE-8]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./rvalue_ref.h:5:6: note: candidate function not viable: expects an rvalue for 1st argument [CppInteropParseNote]
+  // CHECK:STDERR:     5 | auto TakesRValue(S&&) -> void;
+  // CHECK:STDERR:       |      ^           ~~~
   // CHECK:STDERR:
   Cpp.TakesRValue(s);
 
   var t: Cpp.T;
-  // CHECK:STDERR: fail_rvalue_ref.carbon:[[@LINE+7]]:3: error: no matching function for call to `TakesRValue` [CppOverloadingNoViableFunctionFound]
-  // CHECK:STDERR:   Cpp.TakesRValue(t);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_rvalue_ref.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   Cpp.TakesRValue(t);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_rvalue_ref.carbon:[[@LINE+8]]:20: error: no matching function for call to 'TakesRValue' [CppInteropParseError]
+  // CHECK:STDERR:    28 |   Cpp.TakesRValue(t);
+  // CHECK:STDERR:       |                    ^
+  // CHECK:STDERR: fail_rvalue_ref.carbon:[[@LINE-19]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./rvalue_ref.h:5:6: note: candidate function not viable: no known conversion from 'T' to 'S' for 1st argument [CppInteropParseNote]
+  // CHECK:STDERR:     5 | auto TakesRValue(S&&) -> void;
+  // CHECK:STDERR:       |      ^           ~~~
   // CHECK:STDERR:
   Cpp.TakesRValue(t);
 
-  // CHECK:STDERR: fail_rvalue_ref.carbon:[[@LINE+7]]:3: error: no matching function for call to `TakesRValue` [CppOverloadingNoViableFunctionFound]
-  // CHECK:STDERR:   Cpp.TakesRValue(({} as Cpp.S) as const Cpp.S);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_rvalue_ref.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   Cpp.TakesRValue(({} as Cpp.S) as const Cpp.S);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_rvalue_ref.carbon:[[@LINE+8]]:47: error: no matching function for call to 'TakesRValue' [CppInteropParseError]
+  // CHECK:STDERR:    38 |   Cpp.TakesRValue(({} as Cpp.S) as const Cpp.S);
+  // CHECK:STDERR:       |                                               ^
+  // CHECK:STDERR: fail_rvalue_ref.carbon:[[@LINE-29]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./rvalue_ref.h:5:6: note: candidate function not viable: 1st argument ('const S') would lose const qualifier [CppInteropParseNote]
+  // CHECK:STDERR:     5 | auto TakesRValue(S&&) -> void;
+  // CHECK:STDERR:       |      ^           ~~~
   // CHECK:STDERR:
   Cpp.TakesRValue(({} as Cpp.S) as const Cpp.S);
   //@dump-sem-ir-end
@@ -201,12 +207,13 @@ fn F() {
   Cpp.TakesConstLValue(s);
 
   var t: Cpp.T;
-  // CHECK:STDERR: fail_const_lvalue_ref.carbon:[[@LINE+7]]:3: error: no matching function for call to `TakesConstLValue` [CppOverloadingNoViableFunctionFound]
-  // CHECK:STDERR:   Cpp.TakesConstLValue(t);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR: fail_const_lvalue_ref.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   Cpp.TakesConstLValue(t);
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR: fail_const_lvalue_ref.carbon:[[@LINE+8]]:25: error: no matching function for call to 'TakesConstLValue' [CppInteropParseError]
+  // CHECK:STDERR:    29 |   Cpp.TakesConstLValue(t);
+  // CHECK:STDERR:       |                         ^
+  // CHECK:STDERR: fail_const_lvalue_ref.carbon:[[@LINE-20]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./const_lvalue_ref.h:5:6: note: candidate function not viable: no known conversion from 'T' to 'const S' for 1st argument [CppInteropParseNote]
+  // CHECK:STDERR:     5 | auto TakesConstLValue(const S&) -> void;
+  // CHECK:STDERR:       |      ^                ~~~~~~~~
   // CHECK:STDERR:
   Cpp.TakesConstLValue(t);
   //@dump-sem-ir-end
@@ -333,54 +340,54 @@ fn F() {
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %.loc9_18: %S = bind_value %v.ref
 // CHECK:STDOUT:   %s: %S = bind_name s, %.loc9_18
-// CHECK:STDOUT:   %Cpp.ref.loc17: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:   %TakesLValue.ref.loc17: %.547 = name_ref TakesLValue, imports.%.a7f [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %Cpp.ref.loc18: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %TakesLValue.ref.loc18: %.547 = name_ref TakesLValue, imports.%.a7f [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %s.ref: %S = name_ref s, %s
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %t.patt: %pattern_type.e6b = binding_pattern t [concrete]
 // CHECK:STDOUT:     %t.var_patt: %pattern_type.e6b = var_pattern %t.patt [concrete]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %t.var: ref %T = var %t.var_patt
-// CHECK:STDOUT:   %.loc19_13: type = splice_block %T.ref [concrete = constants.%T] {
-// CHECK:STDOUT:     %Cpp.ref.loc19: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %.loc20_13: type = splice_block %T.ref [concrete = constants.%T] {
+// CHECK:STDOUT:     %Cpp.ref.loc20: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:     %T.ref: type = name_ref T, imports.%T.decl [concrete = constants.%T]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %t: ref %T = bind_name t, %t.var
-// CHECK:STDOUT:   %Cpp.ref.loc27: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:   %TakesLValue.ref.loc27: %.547 = name_ref TakesLValue, imports.%.a7f [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %Cpp.ref.loc29: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %TakesLValue.ref.loc29: %.547 = name_ref TakesLValue, imports.%.a7f [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %t.ref: ref %T = name_ref t, %t
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %u.patt: %pattern_type.7da = binding_pattern u [concrete]
 // CHECK:STDOUT:     %u.var_patt: %pattern_type.7da = var_pattern %u.patt [concrete]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %u.var: ref %S = var %u.var_patt
-// CHECK:STDOUT:   %.loc29_13: type = splice_block %S.ref.loc29 [concrete = constants.%S] {
-// CHECK:STDOUT:     %Cpp.ref.loc29: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:     %S.ref.loc29: type = name_ref S, imports.%S.decl [concrete = constants.%S]
+// CHECK:STDOUT:   %.loc31_13: type = splice_block %S.ref.loc31 [concrete = constants.%S] {
+// CHECK:STDOUT:     %Cpp.ref.loc31: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:     %S.ref.loc31: type = name_ref S, imports.%S.decl [concrete = constants.%S]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %u: ref %S = bind_name u, %u.var
-// CHECK:STDOUT:   %Cpp.ref.loc37_3: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:   %TakesLValue.ref.loc37: %.547 = name_ref TakesLValue, imports.%.a7f [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %Cpp.ref.loc40_3: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %TakesLValue.ref.loc40: %.547 = name_ref TakesLValue, imports.%.a7f [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %u.ref: ref %S = name_ref u, %u
-// CHECK:STDOUT:   %Cpp.ref.loc37_30: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:   %S.ref.loc37: type = name_ref S, imports.%S.decl [concrete = constants.%S]
-// CHECK:STDOUT:   %const: type = const_type %S.ref.loc37 [concrete = constants.%const.e39]
-// CHECK:STDOUT:   %.loc37_21.1: ref %const.e39 = as_compatible %u.ref
-// CHECK:STDOUT:   %.loc37_21.2: ref %const.e39 = converted %u.ref, %.loc37_21.1
-// CHECK:STDOUT:   %facet_value.loc29: %type_where = facet_value constants.%S, () [concrete = constants.%facet_value.7bd]
-// CHECK:STDOUT:   %.loc29_3: %type_where = converted constants.%S, %facet_value.loc29 [concrete = constants.%facet_value.7bd]
-// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc29: <bound method> = bound_method %u.var, constants.%AggregateT.as_type.as.Destroy.impl.Op.cc2
+// CHECK:STDOUT:   %Cpp.ref.loc40_30: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %S.ref.loc40: type = name_ref S, imports.%S.decl [concrete = constants.%S]
+// CHECK:STDOUT:   %const: type = const_type %S.ref.loc40 [concrete = constants.%const.e39]
+// CHECK:STDOUT:   %.loc40_21.1: ref %const.e39 = as_compatible %u.ref
+// CHECK:STDOUT:   %.loc40_21.2: ref %const.e39 = converted %u.ref, %.loc40_21.1
+// CHECK:STDOUT:   %facet_value.loc31: %type_where = facet_value constants.%S, () [concrete = constants.%facet_value.7bd]
+// CHECK:STDOUT:   %.loc31_3: %type_where = converted constants.%S, %facet_value.loc31 [concrete = constants.%facet_value.7bd]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc31: <bound method> = bound_method %u.var, constants.%AggregateT.as_type.as.Destroy.impl.Op.cc2
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc29: <bound method> = bound_method %u.var, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.1
-// CHECK:STDOUT:   %addr.loc29: %ptr.5c7 = addr_of %u.var
-// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc29: init %empty_tuple.type = call %bound_method.loc29(%addr.loc29)
-// CHECK:STDOUT:   %facet_value.loc19: %type_where = facet_value constants.%T, () [concrete = constants.%facet_value.19d]
-// CHECK:STDOUT:   %.loc19_3: %type_where = converted constants.%T, %facet_value.loc19 [concrete = constants.%facet_value.19d]
-// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc19: <bound method> = bound_method %t.var, constants.%AggregateT.as_type.as.Destroy.impl.Op.8c8
+// CHECK:STDOUT:   %bound_method.loc31: <bound method> = bound_method %u.var, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.1
+// CHECK:STDOUT:   %addr.loc31: %ptr.5c7 = addr_of %u.var
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc31: init %empty_tuple.type = call %bound_method.loc31(%addr.loc31)
+// CHECK:STDOUT:   %facet_value.loc20: %type_where = facet_value constants.%T, () [concrete = constants.%facet_value.19d]
+// CHECK:STDOUT:   %.loc20_3: %type_where = converted constants.%T, %facet_value.loc20 [concrete = constants.%facet_value.19d]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc20: <bound method> = bound_method %t.var, constants.%AggregateT.as_type.as.Destroy.impl.Op.8c8
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc19: <bound method> = bound_method %t.var, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.2
-// CHECK:STDOUT:   %addr.loc19: %ptr.b04 = addr_of %t.var
-// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc19: init %empty_tuple.type = call %bound_method.loc19(%addr.loc19)
+// CHECK:STDOUT:   %bound_method.loc20: <bound method> = bound_method %t.var, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.2
+// CHECK:STDOUT:   %addr.loc20: %ptr.b04 = addr_of %t.var
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc20: init %empty_tuple.type = call %bound_method.loc20(%addr.loc20)
 // CHECK:STDOUT:   %facet_value.loc8: %type_where = facet_value constants.%S, () [concrete = constants.%facet_value.7bd]
 // CHECK:STDOUT:   %.loc8_3: %type_where = converted constants.%S, %facet_value.loc8 [concrete = constants.%facet_value.7bd]
 // CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc8: <bound method> = bound_method %v.var, constants.%AggregateT.as_type.as.Destroy.impl.Op.cc2
@@ -563,50 +570,50 @@ fn F() {
 // CHECK:STDOUT:     %S.ref.loc8: type = name_ref S, imports.%S.decl [concrete = constants.%S]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %s: ref %S = bind_name s, %s.var
-// CHECK:STDOUT:   %Cpp.ref.loc16: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:   %TakesRValue.ref.loc16: %.1b9 = name_ref TakesRValue, imports.%.1f6 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %Cpp.ref.loc17: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %TakesRValue.ref.loc17: %.1b9 = name_ref TakesRValue, imports.%.1f6 [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %s.ref: ref %S = name_ref s, %s
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %t.patt: %pattern_type.e6b = binding_pattern t [concrete]
 // CHECK:STDOUT:     %t.var_patt: %pattern_type.e6b = var_pattern %t.patt [concrete]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %t.var: ref %T = var %t.var_patt
-// CHECK:STDOUT:   %.loc18_13: type = splice_block %T.ref [concrete = constants.%T] {
-// CHECK:STDOUT:     %Cpp.ref.loc18: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %.loc19_13: type = splice_block %T.ref [concrete = constants.%T] {
+// CHECK:STDOUT:     %Cpp.ref.loc19: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:     %T.ref: type = name_ref T, imports.%T.decl [concrete = constants.%T]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %t: ref %T = bind_name t, %t.var
-// CHECK:STDOUT:   %Cpp.ref.loc26: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:   %TakesRValue.ref.loc26: %.1b9 = name_ref TakesRValue, imports.%.1f6 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %Cpp.ref.loc28: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %TakesRValue.ref.loc28: %.1b9 = name_ref TakesRValue, imports.%.1f6 [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %t.ref: ref %T = name_ref t, %t
-// CHECK:STDOUT:   %Cpp.ref.loc35_3: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:   %TakesRValue.ref.loc35: %.1b9 = name_ref TakesRValue, imports.%.1f6 [concrete = constants.%empty_struct]
-// CHECK:STDOUT:   %.loc35_21.1: %empty_struct_type = struct_literal ()
-// CHECK:STDOUT:   %Cpp.ref.loc35_26: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:   %S.ref.loc35_29: type = name_ref S, imports.%S.decl [concrete = constants.%S]
-// CHECK:STDOUT:   %.loc35_21.2: ref %S = temporary_storage
-// CHECK:STDOUT:   %.loc35_21.3: init %S = class_init (), %.loc35_21.2 [concrete = constants.%S.val]
-// CHECK:STDOUT:   %.loc35_21.4: ref %S = temporary %.loc35_21.2, %.loc35_21.3
-// CHECK:STDOUT:   %.loc35_23: ref %S = converted %.loc35_21.1, %.loc35_21.4
-// CHECK:STDOUT:   %Cpp.ref.loc35_42: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:   %S.ref.loc35_45: type = name_ref S, imports.%S.decl [concrete = constants.%S]
-// CHECK:STDOUT:   %const: type = const_type %S.ref.loc35_45 [concrete = constants.%const.e39]
-// CHECK:STDOUT:   %.loc35_33.1: ref %const.e39 = as_compatible %.loc35_23
-// CHECK:STDOUT:   %.loc35_33.2: ref %const.e39 = converted %.loc35_23, %.loc35_33.1
-// CHECK:STDOUT:   %facet_value.loc35: %type_where = facet_value constants.%S, () [concrete = constants.%facet_value.7bd]
-// CHECK:STDOUT:   %.loc35_21.5: %type_where = converted constants.%S, %facet_value.loc35 [concrete = constants.%facet_value.7bd]
-// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc35: <bound method> = bound_method %.loc35_21.4, constants.%AggregateT.as_type.as.Destroy.impl.Op.cc2
+// CHECK:STDOUT:   %Cpp.ref.loc38_3: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %TakesRValue.ref.loc38: %.1b9 = name_ref TakesRValue, imports.%.1f6 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %.loc38_21.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %Cpp.ref.loc38_26: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %S.ref.loc38_29: type = name_ref S, imports.%S.decl [concrete = constants.%S]
+// CHECK:STDOUT:   %.loc38_21.2: ref %S = temporary_storage
+// CHECK:STDOUT:   %.loc38_21.3: init %S = class_init (), %.loc38_21.2 [concrete = constants.%S.val]
+// CHECK:STDOUT:   %.loc38_21.4: ref %S = temporary %.loc38_21.2, %.loc38_21.3
+// CHECK:STDOUT:   %.loc38_23: ref %S = converted %.loc38_21.1, %.loc38_21.4
+// CHECK:STDOUT:   %Cpp.ref.loc38_42: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %S.ref.loc38_45: type = name_ref S, imports.%S.decl [concrete = constants.%S]
+// CHECK:STDOUT:   %const: type = const_type %S.ref.loc38_45 [concrete = constants.%const.e39]
+// CHECK:STDOUT:   %.loc38_33.1: ref %const.e39 = as_compatible %.loc38_23
+// CHECK:STDOUT:   %.loc38_33.2: ref %const.e39 = converted %.loc38_23, %.loc38_33.1
+// CHECK:STDOUT:   %facet_value.loc38: %type_where = facet_value constants.%S, () [concrete = constants.%facet_value.7bd]
+// CHECK:STDOUT:   %.loc38_21.5: %type_where = converted constants.%S, %facet_value.loc38 [concrete = constants.%facet_value.7bd]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc38: <bound method> = bound_method %.loc38_21.4, constants.%AggregateT.as_type.as.Destroy.impl.Op.cc2
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc35: <bound method> = bound_method %.loc35_21.4, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.1
-// CHECK:STDOUT:   %addr.loc35: %ptr.5c7 = addr_of %.loc35_21.4
-// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc35: init %empty_tuple.type = call %bound_method.loc35(%addr.loc35)
-// CHECK:STDOUT:   %facet_value.loc18: %type_where = facet_value constants.%T, () [concrete = constants.%facet_value.19d]
-// CHECK:STDOUT:   %.loc18_3: %type_where = converted constants.%T, %facet_value.loc18 [concrete = constants.%facet_value.19d]
-// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc18: <bound method> = bound_method %t.var, constants.%AggregateT.as_type.as.Destroy.impl.Op.8c8
+// CHECK:STDOUT:   %bound_method.loc38: <bound method> = bound_method %.loc38_21.4, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.1
+// CHECK:STDOUT:   %addr.loc38: %ptr.5c7 = addr_of %.loc38_21.4
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc38: init %empty_tuple.type = call %bound_method.loc38(%addr.loc38)
+// CHECK:STDOUT:   %facet_value.loc19: %type_where = facet_value constants.%T, () [concrete = constants.%facet_value.19d]
+// CHECK:STDOUT:   %.loc19_3: %type_where = converted constants.%T, %facet_value.loc19 [concrete = constants.%facet_value.19d]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc19: <bound method> = bound_method %t.var, constants.%AggregateT.as_type.as.Destroy.impl.Op.8c8
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc18: <bound method> = bound_method %t.var, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.2
-// CHECK:STDOUT:   %addr.loc18: %ptr.b04 = addr_of %t.var
-// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc18: init %empty_tuple.type = call %bound_method.loc18(%addr.loc18)
+// CHECK:STDOUT:   %bound_method.loc19: <bound method> = bound_method %t.var, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.2
+// CHECK:STDOUT:   %addr.loc19: %ptr.b04 = addr_of %t.var
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc19: init %empty_tuple.type = call %bound_method.loc19(%addr.loc19)
 // CHECK:STDOUT:   %facet_value.loc8: %type_where = facet_value constants.%S, () [concrete = constants.%facet_value.7bd]
 // CHECK:STDOUT:   %.loc8_3: %type_where = converted constants.%S, %facet_value.loc8 [concrete = constants.%facet_value.7bd]
 // CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc8: <bound method> = bound_method %s.var, constants.%AggregateT.as_type.as.Destroy.impl.Op.cc2
@@ -774,8 +781,8 @@ fn F() {
 // CHECK:STDOUT:     %T.ref: type = name_ref T, imports.%T.decl [concrete = constants.%T]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %t: ref %T = bind_name t, %t.var
-// CHECK:STDOUT:   %Cpp.ref.loc28: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:   %TakesConstLValue.ref.loc28: %.4e1 = name_ref TakesConstLValue, imports.%.9bc [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %Cpp.ref.loc29: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %TakesConstLValue.ref.loc29: %.4e1 = name_ref TakesConstLValue, imports.%.9bc [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %t.ref: ref %T = name_ref t, %t
 // CHECK:STDOUT:   %facet_value.loc20: %type_where = facet_value constants.%T, () [concrete = constants.%facet_value.19d]
 // CHECK:STDOUT:   %.loc20_3: %type_where = converted constants.%T, %facet_value.loc20 [concrete = constants.%facet_value.19d]

+ 2 - 2
toolchain/check/testdata/interop/cpp/function/struct.carbon

@@ -153,7 +153,7 @@ import Cpp library "non_copyable_param_type.h";
 
 fn F() {
   //@dump-sem-ir-begin
-  // CHECK:STDERR: fail_import_non_copyable_param_type.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_import_non_copyable_param_type.carbon:[[@LINE+4]]:3: note: in thunk for C++ function used here [InCppThunk]
   // CHECK:STDERR:   Cpp.foo({} as Cpp.S);
   // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~
   // CHECK:STDERR:
@@ -439,7 +439,7 @@ library "[[@TEST_NAME]]";
 import Cpp library "decl_value_return_type.h";
 
 fn F() {
-  // CHECK:STDERR: fail_import_decl_value_return_type.carbon:[[@LINE+13]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_import_decl_value_return_type.carbon:[[@LINE+13]]:3: note: in thunk for C++ function used here [InCppThunk]
   // CHECK:STDERR:   Cpp.foo();
   // CHECK:STDERR:   ^~~~~~~~~
   // CHECK:STDERR:

+ 1 - 1
toolchain/check/testdata/interop/cpp/function/union.carbon

@@ -401,7 +401,7 @@ library "[[@TEST_NAME]]";
 import Cpp library "decl_value_return_type.h";
 
 fn F() {
-  // CHECK:STDERR: fail_import_decl_value_return_type.carbon:[[@LINE+13]]:3: note: in call to Cpp function here [InCallToCppFunction]
+  // CHECK:STDERR: fail_import_decl_value_return_type.carbon:[[@LINE+13]]:3: note: in thunk for C++ function used here [InCppThunk]
   // CHECK:STDERR:   Cpp.foo();
   // CHECK:STDERR:   ^~~~~~~~~
   // CHECK:STDERR:

+ 1 - 4
toolchain/diagnostics/diagnostic_kind.def

@@ -180,6 +180,7 @@ CARBON_DIAGNOSTIC_KIND(InCppInclude)
 CARBON_DIAGNOSTIC_KIND(InCppModule)
 CARBON_DIAGNOSTIC_KIND(InCppMacroExpansion)
 CARBON_DIAGNOSTIC_KIND(ResolvingSpecificHere)
+CARBON_DIAGNOSTIC_KIND(InCppThunk)
 CARBON_DIAGNOSTIC_KIND(InCppTypeCompletion)
 
 // Package/import checking diagnostics.
@@ -243,13 +244,9 @@ CARBON_DIAGNOSTIC_KIND(AddrSelfIsNonRef)
 CARBON_DIAGNOSTIC_KIND(CallArgCountMismatch)
 CARBON_DIAGNOSTIC_KIND(CallToNonCallable)
 CARBON_DIAGNOSTIC_KIND(CppCallArgTypeNotSupported)
-CARBON_DIAGNOSTIC_KIND(CppOverloadingAmbiguousCandidatesFound)
-CARBON_DIAGNOSTIC_KIND(CppOverloadingDeletedFunctionFound)
-CARBON_DIAGNOSTIC_KIND(CppOverloadingNoViableFunctionFound)
 CARBON_DIAGNOSTIC_KIND(GenericParamMustBeConstant)
 CARBON_DIAGNOSTIC_KIND(ImplictParamMustBeConstant)
 CARBON_DIAGNOSTIC_KIND(IncompleteReturnTypeHere)
-CARBON_DIAGNOSTIC_KIND(InCallToCppFunction)
 CARBON_DIAGNOSTIC_KIND(InCallToEntity)
 CARBON_DIAGNOSTIC_KIND(InCallToFunction)
 CARBON_DIAGNOSTIC_KIND(InCallToFunctionParam)