Преглед изворни кода

C++ interop: support for default arguments. (#6108)

The general strategy here is to force use of a thunk when we want to use
default arguments, and have Clang generate uses of the default arguments
on its side of the thunk.

To support this, change the key type used in `clang_decls` from being
just a `Decl*` to being a pair of `Decl*` and number of parameters in
the case of function decls. Import distinct `SemIR::Function`s for each
number of parameters that's used, and corresponding distinct thunks.

---------

Co-authored-by: Jon Ross-Perkins <jperkins@google.com>
Richard Smith пре 7 месеци
родитељ
комит
1e7b7e53ae
42 измењених фајлова са 2098 додато и 721 уклоњено
  1. 158 136
      toolchain/check/cpp/import.cpp
  2. 2 1
      toolchain/check/cpp/import.h
  3. 9 2
      toolchain/check/cpp/overload_resolution.cpp
  4. 50 28
      toolchain/check/cpp/thunk.cpp
  5. 8 4
      toolchain/check/cpp/type_mapping.cpp
  6. 1 0
      toolchain/check/testdata/basics/raw_sem_ir/builtins.carbon
  7. 302 0
      toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon
  8. 2 0
      toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon
  9. 2 0
      toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon
  10. 1 0
      toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon
  11. 1 0
      toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon
  12. 112 68
      toolchain/check/testdata/interop/cpp/class/constructor.carbon
  13. 2 2
      toolchain/check/testdata/interop/cpp/enum/anonymous.carbon
  14. 0 107
      toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon
  15. 0 182
      toolchain/check/testdata/interop/cpp/function/arithmetic_types_direct.carbon
  16. 20 20
      toolchain/check/testdata/interop/cpp/function/class.carbon
  17. 196 0
      toolchain/check/testdata/interop/cpp/function/decayed_param.carbon
  18. 761 0
      toolchain/check/testdata/interop/cpp/function/default_arg.carbon
  19. 0 7
      toolchain/check/testdata/interop/cpp/function/overloads.carbon
  20. 147 0
      toolchain/check/testdata/interop/cpp/function/qualified_param.carbon
  21. 2 2
      toolchain/check/testdata/interop/cpp/function/reference.carbon
  22. 20 20
      toolchain/check/testdata/interop/cpp/function/struct.carbon
  23. 20 20
      toolchain/check/testdata/interop/cpp/function/union.carbon
  24. 1 1
      toolchain/check/testdata/interop/cpp/namespace.carbon
  25. 2 2
      toolchain/check/testdata/interop/cpp/stdlib/string_view.carbon
  26. 1 0
      toolchain/driver/testdata/stdin.carbon
  27. 15 1
      toolchain/lower/file_context.cpp
  28. 6 3
      toolchain/lower/mangler.cpp
  29. 15 15
      toolchain/lower/testdata/interop/cpp/constructor.carbon
  30. 6 4
      toolchain/lower/testdata/interop/cpp/enum.carbon
  31. 3 2
      toolchain/lower/testdata/interop/cpp/extern_c.carbon
  32. 72 1
      toolchain/lower/testdata/interop/cpp/function_decl.carbon
  33. 18 17
      toolchain/lower/testdata/interop/cpp/method.carbon
  34. 12 10
      toolchain/lower/testdata/interop/cpp/parameters.carbon
  35. 13 12
      toolchain/lower/testdata/interop/cpp/reference.carbon
  36. 12 10
      toolchain/lower/testdata/interop/cpp/return.carbon
  37. 7 7
      toolchain/lower/testdata/interop/cpp/template.carbon
  38. 1 0
      toolchain/sem_ir/BUILD
  39. 22 3
      toolchain/sem_ir/clang_decl.cpp
  40. 73 34
      toolchain/sem_ir/clang_decl.h
  41. 2 0
      toolchain/sem_ir/file.cpp
  42. 1 0
      toolchain/sem_ir/yaml_test.cpp

+ 158 - 136
toolchain/check/cpp/import.cpp

@@ -480,7 +480,8 @@ auto ImportCppFiles(Context& context,
   SemIR::NameScope& name_scope = context.name_scopes().Get(name_scope_id);
   SemIR::NameScope& name_scope = context.name_scopes().Get(name_scope_id);
   name_scope.set_is_closed_import(true);
   name_scope.set_is_closed_import(true);
   name_scope.set_clang_decl_context_id(context.clang_decls().Add(
   name_scope.set_clang_decl_context_id(context.clang_decls().Add(
-      {.decl = generated_ast->getASTContext().getTranslationUnitDecl(),
+      {.key = SemIR::ClangDeclKey(
+           generated_ast->getASTContext().getTranslationUnitDecl()),
        .inst_id = name_scope.inst_id()}));
        .inst_id = name_scope.inst_id()}));
 
 
   if (ast_has_error) {
   if (ast_has_error) {
@@ -500,7 +501,7 @@ static auto GetDeclContext(Context& context, SemIR::NameScopeId scope_id)
   auto scope_clang_decl_context_id =
   auto scope_clang_decl_context_id =
       context.name_scopes().Get(scope_id).clang_decl_context_id();
       context.name_scopes().Get(scope_id).clang_decl_context_id();
   return dyn_cast<clang::DeclContext>(
   return dyn_cast<clang::DeclContext>(
-      context.clang_decls().Get(scope_clang_decl_context_id).decl);
+      context.clang_decls().Get(scope_clang_decl_context_id).key.decl);
 }
 }
 
 
 // Looks up for constructors in the class scope and returns the lookup result.
 // Looks up for constructors in the class scope and returns the lookup result.
@@ -511,7 +512,7 @@ static auto ClangConstructorLookup(Context& context,
 
 
   clang::Sema& sema = context.clang_sema();
   clang::Sema& sema = context.clang_sema();
   clang::Decl* decl =
   clang::Decl* decl =
-      context.clang_decls().Get(scope.clang_decl_context_id()).decl;
+      context.clang_decls().Get(scope.clang_decl_context_id()).key.decl;
   return sema.LookupConstructors(cast<clang::CXXRecordDecl>(decl));
   return sema.LookupConstructors(cast<clang::CXXRecordDecl>(decl));
 }
 }
 
 
@@ -533,7 +534,8 @@ static auto IsDeclInjectedClassName(Context& context,
 
 
   const SemIR::ClangDecl& clang_decl = context.clang_decls().Get(
   const SemIR::ClangDecl& clang_decl = context.clang_decls().Get(
       context.name_scopes().Get(scope_id).clang_decl_context_id());
       context.name_scopes().Get(scope_id).clang_decl_context_id());
-  const auto* scope_record_decl = cast<clang::CXXRecordDecl>(clang_decl.decl);
+  const auto* scope_record_decl =
+      cast<clang::CXXRecordDecl>(clang_decl.key.decl);
 
 
   const clang::ASTContext& ast_context = context.ast_context();
   const clang::ASTContext& ast_context = context.ast_context();
   CARBON_CHECK(ast_context.getCanonicalTagType(scope_record_decl) ==
   CARBON_CHECK(ast_context.getCanonicalTagType(scope_record_decl) ==
@@ -596,16 +598,17 @@ static auto ClangLookupName(Context& context, SemIR::NameScopeId scope_id,
 }
 }
 
 
 // Returns whether `decl` already mapped to an instruction.
 // Returns whether `decl` already mapped to an instruction.
-static auto IsClangDeclImported(Context& context, clang::Decl* decl) -> bool {
-  return context.clang_decls().Lookup(decl->getCanonicalDecl()).has_value();
+static auto IsClangDeclImported(Context& context, SemIR::ClangDeclKey key)
+    -> bool {
+  return context.clang_decls().Lookup(key).has_value();
 }
 }
 
 
 // If `decl` already mapped to an instruction, returns that instruction.
 // If `decl` already mapped to an instruction, returns that instruction.
 // Otherwise returns `None`.
 // Otherwise returns `None`.
-static auto LookupClangDeclInstId(Context& context, clang::Decl* decl)
+static auto LookupClangDeclInstId(Context& context, SemIR::ClangDeclKey key)
     -> SemIR::InstId {
     -> SemIR::InstId {
   const auto& clang_decls = context.clang_decls();
   const auto& clang_decls = context.clang_decls();
-  if (auto context_clang_decl_id = clang_decls.Lookup(decl->getCanonicalDecl());
+  if (auto context_clang_decl_id = clang_decls.Lookup(key);
       context_clang_decl_id.has_value()) {
       context_clang_decl_id.has_value()) {
     return clang_decls.Get(context_clang_decl_id).inst_id;
     return clang_decls.Get(context_clang_decl_id).inst_id;
   }
   }
@@ -626,24 +629,27 @@ static auto GetParentDecl(clang::Decl* clang_decl) -> clang::Decl* {
 // was already imported.
 // was already imported.
 static auto GetParentNameScopeId(Context& context, clang::Decl* clang_decl)
 static auto GetParentNameScopeId(Context& context, clang::Decl* clang_decl)
     -> SemIR::NameScopeId {
     -> SemIR::NameScopeId {
-  SemIR::InstId parent_inst_id =
-      LookupClangDeclInstId(context, GetParentDecl(clang_decl));
-  CARBON_CHECK(parent_inst_id.has_value());
+  auto* parent_decl = GetParentDecl(clang_decl);
 
 
-  CARBON_KIND_SWITCH(context.insts().Get(parent_inst_id)) {
-    case CARBON_KIND(SemIR::ClassDecl class_decl): {
-      return context.classes().Get(class_decl.class_id).scope_id;
-    }
-    case CARBON_KIND(SemIR::InterfaceDecl interface_decl): {
-      return context.interfaces().Get(interface_decl.interface_id).scope_id;
-    }
-    case CARBON_KIND(SemIR::Namespace namespace_inst): {
-      return namespace_inst.name_scope_id;
-    }
-    default: {
-      CARBON_FATAL("Unexpected parent instruction kind");
-    }
+  if (auto* tag_decl = dyn_cast<clang::TagDecl>(parent_decl)) {
+    auto class_inst_id =
+        LookupClangDeclInstId(context, SemIR::ClangDeclKey(tag_decl));
+    CARBON_CHECK(class_inst_id.has_value());
+    return context.classes()
+        .Get(context.insts().GetAs<SemIR::ClassDecl>(class_inst_id).class_id)
+        .scope_id;
+  }
+
+  if (isa<clang::NamespaceDecl, clang::TranslationUnitDecl>(parent_decl)) {
+    auto namespace_inst_id = LookupClangDeclInstId(
+        context, SemIR::ClangDeclKey::ForNonFunctionDecl(parent_decl));
+    CARBON_CHECK(namespace_inst_id.has_value());
+    return context.insts()
+        .GetAs<SemIR::Namespace>(namespace_inst_id)
+        .name_scope_id;
   }
   }
+
+  CARBON_FATAL("Unexpected kind of parent {0}", parent_decl->getDeclKindName());
 }
 }
 
 
 // Imports a namespace declaration from Clang to Carbon. If successful, returns
 // Imports a namespace declaration from Clang to Carbon. If successful, returns
@@ -652,9 +658,9 @@ static auto GetParentNameScopeId(Context& context, clang::Decl* clang_decl)
 static auto ImportNamespaceDecl(Context& context,
 static auto ImportNamespaceDecl(Context& context,
                                 clang::NamespaceDecl* clang_decl)
                                 clang::NamespaceDecl* clang_decl)
     -> SemIR::InstId {
     -> SemIR::InstId {
+  auto key = SemIR::ClangDeclKey(clang_decl);
   // Check if the declaration is already mapped.
   // Check if the declaration is already mapped.
-  if (SemIR::InstId existing_inst_id =
-          LookupClangDeclInstId(context, clang_decl);
+  if (SemIR::InstId existing_inst_id = LookupClangDeclInstId(context, key);
       existing_inst_id.has_value()) {
       existing_inst_id.has_value()) {
     return existing_inst_id;
     return existing_inst_id;
   }
   }
@@ -665,8 +671,8 @@ static auto ImportNamespaceDecl(Context& context,
       /*import_id=*/SemIR::InstId::None);
       /*import_id=*/SemIR::InstId::None);
   context.name_scopes()
   context.name_scopes()
       .Get(result.name_scope_id)
       .Get(result.name_scope_id)
-      .set_clang_decl_context_id(context.clang_decls().Add(
-          {.decl = clang_decl->getCanonicalDecl(), .inst_id = result.inst_id}));
+      .set_clang_decl_context_id(
+          context.clang_decls().Add({.key = key, .inst_id = result.inst_id}));
   return result.inst_id;
   return result.inst_id;
 }
 }
 
 
@@ -731,8 +737,9 @@ static auto ImportTagDecl(Context& context, clang::TagDecl* clang_decl)
       AddIdentifierName(context, clang_decl->getName()));
       AddIdentifierName(context, clang_decl->getName()));
 
 
   // TODO: The caller does the same lookup. Avoid doing it twice.
   // TODO: The caller does the same lookup. Avoid doing it twice.
-  auto clang_decl_id = context.clang_decls().Add(
-      {.decl = clang_decl->getCanonicalDecl(), .inst_id = class_inst_id});
+  auto key = SemIR::ClangDeclKey(clang_decl);
+  auto clang_decl_id =
+      context.clang_decls().Add({.key = key, .inst_id = class_inst_id});
 
 
   // Name lookup into the Carbon class looks in the C++ class definition.
   // Name lookup into the Carbon class looks in the C++ class definition.
   auto& class_info = context.classes().Get(class_id);
   auto& class_info = context.classes().Get(class_id);
@@ -909,8 +916,10 @@ static auto ImportClassObjectRepr(Context& context, SemIR::ClassId class_id,
                              context, class_type_inst_id, field_type_inst_id),
                              context, class_type_inst_id, field_type_inst_id),
                          .name_id = field_name_id,
                          .name_id = field_name_id,
                          .index = SemIR::ElementIndex(fields.size())}));
                          .index = SemIR::ElementIndex(fields.size())}));
-    context.clang_decls().Add(
-        {.decl = decl->getCanonicalDecl(), .inst_id = field_decl_id});
+    // The imported SemIR::FieldDecl represents the original declaration `decl`,
+    // which is either the field or the indirect field declaration.
+    auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(decl);
+    context.clang_decls().Add({.key = key, .inst_id = field_decl_id});
 
 
     // Compute the offset to the field that appears directly in the class.
     // Compute the offset to the field that appears directly in the class.
     uint64_t offset = clang_layout.getFieldOffset(
     uint64_t offset = clang_layout.getFieldOffset(
@@ -1026,11 +1035,13 @@ static auto BuildEnumDefinition(Context& context,
 static auto ImportEnumConstantDecl(Context& context,
 static auto ImportEnumConstantDecl(Context& context,
                                    clang::EnumConstantDecl* enumerator_decl)
                                    clang::EnumConstantDecl* enumerator_decl)
     -> SemIR::InstId {
     -> SemIR::InstId {
-  CARBON_CHECK(!IsClangDeclImported(context, enumerator_decl));
+  auto key = SemIR::ClangDeclKey(enumerator_decl);
+  CARBON_CHECK(!IsClangDeclImported(context, key));
 
 
   // Find the enclosing enum type.
   // Find the enclosing enum type.
-  auto type_inst_id = LookupClangDeclInstId(
-      context, cast<clang::EnumDecl>(enumerator_decl->getDeclContext()));
+  auto enum_key = SemIR::ClangDeclKey(
+      cast<clang::EnumDecl>(enumerator_decl->getDeclContext()));
+  auto type_inst_id = LookupClangDeclInstId(context, enum_key);
   auto type_id = context.types().GetTypeIdForTypeInstId(type_inst_id);
   auto type_id = context.types().GetTypeIdForTypeInstId(type_inst_id);
 
 
   // Build a corresponding IntValue.
   // Build a corresponding IntValue.
@@ -1040,15 +1051,13 @@ static auto ImportEnumConstantDecl(Context& context,
   auto inst_id = AddInstInNoBlock<SemIR::IntValue>(
   auto inst_id = AddInstInNoBlock<SemIR::IntValue>(
       context, loc_id, {.type_id = type_id, .int_id = int_id});
       context, loc_id, {.type_id = type_id, .int_id = int_id});
   context.imports().push_back(inst_id);
   context.imports().push_back(inst_id);
-  context.clang_decls().Add(
-      {.decl = enumerator_decl->getCanonicalDecl(), .inst_id = inst_id});
+  context.clang_decls().Add({.key = key, .inst_id = inst_id});
   return inst_id;
   return inst_id;
 }
 }
 
 
-// Mark the given `Decl` as failed in `clang_decls`.
-static auto MarkFailedDecl(Context& context, clang::Decl* clang_decl) {
-  context.clang_decls().Add({.decl = clang_decl->getCanonicalDecl(),
-                             .inst_id = SemIR::ErrorInst::InstId});
+// Mark the given `key` as failed in `clang_decls`.
+static auto MarkFailedDecl(Context& context, SemIR::ClangDeclKey key) {
+  context.clang_decls().Add({.key = key, .inst_id = SemIR::ErrorInst::InstId});
 }
 }
 
 
 // Creates an integer type of the given size.
 // Creates an integer type of the given size.
@@ -1143,13 +1152,13 @@ static auto MapTagType(Context& context, const clang::TagType& type)
   CARBON_CHECK(tag_decl);
   CARBON_CHECK(tag_decl);
 
 
   // Check if the declaration is already mapped.
   // Check if the declaration is already mapped.
-  SemIR::InstId tag_inst_id = LookupClangDeclInstId(context, tag_decl);
+  auto key = SemIR::ClangDeclKey(tag_decl);
+  SemIR::InstId tag_inst_id = LookupClangDeclInstId(context, key);
   if (!tag_inst_id.has_value()) {
   if (!tag_inst_id.has_value()) {
     if (auto* record_decl = dyn_cast<clang::CXXRecordDecl>(tag_decl)) {
     if (auto* record_decl = dyn_cast<clang::CXXRecordDecl>(tag_decl)) {
       auto custom_type = LookupCustomRecordType(context, record_decl);
       auto custom_type = LookupCustomRecordType(context, record_decl);
       if (custom_type.inst_id.has_value()) {
       if (custom_type.inst_id.has_value()) {
-        context.clang_decls().Add(
-            {.decl = record_decl, .inst_id = custom_type.inst_id});
+        context.clang_decls().Add({.key = key, .inst_id = custom_type.inst_id});
         return custom_type;
         return custom_type;
       }
       }
     }
     }
@@ -1332,21 +1341,28 @@ static auto MakeImplicitParamPatternsBlockId(
 // TODO: Consider refactoring to extract and reuse more logic from
 // TODO: Consider refactoring to extract and reuse more logic from
 // `HandleAnyBindingPattern()`.
 // `HandleAnyBindingPattern()`.
 static auto MakeParamPatternsBlockId(Context& context, SemIR::LocId loc_id,
 static auto MakeParamPatternsBlockId(Context& context, SemIR::LocId loc_id,
-                                     const clang::FunctionDecl& clang_decl)
-    -> SemIR::InstBlockId {
-  if (clang_decl.parameters().empty()) {
+                                     const clang::FunctionDecl& clang_decl,
+                                     int num_params) -> SemIR::InstBlockId {
+  if (clang_decl.parameters().empty() || num_params == 0) {
     return SemIR::InstBlockId::Empty;
     return SemIR::InstBlockId::Empty;
   }
   }
   llvm::SmallVector<SemIR::InstId> params;
   llvm::SmallVector<SemIR::InstId> params;
-  params.reserve(clang_decl.getNumNonObjectParams());
-  for (unsigned i : llvm::seq(clang_decl.getNumNonObjectParams())) {
+  params.reserve(num_params);
+  CARBON_CHECK(
+      static_cast<int>(clang_decl.getNumNonObjectParams()) >= num_params,
+      "varargs functions are not supported");
+  const auto* function_type =
+      clang_decl.getType()->castAs<clang::FunctionProtoType>();
+  for (int i : llvm::seq(num_params)) {
     const auto* param = clang_decl.getNonObjectParameter(i);
     const auto* param = clang_decl.getNonObjectParameter(i);
-    // TODO: Get the parameter type from the function, not from the
-    // `ParmVarDecl`. The type of the `ParmVarDecl` is the type within the
-    // function, and isn't in general the same as the type that's exposed to
-    // callers. In particular, the parameter type exposed to callers will never
-    // be cv-qualified.
-    clang::QualType param_type = param->getType();
+    // The parameter type is decayed but hasn't necessarily had its qualifiers
+    // removed.
+    // TODO: The presence of qualifiers here is probably a Clang bug.
+    clang::QualType param_type =
+        function_type
+            ->getParamType(clang_decl.hasCXXExplicitFunctionObjectParameter() +
+                           i)
+            .getUnqualifiedType();
 
 
     // We map `T&` parameters to `addr param: T*`, and `T&&` parameters to
     // We map `T&` parameters to `addr param: T*`, and `T&&` parameters to
     // `param: T`.
     // `param: T`.
@@ -1365,8 +1381,9 @@ static auto MakeParamPatternsBlockId(Context& context, SemIR::LocId loc_id,
         EndSubpatternAsExpr(context, orig_type_inst_id);
         EndSubpatternAsExpr(context, orig_type_inst_id);
 
 
     if (!type_id.has_value()) {
     if (!type_id.has_value()) {
-      context.TODO(loc_id, llvm::formatv("Unsupported: parameter type: {0}",
-                                         param->getType().getAsString()));
+      context.TODO(loc_id,
+                   llvm::formatv("Unsupported: parameter type: {0}",
+                                 function_type->getParamType(i).getAsString()));
       return SemIR::InstBlockId::None;
       return SemIR::InstBlockId::None;
     }
     }
 
 
@@ -1431,17 +1448,15 @@ static auto GetReturnTypeExpr(Context& context, SemIR::LocId loc_id,
     return mapped_type;
     return mapped_type;
   }
   }
 
 
-  if (!isa<clang::CXXConstructorDecl>(clang_decl)) {
+  auto* ctor = dyn_cast<clang::CXXConstructorDecl>(clang_decl);
+  if (!ctor) {
     // void.
     // void.
     return TypeExpr::None;
     return TypeExpr::None;
   }
   }
 
 
   // TODO: Make this a `PartialType`.
   // TODO: Make this a `PartialType`.
   SemIR::TypeInstId record_type_inst_id = context.types().GetAsTypeInstId(
   SemIR::TypeInstId record_type_inst_id = context.types().GetAsTypeInstId(
-      context.clang_decls()
-          .Get(context.clang_decls().Lookup(
-              cast<clang::Decl>(clang_decl->getParent())))
-          .inst_id);
+      LookupClangDeclInstId(context, SemIR::ClangDeclKey(ctor->getParent())));
   return {
   return {
       .inst_id = record_type_inst_id,
       .inst_id = record_type_inst_id,
       .type_id = context.types().GetTypeIdForTypeInstId(record_type_inst_id)};
       .type_id = context.types().GetTypeIdForTypeInstId(record_type_inst_id)};
@@ -1494,7 +1509,8 @@ struct FunctionParamsInsts {
 // Produces a diagnostic and returns `std::nullopt` if the function declaration
 // Produces a diagnostic and returns `std::nullopt` if the function declaration
 // has an unsupported parameter type.
 // has an unsupported parameter type.
 static auto CreateFunctionParamsInsts(Context& context, SemIR::LocId loc_id,
 static auto CreateFunctionParamsInsts(Context& context, SemIR::LocId loc_id,
-                                      clang::FunctionDecl* clang_decl)
+                                      clang::FunctionDecl* clang_decl,
+                                      int num_params)
     -> std::optional<FunctionParamsInsts> {
     -> std::optional<FunctionParamsInsts> {
   if (isa<clang::CXXDestructorDecl>(clang_decl)) {
   if (isa<clang::CXXDestructorDecl>(clang_decl)) {
     context.TODO(loc_id, "Unsupported: Destructor");
     context.TODO(loc_id, "Unsupported: Destructor");
@@ -1507,7 +1523,7 @@ static auto CreateFunctionParamsInsts(Context& context, SemIR::LocId loc_id,
     return std::nullopt;
     return std::nullopt;
   }
   }
   auto param_patterns_id =
   auto param_patterns_id =
-      MakeParamPatternsBlockId(context, loc_id, *clang_decl);
+      MakeParamPatternsBlockId(context, loc_id, *clang_decl, num_params);
   if (!param_patterns_id.has_value()) {
   if (!param_patterns_id.has_value()) {
     return std::nullopt;
     return std::nullopt;
   }
   }
@@ -1531,10 +1547,11 @@ static auto GetFunctionName(Context& context, clang::FunctionDecl* clang_decl)
     -> SemIR::NameId {
     -> SemIR::NameId {
   switch (clang_decl->getDeclName().getNameKind()) {
   switch (clang_decl->getDeclName().getNameKind()) {
     case clang::DeclarationName::CXXConstructorName: {
     case clang::DeclarationName::CXXConstructorName: {
+      auto key = SemIR::ClangDeclKey(
+          cast<clang::CXXConstructorDecl>(clang_decl)->getParent());
       return context.classes()
       return context.classes()
           .Get(context.insts()
           .Get(context.insts()
-                   .GetAs<SemIR::ClassDecl>(LookupClangDeclInstId(
-                       context, cast<clang::Decl>(clang_decl->getParent())))
+                   .GetAs<SemIR::ClassDecl>(LookupClangDeclInstId(context, key))
                    .class_id)
                    .class_id)
           .name_id;
           .name_id;
     }
     }
@@ -1554,14 +1571,14 @@ static auto GetFunctionName(Context& context, clang::FunctionDecl* clang_decl)
 // * Have not been imported before.
 // * Have not been imported before.
 // * Be of supported type (ignoring parameters).
 // * Be of supported type (ignoring parameters).
 static auto ImportFunction(Context& context, SemIR::LocId loc_id,
 static auto ImportFunction(Context& context, SemIR::LocId loc_id,
-                           clang::FunctionDecl* clang_decl)
+                           clang::FunctionDecl* clang_decl, int num_params)
     -> std::optional<SemIR::FunctionId> {
     -> std::optional<SemIR::FunctionId> {
   context.scope_stack().PushForDeclName();
   context.scope_stack().PushForDeclName();
   context.inst_block_stack().Push();
   context.inst_block_stack().Push();
   context.pattern_block_stack().Push();
   context.pattern_block_stack().Push();
 
 
   auto function_params_insts =
   auto function_params_insts =
-      CreateFunctionParamsInsts(context, loc_id, clang_decl);
+      CreateFunctionParamsInsts(context, loc_id, clang_decl, num_params);
 
 
   auto pattern_block_id = context.pattern_block_stack().Pop();
   auto pattern_block_id = context.pattern_block_stack().Pop();
   auto decl_block_id = context.inst_block_stack().Pop();
   auto decl_block_id = context.inst_block_stack().Pop();
@@ -1614,7 +1631,8 @@ static auto ImportFunction(Context& context, SemIR::LocId loc_id,
        .self_param_id = FindSelfPattern(
        .self_param_id = FindSelfPattern(
            context, function_params_insts->implicit_param_patterns_id),
            context, function_params_insts->implicit_param_patterns_id),
        .clang_decl_id = context.clang_decls().Add(
        .clang_decl_id = context.clang_decls().Add(
-           {.decl = clang_decl, .inst_id = decl_id})}};
+           {.key = SemIR::ClangDeclKey::ForFunctionDecl(clang_decl, num_params),
+            .inst_id = decl_id})}};
 
 
   function_decl.function_id = context.functions().Add(function_info);
   function_decl.function_id = context.functions().Add(function_info);
   function_decl.type_id = GetFunctionType(context, function_decl.function_id,
   function_decl.type_id = GetFunctionType(context, function_decl.function_id,
@@ -1623,34 +1641,40 @@ static auto ImportFunction(Context& context, SemIR::LocId loc_id,
   return function_decl.function_id;
   return function_decl.function_id;
 }
 }
 
 
-auto ImportCppFunctionDecl(Context& context, SemIR::LocId loc_id,
-                           clang::FunctionDecl* clang_decl) -> SemIR::InstId {
+// Imports a C++ function, returning a corresponding Carbon function.
+// `num_params` specifies how many parameters the corresponding Carbon function
+// should have, which may be fewer than the number of parameters that the C++
+// function has if default arguments are available for the trailing parameters.
+static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
+                               clang::FunctionDecl* clang_decl, int num_params)
+    -> SemIR::InstId {
+  auto key = SemIR::ClangDeclKey::ForFunctionDecl(clang_decl, num_params);
+
   // Check if the declaration is already mapped.
   // Check if the declaration is already mapped.
-  if (SemIR::InstId existing_inst_id =
-          LookupClangDeclInstId(context, clang_decl);
+  if (SemIR::InstId existing_inst_id = LookupClangDeclInstId(context, key);
       existing_inst_id.has_value()) {
       existing_inst_id.has_value()) {
     return existing_inst_id;
     return existing_inst_id;
   }
   }
 
 
   if (clang_decl->isVariadic()) {
   if (clang_decl->isVariadic()) {
     context.TODO(loc_id, "Unsupported: Variadic function");
     context.TODO(loc_id, "Unsupported: Variadic function");
-    MarkFailedDecl(context, clang_decl);
+    MarkFailedDecl(context, key);
     return SemIR::ErrorInst::InstId;
     return SemIR::ErrorInst::InstId;
   }
   }
 
 
   if (clang_decl->getTemplatedKind() ==
   if (clang_decl->getTemplatedKind() ==
       clang::FunctionDecl::TK_FunctionTemplate) {
       clang::FunctionDecl::TK_FunctionTemplate) {
     context.TODO(loc_id, "Unsupported: Template function");
     context.TODO(loc_id, "Unsupported: Template function");
-    MarkFailedDecl(context, clang_decl);
+    MarkFailedDecl(context, key);
     return SemIR::ErrorInst::InstId;
     return SemIR::ErrorInst::InstId;
   }
   }
 
 
   CARBON_CHECK(clang_decl->getFunctionType()->isFunctionProtoType(),
   CARBON_CHECK(clang_decl->getFunctionType()->isFunctionProtoType(),
                "Not Prototype function (non-C++ code)");
                "Not Prototype function (non-C++ code)");
 
 
-  auto function_id = ImportFunction(context, loc_id, clang_decl);
+  auto function_id = ImportFunction(context, loc_id, clang_decl, num_params);
   if (!function_id) {
   if (!function_id) {
-    MarkFailedDecl(context, clang_decl);
+    MarkFailedDecl(context, key);
     return SemIR::ErrorInst::InstId;
     return SemIR::ErrorInst::InstId;
   }
   }
 
 
@@ -1666,8 +1690,8 @@ auto ImportCppFunctionDecl(Context& context, SemIR::LocId loc_id,
     clang::FunctionDecl* thunk_clang_decl =
     clang::FunctionDecl* thunk_clang_decl =
         BuildCppThunk(context, function_info);
         BuildCppThunk(context, function_info);
     if (thunk_clang_decl) {
     if (thunk_clang_decl) {
-      SemIR::FunctionId thunk_function_id =
-          *ImportFunction(context, loc_id, thunk_clang_decl);
+      SemIR::FunctionId thunk_function_id = *ImportFunction(
+          context, loc_id, thunk_clang_decl, thunk_clang_decl->getNumParams());
       SemIR::InstId thunk_function_decl_id =
       SemIR::InstId thunk_function_decl_id =
           context.functions().Get(thunk_function_id).first_owning_decl_id;
           context.functions().Get(thunk_function_id).first_owning_decl_id;
       function_info.SetHasCppThunk(thunk_function_decl_id);
       function_info.SetHasCppThunk(thunk_function_decl_id);
@@ -1683,7 +1707,7 @@ namespace {
 // to use a `PointerIntPair`.
 // to use a `PointerIntPair`.
 struct ImportItem {
 struct ImportItem {
   // A declaration that we want to import.
   // A declaration that we want to import.
-  clang::Decl* decl;
+  SemIR::ClangDeclKey decl_key;
   // Whether we have added `decl`'s dependencies to the worklist.
   // Whether we have added `decl`'s dependencies to the worklist.
   bool added_dependencies;
   bool added_dependencies;
 };
 };
@@ -1692,10 +1716,10 @@ using ImportWorklist = llvm::SmallVector<ImportItem>;
 }  // namespace
 }  // namespace
 
 
 // Adds the given declaration to our list of declarations to import.
 // Adds the given declaration to our list of declarations to import.
-static auto AddDependentDecl(Context& context, clang::Decl* decl,
+static auto AddDependentDecl(Context& context, SemIR::ClangDeclKey decl,
                              ImportWorklist& worklist) -> void {
                              ImportWorklist& worklist) -> void {
   if (!IsClangDeclImported(context, decl)) {
   if (!IsClangDeclImported(context, decl)) {
-    worklist.push_back({.decl = decl, .added_dependencies = false});
+    worklist.push_back({.decl_key = decl, .added_dependencies = false});
   }
   }
 }
 }
 
 
@@ -1716,17 +1740,22 @@ static auto AddDependentUnimportedTypeDecls(Context& context,
   }
   }
 
 
   if (const auto* tag_type = type->getAs<clang::TagType>()) {
   if (const auto* tag_type = type->getAs<clang::TagType>()) {
-    AddDependentDecl(context, tag_type->getOriginalDecl(), worklist);
+    AddDependentDecl(context, SemIR::ClangDeclKey(tag_type->getOriginalDecl()),
+                     worklist);
   }
   }
 }
 }
 
 
 // Finds all decls that need to be imported before importing the given function
 // Finds all decls that need to be imported before importing the given function
 // and adds them to the given set.
 // and adds them to the given set.
 static auto AddDependentUnimportedFunctionDecls(
 static auto AddDependentUnimportedFunctionDecls(
-    Context& context, const clang::FunctionDecl& clang_decl,
+    Context& context, const clang::FunctionDecl& clang_decl, int num_params,
     ImportWorklist& worklist) -> void {
     ImportWorklist& worklist) -> void {
-  for (const auto* param : clang_decl.parameters()) {
-    AddDependentUnimportedTypeDecls(context, param->getType(), worklist);
+  const auto* function_type =
+      clang_decl.getType()->castAs<clang::FunctionProtoType>();
+  for (int i : llvm::seq(clang_decl.hasCXXExplicitFunctionObjectParameter() +
+                         num_params)) {
+    AddDependentUnimportedTypeDecls(context, function_type->getParamType(i),
+                                    worklist);
   }
   }
   AddDependentUnimportedTypeDecls(context, clang_decl.getReturnType(),
   AddDependentUnimportedTypeDecls(context, clang_decl.getReturnType(),
                                   worklist);
                                   worklist);
@@ -1735,11 +1764,12 @@ static auto AddDependentUnimportedFunctionDecls(
 // Finds all decls that need to be imported before importing the given
 // Finds all decls that need to be imported before importing the given
 // declaration and adds them to the given set.
 // declaration and adds them to the given set.
 static auto AddDependentUnimportedDecls(Context& context,
 static auto AddDependentUnimportedDecls(Context& context,
-                                        clang::Decl* clang_decl,
+                                        SemIR::ClangDeclKey key,
                                         ImportWorklist& worklist) -> void {
                                         ImportWorklist& worklist) -> void {
+  clang::Decl* clang_decl = key.decl;
   if (auto* clang_function_decl = clang_decl->getAsFunction()) {
   if (auto* clang_function_decl = clang_decl->getAsFunction()) {
     AddDependentUnimportedFunctionDecls(context, *clang_function_decl,
     AddDependentUnimportedFunctionDecls(context, *clang_function_decl,
-                                        worklist);
+                                        key.num_params, worklist);
   } else if (auto* type_decl = dyn_cast<clang::TypeDecl>(clang_decl)) {
   } else if (auto* type_decl = dyn_cast<clang::TypeDecl>(clang_decl)) {
     if (!isa<clang::TagDecl>(clang_decl)) {
     if (!isa<clang::TagDecl>(clang_decl)) {
       AddDependentUnimportedTypeDecls(
       AddDependentUnimportedTypeDecls(
@@ -1747,14 +1777,18 @@ static auto AddDependentUnimportedDecls(Context& context,
           worklist);
           worklist);
     }
     }
   }
   }
-  if (!isa<clang::TranslationUnitDecl>(clang_decl)) {
-    AddDependentDecl(context, GetParentDecl(clang_decl), worklist);
+  auto* parent = GetParentDecl(clang_decl);
+  if (llvm::isa_and_nonnull<clang::TagDecl, clang::NamespaceDecl,
+                            clang::TranslationUnitDecl>(parent)) {
+    AddDependentDecl(context, SemIR::ClangDeclKey::ForNonFunctionDecl(parent),
+                     worklist);
   }
   }
 }
 }
 
 
 static auto ImportVarDecl(Context& context, SemIR::LocId loc_id,
 static auto ImportVarDecl(Context& context, SemIR::LocId loc_id,
                           clang::VarDecl* var_decl) -> SemIR::InstId {
                           clang::VarDecl* var_decl) -> SemIR::InstId {
-  if (SemIR::InstId existing_inst_id = LookupClangDeclInstId(context, var_decl);
+  if (SemIR::InstId existing_inst_id =
+          LookupClangDeclInstId(context, SemIR::ClangDeclKey(var_decl));
       existing_inst_id.has_value()) {
       existing_inst_id.has_value()) {
     return existing_inst_id;
     return existing_inst_id;
   }
   }
@@ -1777,7 +1811,7 @@ static auto ImportVarDecl(Context& context, SemIR::LocId loc_id,
       AddPlaceholderInstInNoBlock(context, {loc_id, var_storage});
       AddPlaceholderInstInNoBlock(context, {loc_id, var_storage});
 
 
   auto clang_decl_id = context.clang_decls().Add(
   auto clang_decl_id = context.clang_decls().Add(
-      {.decl = var_decl, .inst_id = var_storage_inst_id});
+      {.key = SemIR::ClangDeclKey(var_decl), .inst_id = var_storage_inst_id});
 
 
   // Entity name referring to a Clang decl for mangling.
   // Entity name referring to a Clang decl for mangling.
   SemIR::EntityNameId entity_name_id =
   SemIR::EntityNameId entity_name_id =
@@ -1809,10 +1843,12 @@ static auto ImportVarDecl(Context& context, SemIR::LocId loc_id,
 // new Carbon declaration, which will be an ErrorInst on failure. Assumes all
 // new Carbon declaration, which will be an ErrorInst on failure. Assumes all
 // dependencies have already been imported.
 // dependencies have already been imported.
 static auto ImportDeclAfterDependencies(Context& context, SemIR::LocId loc_id,
 static auto ImportDeclAfterDependencies(Context& context, SemIR::LocId loc_id,
-                                        clang::Decl* clang_decl)
+                                        SemIR::ClangDeclKey key)
     -> SemIR::InstId {
     -> SemIR::InstId {
+  clang::Decl* clang_decl = key.decl;
   if (auto* clang_function_decl = clang_decl->getAsFunction()) {
   if (auto* clang_function_decl = clang_decl->getAsFunction()) {
-    return ImportCppFunctionDecl(context, loc_id, clang_function_decl);
+    return ImportFunctionDecl(context, loc_id, clang_function_decl,
+                              key.num_params);
   }
   }
   if (auto* clang_namespace_decl = dyn_cast<clang::NamespaceDecl>(clang_decl)) {
   if (auto* clang_namespace_decl = dyn_cast<clang::NamespaceDecl>(clang_decl)) {
     return ImportNamespaceDecl(context, clang_namespace_decl);
     return ImportNamespaceDecl(context, clang_namespace_decl);
@@ -1826,13 +1862,12 @@ static auto ImportDeclAfterDependencies(Context& context, SemIR::LocId loc_id,
                                  type.getAsString()));
                                  type.getAsString()));
       return SemIR::ErrorInst::InstId;
       return SemIR::ErrorInst::InstId;
     }
     }
-    context.clang_decls().Add({.decl = clang_decl, .inst_id = type_inst_id});
+    context.clang_decls().Add({.key = key, .inst_id = type_inst_id});
     return type_inst_id;
     return type_inst_id;
   }
   }
   if (isa<clang::FieldDecl, clang::IndirectFieldDecl>(clang_decl)) {
   if (isa<clang::FieldDecl, clang::IndirectFieldDecl>(clang_decl)) {
     // Usable fields get imported as a side effect of importing the class.
     // Usable fields get imported as a side effect of importing the class.
-    if (SemIR::InstId existing_inst_id =
-            LookupClangDeclInstId(context, clang_decl);
+    if (SemIR::InstId existing_inst_id = LookupClangDeclInstId(context, key);
         existing_inst_id.has_value()) {
         existing_inst_id.has_value()) {
       return existing_inst_id;
       return existing_inst_id;
     }
     }
@@ -1866,7 +1901,7 @@ static auto ImportDeclSet(Context& context, SemIR::LocId loc_id,
       // adding the item to the worklist, but it might have been added to the
       // adding the item to the worklist, but it might have been added to the
       // worklist twice before the first time we visited it. For example, this
       // worklist twice before the first time we visited it. For example, this
       // happens for `fn F(a: Cpp.T, b: Cpp.T)`.
       // happens for `fn F(a: Cpp.T, b: Cpp.T)`.
-      if (IsClangDeclImported(context, item.decl)) {
+      if (IsClangDeclImported(context, item.decl_key)) {
         worklist.pop_back();
         worklist.pop_back();
         continue;
         continue;
       }
       }
@@ -1874,23 +1909,17 @@ static auto ImportDeclSet(Context& context, SemIR::LocId loc_id,
       // First time visiting this declaration (preorder): add its dependencies
       // First time visiting this declaration (preorder): add its dependencies
       // to the work list.
       // to the work list.
       item.added_dependencies = true;
       item.added_dependencies = true;
-      AddDependentUnimportedDecls(context, item.decl, worklist);
+      AddDependentUnimportedDecls(context, item.decl_key, worklist);
     } else {
     } else {
       // Second time visiting this declaration (postorder): its dependencies are
       // Second time visiting this declaration (postorder): its dependencies are
       // already imported, so we can import it now.
       // already imported, so we can import it now.
-      auto* decl = worklist.pop_back_val().decl;
-      // Functions that are part of the overload set are imported at a later
-      // point, once the overload resolution has selected the suitable function
-      // for the call.
-      if (decl->getAsFunction()) {
-        continue;
-      }
-      auto inst_id = ImportDeclAfterDependencies(context, loc_id, decl);
+      auto decl_key = worklist.pop_back_val().decl_key;
+      auto inst_id = ImportDeclAfterDependencies(context, loc_id, decl_key);
       CARBON_CHECK(inst_id.has_value());
       CARBON_CHECK(inst_id.has_value());
       if (inst_id == SemIR::ErrorInst::InstId) {
       if (inst_id == SemIR::ErrorInst::InstId) {
         return false;
         return false;
       }
       }
-      CARBON_CHECK(IsClangDeclImported(context, decl));
+      CARBON_CHECK(IsClangDeclImported(context, decl_key));
     }
     }
   }
   }
 
 
@@ -1901,15 +1930,15 @@ static auto ImportDeclSet(Context& context, SemIR::LocId loc_id,
 // instruction for the new Carbon declaration. All unimported dependencies are
 // instruction for the new Carbon declaration. All unimported dependencies are
 // imported first.
 // imported first.
 static auto ImportDeclAndDependencies(Context& context, SemIR::LocId loc_id,
 static auto ImportDeclAndDependencies(Context& context, SemIR::LocId loc_id,
-                                      clang::Decl* clang_decl)
+                                      SemIR::ClangDeclKey key)
     -> SemIR::InstId {
     -> SemIR::InstId {
   // Collect dependencies by walking the dependency graph in depth-first order.
   // Collect dependencies by walking the dependency graph in depth-first order.
   ImportWorklist worklist;
   ImportWorklist worklist;
-  AddDependentDecl(context, clang_decl, worklist);
+  AddDependentDecl(context, key, worklist);
   if (!ImportDeclSet(context, loc_id, worklist)) {
   if (!ImportDeclSet(context, loc_id, worklist)) {
     return SemIR::ErrorInst::InstId;
     return SemIR::ErrorInst::InstId;
   }
   }
-  return LookupClangDeclInstId(context, clang_decl);
+  return LookupClangDeclInstId(context, key);
 }
 }
 
 
 // Imports a type from Clang to Carbon. If successful, returns the imported
 // Imports a type from Clang to Carbon. If successful, returns the imported
@@ -1926,6 +1955,14 @@ static auto ImportTypeAndDependencies(Context& context, SemIR::LocId loc_id,
   return MapType(context, loc_id, type);
   return MapType(context, loc_id, type);
 }
 }
 
 
+auto ImportCppFunctionDecl(Context& context, SemIR::LocId loc_id,
+                           clang::FunctionDecl* clang_decl, int num_params)
+    -> SemIR::InstId {
+  return ImportDeclAndDependencies(
+      context, loc_id,
+      SemIR::ClangDeclKey::ForFunctionDecl(clang_decl, num_params));
+}
+
 // Maps `clang::AccessSpecifier` to `SemIR::AccessKind`.
 // Maps `clang::AccessSpecifier` to `SemIR::AccessKind`.
 static auto MapAccess(clang::AccessSpecifier access_specifier)
 static auto MapAccess(clang::AccessSpecifier access_specifier)
     -> SemIR::AccessKind {
     -> SemIR::AccessKind {
@@ -1940,16 +1977,15 @@ static auto MapAccess(clang::AccessSpecifier access_specifier)
   }
   }
 }
 }
 
 
-// Imports a `clang::NamedDecl` into Carbon and adds that name into the
+// Imports a Clang declaration into Carbon and adds that name into the
 // `NameScope`.
 // `NameScope`.
 static auto ImportNameDeclIntoScope(Context& context, SemIR::LocId loc_id,
 static auto ImportNameDeclIntoScope(Context& context, SemIR::LocId loc_id,
                                     SemIR::NameScopeId scope_id,
                                     SemIR::NameScopeId scope_id,
                                     SemIR::NameId name_id,
                                     SemIR::NameId name_id,
-                                    clang::NamedDecl* clang_decl,
+                                    SemIR::ClangDeclKey key,
                                     clang::AccessSpecifier access)
                                     clang::AccessSpecifier access)
     -> SemIR::ScopeLookupResult {
     -> SemIR::ScopeLookupResult {
-  SemIR::InstId inst_id =
-      ImportDeclAndDependencies(context, loc_id, clang_decl);
+  SemIR::InstId inst_id = ImportDeclAndDependencies(context, loc_id, key);
   if (!inst_id.has_value()) {
   if (!inst_id.has_value()) {
     return SemIR::ScopeLookupResult::MakeNotFound();
     return SemIR::ScopeLookupResult::MakeNotFound();
   }
   }
@@ -2051,22 +2087,8 @@ static auto GetOverloadSetAccess(Context& context, SemIR::LocId loc_id,
   return MapAccess(access);
   return MapAccess(access);
 }
 }
 
 
-static auto ImportOverloadSetAndDependencies(
-    Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id,
-    SemIR::NameId name_id, const clang::UnresolvedSet<4>& overloaded_set)
-    -> SemIR::InstId {
-  ImportWorklist worklist;
-  for (clang::NamedDecl* fn_decl : overloaded_set) {
-    AddDependentDecl(context, fn_decl, worklist);
-  }
-  if (!ImportDeclSet(context, loc_id, worklist)) {
-    return SemIR::ErrorInst::InstId;
-  }
-  return ImportCppOverloadSet(context, scope_id, name_id, overloaded_set);
-}
-
-// Imports an overloaded function set from Clang to Carbon and adds the
-// name into the `NameScope`.
+// Imports an overload set from Clang to Carbon and adds the name into the
+// `NameScope`.
 static auto ImportOverloadSetIntoScope(
 static auto ImportOverloadSetIntoScope(
     Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id,
     Context& context, SemIR::LocId loc_id, SemIR::NameScopeId scope_id,
     SemIR::NameId name_id, const clang::UnresolvedSet<4>& overload_set)
     SemIR::NameId name_id, const clang::UnresolvedSet<4>& overload_set)
@@ -2077,8 +2099,8 @@ static auto ImportOverloadSetIntoScope(
     return SemIR::ScopeLookupResult::MakeError();
     return SemIR::ScopeLookupResult::MakeError();
   }
   }
 
 
-  SemIR::InstId inst_id = ImportOverloadSetAndDependencies(
-      context, loc_id, scope_id, name_id, overload_set);
+  SemIR::InstId inst_id =
+      ImportCppOverloadSet(context, scope_id, name_id, overload_set);
   AddNameToScope(context, scope_id, name_id, access_kind.value(), inst_id);
   AddNameToScope(context, scope_id, name_id, access_kind.value(), inst_id);
   return SemIR::ScopeLookupResult::MakeWrappedLookupResult(inst_id,
   return SemIR::ScopeLookupResult::MakeWrappedLookupResult(inst_id,
                                                            access_kind.value());
                                                            access_kind.value());
@@ -2182,8 +2204,8 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id,
                               lookup->getFoundDecl())) {
                               lookup->getFoundDecl())) {
     return ImportConstructorsIntoScope(context, loc_id, scope_id, name_id);
     return ImportConstructorsIntoScope(context, loc_id, scope_id, name_id);
   }
   }
-  return ImportNameDeclIntoScope(context, loc_id, scope_id, name_id,
-                                 lookup->getFoundDecl(),
+  auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(lookup->getFoundDecl());
+  return ImportNameDeclIntoScope(context, loc_id, scope_id, name_id, key,
                                  lookup->begin().getAccess());
                                  lookup->begin().getAccess());
 }
 }
 
 
@@ -2195,7 +2217,7 @@ auto ImportClassDefinitionForClangDecl(Context& context, SemIR::LocId loc_id,
   CARBON_CHECK(ast);
   CARBON_CHECK(ast);
 
 
   auto* clang_decl =
   auto* clang_decl =
-      cast<clang::TagDecl>(context.clang_decls().Get(clang_decl_id).decl);
+      cast<clang::TagDecl>(context.clang_decls().Get(clang_decl_id).key.decl);
   auto class_inst_id = context.types().GetAsTypeInstId(
   auto class_inst_id = context.types().GetAsTypeInstId(
       context.classes().Get(class_id).first_owning_decl_id);
       context.classes().Get(class_id).first_owning_decl_id);
 
 

+ 2 - 1
toolchain/check/cpp/import.h

@@ -28,7 +28,8 @@ auto ImportCppFiles(Context& context,
 // the new Carbon function declaration `InstId`. If the declaration was already
 // the new Carbon function declaration `InstId`. If the declaration was already
 // imported, returns the mapped instruction.
 // imported, returns the mapped instruction.
 auto ImportCppFunctionDecl(Context& context, SemIR::LocId loc_id,
 auto ImportCppFunctionDecl(Context& context, SemIR::LocId loc_id,
-                           clang::FunctionDecl* clang_decl) -> SemIR::InstId;
+                           clang::FunctionDecl* clang_decl, int num_params)
+    -> SemIR::InstId;
 
 
 // Imports an overloaded function set from Clang to Carbon.
 // Imports an overloaded function set from Clang to Carbon.
 auto ImportCppOverloadSet(Context& context, SemIR::NameScopeId scope_id,
 auto ImportCppOverloadSet(Context& context, SemIR::NameScopeId scope_id,

+ 9 - 2
toolchain/check/cpp/overload_resolution.cpp

@@ -10,6 +10,7 @@
 #include "toolchain/check/cpp/import.h"
 #include "toolchain/check/cpp/import.h"
 #include "toolchain/check/cpp/location.h"
 #include "toolchain/check/cpp/location.h"
 #include "toolchain/check/cpp/type_mapping.h"
 #include "toolchain/check/cpp/type_mapping.h"
+#include "toolchain/diagnostics/diagnostic_emitter.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/typed_insts.h"
 #include "toolchain/sem_ir/typed_insts.h"
 
 
@@ -79,6 +80,12 @@ auto PerformCppOverloadResolution(Context& context, SemIR::LocId loc_id,
                                   SemIR::InstId self_id,
                                   SemIR::InstId self_id,
                                   llvm::ArrayRef<SemIR::InstId> arg_ids)
                                   llvm::ArrayRef<SemIR::InstId> arg_ids)
     -> SemIR::InstId {
     -> SemIR::InstId {
+  // Register an annotation scope to flush any Clang diagnostics when we return.
+  // This is important to ensure that Clang diagnostics are properly interleaved
+  // with Carbon diagnostics.
+  Diagnostics::AnnotationScope annotate_diagnostics(&context.emitter(),
+                                                    [](auto& /*builder*/) {});
+
   // Map Carbon call argument types to C++ types.
   // Map Carbon call argument types to C++ types.
   clang::Expr* self_expr = nullptr;
   clang::Expr* self_expr = nullptr;
   if (self_id.has_value()) {
   if (self_id.has_value()) {
@@ -117,8 +124,8 @@ auto PerformCppOverloadResolution(Context& context, SemIR::LocId loc_id,
       // TODO: Handle the cases when Function is null.
       // TODO: Handle the cases when Function is null.
       CARBON_CHECK(best_viable_fn->Function);
       CARBON_CHECK(best_viable_fn->Function);
       sema.MarkFunctionReferenced(loc, best_viable_fn->Function);
       sema.MarkFunctionReferenced(loc, best_viable_fn->Function);
-      SemIR::InstId result =
-          ImportCppFunctionDecl(context, loc_id, best_viable_fn->Function);
+      SemIR::InstId result = ImportCppFunctionDecl(
+          context, loc_id, best_viable_fn->Function, arg_exprs.size());
       return result;
       return result;
     }
     }
     case clang::OverloadingResult::OR_No_Viable_Function: {
     case clang::OverloadingResult::OR_No_Viable_Function: {

+ 50 - 28
toolchain/check/cpp/thunk.cpp

@@ -35,11 +35,16 @@ static auto GetGlobalDecl(const clang::FunctionDecl* decl)
 // Returns the C++ thunk mangled name given the callee function.
 // Returns the C++ thunk mangled name given the callee function.
 static auto GenerateThunkMangledName(
 static auto GenerateThunkMangledName(
     clang::MangleContext& mangle_context,
     clang::MangleContext& mangle_context,
-    const clang::FunctionDecl& callee_function_decl) -> std::string {
+    const clang::FunctionDecl& callee_function_decl, int num_params)
+    -> std::string {
   RawStringOstream mangled_name_stream;
   RawStringOstream mangled_name_stream;
   mangle_context.mangleName(GetGlobalDecl(&callee_function_decl),
   mangle_context.mangleName(GetGlobalDecl(&callee_function_decl),
                             mangled_name_stream);
                             mangled_name_stream);
   mangled_name_stream << ".carbon_thunk";
   mangled_name_stream << ".carbon_thunk";
+  if (num_params !=
+      static_cast<int>(callee_function_decl.getNumNonObjectParams())) {
+    mangled_name_stream << num_params;
+  }
 
 
   return mangled_name_stream.TakeStr();
   return mangled_name_stream.TakeStr();
 }
 }
@@ -90,14 +95,22 @@ auto IsCppThunkRequired(Context& context, const SemIR::Function& function)
   // we don't generate a thunk if any relevant type is erroneous.
   // we don't generate a thunk if any relevant type is erroneous.
   bool thunk_required = false;
   bool thunk_required = false;
 
 
-  // We require a thunk if any parameter is of reference type, even if the
-  // corresponding SemIR function has an acceptable parameter type.
-  // TODO: We should be able to avoid thunks for reference parameters.
-  const auto* decl = cast<clang::FunctionDecl>(
-      context.clang_decls().Get(function.clang_decl_id).decl);
-  for (auto* param : decl->parameters()) {
-    if (param->getType()->isReferenceType()) {
-      thunk_required = true;
+  const auto& decl_info = context.clang_decls().Get(function.clang_decl_id);
+  const auto* decl = cast<clang::FunctionDecl>(decl_info.key.decl);
+  if (decl_info.key.num_params !=
+      static_cast<int>(decl->getNumNonObjectParams())) {
+    // We require a thunk if the number of parameters we want isn't all of them.
+    // This happens if default arguments are in use, or (eventually) when
+    // calling a varargs function.
+    thunk_required = true;
+  } else {
+    // We require a thunk if any parameter is of reference type, even if the
+    // corresponding SemIR function has an acceptable parameter type.
+    // TODO: We should be able to avoid thunks for reference parameters.
+    for (auto* param : decl->parameters()) {
+      if (param->getType()->isReferenceType()) {
+        thunk_required = true;
+      }
     }
     }
   }
   }
 
 
@@ -147,7 +160,9 @@ static auto IsSimpleAbiType(clang::ASTContext& ast_context,
 namespace {
 namespace {
 // Information about the callee of a thunk.
 // Information about the callee of a thunk.
 struct CalleeFunctionInfo {
 struct CalleeFunctionInfo {
-  explicit CalleeFunctionInfo(clang::FunctionDecl* decl) : decl(decl) {
+  explicit CalleeFunctionInfo(clang::FunctionDecl* decl, int num_params)
+      : decl(decl),
+        num_params(num_params + decl->hasCXXExplicitFunctionObjectParameter()) {
     auto& ast_context = decl->getASTContext();
     auto& ast_context = decl->getASTContext();
     const auto* method_decl = dyn_cast<clang::CXXMethodDecl>(decl);
     const auto* method_decl = dyn_cast<clang::CXXMethodDecl>(decl);
     bool is_ctor = isa<clang::CXXConstructorDecl>(decl);
     bool is_ctor = isa<clang::CXXConstructorDecl>(decl);
@@ -174,7 +189,7 @@ struct CalleeFunctionInfo {
 
 
   // Returns the number of parameters the thunk should have.
   // Returns the number of parameters the thunk should have.
   auto num_thunk_params() const -> unsigned {
   auto num_thunk_params() const -> unsigned {
-    return has_implicit_object_parameter() + decl->getNumParams() +
+    return has_implicit_object_parameter() + num_params +
            !has_simple_return_type;
            !has_simple_return_type;
   }
   }
 
 
@@ -188,12 +203,17 @@ struct CalleeFunctionInfo {
   // the address of the return value.
   // the address of the return value.
   auto GetThunkReturnParamIndex() const -> unsigned {
   auto GetThunkReturnParamIndex() const -> unsigned {
     CARBON_CHECK(!has_simple_return_type);
     CARBON_CHECK(!has_simple_return_type);
-    return has_implicit_object_parameter() + decl->getNumParams();
+    return has_implicit_object_parameter() + num_params;
   }
   }
 
 
   // The callee function.
   // The callee function.
   clang::FunctionDecl* decl;
   clang::FunctionDecl* decl;
 
 
+  // The number of explicit parameters to import. This may be less than the
+  // number of parameters that the function has if default arguments are being
+  // used.
+  int num_params;
+
   // Whether the callee has an object parameter, which might be explicit or
   // Whether the callee has an object parameter, which might be explicit or
   // implicit.
   // implicit.
   bool has_object_parameter;
   bool has_object_parameter;
@@ -244,12 +264,11 @@ static auto BuildThunkParameterTypes(clang::ASTContext& ast_context,
         GetNonnullType(ast_context, callee_info.implicit_this_type));
         GetNonnullType(ast_context, callee_info.implicit_this_type));
   }
   }
 
 
-  for (const clang::ParmVarDecl* callee_param :
-       callee_info.decl->parameters()) {
-    // TODO: We should use the type from the function signature, not the type of
-    // the parameter here.
+  const auto* function_type =
+      callee_info.decl->getType()->castAs<clang::FunctionProtoType>();
+  for (int i : llvm::seq(callee_info.num_params)) {
     thunk_param_types.push_back(
     thunk_param_types.push_back(
-        GetThunkParameterType(ast_context, callee_param->getType()));
+        GetThunkParameterType(ast_context, function_type->getParamType(i)));
   }
   }
 
 
   if (!callee_info.has_simple_return_type) {
   if (!callee_info.has_simple_return_type) {
@@ -270,7 +289,7 @@ static auto BuildThunkParameters(clang::ASTContext& ast_context,
   clang::SourceLocation clang_loc = callee_info.decl->getLocation();
   clang::SourceLocation clang_loc = callee_info.decl->getLocation();
 
 
   const auto* thunk_function_proto_type =
   const auto* thunk_function_proto_type =
-      thunk_function_decl->getFunctionType()->getAs<clang::FunctionProtoType>();
+      thunk_function_decl->getType()->castAs<clang::FunctionProtoType>();
 
 
   llvm::SmallVector<clang::ParmVarDecl*> thunk_params;
   llvm::SmallVector<clang::ParmVarDecl*> thunk_params;
   unsigned num_thunk_params = thunk_function_decl->getNumParams();
   unsigned num_thunk_params = thunk_function_decl->getNumParams();
@@ -285,7 +304,7 @@ static auto BuildThunkParameters(clang::ASTContext& ast_context,
     thunk_params.push_back(thunk_param);
     thunk_params.push_back(thunk_param);
   }
   }
 
 
-  for (unsigned i : llvm::seq(callee_info.decl->getNumParams())) {
+  for (int i : llvm::seq(callee_info.num_params)) {
     clang::ParmVarDecl* thunk_param = clang::ParmVarDecl::Create(
     clang::ParmVarDecl* thunk_param = clang::ParmVarDecl::Create(
         ast_context, thunk_function_decl, clang_loc, clang_loc,
         ast_context, thunk_function_decl, clang_loc, clang_loc,
         callee_info.decl->getParamDecl(i)->getIdentifier(),
         callee_info.decl->getParamDecl(i)->getIdentifier(),
@@ -344,8 +363,9 @@ static auto CreateThunkFunctionDecl(
   // Set asm("<callee function mangled name>.carbon_thunk").
   // Set asm("<callee function mangled name>.carbon_thunk").
   thunk_function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit(
   thunk_function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit(
       ast_context,
       ast_context,
-      GenerateThunkMangledName(*context.sem_ir().clang_mangle_context(),
-                               *callee_info.decl),
+      GenerateThunkMangledName(
+          *context.sem_ir().clang_mangle_context(), *callee_info.decl,
+          callee_info.num_params - callee_info.has_explicit_object_parameter()),
       clang_loc));
       clang_loc));
 
 
   // Set function declaration type source info.
   // Set function declaration type source info.
@@ -416,10 +436,9 @@ static auto BuildCalleeArgs(clang::Sema& sema,
   // The object parameter is always passed as `self`, not in the callee argument
   // The object parameter is always passed as `self`, not in the callee argument
   // list, so the first argument corresponds to the second parameter if there is
   // list, so the first argument corresponds to the second parameter if there is
   // an explicit object parameter and the first parameter otherwise.
   // an explicit object parameter and the first parameter otherwise.
-  unsigned first_param = callee_info.has_explicit_object_parameter();
-  unsigned num_params = callee_info.decl->getNumParams();
-  call_args.reserve(num_params - first_param);
-  for (unsigned callee_index : llvm::seq(first_param, num_params)) {
+  int first_param = callee_info.has_explicit_object_parameter();
+  call_args.reserve(callee_info.num_params - first_param);
+  for (unsigned callee_index : llvm::seq(first_param, callee_info.num_params)) {
     call_args.push_back(BuildParamRefForCalleeArg(sema, thunk_function_decl,
     call_args.push_back(BuildParamRefForCalleeArg(sema, thunk_function_decl,
                                                   callee_info, callee_index));
                                                   callee_info, callee_index));
   }
   }
@@ -520,10 +539,12 @@ auto BuildCppThunk(Context& context, const SemIR::Function& callee_function)
   clang::FunctionDecl* callee_function_decl =
   clang::FunctionDecl* callee_function_decl =
       context.clang_decls()
       context.clang_decls()
           .Get(callee_function.clang_decl_id)
           .Get(callee_function.clang_decl_id)
-          .decl->getAsFunction();
+          .key.decl->getAsFunction();
   CARBON_CHECK(callee_function_decl);
   CARBON_CHECK(callee_function_decl);
 
 
-  CalleeFunctionInfo callee_info(callee_function_decl);
+  CalleeFunctionInfo callee_info(
+      callee_function_decl,
+      context.inst_blocks().Get(callee_function.param_patterns_id).size());
 
 
   // Build the thunk function declaration.
   // Build the thunk function declaration.
   auto thunk_param_types =
   auto thunk_param_types =
@@ -591,7 +612,8 @@ auto PerformCppThunkCall(Context& context, SemIR::LocId loc_id,
   // We assume that the call parameters exactly match the parameter patterns for
   // We assume that the call parameters exactly match the parameter patterns for
   // both the thunk and the callee. This is currently guaranteed because we only
   // both the thunk and the callee. This is currently guaranteed because we only
   // create trivial *ParamPatterns when importing a C++ function.
   // create trivial *ParamPatterns when importing a C++ function.
-  CARBON_CHECK(num_callee_args == callee_function_params.size());
+  CARBON_CHECK(num_callee_args == callee_function_params.size(), "{0} != {1}",
+               num_callee_args, callee_function_params.size());
   CARBON_CHECK(num_callee_args == callee_arg_ids.size());
   CARBON_CHECK(num_callee_args == callee_arg_ids.size());
   CARBON_CHECK(num_thunk_args == thunk_function_params.size());
   CARBON_CHECK(num_thunk_args == thunk_function_params.size());
 
 

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

@@ -95,7 +95,7 @@ static auto TryMapClassType(Context& context, SemIR::ClassType class_type)
           .Get(context.classes().Get(class_type.class_id).scope_id)
           .Get(context.classes().Get(class_type.class_id).scope_id)
           .clang_decl_context_id();
           .clang_decl_context_id();
   if (clang_decl_id.has_value()) {
   if (clang_decl_id.has_value()) {
-    clang::Decl* clang_decl = context.clang_decls().Get(clang_decl_id).decl;
+    clang::Decl* clang_decl = context.clang_decls().Get(clang_decl_id).key.decl;
     auto* tag_type_decl = clang::cast<clang::TagDecl>(clang_decl);
     auto* tag_type_decl = clang::cast<clang::TagDecl>(clang_decl);
     return context.ast_context().getCanonicalTagType(tag_type_decl);
     return context.ast_context().getCanonicalTagType(tag_type_decl);
   }
   }
@@ -230,9 +230,6 @@ static auto MapToCppType(Context& context, SemIR::InstId inst_id)
 auto InventClangArg(Context& context, SemIR::InstId arg_id) -> clang::Expr* {
 auto InventClangArg(Context& context, SemIR::InstId arg_id) -> clang::Expr* {
   clang::ExprValueKind value_kind;
   clang::ExprValueKind value_kind;
   switch (SemIR::GetExprCategory(context.sem_ir(), arg_id)) {
   switch (SemIR::GetExprCategory(context.sem_ir(), arg_id)) {
-    case SemIR::ExprCategory::NotExpr:
-      CARBON_FATAL("Should not see these here");
-
     case SemIR::ExprCategory::Error:
     case SemIR::ExprCategory::Error:
       return nullptr;
       return nullptr;
 
 
@@ -244,6 +241,13 @@ auto InventClangArg(Context& context, SemIR::InstId arg_id) -> clang::Expr* {
       value_kind = clang::ExprValueKind::VK_XValue;
       value_kind = clang::ExprValueKind::VK_XValue;
       break;
       break;
 
 
+    case SemIR::ExprCategory::NotExpr:
+      // A call using this expression as an argument won't be valid, but we
+      // don't diagnose that until we convert the expression to the parameter
+      // type.
+      value_kind = clang::ExprValueKind::VK_PRValue;
+      break;
+
     case SemIR::ExprCategory::Value:
     case SemIR::ExprCategory::Value:
     case SemIR::ExprCategory::Initializing:
     case SemIR::ExprCategory::Initializing:
       value_kind = clang::ExprValueKind::VK_PRValue;
       value_kind = clang::ExprValueKind::VK_PRValue;

+ 1 - 0
toolchain/check/testdata/basics/raw_sem_ir/builtins.carbon

@@ -18,6 +18,7 @@
 // CHECK:STDOUT:     ir0:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:     ir0:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:     ir1:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:     ir1:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:   import_ir_insts: {}
 // CHECK:STDOUT:   import_ir_insts: {}
+// CHECK:STDOUT:   clang_decls:     {}
 // CHECK:STDOUT:   name_scopes:
 // CHECK:STDOUT:   name_scopes:
 // CHECK:STDOUT:     name_scope0:     {inst: inst14, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {}}
 // CHECK:STDOUT:     name_scope0:     {inst: inst14, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {}}
 // CHECK:STDOUT:   entity_names:    {}
 // CHECK:STDOUT:   entity_names:    {}

+ 302 - 0
toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon

@@ -0,0 +1,302 @@
+// 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-FILE: toolchain/testing/testdata/min_prelude/none.carbon
+// EXTRA-ARGS: --dump-raw-sem-ir --builtin-sem-ir
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon
+
+// --- header.h
+
+struct X {
+  X *p;
+};
+
+void f(X x = {}) {
+}
+
+// --- import.carbon
+
+import Cpp library "header.h";
+
+fn G(x: Cpp.X) {
+  Cpp.f();
+  Cpp.f(x);
+}
+
+// CHECK:STDOUT: ---
+// CHECK:STDOUT: filename:        import.carbon
+// CHECK:STDOUT: sem_ir:
+// CHECK:STDOUT:   import_irs:
+// CHECK:STDOUT:     ir0:             {decl_id: inst<none>, is_export: false}
+// CHECK:STDOUT:     ir1:             {decl_id: inst<none>, is_export: false}
+// CHECK:STDOUT:   import_ir_insts:
+// CHECK:STDOUT:     import_ir_inst0: {ir_id: ir1, clang_source_loc_id: clang_source_loc0}
+// CHECK:STDOUT:   clang_decls:
+// CHECK:STDOUT:     clang_decl_id0:  {key: "<translation unit>", inst_id: inst16}
+// CHECK:STDOUT:     clang_decl_id1:  {key: "struct X {}", inst_id: inst18}
+// CHECK:STDOUT:     clang_decl_id2:  {key: {decl: "void f(X x = {})", num_params: 0}, inst_id: inst41}
+// CHECK:STDOUT:     clang_decl_id3:  {key: {decl: "extern void f__carbon_thunk()", num_params: 0}, inst_id: inst44}
+// CHECK:STDOUT:     clang_decl_id4:  {key: {decl: "void f(X x = {})", num_params: 1}, inst_id: inst55}
+// CHECK:STDOUT:     clang_decl_id5:  {key: {decl: "extern void f__carbon_thunk(X * _Nonnull x)", num_params: 1}, inst_id: inst64}
+// CHECK:STDOUT:   name_scopes:
+// CHECK:STDOUT:     name_scope0:     {inst: inst14, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name0: inst16, name1: inst27}}
+// CHECK:STDOUT:     name_scope1:     {inst: inst16, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name3: inst18, name4: inst38}}
+// CHECK:STDOUT:     name_scope2:     {inst: inst18, parent_scope: name_scope1, has_error: false, extended_scopes: [], names: {}}
+// CHECK:STDOUT:   entity_names:
+// CHECK:STDOUT:     entity_name0:    {name: name2, parent_scope: name_scope<none>, index: -1, is_template: 0, clang_decl_id: clang_decl_id<none>}
+// CHECK:STDOUT:     entity_name1:    {name: name2, parent_scope: name_scope<none>, index: -1, is_template: 0, clang_decl_id: clang_decl_id<none>}
+// CHECK:STDOUT:     entity_name2:    {name: name2, parent_scope: name_scope<none>, index: -1, is_template: 0, clang_decl_id: clang_decl_id<none>}
+// CHECK:STDOUT:   functions:
+// CHECK:STDOUT:     function0:       {name: name1, parent_scope: name_scope0, call_params_id: inst_block6, body: [inst_block9]}
+// CHECK:STDOUT:     function1:       {name: name4, parent_scope: name_scope1, call_params_id: inst_block_empty}
+// CHECK:STDOUT:     function2:       {name: name6, parent_scope: name_scope1, call_params_id: inst_block_empty}
+// CHECK:STDOUT:     function3:       {name: name4, parent_scope: name_scope1, call_params_id: inst_block13}
+// CHECK:STDOUT:     function4:       {name: name6, parent_scope: name_scope1, call_params_id: inst_block18}
+// CHECK:STDOUT:   classes:
+// CHECK:STDOUT:     class0:          {name: name3, parent_scope: name_scope1, self_type_id: type(inst19), inheritance_kind: Base, is_dynamic: 0, scope_id: name_scope2, body_block_id: inst_block10, adapt_id: inst<none>, base_id: inst<none>, complete_type_witness_id: inst33, vtable_decl_id: inst<none>}}
+// CHECK:STDOUT:   generics:        {}
+// CHECK:STDOUT:   specifics:       {}
+// CHECK:STDOUT:   struct_type_fields:
+// CHECK:STDOUT:     struct_type_fields0: {}
+// CHECK:STDOUT:   types:
+// CHECK:STDOUT:     'type(TypeType)':
+// CHECK:STDOUT:       value_repr:      {kind: copy, type: type(TypeType)}
+// CHECK:STDOUT:     'type(Error)':
+// CHECK:STDOUT:       value_repr:      {kind: copy, type: type(Error)}
+// CHECK:STDOUT:     'type(inst(NamespaceType))':
+// CHECK:STDOUT:       value_repr:      {kind: copy, type: type(inst(NamespaceType))}
+// CHECK:STDOUT:     'type(inst28)':
+// CHECK:STDOUT:       value_repr:      {kind: none, type: type(inst29)}
+// CHECK:STDOUT:     'type(inst29)':
+// CHECK:STDOUT:       value_repr:      {kind: none, type: type(inst29)}
+// CHECK:STDOUT:     'type(inst(WitnessType))':
+// CHECK:STDOUT:       value_repr:      {kind: copy, type: type(inst(WitnessType))}
+// CHECK:STDOUT:     'type(inst32)':
+// CHECK:STDOUT:       value_repr:      {kind: pointer, type: type(inst35)}
+// CHECK:STDOUT:     'type(inst35)':
+// CHECK:STDOUT:       value_repr:      {kind: copy, type: type(inst35)}
+// CHECK:STDOUT:     'type(inst19)':
+// CHECK:STDOUT:       value_repr:      {kind: pointer, type: type(inst35)}
+// CHECK:STDOUT:     'type(inst37)':
+// CHECK:STDOUT:       value_repr:      {kind: none, type: type(inst29)}
+// CHECK:STDOUT:     'type(inst42)':
+// CHECK:STDOUT:       value_repr:      {kind: none, type: type(inst29)}
+// CHECK:STDOUT:     'type(inst45)':
+// CHECK:STDOUT:       value_repr:      {kind: none, type: type(inst29)}
+// CHECK:STDOUT:     'type(inst56)':
+// CHECK:STDOUT:       value_repr:      {kind: none, type: type(inst29)}
+// CHECK:STDOUT:     'type(inst65)':
+// CHECK:STDOUT:       value_repr:      {kind: none, type: type(inst29)}
+// CHECK:STDOUT:     'type(inst58)':
+// CHECK:STDOUT:       value_repr:      {kind: copy, type: type(inst58)}
+// CHECK:STDOUT:   insts:
+// CHECK:STDOUT:     'inst(TypeType)':  {kind: TypeType, type: type(TypeType)}
+// CHECK:STDOUT:     'inst(AutoType)':  {kind: AutoType, type: type(TypeType)}
+// CHECK:STDOUT:     'inst(BoolType)':  {kind: BoolType, type: type(TypeType)}
+// CHECK:STDOUT:     'inst(BoundMethodType)': {kind: BoundMethodType, type: type(TypeType)}
+// CHECK:STDOUT:     'inst(CharLiteralType)': {kind: CharLiteralType, type: type(TypeType)}
+// CHECK:STDOUT:     'inst(ErrorInst)': {kind: ErrorInst, type: type(Error)}
+// CHECK:STDOUT:     'inst(FloatLiteralType)': {kind: FloatLiteralType, type: type(TypeType)}
+// CHECK:STDOUT:     'inst(ImplWitnessTablePlaceholder)': {kind: ImplWitnessTablePlaceholder, type: type(TypeType)}
+// CHECK:STDOUT:     'inst(InstType)':  {kind: InstType, type: type(TypeType)}
+// CHECK:STDOUT:     'inst(IntLiteralType)': {kind: IntLiteralType, type: type(TypeType)}
+// CHECK:STDOUT:     'inst(NamespaceType)': {kind: NamespaceType, type: type(TypeType)}
+// CHECK:STDOUT:     'inst(SpecificFunctionType)': {kind: SpecificFunctionType, type: type(TypeType)}
+// CHECK:STDOUT:     'inst(VtableType)': {kind: VtableType, type: type(TypeType)}
+// CHECK:STDOUT:     'inst(WitnessType)': {kind: WitnessType, type: type(TypeType)}
+// CHECK:STDOUT:     inst14:          {kind: Namespace, arg0: name_scope0, arg1: inst<none>, type: type(inst(NamespaceType))}
+// CHECK:STDOUT:     inst15:          {kind: ImportCppDecl}
+// CHECK:STDOUT:     inst16:          {kind: Namespace, arg0: name_scope1, arg1: inst15, type: type(inst(NamespaceType))}
+// CHECK:STDOUT:     inst17:          {kind: NameRef, arg0: name0, arg1: inst16, type: type(inst(NamespaceType))}
+// CHECK:STDOUT:     inst18:          {kind: ClassDecl, arg0: class0, arg1: inst_block<none>, type: type(TypeType)}
+// CHECK:STDOUT:     inst19:          {kind: ClassType, arg0: class0, arg1: specific<none>, type: type(TypeType)}
+// CHECK:STDOUT:     inst20:          {kind: NameRef, arg0: name3, arg1: inst18, type: type(TypeType)}
+// CHECK:STDOUT:     inst21:          {kind: BindName, arg0: entity_name0, arg1: inst25, type: type(inst19)}
+// CHECK:STDOUT:     inst22:          {kind: PatternType, arg0: inst19, type: type(TypeType)}
+// CHECK:STDOUT:     inst23:          {kind: BindingPattern, arg0: entity_name0, type: type(inst22)}
+// CHECK:STDOUT:     inst24:          {kind: ValueParamPattern, arg0: inst23, arg1: call_param0, type: type(inst22)}
+// CHECK:STDOUT:     inst25:          {kind: ValueParam, arg0: call_param0, arg1: name2, type: type(inst19)}
+// CHECK:STDOUT:     inst26:          {kind: SpliceBlock, arg0: inst_block4, arg1: inst20, type: type(TypeType)}
+// CHECK:STDOUT:     inst27:          {kind: FunctionDecl, arg0: function0, arg1: inst_block8, type: type(inst28)}
+// CHECK:STDOUT:     inst28:          {kind: FunctionType, arg0: function0, arg1: specific<none>, type: type(TypeType)}
+// CHECK:STDOUT:     inst29:          {kind: TupleType, arg0: inst_block_empty, type: type(TypeType)}
+// CHECK:STDOUT:     inst30:          {kind: StructValue, arg0: inst_block_empty, type: type(inst28)}
+// CHECK:STDOUT:     inst31:          {kind: CustomLayoutType, arg0: struct_type_fields0, arg1: custom_layout1, type: type(TypeType)}
+// CHECK:STDOUT:     inst32:          {kind: CustomLayoutType, arg0: struct_type_fields0, arg1: custom_layout1, type: type(TypeType)}
+// CHECK:STDOUT:     inst33:          {kind: CompleteTypeWitness, arg0: inst31, type: type(inst(WitnessType))}
+// CHECK:STDOUT:     inst34:          {kind: CompleteTypeWitness, arg0: inst32, type: type(inst(WitnessType))}
+// CHECK:STDOUT:     inst35:          {kind: PointerType, arg0: inst32, type: type(TypeType)}
+// CHECK:STDOUT:     inst36:          {kind: NameRef, arg0: name0, arg1: inst16, type: type(inst(NamespaceType))}
+// CHECK:STDOUT:     inst37:          {kind: CppOverloadSetType, arg0: cpp_overload_set0, arg1: specific<none>, type: type(TypeType)}
+// CHECK:STDOUT:     inst38:          {kind: CppOverloadSetValue, arg0: cpp_overload_set0, type: type(inst37)}
+// CHECK:STDOUT:     inst39:          {kind: StructValue, arg0: inst_block_empty, type: type(inst37)}
+// CHECK:STDOUT:     inst40:          {kind: NameRef, arg0: name4, arg1: inst38, type: type(inst37)}
+// CHECK:STDOUT:     inst41:          {kind: FunctionDecl, arg0: function1, arg1: inst_block_empty, type: type(inst42)}
+// CHECK:STDOUT:     inst42:          {kind: FunctionType, arg0: function1, arg1: specific<none>, type: type(TypeType)}
+// CHECK:STDOUT:     inst43:          {kind: StructValue, arg0: inst_block_empty, type: type(inst42)}
+// CHECK:STDOUT:     inst44:          {kind: FunctionDecl, arg0: function2, arg1: inst_block_empty, type: type(inst45)}
+// CHECK:STDOUT:     inst45:          {kind: FunctionType, arg0: function2, arg1: specific<none>, type: type(TypeType)}
+// CHECK:STDOUT:     inst46:          {kind: StructValue, arg0: inst_block_empty, type: type(inst45)}
+// CHECK:STDOUT:     inst47:          {kind: Call, arg0: inst44, arg1: inst_block_empty, type: type(inst29)}
+// CHECK:STDOUT:     inst48:          {kind: NameRef, arg0: name0, arg1: inst16, type: type(inst(NamespaceType))}
+// CHECK:STDOUT:     inst49:          {kind: NameRef, arg0: name4, arg1: inst38, type: type(inst37)}
+// CHECK:STDOUT:     inst50:          {kind: NameRef, arg0: name2, arg1: inst21, type: type(inst19)}
+// CHECK:STDOUT:     inst51:          {kind: BindName, arg0: entity_name1, arg1: inst54, type: type(inst19)}
+// CHECK:STDOUT:     inst52:          {kind: BindingPattern, arg0: entity_name1, type: type(inst22)}
+// CHECK:STDOUT:     inst53:          {kind: ValueParamPattern, arg0: inst52, arg1: call_param0, type: type(inst22)}
+// CHECK:STDOUT:     inst54:          {kind: ValueParam, arg0: call_param0, arg1: name2, type: type(inst19)}
+// CHECK:STDOUT:     inst55:          {kind: FunctionDecl, arg0: function3, arg1: inst_block15, type: type(inst56)}
+// CHECK:STDOUT:     inst56:          {kind: FunctionType, arg0: function3, arg1: specific<none>, type: type(TypeType)}
+// CHECK:STDOUT:     inst57:          {kind: StructValue, arg0: inst_block_empty, type: type(inst56)}
+// CHECK:STDOUT:     inst58:          {kind: PointerType, arg0: inst19, type: type(TypeType)}
+// CHECK:STDOUT:     inst59:          {kind: BindName, arg0: entity_name2, arg1: inst63, type: type(inst58)}
+// CHECK:STDOUT:     inst60:          {kind: PatternType, arg0: inst58, type: type(TypeType)}
+// CHECK:STDOUT:     inst61:          {kind: BindingPattern, arg0: entity_name2, type: type(inst60)}
+// CHECK:STDOUT:     inst62:          {kind: ValueParamPattern, arg0: inst61, arg1: call_param0, type: type(inst60)}
+// CHECK:STDOUT:     inst63:          {kind: ValueParam, arg0: call_param0, arg1: name2, type: type(inst58)}
+// CHECK:STDOUT:     inst64:          {kind: FunctionDecl, arg0: function4, arg1: inst_block20, type: type(inst65)}
+// CHECK:STDOUT:     inst65:          {kind: FunctionType, arg0: function4, arg1: specific<none>, type: type(TypeType)}
+// CHECK:STDOUT:     inst66:          {kind: StructValue, arg0: inst_block_empty, type: type(inst65)}
+// CHECK:STDOUT:     inst67:          {kind: ValueAsRef, arg0: inst50, type: type(inst19)}
+// CHECK:STDOUT:     inst68:          {kind: AddrOf, arg0: inst67, type: type(inst58)}
+// CHECK:STDOUT:     inst69:          {kind: Call, arg0: inst64, arg1: inst_block22, type: type(inst29)}
+// CHECK:STDOUT:     inst70:          {kind: Return}
+// CHECK:STDOUT:   constant_values:
+// CHECK:STDOUT:     values:
+// CHECK:STDOUT:       'inst(TypeType)':  concrete_constant(inst(TypeType))
+// CHECK:STDOUT:       'inst(AutoType)':  concrete_constant(inst(AutoType))
+// CHECK:STDOUT:       'inst(BoolType)':  concrete_constant(inst(BoolType))
+// CHECK:STDOUT:       'inst(BoundMethodType)': concrete_constant(inst(BoundMethodType))
+// CHECK:STDOUT:       'inst(CharLiteralType)': concrete_constant(inst(CharLiteralType))
+// CHECK:STDOUT:       'inst(ErrorInst)': concrete_constant(inst(ErrorInst))
+// CHECK:STDOUT:       'inst(FloatLiteralType)': concrete_constant(inst(FloatLiteralType))
+// CHECK:STDOUT:       'inst(ImplWitnessTablePlaceholder)': concrete_constant(inst(ImplWitnessTablePlaceholder))
+// CHECK:STDOUT:       'inst(InstType)':  concrete_constant(inst(InstType))
+// CHECK:STDOUT:       'inst(IntLiteralType)': concrete_constant(inst(IntLiteralType))
+// CHECK:STDOUT:       'inst(NamespaceType)': concrete_constant(inst(NamespaceType))
+// CHECK:STDOUT:       'inst(SpecificFunctionType)': concrete_constant(inst(SpecificFunctionType))
+// CHECK:STDOUT:       'inst(VtableType)': concrete_constant(inst(VtableType))
+// CHECK:STDOUT:       'inst(WitnessType)': concrete_constant(inst(WitnessType))
+// CHECK:STDOUT:       inst14:          concrete_constant(inst14)
+// CHECK:STDOUT:       inst16:          concrete_constant(inst16)
+// CHECK:STDOUT:       inst17:          concrete_constant(inst16)
+// CHECK:STDOUT:       inst18:          concrete_constant(inst19)
+// CHECK:STDOUT:       inst19:          concrete_constant(inst19)
+// CHECK:STDOUT:       inst20:          concrete_constant(inst19)
+// CHECK:STDOUT:       inst22:          concrete_constant(inst22)
+// CHECK:STDOUT:       inst23:          concrete_constant(inst23)
+// CHECK:STDOUT:       inst24:          concrete_constant(inst24)
+// CHECK:STDOUT:       inst26:          concrete_constant(inst19)
+// CHECK:STDOUT:       inst27:          concrete_constant(inst30)
+// CHECK:STDOUT:       inst28:          concrete_constant(inst28)
+// CHECK:STDOUT:       inst29:          concrete_constant(inst29)
+// CHECK:STDOUT:       inst30:          concrete_constant(inst30)
+// CHECK:STDOUT:       inst31:          concrete_constant(inst32)
+// CHECK:STDOUT:       inst32:          concrete_constant(inst32)
+// CHECK:STDOUT:       inst33:          concrete_constant(inst34)
+// CHECK:STDOUT:       inst34:          concrete_constant(inst34)
+// CHECK:STDOUT:       inst35:          concrete_constant(inst35)
+// CHECK:STDOUT:       inst36:          concrete_constant(inst16)
+// CHECK:STDOUT:       inst37:          concrete_constant(inst37)
+// CHECK:STDOUT:       inst38:          concrete_constant(inst39)
+// CHECK:STDOUT:       inst39:          concrete_constant(inst39)
+// CHECK:STDOUT:       inst40:          concrete_constant(inst39)
+// CHECK:STDOUT:       inst41:          concrete_constant(inst43)
+// CHECK:STDOUT:       inst42:          concrete_constant(inst42)
+// CHECK:STDOUT:       inst43:          concrete_constant(inst43)
+// CHECK:STDOUT:       inst44:          concrete_constant(inst46)
+// CHECK:STDOUT:       inst45:          concrete_constant(inst45)
+// CHECK:STDOUT:       inst46:          concrete_constant(inst46)
+// CHECK:STDOUT:       inst48:          concrete_constant(inst16)
+// CHECK:STDOUT:       inst49:          concrete_constant(inst39)
+// CHECK:STDOUT:       inst52:          concrete_constant(inst52)
+// CHECK:STDOUT:       inst53:          concrete_constant(inst53)
+// CHECK:STDOUT:       inst55:          concrete_constant(inst57)
+// CHECK:STDOUT:       inst56:          concrete_constant(inst56)
+// CHECK:STDOUT:       inst57:          concrete_constant(inst57)
+// CHECK:STDOUT:       inst58:          concrete_constant(inst58)
+// CHECK:STDOUT:       inst60:          concrete_constant(inst60)
+// CHECK:STDOUT:       inst61:          concrete_constant(inst61)
+// CHECK:STDOUT:       inst62:          concrete_constant(inst62)
+// CHECK:STDOUT:       inst64:          concrete_constant(inst66)
+// CHECK:STDOUT:       inst65:          concrete_constant(inst65)
+// CHECK:STDOUT:       inst66:          concrete_constant(inst66)
+// CHECK:STDOUT:     symbolic_constants: {}
+// CHECK:STDOUT:   inst_blocks:
+// CHECK:STDOUT:     inst_block_empty: {}
+// CHECK:STDOUT:     exports:
+// CHECK:STDOUT:       0:               inst27
+// CHECK:STDOUT:     imports:
+// CHECK:STDOUT:       0:               inst16
+// CHECK:STDOUT:       1:               inst18
+// CHECK:STDOUT:       2:               inst38
+// CHECK:STDOUT:       3:               inst41
+// CHECK:STDOUT:       4:               inst44
+// CHECK:STDOUT:       5:               inst55
+// CHECK:STDOUT:       6:               inst64
+// CHECK:STDOUT:     global_init:     {}
+// CHECK:STDOUT:     inst_block4:
+// CHECK:STDOUT:       0:               inst17
+// CHECK:STDOUT:       1:               inst20
+// CHECK:STDOUT:     inst_block5:
+// CHECK:STDOUT:       0:               inst24
+// CHECK:STDOUT:     inst_block6:
+// CHECK:STDOUT:       0:               inst25
+// CHECK:STDOUT:     inst_block7:
+// CHECK:STDOUT:       0:               inst23
+// CHECK:STDOUT:       1:               inst24
+// CHECK:STDOUT:     inst_block8:
+// CHECK:STDOUT:       0:               inst25
+// CHECK:STDOUT:       1:               inst26
+// CHECK:STDOUT:       2:               inst21
+// CHECK:STDOUT:     inst_block9:
+// CHECK:STDOUT:       0:               inst36
+// CHECK:STDOUT:       1:               inst40
+// CHECK:STDOUT:       2:               inst47
+// CHECK:STDOUT:       3:               inst48
+// CHECK:STDOUT:       4:               inst49
+// CHECK:STDOUT:       5:               inst50
+// CHECK:STDOUT:       6:               inst67
+// CHECK:STDOUT:       7:               inst68
+// CHECK:STDOUT:       8:               inst69
+// CHECK:STDOUT:       9:               inst70
+// CHECK:STDOUT:     inst_block10:
+// CHECK:STDOUT:       0:               inst31
+// CHECK:STDOUT:       1:               inst33
+// CHECK:STDOUT:     inst_block11:    {}
+// CHECK:STDOUT:     inst_block12:
+// CHECK:STDOUT:       0:               inst53
+// CHECK:STDOUT:     inst_block13:
+// CHECK:STDOUT:       0:               inst54
+// CHECK:STDOUT:     inst_block14:
+// CHECK:STDOUT:       0:               inst52
+// CHECK:STDOUT:       1:               inst53
+// CHECK:STDOUT:     inst_block15:
+// CHECK:STDOUT:       0:               inst54
+// CHECK:STDOUT:       1:               inst51
+// CHECK:STDOUT:     inst_block16:    {}
+// CHECK:STDOUT:     inst_block17:
+// CHECK:STDOUT:       0:               inst62
+// CHECK:STDOUT:     inst_block18:
+// CHECK:STDOUT:       0:               inst63
+// CHECK:STDOUT:     inst_block19:
+// CHECK:STDOUT:       0:               inst61
+// CHECK:STDOUT:       1:               inst62
+// CHECK:STDOUT:     inst_block20:
+// CHECK:STDOUT:       0:               inst63
+// CHECK:STDOUT:       1:               inst59
+// CHECK:STDOUT:     inst_block21:
+// CHECK:STDOUT:       0:               inst50
+// CHECK:STDOUT:     inst_block22:
+// CHECK:STDOUT:       0:               inst68
+// CHECK:STDOUT:     inst_block23:
+// CHECK:STDOUT:       0:               inst14
+// CHECK:STDOUT:       1:               inst15
+// CHECK:STDOUT:       2:               inst27
+// CHECK:STDOUT: ...

+ 2 - 0
toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon

@@ -34,6 +34,7 @@ fn B() {
 // CHECK:STDOUT:     ir0:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:     ir0:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:     ir1:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:     ir1:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:   import_ir_insts: {}
 // CHECK:STDOUT:   import_ir_insts: {}
+// CHECK:STDOUT:   clang_decls:     {}
 // CHECK:STDOUT:   name_scopes:
 // CHECK:STDOUT:   name_scopes:
 // CHECK:STDOUT:     name_scope0:     {inst: inst14, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name0: inst15}}
 // CHECK:STDOUT:     name_scope0:     {inst: inst14, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name0: inst15}}
 // CHECK:STDOUT:   entity_names:    {}
 // CHECK:STDOUT:   entity_names:    {}
@@ -93,6 +94,7 @@ fn B() {
 // CHECK:STDOUT:   import_ir_insts:
 // CHECK:STDOUT:   import_ir_insts:
 // CHECK:STDOUT:     import_ir_inst0: {ir_id: ir2, inst_id: inst15}
 // CHECK:STDOUT:     import_ir_inst0: {ir_id: ir2, inst_id: inst15}
 // CHECK:STDOUT:     import_ir_inst1: {ir_id: ir2, inst_id: inst15}
 // CHECK:STDOUT:     import_ir_inst1: {ir_id: ir2, inst_id: inst15}
+// CHECK:STDOUT:   clang_decls:     {}
 // CHECK:STDOUT:   name_scopes:
 // CHECK:STDOUT:   name_scopes:
 // CHECK:STDOUT:     name_scope0:     {inst: inst14, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name1: inst16, name0: inst17}}
 // CHECK:STDOUT:     name_scope0:     {inst: inst14, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name1: inst16, name0: inst17}}
 // CHECK:STDOUT:     name_scope1:     {inst: inst16, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name1: inst22}}
 // CHECK:STDOUT:     name_scope1:     {inst: inst16, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name1: inst22}}

+ 2 - 0
toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon

@@ -34,6 +34,7 @@ fn B() {
 // CHECK:STDOUT:     ir0:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:     ir0:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:     ir1:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:     ir1:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:   import_ir_insts: {}
 // CHECK:STDOUT:   import_ir_insts: {}
+// CHECK:STDOUT:   clang_decls:     {}
 // CHECK:STDOUT:   name_scopes:
 // CHECK:STDOUT:   name_scopes:
 // CHECK:STDOUT:     name_scope0:     {inst: inst14, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name0: inst15}}
 // CHECK:STDOUT:     name_scope0:     {inst: inst14, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name0: inst15}}
 // CHECK:STDOUT:   entity_names:    {}
 // CHECK:STDOUT:   entity_names:    {}
@@ -112,6 +113,7 @@ fn B() {
 // CHECK:STDOUT:   import_ir_insts:
 // CHECK:STDOUT:   import_ir_insts:
 // CHECK:STDOUT:     import_ir_inst0: {ir_id: ir2, inst_id: inst15}
 // CHECK:STDOUT:     import_ir_inst0: {ir_id: ir2, inst_id: inst15}
 // CHECK:STDOUT:     import_ir_inst1: {ir_id: ir2, inst_id: inst15}
 // CHECK:STDOUT:     import_ir_inst1: {ir_id: ir2, inst_id: inst15}
+// CHECK:STDOUT:   clang_decls:     {}
 // CHECK:STDOUT:   name_scopes:
 // CHECK:STDOUT:   name_scopes:
 // CHECK:STDOUT:     name_scope0:     {inst: inst14, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name1: inst16, name0: inst17}}
 // CHECK:STDOUT:     name_scope0:     {inst: inst14, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name1: inst16, name0: inst17}}
 // CHECK:STDOUT:     name_scope1:     {inst: inst16, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name1: inst22}}
 // CHECK:STDOUT:     name_scope1:     {inst: inst16, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name1: inst22}}

+ 1 - 0
toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon

@@ -216,6 +216,7 @@ fn Foo[T:! type](p: T*) -> (T*, ()) {
 // CHECK:STDOUT:     import_ir_inst185: {ir_id: ir4, inst_id: inst605}
 // CHECK:STDOUT:     import_ir_inst185: {ir_id: ir4, inst_id: inst605}
 // CHECK:STDOUT:     import_ir_inst186: {ir_id: ir4, inst_id: inst606}
 // CHECK:STDOUT:     import_ir_inst186: {ir_id: ir4, inst_id: inst606}
 // CHECK:STDOUT:     import_ir_inst187: {ir_id: ir4, inst_id: inst611}
 // CHECK:STDOUT:     import_ir_inst187: {ir_id: ir4, inst_id: inst611}
+// CHECK:STDOUT:   clang_decls:     {}
 // CHECK:STDOUT:   name_scopes:
 // CHECK:STDOUT:   name_scopes:
 // CHECK:STDOUT:     name_scope0:     {inst: inst14, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name(Core): inst16, name0: inst53}}
 // CHECK:STDOUT:     name_scope0:     {inst: inst14, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name(Core): inst16, name0: inst53}}
 // CHECK:STDOUT:     name_scope1:     {inst: inst16, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name3: inst69}}
 // CHECK:STDOUT:     name_scope1:     {inst: inst16, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name3: inst69}}

+ 1 - 0
toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon

@@ -24,6 +24,7 @@ fn Foo(n: ()) -> ((), ()) {
 // CHECK:STDOUT:     ir0:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:     ir0:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:     ir1:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:     ir1:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:   import_ir_insts: {}
 // CHECK:STDOUT:   import_ir_insts: {}
+// CHECK:STDOUT:   clang_decls:     {}
 // CHECK:STDOUT:   name_scopes:
 // CHECK:STDOUT:   name_scopes:
 // CHECK:STDOUT:     name_scope0:     {inst: inst14, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name0: inst37}}
 // CHECK:STDOUT:     name_scope0:     {inst: inst14, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name0: inst37}}
 // CHECK:STDOUT:   entity_names:
 // CHECK:STDOUT:   entity_names:

+ 112 - 68
toolchain/check/testdata/interop/cpp/class/constructor.carbon

@@ -82,30 +82,25 @@ fn F() {
 }
 }
 
 
 // ============================================================================
 // ============================================================================
-// Constructor with default values
+// Constructor with default arguments
 // ============================================================================
 // ============================================================================
 
 
-// --- default_values.h
+// --- default_arguments.h
 
 
 class C {
 class C {
  public:
  public:
   C(int x, int y = 8);
   C(int x, int y = 8);
 };
 };
 
 
-// --- fail_todo_import_default_values.carbon
+// --- import_default_arguments.carbon
 
 
 library "[[@TEST_NAME]]";
 library "[[@TEST_NAME]]";
 
 
-import Cpp library "default_values.h";
+import Cpp library "default_arguments.h";
 
 
 fn F() {
 fn F() {
   //@dump-sem-ir-begin
   //@dump-sem-ir-begin
   let c1: Cpp.C = Cpp.C.C(8, 9);
   let c1: Cpp.C = Cpp.C.C(8, 9);
-  // CHECK:STDERR: fail_todo_import_default_values.carbon:[[@LINE+5]]:19: error: 1 argument passed to function expecting 2 arguments [CallArgCountMismatch]
-  // CHECK:STDERR:   let c2: Cpp.C = Cpp.C.C(8);
-  // CHECK:STDERR:                   ^~~~~~~~~~
-  // CHECK:STDERR: fail_todo_import_default_values.carbon: note: calling function declared here [InCallToEntity]
-  // CHECK:STDERR:
   let c2: Cpp.C = Cpp.C.C(8);
   let c2: Cpp.C = Cpp.C.C(8);
   //@dump-sem-ir-end
   //@dump-sem-ir-end
 }
 }
@@ -186,11 +181,6 @@ import Cpp library "implicit_multi_arguments.h";
 fn F() {
 fn F() {
   //@dump-sem-ir-begin
   //@dump-sem-ir-begin
   let c1: Cpp.C = Cpp.C.C(8, 9);
   let c1: Cpp.C = Cpp.C.C(8, 9);
-  // CHECK:STDERR: fail_todo_import_implicit_multi_arguments.carbon:[[@LINE+5]]:19: error: 1 argument passed to function expecting 2 arguments [CallArgCountMismatch]
-  // CHECK:STDERR:   let c2: Cpp.C = Cpp.C.C(8);
-  // CHECK:STDERR:                   ^~~~~~~~~~
-  // CHECK:STDERR: fail_todo_import_implicit_multi_arguments.carbon: note: calling function declared here [InCallToEntity]
-  // CHECK:STDERR:
   let c2: Cpp.C = Cpp.C.C(8);
   let c2: Cpp.C = Cpp.C.C(8);
   // CHECK:STDERR: fail_todo_import_implicit_multi_arguments.carbon:[[@LINE+7]]:19: error: cannot implicitly convert expression of type `i32` to `Cpp.C` [ConversionFailure]
   // CHECK:STDERR: fail_todo_import_implicit_multi_arguments.carbon:[[@LINE+7]]:19: error: cannot implicitly convert expression of type `i32` to `Cpp.C` [ConversionFailure]
   // CHECK:STDERR:   let c3: Cpp.C = 8 as i32;
   // CHECK:STDERR:   let c3: Cpp.C = 8 as i32;
@@ -518,21 +508,21 @@ fn F() {
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_import_default_values.carbon
+// CHECK:STDOUT: --- import_default_arguments.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %pattern_type.217: type = pattern_type %C [concrete]
 // CHECK:STDOUT:   %pattern_type.217: type = pattern_type %C [concrete]
-// CHECK:STDOUT:   %.d40: type = cpp_overload_set_type @C.C [concrete]
+// CHECK:STDOUT:   %.d40: type = cpp_overload_set_type @C.C.1 [concrete]
 // CHECK:STDOUT:   %empty_struct: %.d40 = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.d40 = struct_value () [concrete]
 // CHECK:STDOUT:   %int_8.b85: Core.IntLiteral = int_value 8 [concrete]
 // CHECK:STDOUT:   %int_8.b85: Core.IntLiteral = int_value 8 [concrete]
 // CHECK:STDOUT:   %int_9.988: Core.IntLiteral = int_value 9 [concrete]
 // CHECK:STDOUT:   %int_9.988: Core.IntLiteral = int_value 9 [concrete]
 // CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
 // CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
 // CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
 // CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
 // CHECK:STDOUT:   %ptr.d9e: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %ptr.d9e: type = ptr_type %C [concrete]
-// CHECK:STDOUT:   %C__carbon_thunk.type: type = fn_type @C__carbon_thunk [concrete]
-// CHECK:STDOUT:   %C__carbon_thunk: %C__carbon_thunk.type = struct_value () [concrete]
+// CHECK:STDOUT:   %C__carbon_thunk.type.65f120.1: type = fn_type @C__carbon_thunk.1 [concrete]
+// CHECK:STDOUT:   %C__carbon_thunk.d98342.1: %C__carbon_thunk.type.65f120.1 = struct_value () [concrete]
 // CHECK:STDOUT:   %ImplicitAs.type.d14: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
 // CHECK:STDOUT:   %ImplicitAs.type.d14: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
 // CHECK:STDOUT:   %ImplicitAs.Convert.type.1b6: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%i32) [concrete]
 // CHECK:STDOUT:   %ImplicitAs.Convert.type.1b6: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%i32) [concrete]
 // CHECK:STDOUT:   %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic]
 // CHECK:STDOUT:   %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic]
@@ -550,6 +540,8 @@ fn F() {
 // CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.36a: <bound method> = bound_method %int_9.988, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0 [concrete]
 // CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.36a: <bound method> = bound_method %int_9.988, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0 [concrete]
 // CHECK:STDOUT:   %bound_method.942: <bound method> = bound_method %int_9.988, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
 // CHECK:STDOUT:   %bound_method.942: <bound method> = bound_method %int_9.988, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
 // CHECK:STDOUT:   %int_9.f88: %i32 = int_value 9 [concrete]
 // CHECK:STDOUT:   %int_9.f88: %i32 = int_value 9 [concrete]
+// CHECK:STDOUT:   %C__carbon_thunk.type.65f120.2: type = fn_type @C__carbon_thunk.2 [concrete]
+// CHECK:STDOUT:   %C__carbon_thunk.d98342.2: %C__carbon_thunk.type.65f120.2 = struct_value () [concrete]
 // CHECK:STDOUT:   %type_where: type = facet_type <type where .Self impls <CanAggregateDestroy>> [concrete]
 // CHECK:STDOUT:   %type_where: type = facet_type <type where .Self impls <CanAggregateDestroy>> [concrete]
 // CHECK:STDOUT:   %facet_value: %type_where = facet_value %C, () [concrete]
 // CHECK:STDOUT:   %facet_value: %type_where = facet_value %C, () [concrete]
 // CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.type.fc1: type = fn_type @AggregateT.as_type.as.Destroy.impl.Op, @AggregateT.as_type.as.Destroy.impl(%facet_value) [concrete]
 // CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.type.fc1: type = fn_type @AggregateT.as_type.as.Destroy.impl.Op, @AggregateT.as_type.as.Destroy.impl(%facet_value) [concrete]
@@ -562,14 +554,19 @@ fn F() {
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
-// CHECK:STDOUT:   %.40b: %.d40 = cpp_overload_set_value @C.C [concrete = constants.%empty_struct]
-// CHECK:STDOUT:   %C__carbon_thunk.decl: %C__carbon_thunk.type = fn_decl @C__carbon_thunk [concrete = constants.%C__carbon_thunk] {
+// CHECK:STDOUT:   %.40b: %.d40 = cpp_overload_set_value @C.C.1 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %C__carbon_thunk.decl.8acdfe.1: %C__carbon_thunk.type.65f120.1 = fn_decl @C__carbon_thunk.1 [concrete = constants.%C__carbon_thunk.d98342.1] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core.import_ref.ee7: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340) = import_ref Core//prelude/parts/int, loc23_39, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.1c0)]
 // CHECK:STDOUT:   %Core.import_ref.ee7: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340) = import_ref Core//prelude/parts/int, loc23_39, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.1c0)]
 // CHECK:STDOUT:   %ImplicitAs.impl_witness_table.9e9 = impl_witness_table (%Core.import_ref.ee7), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
 // CHECK:STDOUT:   %ImplicitAs.impl_witness_table.9e9 = impl_witness_table (%Core.import_ref.ee7), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
+// CHECK:STDOUT:   %C__carbon_thunk.decl.8acdfe.2: %C__carbon_thunk.type.65f120.2 = fn_decl @C__carbon_thunk.2 [concrete = constants.%C__carbon_thunk.d98342.2] {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F() {
 // CHECK:STDOUT: fn @F() {
@@ -598,8 +595,8 @@ fn F() {
 // CHECK:STDOUT:   %.loc8_30.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_30 [concrete = constants.%int_9.f88]
 // CHECK:STDOUT:   %.loc8_30.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_30 [concrete = constants.%int_9.f88]
 // CHECK:STDOUT:   %.loc8_30.2: %i32 = converted %int_9, %.loc8_30.1 [concrete = constants.%int_9.f88]
 // CHECK:STDOUT:   %.loc8_30.2: %i32 = converted %int_9, %.loc8_30.1 [concrete = constants.%int_9.f88]
 // CHECK:STDOUT:   %addr.loc8_31.1: %ptr.d9e = addr_of %.loc8_31.1
 // CHECK:STDOUT:   %addr.loc8_31.1: %ptr.d9e = addr_of %.loc8_31.1
-// CHECK:STDOUT:   %C__carbon_thunk.call: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%.loc8_27.2, %.loc8_30.2, %addr.loc8_31.1)
-// CHECK:STDOUT:   %.loc8_31.2: init %C = in_place_init %C__carbon_thunk.call, %.loc8_31.1
+// CHECK:STDOUT:   %C__carbon_thunk.call.loc8: init %empty_tuple.type = call imports.%C__carbon_thunk.decl.8acdfe.1(%.loc8_27.2, %.loc8_30.2, %addr.loc8_31.1)
+// CHECK:STDOUT:   %.loc8_31.2: init %C = in_place_init %C__carbon_thunk.call.loc8, %.loc8_31.1
 // CHECK:STDOUT:   %.loc8_14: type = splice_block %C.ref.loc8_14 [concrete = constants.%C] {
 // CHECK:STDOUT:   %.loc8_14: type = splice_block %C.ref.loc8_14 [concrete = constants.%C] {
 // CHECK:STDOUT:     %Cpp.ref.loc8_11: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:     %Cpp.ref.loc8_11: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:     %C.ref.loc8_14: type = name_ref C, imports.%C.decl [concrete = constants.%C]
 // CHECK:STDOUT:     %C.ref.loc8_14: type = name_ref C, imports.%C.decl [concrete = constants.%C]
@@ -610,22 +607,42 @@ fn F() {
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %c2.patt: %pattern_type.217 = binding_pattern c2 [concrete]
 // CHECK:STDOUT:     %c2.patt: %pattern_type.217 = binding_pattern c2 [concrete]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Cpp.ref.loc14_19: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:   %C.ref.loc14_22: type = name_ref C, imports.%C.decl [concrete = constants.%C]
-// CHECK:STDOUT:   %C.ref.loc14_24: %.d40 = name_ref C, imports.%.40b [concrete = constants.%empty_struct]
-// CHECK:STDOUT:   %int_8.loc14: Core.IntLiteral = int_value 8 [concrete = constants.%int_8.b85]
-// CHECK:STDOUT:   %.loc14: type = splice_block %C.ref.loc14_14 [concrete = constants.%C] {
-// CHECK:STDOUT:     %Cpp.ref.loc14_11: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:     %C.ref.loc14_14: type = name_ref C, imports.%C.decl [concrete = constants.%C]
+// CHECK:STDOUT:   %Cpp.ref.loc9_19: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %C.ref.loc9_22: type = name_ref C, imports.%C.decl [concrete = constants.%C]
+// CHECK:STDOUT:   %C.ref.loc9_24: %.d40 = name_ref C, imports.%.40b [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %int_8.loc9: Core.IntLiteral = int_value 8 [concrete = constants.%int_8.b85]
+// CHECK:STDOUT:   %.loc9_28.1: ref %C = temporary_storage
+// CHECK:STDOUT:   %impl.elem0.loc9: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc9_27.1: <bound method> = bound_method %int_8.loc9, %impl.elem0.loc9 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.0ab]
+// CHECK:STDOUT:   %specific_fn.loc9: <specific function> = specific_function %impl.elem0.loc9, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc9_27.2: <bound method> = bound_method %int_8.loc9, %specific_fn.loc9 [concrete = constants.%bound_method.b23]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9: init %i32 = call %bound_method.loc9_27.2(%int_8.loc9) [concrete = constants.%int_8.98c]
+// CHECK:STDOUT:   %.loc9_27.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9 [concrete = constants.%int_8.98c]
+// CHECK:STDOUT:   %.loc9_27.2: %i32 = converted %int_8.loc9, %.loc9_27.1 [concrete = constants.%int_8.98c]
+// CHECK:STDOUT:   %addr.loc9_28.1: %ptr.d9e = addr_of %.loc9_28.1
+// CHECK:STDOUT:   %C__carbon_thunk.call.loc9: init %empty_tuple.type = call imports.%C__carbon_thunk.decl.8acdfe.2(%.loc9_27.2, %addr.loc9_28.1)
+// CHECK:STDOUT:   %.loc9_28.2: init %C = in_place_init %C__carbon_thunk.call.loc9, %.loc9_28.1
+// CHECK:STDOUT:   %.loc9_14: type = splice_block %C.ref.loc9_14 [concrete = constants.%C] {
+// CHECK:STDOUT:     %Cpp.ref.loc9_11: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:     %C.ref.loc9_14: type = name_ref C, imports.%C.decl [concrete = constants.%C]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %c2: %C = bind_name c2, <error> [concrete = <error>]
-// CHECK:STDOUT:   %facet_value: %type_where = facet_value constants.%C, () [concrete = constants.%facet_value]
-// CHECK:STDOUT:   %.loc8_31.5: %type_where = converted constants.%C, %facet_value [concrete = constants.%facet_value]
-// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_31.3, constants.%AggregateT.as_type.as.Destroy.impl.Op.6b9
+// CHECK:STDOUT:   %.loc9_28.3: ref %C = temporary %.loc9_28.1, %.loc9_28.2
+// CHECK:STDOUT:   %.loc9_28.4: %C = bind_value %.loc9_28.3
+// CHECK:STDOUT:   %c2: %C = bind_name c2, %.loc9_28.4
+// CHECK:STDOUT:   %facet_value.loc9: %type_where = facet_value constants.%C, () [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %.loc9_28.5: %type_where = converted constants.%C, %facet_value.loc9 [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc9: <bound method> = bound_method %.loc9_28.3, constants.%AggregateT.as_type.as.Destroy.impl.Op.6b9
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT:   %bound_method.loc9_28: <bound method> = bound_method %.loc9_28.3, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.1
+// CHECK:STDOUT:   %addr.loc9_28.2: %ptr.d9e = addr_of %.loc9_28.3
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9_28(%addr.loc9_28.2)
+// CHECK:STDOUT:   %facet_value.loc8: %type_where = facet_value constants.%C, () [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %.loc8_31.5: %type_where = converted constants.%C, %facet_value.loc8 [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc8: <bound method> = bound_method %.loc8_31.3, constants.%AggregateT.as_type.as.Destroy.impl.Op.6b9
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_31: <bound method> = bound_method %.loc8_31.3, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %bound_method.loc8_31: <bound method> = bound_method %.loc8_31.3, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.2
 // CHECK:STDOUT:   %addr.loc8_31.2: %ptr.d9e = addr_of %.loc8_31.3
 // CHECK:STDOUT:   %addr.loc8_31.2: %ptr.d9e = addr_of %.loc8_31.3
-// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_31(%addr.loc8_31.2)
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8_31(%addr.loc8_31.2)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
@@ -755,15 +772,15 @@ fn F() {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %pattern_type.217: type = pattern_type %C [concrete]
 // CHECK:STDOUT:   %pattern_type.217: type = pattern_type %C [concrete]
-// CHECK:STDOUT:   %.d40: type = cpp_overload_set_type @C.C [concrete]
+// CHECK:STDOUT:   %.d40: type = cpp_overload_set_type @C.C.1 [concrete]
 // CHECK:STDOUT:   %empty_struct: %.d40 = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.d40 = struct_value () [concrete]
 // CHECK:STDOUT:   %int_8.b85: Core.IntLiteral = int_value 8 [concrete]
 // CHECK:STDOUT:   %int_8.b85: Core.IntLiteral = int_value 8 [concrete]
 // CHECK:STDOUT:   %int_9.988: Core.IntLiteral = int_value 9 [concrete]
 // CHECK:STDOUT:   %int_9.988: Core.IntLiteral = int_value 9 [concrete]
 // CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
 // CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
 // CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
 // CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
 // CHECK:STDOUT:   %ptr.d9e: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %ptr.d9e: type = ptr_type %C [concrete]
-// CHECK:STDOUT:   %C__carbon_thunk.type: type = fn_type @C__carbon_thunk [concrete]
-// CHECK:STDOUT:   %C__carbon_thunk: %C__carbon_thunk.type = struct_value () [concrete]
+// CHECK:STDOUT:   %C__carbon_thunk.type.65f120.1: type = fn_type @C__carbon_thunk.1 [concrete]
+// CHECK:STDOUT:   %C__carbon_thunk.d98342.1: %C__carbon_thunk.type.65f120.1 = struct_value () [concrete]
 // CHECK:STDOUT:   %ImplicitAs.type.d14: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
 // CHECK:STDOUT:   %ImplicitAs.type.d14: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
 // CHECK:STDOUT:   %ImplicitAs.Convert.type.1b6: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%i32) [concrete]
 // CHECK:STDOUT:   %ImplicitAs.Convert.type.1b6: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%i32) [concrete]
 // CHECK:STDOUT:   %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic]
 // CHECK:STDOUT:   %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic]
@@ -781,6 +798,8 @@ fn F() {
 // CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.36a: <bound method> = bound_method %int_9.988, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0 [concrete]
 // CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.36a: <bound method> = bound_method %int_9.988, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0 [concrete]
 // CHECK:STDOUT:   %bound_method.942: <bound method> = bound_method %int_9.988, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
 // CHECK:STDOUT:   %bound_method.942: <bound method> = bound_method %int_9.988, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
 // CHECK:STDOUT:   %int_9.f88: %i32 = int_value 9 [concrete]
 // CHECK:STDOUT:   %int_9.f88: %i32 = int_value 9 [concrete]
+// CHECK:STDOUT:   %C__carbon_thunk.type.65f120.2: type = fn_type @C__carbon_thunk.2 [concrete]
+// CHECK:STDOUT:   %C__carbon_thunk.d98342.2: %C__carbon_thunk.type.65f120.2 = struct_value () [concrete]
 // CHECK:STDOUT:   %As.type.dbd: type = facet_type <@As, @As(%i32)> [concrete]
 // CHECK:STDOUT:   %As.type.dbd: type = facet_type <@As, @As(%i32)> [concrete]
 // CHECK:STDOUT:   %As.Convert.type.99b: type = fn_type @As.Convert, @As(%i32) [concrete]
 // CHECK:STDOUT:   %As.Convert.type.99b: type = fn_type @As.Convert, @As(%i32) [concrete]
 // CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.type.565: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%To) [symbolic]
 // CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.type.565: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%To) [symbolic]
@@ -805,14 +824,19 @@ fn F() {
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
-// CHECK:STDOUT:   %.40b: %.d40 = cpp_overload_set_value @C.C [concrete = constants.%empty_struct]
-// CHECK:STDOUT:   %C__carbon_thunk.decl: %C__carbon_thunk.type = fn_decl @C__carbon_thunk [concrete = constants.%C__carbon_thunk] {
+// CHECK:STDOUT:   %.40b: %.d40 = cpp_overload_set_value @C.C.1 [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %C__carbon_thunk.decl.8acdfe.1: %C__carbon_thunk.type.65f120.1 = fn_decl @C__carbon_thunk.1 [concrete = constants.%C__carbon_thunk.d98342.1] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core.import_ref.ee7: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340) = import_ref Core//prelude/parts/int, loc23_39, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.1c0)]
 // CHECK:STDOUT:   %Core.import_ref.ee7: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340) = import_ref Core//prelude/parts/int, loc23_39, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.1c0)]
 // CHECK:STDOUT:   %ImplicitAs.impl_witness_table.9e9 = impl_witness_table (%Core.import_ref.ee7), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
 // CHECK:STDOUT:   %ImplicitAs.impl_witness_table.9e9 = impl_witness_table (%Core.import_ref.ee7), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
+// CHECK:STDOUT:   %C__carbon_thunk.decl.8acdfe.2: %C__carbon_thunk.type.65f120.2 = fn_decl @C__carbon_thunk.2 [concrete = constants.%C__carbon_thunk.d98342.2] {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core.import_ref.99c: @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert.type (%Core.IntLiteral.as.As.impl.Convert.type.565) = import_ref Core//prelude/parts/int, loc32_39, loaded [symbolic = @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert (constants.%Core.IntLiteral.as.As.impl.Convert.d2c)]
 // CHECK:STDOUT:   %Core.import_ref.99c: @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert.type (%Core.IntLiteral.as.As.impl.Convert.type.565) = import_ref Core//prelude/parts/int, loc32_39, loaded [symbolic = @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert (constants.%Core.IntLiteral.as.As.impl.Convert.d2c)]
 // CHECK:STDOUT:   %As.impl_witness_table.5ad = impl_witness_table (%Core.import_ref.99c), @Core.IntLiteral.as.As.impl [concrete]
 // CHECK:STDOUT:   %As.impl_witness_table.5ad = impl_witness_table (%Core.import_ref.99c), @Core.IntLiteral.as.As.impl [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
@@ -843,8 +867,8 @@ fn F() {
 // CHECK:STDOUT:   %.loc8_30.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_30 [concrete = constants.%int_9.f88]
 // CHECK:STDOUT:   %.loc8_30.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_30 [concrete = constants.%int_9.f88]
 // CHECK:STDOUT:   %.loc8_30.2: %i32 = converted %int_9, %.loc8_30.1 [concrete = constants.%int_9.f88]
 // CHECK:STDOUT:   %.loc8_30.2: %i32 = converted %int_9, %.loc8_30.1 [concrete = constants.%int_9.f88]
 // CHECK:STDOUT:   %addr.loc8_31.1: %ptr.d9e = addr_of %.loc8_31.1
 // CHECK:STDOUT:   %addr.loc8_31.1: %ptr.d9e = addr_of %.loc8_31.1
-// CHECK:STDOUT:   %C__carbon_thunk.call: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%.loc8_27.2, %.loc8_30.2, %addr.loc8_31.1)
-// CHECK:STDOUT:   %.loc8_31.2: init %C = in_place_init %C__carbon_thunk.call, %.loc8_31.1
+// CHECK:STDOUT:   %C__carbon_thunk.call.loc8: init %empty_tuple.type = call imports.%C__carbon_thunk.decl.8acdfe.1(%.loc8_27.2, %.loc8_30.2, %addr.loc8_31.1)
+// CHECK:STDOUT:   %.loc8_31.2: init %C = in_place_init %C__carbon_thunk.call.loc8, %.loc8_31.1
 // CHECK:STDOUT:   %.loc8_14: type = splice_block %C.ref.loc8_14 [concrete = constants.%C] {
 // CHECK:STDOUT:   %.loc8_14: type = splice_block %C.ref.loc8_14 [concrete = constants.%C] {
 // CHECK:STDOUT:     %Cpp.ref.loc8_11: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:     %Cpp.ref.loc8_11: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:     %C.ref.loc8_14: type = name_ref C, imports.%C.decl [concrete = constants.%C]
 // CHECK:STDOUT:     %C.ref.loc8_14: type = name_ref C, imports.%C.decl [concrete = constants.%C]
@@ -855,41 +879,61 @@ fn F() {
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %c2.patt: %pattern_type.217 = binding_pattern c2 [concrete]
 // CHECK:STDOUT:     %c2.patt: %pattern_type.217 = binding_pattern c2 [concrete]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Cpp.ref.loc14_19: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:   %C.ref.loc14_22: type = name_ref C, imports.%C.decl [concrete = constants.%C]
-// CHECK:STDOUT:   %C.ref.loc14_24: %.d40 = name_ref C, imports.%.40b [concrete = constants.%empty_struct]
-// CHECK:STDOUT:   %int_8.loc14: Core.IntLiteral = int_value 8 [concrete = constants.%int_8.b85]
-// CHECK:STDOUT:   %.loc14: type = splice_block %C.ref.loc14_14 [concrete = constants.%C] {
-// CHECK:STDOUT:     %Cpp.ref.loc14_11: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:     %C.ref.loc14_14: type = name_ref C, imports.%C.decl [concrete = constants.%C]
+// CHECK:STDOUT:   %Cpp.ref.loc9_19: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %C.ref.loc9_22: type = name_ref C, imports.%C.decl [concrete = constants.%C]
+// CHECK:STDOUT:   %C.ref.loc9_24: %.d40 = name_ref C, imports.%.40b [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %int_8.loc9: Core.IntLiteral = int_value 8 [concrete = constants.%int_8.b85]
+// CHECK:STDOUT:   %.loc9_28.1: ref %C = temporary_storage
+// CHECK:STDOUT:   %impl.elem0.loc9: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc9_27.1: <bound method> = bound_method %int_8.loc9, %impl.elem0.loc9 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.0ab]
+// CHECK:STDOUT:   %specific_fn.loc9: <specific function> = specific_function %impl.elem0.loc9, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc9_27.2: <bound method> = bound_method %int_8.loc9, %specific_fn.loc9 [concrete = constants.%bound_method.b23]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9: init %i32 = call %bound_method.loc9_27.2(%int_8.loc9) [concrete = constants.%int_8.98c]
+// CHECK:STDOUT:   %.loc9_27.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9 [concrete = constants.%int_8.98c]
+// CHECK:STDOUT:   %.loc9_27.2: %i32 = converted %int_8.loc9, %.loc9_27.1 [concrete = constants.%int_8.98c]
+// CHECK:STDOUT:   %addr.loc9_28.1: %ptr.d9e = addr_of %.loc9_28.1
+// CHECK:STDOUT:   %C__carbon_thunk.call.loc9: init %empty_tuple.type = call imports.%C__carbon_thunk.decl.8acdfe.2(%.loc9_27.2, %addr.loc9_28.1)
+// CHECK:STDOUT:   %.loc9_28.2: init %C = in_place_init %C__carbon_thunk.call.loc9, %.loc9_28.1
+// CHECK:STDOUT:   %.loc9_14: type = splice_block %C.ref.loc9_14 [concrete = constants.%C] {
+// CHECK:STDOUT:     %Cpp.ref.loc9_11: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:     %C.ref.loc9_14: type = name_ref C, imports.%C.decl [concrete = constants.%C]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %c2: %C = bind_name c2, <error> [concrete = <error>]
+// CHECK:STDOUT:   %.loc9_28.3: ref %C = temporary %.loc9_28.1, %.loc9_28.2
+// CHECK:STDOUT:   %.loc9_28.4: %C = bind_value %.loc9_28.3
+// CHECK:STDOUT:   %c2: %C = bind_name c2, %.loc9_28.4
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:   name_binding_decl {
 // CHECK:STDOUT:     %c3.patt: %pattern_type.217 = binding_pattern c3 [concrete]
 // CHECK:STDOUT:     %c3.patt: %pattern_type.217 = binding_pattern c3 [concrete]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %int_8.loc22: Core.IntLiteral = int_value 8 [concrete = constants.%int_8.b85]
+// CHECK:STDOUT:   %int_8.loc17: Core.IntLiteral = int_value 8 [concrete = constants.%int_8.b85]
 // CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
 // 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:   %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
-// CHECK:STDOUT:   %impl.elem0.loc22: %.351 = impl_witness_access constants.%As.impl_witness.080, element0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.414]
-// CHECK:STDOUT:   %bound_method.loc22_21.1: <bound method> = bound_method %int_8.loc22, %impl.elem0.loc22 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound]
-// CHECK:STDOUT:   %specific_fn.loc22: <specific function> = specific_function %impl.elem0.loc22, @Core.IntLiteral.as.As.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.specific_fn]
-// CHECK:STDOUT:   %bound_method.loc22_21.2: <bound method> = bound_method %int_8.loc22, %specific_fn.loc22 [concrete = constants.%bound_method.80c]
-// CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.call: init %i32 = call %bound_method.loc22_21.2(%int_8.loc22) [concrete = constants.%int_8.98c]
-// CHECK:STDOUT:   %.loc22_21.1: %i32 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_8.98c]
-// CHECK:STDOUT:   %.loc22_21.2: %i32 = converted %int_8.loc22, %.loc22_21.1 [concrete = constants.%int_8.98c]
-// CHECK:STDOUT:   %.loc22_14: type = splice_block %C.ref.loc22 [concrete = constants.%C] {
-// CHECK:STDOUT:     %Cpp.ref.loc22: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
-// CHECK:STDOUT:     %C.ref.loc22: type = name_ref C, imports.%C.decl [concrete = constants.%C]
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.loc22_21.3: %C = converted %.loc22_21.2, <error> [concrete = <error>]
+// CHECK:STDOUT:   %impl.elem0.loc17: %.351 = impl_witness_access constants.%As.impl_witness.080, element0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.414]
+// CHECK:STDOUT:   %bound_method.loc17_21.1: <bound method> = bound_method %int_8.loc17, %impl.elem0.loc17 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound]
+// CHECK:STDOUT:   %specific_fn.loc17: <specific function> = specific_function %impl.elem0.loc17, @Core.IntLiteral.as.As.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc17_21.2: <bound method> = bound_method %int_8.loc17, %specific_fn.loc17 [concrete = constants.%bound_method.80c]
+// CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.call: init %i32 = call %bound_method.loc17_21.2(%int_8.loc17) [concrete = constants.%int_8.98c]
+// CHECK:STDOUT:   %.loc17_21.1: %i32 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_8.98c]
+// CHECK:STDOUT:   %.loc17_21.2: %i32 = converted %int_8.loc17, %.loc17_21.1 [concrete = constants.%int_8.98c]
+// CHECK:STDOUT:   %.loc17_14: 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:   }
+// CHECK:STDOUT:   %.loc17_21.3: %C = converted %.loc17_21.2, <error> [concrete = <error>]
 // CHECK:STDOUT:   %c3: %C = bind_name c3, <error> [concrete = <error>]
 // CHECK:STDOUT:   %c3: %C = bind_name c3, <error> [concrete = <error>]
-// CHECK:STDOUT:   %facet_value: %type_where = facet_value constants.%C, () [concrete = constants.%facet_value]
-// CHECK:STDOUT:   %.loc8_31.5: %type_where = converted constants.%C, %facet_value [concrete = constants.%facet_value]
-// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc8_31.3, constants.%AggregateT.as_type.as.Destroy.impl.Op.6b9
+// CHECK:STDOUT:   %facet_value.loc9: %type_where = facet_value constants.%C, () [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %.loc9_28.5: %type_where = converted constants.%C, %facet_value.loc9 [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc9: <bound method> = bound_method %.loc9_28.3, constants.%AggregateT.as_type.as.Destroy.impl.Op.6b9
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT:   %bound_method.loc9_28: <bound method> = bound_method %.loc9_28.3, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.1
+// CHECK:STDOUT:   %addr.loc9_28.2: %ptr.d9e = addr_of %.loc9_28.3
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9_28(%addr.loc9_28.2)
+// CHECK:STDOUT:   %facet_value.loc8: %type_where = facet_value constants.%C, () [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %.loc8_31.5: %type_where = converted constants.%C, %facet_value.loc8 [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc8: <bound method> = bound_method %.loc8_31.3, constants.%AggregateT.as_type.as.Destroy.impl.Op.6b9
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT:   <elided>
-// CHECK:STDOUT:   %bound_method.loc8_31: <bound method> = bound_method %.loc8_31.3, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %bound_method.loc8_31: <bound method> = bound_method %.loc8_31.3, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.2
 // CHECK:STDOUT:   %addr.loc8_31.2: %ptr.d9e = addr_of %.loc8_31.3
 // CHECK:STDOUT:   %addr.loc8_31.2: %ptr.d9e = addr_of %.loc8_31.3
-// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc8_31(%addr.loc8_31.2)
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8_31(%addr.loc8_31.2)
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:

+ 2 - 2
toolchain/check/testdata/interop/cpp/enum/anonymous.carbon

@@ -43,9 +43,9 @@ fn G() {
 // CHECK:STDOUT:   %G.type: type = fn_type @G [concrete]
 // CHECK:STDOUT:   %G.type: type = fn_type @G [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %G: %G.type = struct_value () [concrete]
 // CHECK:STDOUT:   %G: %G.type = struct_value () [concrete]
-// CHECK:STDOUT:   %.4f0: type = class_type @.1 [concrete]
 // CHECK:STDOUT:   %.557: type = cpp_overload_set_type @C.C [concrete]
 // CHECK:STDOUT:   %.557: type = cpp_overload_set_type @C.C [concrete]
 // CHECK:STDOUT:   %empty_struct.56a: %.557 = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct.56a: %.557 = struct_value () [concrete]
+// CHECK:STDOUT:   %.4f0: type = class_type @.1 [concrete]
 // CHECK:STDOUT:   %int_1.81a: %.4f0 = int_value 1 [concrete]
 // CHECK:STDOUT:   %int_1.81a: %.4f0 = int_value 1 [concrete]
 // CHECK:STDOUT:   %ptr.793: type = ptr_type %.4f0 [concrete]
 // CHECK:STDOUT:   %ptr.793: type = ptr_type %.4f0 [concrete]
 // CHECK:STDOUT:   %F__carbon_thunk.type.eda1ac.1: type = fn_type @F__carbon_thunk.1 [concrete]
 // CHECK:STDOUT:   %F__carbon_thunk.type.eda1ac.1: type = fn_type @F__carbon_thunk.1 [concrete]
@@ -56,9 +56,9 @@ fn G() {
 // CHECK:STDOUT:   %ptr.d9e: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %ptr.d9e: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %C__carbon_thunk.type: type = fn_type @C__carbon_thunk [concrete]
 // CHECK:STDOUT:   %C__carbon_thunk.type: type = fn_type @C__carbon_thunk [concrete]
 // CHECK:STDOUT:   %C__carbon_thunk: %C__carbon_thunk.type = struct_value () [concrete]
 // CHECK:STDOUT:   %C__carbon_thunk: %C__carbon_thunk.type = struct_value () [concrete]
-// CHECK:STDOUT:   %.bb7: type = class_type @.2 [concrete]
 // CHECK:STDOUT:   %.9a3: type = cpp_overload_set_type @C.F [concrete]
 // CHECK:STDOUT:   %.9a3: type = cpp_overload_set_type @C.F [concrete]
 // CHECK:STDOUT:   %empty_struct.acc: %.9a3 = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct.acc: %.9a3 = struct_value () [concrete]
+// CHECK:STDOUT:   %.bb7: type = class_type @.2 [concrete]
 // CHECK:STDOUT:   %int_1.1d6: %.bb7 = int_value 1 [concrete]
 // CHECK:STDOUT:   %int_1.1d6: %.bb7 = int_value 1 [concrete]
 // CHECK:STDOUT:   %ptr.73d: type = ptr_type %.bb7 [concrete]
 // CHECK:STDOUT:   %ptr.73d: type = ptr_type %.bb7 [concrete]
 // CHECK:STDOUT:   %F__carbon_thunk.type.eda1ac.2: type = fn_type @F__carbon_thunk.2 [concrete]
 // CHECK:STDOUT:   %F__carbon_thunk.type.eda1ac.2: type = fn_type @F__carbon_thunk.2 [concrete]

+ 0 - 107
toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon

@@ -386,34 +386,6 @@ fn F() {
   //@dump-sem-ir-end
   //@dump-sem-ir-end
 }
 }
 
 
-// ============================================================================
-// const short param
-// ============================================================================
-
-// --- const_short_param.h
-
-auto foo(const short a) -> void;
-
-// --- fail_todo_import_const_short_param.carbon
-
-library "[[@TEST_NAME]]";
-
-import Cpp library "const_short_param.h";
-
-fn F() {
-  //@dump-sem-ir-begin
-  // CHECK:STDERR: fail_todo_import_const_short_param.carbon:[[@LINE+8]]:11: error: cannot implicitly convert expression of type `i16` to `const i16` [ConversionFailure]
-  // CHECK:STDERR:   Cpp.foo(1 as i16);
-  // CHECK:STDERR:           ^~~~~~~~
-  // CHECK:STDERR: fail_todo_import_const_short_param.carbon:[[@LINE+5]]:11: note: type `i16` does not implement interface `Core.ImplicitAs(const i16)` [MissingImplInMemberAccessNote]
-  // CHECK:STDERR:   Cpp.foo(1 as i16);
-  // CHECK:STDERR:           ^~~~~~~~
-  // CHECK:STDERR: fail_todo_import_const_short_param.carbon: note: initializing function parameter [InCallToFunctionParam]
-  // CHECK:STDERR:
-  Cpp.foo(1 as i16);
-  //@dump-sem-ir-end
-}
-
 // ============================================================================
 // ============================================================================
 // _Float16 param
 // _Float16 param
 // ============================================================================
 // ============================================================================
@@ -1738,85 +1710,6 @@ fn F() {
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_import_const_short_param.carbon
-// CHECK:STDOUT:
-// CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @As.Convert [concrete]
-// CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
-// CHECK:STDOUT:   %int_1.5b8: Core.IntLiteral = int_value 1 [concrete]
-// CHECK:STDOUT:   %int_16: Core.IntLiteral = int_value 16 [concrete]
-// CHECK:STDOUT:   %i16: type = class_type @Int, @Int(%int_16) [concrete]
-// CHECK:STDOUT:   %As.type.771: type = facet_type <@As, @As(%i16)> [concrete]
-// CHECK:STDOUT:   %As.Convert.type.be5: type = fn_type @As.Convert, @As(%i16) [concrete]
-// CHECK:STDOUT:   %To.c80: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic]
-// CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.type.565: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%To.c80) [symbolic]
-// CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.d2c: %Core.IntLiteral.as.As.impl.Convert.type.565 = struct_value () [symbolic]
-// CHECK:STDOUT:   %As.impl_witness.2d2: <witness> = impl_witness imports.%As.impl_witness_table.5ad, @Core.IntLiteral.as.As.impl(%int_16) [concrete]
-// CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.type.38a: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%int_16) [concrete]
-// CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.97a: %Core.IntLiteral.as.As.impl.Convert.type.38a = struct_value () [concrete]
-// CHECK:STDOUT:   %As.facet: %As.type.771 = facet_value Core.IntLiteral, (%As.impl_witness.2d2) [concrete]
-// CHECK:STDOUT:   %.026: type = fn_type_with_self_type %As.Convert.type.be5, %As.facet [concrete]
-// CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.bound: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.As.impl.Convert.97a [concrete]
-// CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.As.impl.Convert.97a, @Core.IntLiteral.as.As.impl.Convert(%int_16) [concrete]
-// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.As.impl.Convert.specific_fn [concrete]
-// CHECK:STDOUT:   %int_1.f90: %i16 = int_value 1 [concrete]
-// CHECK:STDOUT:   %const.660: type = const_type %i16 [concrete]
-// CHECK:STDOUT:   %ptr.758: type = ptr_type %const.660 [concrete]
-// CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
-// CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
-// CHECK:STDOUT:   %Copy.type: type = facet_type <@Copy> [concrete]
-// CHECK:STDOUT:   %Copy.impl_witness.51d: <witness> = impl_witness imports.%Copy.impl_witness_table.965, @Int.as.Copy.impl(%int_16) [concrete]
-// CHECK:STDOUT:   %Copy.facet.b88: %Copy.type = facet_value %i16, (%Copy.impl_witness.51d) [concrete]
-// CHECK:STDOUT:   %Copy.impl_witness.184: <witness> = impl_witness imports.%Copy.impl_witness_table.df8, @const.as.Copy.impl(%Copy.facet.b88) [concrete]
-// CHECK:STDOUT:   %Copy.facet.61b: %Copy.type = facet_value %const.660, (%Copy.impl_witness.184) [concrete]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
-// CHECK:STDOUT:     .foo = %.a21
-// CHECK:STDOUT:     import Cpp//...
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @As.Convert [concrete = constants.%empty_struct]
-// CHECK:STDOUT:   %Core.import_ref.99c: @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert.type (%Core.IntLiteral.as.As.impl.Convert.type.565) = import_ref Core//prelude/types/int, loc36_39, loaded [symbolic = @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert (constants.%Core.IntLiteral.as.As.impl.Convert.d2c)]
-// CHECK:STDOUT:   %As.impl_witness_table.5ad = impl_witness_table (%Core.import_ref.99c), @Core.IntLiteral.as.As.impl [concrete]
-// CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
-// CHECK:STDOUT:     <elided>
-// CHECK:STDOUT:   } {
-// CHECK:STDOUT:     <elided>
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import_ref.71e = import_ref Core//prelude/copy, loc19_31, unloaded
-// CHECK:STDOUT:   %Copy.impl_witness_table.df8 = impl_witness_table (%Core.import_ref.71e), @const.as.Copy.impl [concrete]
-// CHECK:STDOUT:   %Core.import_ref.2eb = import_ref Core//prelude/types/int, loc21_31, unloaded
-// CHECK:STDOUT:   %Copy.impl_witness_table.965 = impl_witness_table (%Core.import_ref.2eb), @Int.as.Copy.impl [concrete]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: 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:   %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
-// CHECK:STDOUT:   %int_16: Core.IntLiteral = int_value 16 [concrete = constants.%int_16]
-// CHECK:STDOUT:   %i16: type = class_type @Int, @Int(constants.%int_16) [concrete = constants.%i16]
-// CHECK:STDOUT:   %impl.elem0: %.026 = impl_witness_access constants.%As.impl_witness.2d2, element0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.97a]
-// CHECK:STDOUT:   %bound_method.loc16_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_16) [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.specific_fn]
-// CHECK:STDOUT:   %bound_method.loc16_13.2: <bound method> = bound_method %int_1, %specific_fn [concrete = constants.%bound_method]
-// CHECK:STDOUT:   %Core.IntLiteral.as.As.impl.Convert.call: init %i16 = call %bound_method.loc16_13.2(%int_1) [concrete = constants.%int_1.f90]
-// CHECK:STDOUT:   %.loc16_13.1: %i16 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_1.f90]
-// CHECK:STDOUT:   %.loc16_13.2: %i16 = converted %int_1, %.loc16_13.1 [concrete = constants.%int_1.f90]
-// CHECK:STDOUT:   %Copy.facet.loc16_13.1: %Copy.type = facet_value constants.%i16, (constants.%Copy.impl_witness.51d) [concrete = constants.%Copy.facet.b88]
-// CHECK:STDOUT:   %.loc16_13.3: %Copy.type = converted constants.%i16, %Copy.facet.loc16_13.1 [concrete = constants.%Copy.facet.b88]
-// CHECK:STDOUT:   %Copy.facet.loc16_13.2: %Copy.type = facet_value constants.%const.660, (constants.%Copy.impl_witness.184) [concrete = constants.%Copy.facet.61b]
-// CHECK:STDOUT:   %.loc16_13.4: %Copy.type = converted constants.%const.660, %Copy.facet.loc16_13.2 [concrete = constants.%Copy.facet.61b]
-// CHECK:STDOUT:   %Copy.facet.loc16_13.3: %Copy.type = facet_value constants.%i16, (constants.%Copy.impl_witness.51d) [concrete = constants.%Copy.facet.b88]
-// CHECK:STDOUT:   %.loc16_13.5: %Copy.type = converted constants.%i16, %Copy.facet.loc16_13.3 [concrete = constants.%Copy.facet.b88]
-// CHECK:STDOUT:   %.loc16_13.6: %const.660 = converted %.loc16_13.2, <error> [concrete = <error>]
-// CHECK:STDOUT:   %addr: %ptr.758 = addr_of <error> [concrete = <error>]
-// CHECK:STDOUT:   %foo__carbon_thunk.call: init %empty_tuple.type = call imports.%foo__carbon_thunk.decl(%addr)
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: --- import_float16_param.carbon
 // CHECK:STDOUT: --- import_float16_param.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {

+ 0 - 182
toolchain/check/testdata/interop/cpp/function/arithmetic_types_direct.carbon

@@ -262,71 +262,6 @@ fn F() {
   //@dump-sem-ir-end
   //@dump-sem-ir-end
 }
 }
 
 
-// ============================================================================
-// int default param
-// ============================================================================
-
-// --- int_default_param.h
-
-auto foo(int a = 0) -> void;
-
-// --- fail_todo_import_int_default_parameterless_call.carbon
-
-library "[[@TEST_NAME]]";
-
-import Cpp library "int_default_param.h";
-
-fn F() {
-  //@dump-sem-ir-begin
-  // CHECK:STDERR: fail_todo_import_int_default_parameterless_call.carbon:[[@LINE+5]]:3: error: 0 arguments passed to function expecting 1 argument [CallArgCountMismatch]
-  // CHECK:STDERR:   Cpp.foo();
-  // CHECK:STDERR:   ^~~~~~~~~
-  // CHECK:STDERR: fail_todo_import_int_default_parameterless_call.carbon: note: calling function declared here [InCallToEntity]
-  // CHECK:STDERR:
-  Cpp.foo();
-  //@dump-sem-ir-end
-}
-
-// --- import_int_default_param_sucessful_call.carbon
-
-library "[[@TEST_NAME]]";
-
-import Cpp library "int_default_param.h";
-
-fn F() {
-  //@dump-sem-ir-begin
-  Cpp.foo(1);
-  //@dump-sem-ir-end
-}
-
-// ============================================================================
-// const int param
-// ============================================================================
-
-// --- const_int_param.h
-
-auto foo(const int a) -> void;
-
-// --- fail_todo_import_const_int_param.carbon
-
-library "[[@TEST_NAME]]";
-
-import Cpp library "const_int_param.h";
-
-fn F() {
-  //@dump-sem-ir-begin
-  // CHECK:STDERR: fail_todo_import_const_int_param.carbon:[[@LINE+8]]:11: error: cannot implicitly convert expression of type `Core.IntLiteral` to `const i32` [ConversionFailure]
-  // CHECK:STDERR:   Cpp.foo(1);
-  // CHECK:STDERR:           ^
-  // CHECK:STDERR: fail_todo_import_const_int_param.carbon:[[@LINE+5]]:11: note: type `Core.IntLiteral` does not implement interface `Core.ImplicitAs(const i32)` [MissingImplInMemberAccessNote]
-  // CHECK:STDERR:   Cpp.foo(1);
-  // CHECK:STDERR:           ^
-  // CHECK:STDERR: fail_todo_import_const_int_param.carbon: note: initializing function parameter [InCallToFunctionParam]
-  // CHECK:STDERR:
-  Cpp.foo(1);
-  //@dump-sem-ir-end
-}
-
 // ============================================================================
 // ============================================================================
 // int return
 // int return
 // ============================================================================
 // ============================================================================
@@ -1028,123 +963,6 @@ fn F() {
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_import_int_default_parameterless_call.carbon
-// CHECK:STDOUT:
-// CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
-// CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
-// CHECK:STDOUT:     .foo = %.a21
-// CHECK:STDOUT:     import Cpp//...
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: 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:   <elided>
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: --- import_int_default_param_sucessful_call.carbon
-// CHECK:STDOUT:
-// CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
-// CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
-// CHECK:STDOUT:   %int_1.5b8: Core.IntLiteral = int_value 1 [concrete]
-// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
-// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
-// CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
-// CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
-// CHECK:STDOUT:   %ImplicitAs.type.d14: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
-// CHECK:STDOUT:   %ImplicitAs.Convert.type.1b6: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%i32) [concrete]
-// CHECK:STDOUT:   %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic]
-// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic]
-// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.1c0: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340 = struct_value () [symbolic]
-// CHECK:STDOUT:   %ImplicitAs.impl_witness.204: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.9e9, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
-// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.584: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
-// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.584 = struct_value () [concrete]
-// CHECK:STDOUT:   %ImplicitAs.facet: %ImplicitAs.type.d14 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.204) [concrete]
-// CHECK:STDOUT:   %.1df: type = fn_type_with_self_type %ImplicitAs.Convert.type.1b6, %ImplicitAs.facet [concrete]
-// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0 [concrete]
-// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
-// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
-// CHECK:STDOUT:   %int_1.5d2: %i32 = int_value 1 [concrete]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
-// CHECK:STDOUT:     .foo = %.a21
-// CHECK:STDOUT:     import Cpp//...
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
-// CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {
-// CHECK:STDOUT:     <elided>
-// CHECK:STDOUT:   } {
-// CHECK:STDOUT:     <elided>
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %Core.import_ref.ee7: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340) = import_ref Core//prelude/parts/int, loc23_39, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.1c0)]
-// CHECK:STDOUT:   %ImplicitAs.impl_witness_table.9e9 = impl_witness_table (%Core.import_ref.ee7), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: 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:   %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
-// CHECK:STDOUT:   %impl.elem0: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
-// CHECK:STDOUT:   %bound_method.loc8_11.1: <bound method> = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound]
-// CHECK:STDOUT:   %specific_fn: <specific function> = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
-// CHECK:STDOUT:   %bound_method.loc8_11.2: <bound method> = bound_method %int_1, %specific_fn [concrete = constants.%bound_method]
-// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc8_11.2(%int_1) [concrete = constants.%int_1.5d2]
-// CHECK:STDOUT:   %.loc8_11.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.5d2]
-// CHECK:STDOUT:   %.loc8_11.2: %i32 = converted %int_1, %.loc8_11.1 [concrete = constants.%int_1.5d2]
-// CHECK:STDOUT:   %foo.call: init %empty_tuple.type = call imports.%foo.decl(%.loc8_11.2)
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: --- fail_todo_import_const_int_param.carbon
-// CHECK:STDOUT:
-// CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
-// CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
-// CHECK:STDOUT:   %int_1: Core.IntLiteral = int_value 1 [concrete]
-// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
-// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
-// CHECK:STDOUT:   %const: type = const_type %i32 [concrete]
-// CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
-// CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
-// CHECK:STDOUT:     .foo = %.a21
-// CHECK:STDOUT:     import Cpp//...
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
-// CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {
-// CHECK:STDOUT:     <elided>
-// CHECK:STDOUT:   } {
-// CHECK:STDOUT:     <elided>
-// CHECK:STDOUT:   }
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
-// CHECK:STDOUT: 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:   %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1]
-// CHECK:STDOUT:   %.loc16: %const = converted %int_1, <error> [concrete = <error>]
-// CHECK:STDOUT:   %foo.call: init %empty_tuple.type = call imports.%foo.decl(<error>)
-// CHECK:STDOUT:   <elided>
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: --- import_int_return.carbon
 // CHECK:STDOUT: --- import_int_return.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {

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

@@ -526,10 +526,10 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %C.val: %C = struct_value () [concrete]
 // CHECK:STDOUT:   %C.val: %C = struct_value () [concrete]
 // CHECK:STDOUT:   %ptr.d9e: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %ptr.d9e: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
@@ -546,8 +546,8 @@ fn F() {
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
@@ -584,10 +584,10 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %C.val: %C = struct_value () [concrete]
 // CHECK:STDOUT:   %C.val: %C = struct_value () [concrete]
 // CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
 // CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
 // CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
 // CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
@@ -604,8 +604,8 @@ fn F() {
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
 // CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {
 // CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
@@ -639,10 +639,10 @@ fn F() {
 // CHECK:STDOUT: --- fail_todo_import_definition_single_data_member_value_param_type.carbon
 // CHECK:STDOUT: --- fail_todo_import_definition_single_data_member_value_param_type.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @As.Convert [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @As.Convert [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT: imports {
@@ -651,8 +651,8 @@ fn F() {
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @As.Convert [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @As.Convert [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F() {
 // CHECK:STDOUT: fn @F() {
@@ -669,10 +669,10 @@ fn F() {
 // CHECK:STDOUT: --- fail_todo_import_definition_multiple_data_members_value_param_type.carbon
 // CHECK:STDOUT: --- fail_todo_import_definition_multiple_data_members_value_param_type.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @As.Convert [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @As.Convert [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT: imports {
@@ -681,8 +681,8 @@ fn F() {
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @As.Convert [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @As.Convert [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F() {
 // CHECK:STDOUT: fn @F() {
@@ -700,10 +700,10 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %C.val: %C = struct_value () [concrete]
 // CHECK:STDOUT:   %C.val: %C = struct_value () [concrete]
 // CHECK:STDOUT:   %pattern_type.69f: type = pattern_type %C [concrete]
 // CHECK:STDOUT:   %pattern_type.69f: type = pattern_type %C [concrete]
 // CHECK:STDOUT:   %ptr.838: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %ptr.838: type = ptr_type %C [concrete]
@@ -721,12 +721,12 @@ fn F() {
 // CHECK:STDOUT:     .N = %N
 // CHECK:STDOUT:     .N = %N
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %N: <namespace> = namespace [concrete] {
 // CHECK:STDOUT:   %N: <namespace> = namespace [concrete] {
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
-// CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
@@ -782,10 +782,10 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %.92e: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.92e: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.92e = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.92e = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %C.val: %C = struct_value () [concrete]
 // CHECK:STDOUT:   %C.val: %C = struct_value () [concrete]
 // CHECK:STDOUT:   %ptr.c0c: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %ptr.c0c: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
@@ -806,12 +806,12 @@ fn F() {
 // CHECK:STDOUT:     .N2 = %N2
 // CHECK:STDOUT:     .N2 = %N2
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.218: %.92e = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %N2: <namespace> = namespace [concrete] {
 // CHECK:STDOUT:   %N2: <namespace> = namespace [concrete] {
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
-// CHECK:STDOUT:   %.218: %.92e = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
@@ -851,11 +851,11 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %O: type = class_type @O [concrete]
-// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %O: type = class_type @O [concrete]
+// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %C.val: %C = struct_value () [concrete]
 // CHECK:STDOUT:   %C.val: %C = struct_value () [concrete]
 // CHECK:STDOUT:   %ptr.de2: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %ptr.de2: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
@@ -877,9 +877,9 @@ fn F() {
 // CHECK:STDOUT:     .O = %O.decl
 // CHECK:STDOUT:     .O = %O.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %O.decl: type = class_decl @O [concrete = constants.%O] {} {}
 // CHECK:STDOUT:   %O.decl: type = class_decl @O [concrete = constants.%O] {} {}
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
 // CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
-// CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
@@ -1002,10 +1002,10 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %empty_struct.109: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct.109: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %C.val: %C = struct_value () [concrete]
 // CHECK:STDOUT:   %C.val: %C = struct_value () [concrete]
 // CHECK:STDOUT:   %ptr.d9e: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %ptr.d9e: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
@@ -1026,8 +1026,8 @@ fn F() {
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:     .C = %C.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo__carbon_thunk [concrete = constants.%empty_struct.109]
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo__carbon_thunk [concrete = constants.%empty_struct.109]
+// CHECK:STDOUT:   %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
@@ -1142,9 +1142,9 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
+// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %ptr.d9e: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %ptr.d9e: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
@@ -1189,9 +1189,9 @@ fn F() {
 // CHECK:STDOUT: --- import_decl_pointer_return_type.carbon
 // CHECK:STDOUT: --- import_decl_pointer_return_type.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
+// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %ptr: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %ptr: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
 // CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
 // CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
 // CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
@@ -1221,9 +1221,9 @@ fn F() {
 // CHECK:STDOUT: --- import_definition_pointer_return_type.carbon
 // CHECK:STDOUT: --- import_definition_pointer_return_type.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
+// CHECK:STDOUT:   %C: type = class_type @C [concrete]
 // CHECK:STDOUT:   %ptr: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %ptr: type = ptr_type %C [concrete]
 // CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
 // CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
 // CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
 // CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]

+ 196 - 0
toolchain/check/testdata/interop/cpp/function/decayed_param.carbon

@@ -0,0 +1,196 @@
+// 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-FILE: toolchain/testing/testdata/min_prelude/int.carbon
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/function/decayed_param.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/function/decayed_param.carbon
+
+// --- params.h
+
+void TakesArray(int arr[42]);
+void TakesFunction(int f(int));
+
+int Function(int);
+
+// --- fail_todo_call_params.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "params.h";
+
+fn G(n: i32) -> i32;
+
+fn F() {
+  //@dump-sem-ir-begin
+  var n: array(i32, 42);
+  // CHECK:STDERR: fail_todo_call_params.carbon:[[@LINE+4]]:3: error: semantics TODO: `Unsupported: parameter type: int *` [SemanticsTodo]
+  // CHECK:STDERR:   Cpp.TakesArray(&n[0]);
+  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR:
+  Cpp.TakesArray(&n[0]);
+
+  // CHECK:STDERR: fail_todo_call_params.carbon:[[@LINE+4]]:18: error: member name `nullptr` not found in `Cpp` [MemberNameNotFoundInInstScope]
+  // CHECK:STDERR:   Cpp.TakesArray(Cpp.nullptr);
+  // CHECK:STDERR:                  ^~~~~~~~~~~
+  // CHECK:STDERR:
+  Cpp.TakesArray(Cpp.nullptr);
+
+  // CHECK:STDERR: fail_todo_call_params.carbon:[[@LINE+4]]:21: error: call argument of type `<type of G>` is not supported [CppCallArgTypeNotSupported]
+  // CHECK:STDERR:   Cpp.TakesFunction(G);
+  // CHECK:STDERR:                     ^
+  // CHECK:STDERR:
+  Cpp.TakesFunction(G);
+
+  // CHECK:STDERR: fail_todo_call_params.carbon:[[@LINE+4]]:21: error: call argument of type `<type of Cpp.Function>` is not supported [CppCallArgTypeNotSupported]
+  // CHECK:STDERR:   Cpp.TakesFunction(Cpp.Function);
+  // CHECK:STDERR:                     ^~~~~~~~~~~~
+  // CHECK:STDERR:
+  Cpp.TakesFunction(Cpp.Function);
+
+  // CHECK:STDERR: fail_todo_call_params.carbon:[[@LINE+8]]:26: error: no matching function for call to 'TakesFunction' [CppInteropParseError]
+  // CHECK:STDERR:    43 |   Cpp.TakesFunction(&n[0]);
+  // CHECK:STDERR:       |                          ^
+  // CHECK:STDERR: fail_todo_call_params.carbon:[[@LINE-34]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./params.h:3:6: note: candidate function not viable: no known conversion from 'int * _Nonnull' to 'int (*)(int)' for 1st argument [CppInteropParseNote]
+  // CHECK:STDERR:     3 | void TakesFunction(int f(int));
+  // CHECK:STDERR:       |      ^             ~~~~~~~~~~
+  // CHECK:STDERR:
+  Cpp.TakesFunction(&n[0]);
+
+  // CHECK:STDERR: fail_todo_call_params.carbon:[[@LINE+4]]:21: error: member name `nullptr` not found in `Cpp` [MemberNameNotFoundInInstScope]
+  // CHECK:STDERR:   Cpp.TakesFunction(Cpp.nullptr);
+  // CHECK:STDERR:                     ^~~~~~~~~~~
+  // CHECK:STDERR:
+  Cpp.TakesFunction(Cpp.nullptr);
+  //@dump-sem-ir-end
+}
+
+// CHECK:STDOUT: --- fail_todo_call_params.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
+// CHECK:STDOUT:   %G.type: type = fn_type @G [concrete]
+// CHECK:STDOUT:   %G: %G.type = struct_value () [concrete]
+// CHECK:STDOUT:   %int_42: Core.IntLiteral = int_value 42 [concrete]
+// CHECK:STDOUT:   %array_type: type = array_type %int_42, %i32 [concrete]
+// CHECK:STDOUT:   %ptr.830: type = ptr_type %array_type [concrete]
+// CHECK:STDOUT:   %pattern_type.b6e: type = pattern_type %array_type [concrete]
+// CHECK:STDOUT:   %.cdf: type = cpp_overload_set_type @Core.IntLiteral.as.ImplicitAs.impl.Convert [concrete]
+// CHECK:STDOUT:   %empty_struct.5c5: %.cdf = struct_value () [concrete]
+// CHECK:STDOUT:   %int_0.5c6: Core.IntLiteral = int_value 0 [concrete]
+// CHECK:STDOUT:   %ImplicitAs.type.d14: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
+// CHECK:STDOUT:   %ImplicitAs.Convert.type.1b6: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%i32) [concrete]
+// CHECK:STDOUT:   %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.1c0: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340 = struct_value () [symbolic]
+// CHECK:STDOUT:   %ImplicitAs.impl_witness.204: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.9e9, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.584: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.584 = struct_value () [concrete]
+// CHECK:STDOUT:   %ImplicitAs.facet: %ImplicitAs.type.d14 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.204) [concrete]
+// CHECK:STDOUT:   %.1df: type = fn_type_with_self_type %ImplicitAs.Convert.type.1b6, %ImplicitAs.facet [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0 [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %int_0.6a9: %i32 = int_value 0 [concrete]
+// CHECK:STDOUT:   %ptr.235: type = ptr_type %i32 [concrete]
+// CHECK:STDOUT:   %.319: type = cpp_overload_set_type @Int.as.ImplicitAs.impl.Convert [concrete]
+// CHECK:STDOUT:   %empty_struct.45a: %.319 = struct_value () [concrete]
+// CHECK:STDOUT:   %.001: type = cpp_overload_set_type @Destroy.Op [concrete]
+// CHECK:STDOUT:   %empty_struct.89b: %.001 = struct_value () [concrete]
+// CHECK:STDOUT:   %type_where: type = facet_type <type where .Self impls <CanAggregateDestroy>> [concrete]
+// CHECK:STDOUT:   %facet_value: %type_where = facet_value %array_type, () [concrete]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.type.b6e: type = fn_type @AggregateT.as_type.as.Destroy.impl.Op, @AggregateT.as_type.as.Destroy.impl(%facet_value) [concrete]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.91f: %AggregateT.as_type.as.Destroy.impl.Op.type.b6e = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .TakesArray = %.782
+// CHECK:STDOUT:     .nullptr = <poisoned>
+// CHECK:STDOUT:     .TakesFunction = %.a02
+// CHECK:STDOUT:     .Function = %.5ec
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.782: %.cdf = cpp_overload_set_value @Core.IntLiteral.as.ImplicitAs.impl.Convert [concrete = constants.%empty_struct.5c5]
+// CHECK:STDOUT:   %Core.import_ref.ee7: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340) = import_ref Core//prelude/parts/int, loc23_39, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.1c0)]
+// CHECK:STDOUT:   %ImplicitAs.impl_witness_table.9e9 = impl_witness_table (%Core.import_ref.ee7), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
+// CHECK:STDOUT:   %.a02: %.319 = cpp_overload_set_value @Int.as.ImplicitAs.impl.Convert [concrete = constants.%empty_struct.45a]
+// CHECK:STDOUT:   %.5ec: %.001 = cpp_overload_set_value @Destroy.Op [concrete = constants.%empty_struct.89b]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %n.patt: %pattern_type.b6e = binding_pattern n [concrete]
+// CHECK:STDOUT:     %n.var_patt: %pattern_type.b6e = var_pattern %n.patt [concrete]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %n.var: ref %array_type = var %n.var_patt
+// CHECK:STDOUT:   %.loc10_23: type = splice_block %array_type [concrete = constants.%array_type] {
+// CHECK:STDOUT:     %int_32.loc10: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:     %i32.loc10: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
+// CHECK:STDOUT:     %int_42: Core.IntLiteral = int_value 42 [concrete = constants.%int_42]
+// CHECK:STDOUT:     %array_type: type = array_type %int_42, %i32.loc10 [concrete = constants.%array_type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %n: ref %array_type = bind_name n, %n.var
+// CHECK:STDOUT:   %Cpp.ref.loc15: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %TakesArray.ref.loc15: %.cdf = name_ref TakesArray, imports.%.782 [concrete = constants.%empty_struct.5c5]
+// CHECK:STDOUT:   %n.ref.loc15: ref %array_type = name_ref n, %n
+// CHECK:STDOUT:   %int_0.loc15: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6]
+// CHECK:STDOUT:   %int_32.loc15: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:   %i32.loc15: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
+// CHECK:STDOUT:   %impl.elem0.loc15: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc15_21.1: <bound method> = bound_method %int_0.loc15, %impl.elem0.loc15 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound]
+// CHECK:STDOUT:   %specific_fn.loc15: <specific function> = specific_function %impl.elem0.loc15, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc15_21.2: <bound method> = bound_method %int_0.loc15, %specific_fn.loc15 [concrete = constants.%bound_method]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc15: init %i32 = call %bound_method.loc15_21.2(%int_0.loc15) [concrete = constants.%int_0.6a9]
+// CHECK:STDOUT:   %.loc15_21.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc15 [concrete = constants.%int_0.6a9]
+// CHECK:STDOUT:   %.loc15_21.2: %i32 = converted %int_0.loc15, %.loc15_21.1 [concrete = constants.%int_0.6a9]
+// CHECK:STDOUT:   %.loc15_22: ref %i32 = array_index %n.ref.loc15, %.loc15_21.2
+// CHECK:STDOUT:   %addr.loc15: %ptr.235 = addr_of %.loc15_22
+// CHECK:STDOUT:   %Cpp.ref.loc21_3: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %TakesArray.ref.loc21: %.cdf = name_ref TakesArray, imports.%.782 [concrete = constants.%empty_struct.5c5]
+// CHECK:STDOUT:   %Cpp.ref.loc21_18: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %nullptr.ref.loc21: <error> = name_ref nullptr, <error> [concrete = <error>]
+// CHECK:STDOUT:   %Cpp.ref.loc27: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %TakesFunction.ref.loc27: %.319 = name_ref TakesFunction, imports.%.a02 [concrete = constants.%empty_struct.45a]
+// CHECK:STDOUT:   %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G]
+// CHECK:STDOUT:   %Cpp.ref.loc33_3: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %TakesFunction.ref.loc33: %.319 = name_ref TakesFunction, imports.%.a02 [concrete = constants.%empty_struct.45a]
+// CHECK:STDOUT:   %Cpp.ref.loc33_21: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Function.ref: %.001 = name_ref Function, imports.%.5ec [concrete = constants.%empty_struct.89b]
+// CHECK:STDOUT:   %Cpp.ref.loc43: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %TakesFunction.ref.loc43: %.319 = name_ref TakesFunction, imports.%.a02 [concrete = constants.%empty_struct.45a]
+// CHECK:STDOUT:   %n.ref.loc43: ref %array_type = name_ref n, %n
+// CHECK:STDOUT:   %int_0.loc43: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6]
+// CHECK:STDOUT:   %int_32.loc43: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:   %i32.loc43: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
+// CHECK:STDOUT:   %impl.elem0.loc43: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc43_24.1: <bound method> = bound_method %int_0.loc43, %impl.elem0.loc43 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound]
+// CHECK:STDOUT:   %specific_fn.loc43: <specific function> = specific_function %impl.elem0.loc43, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc43_24.2: <bound method> = bound_method %int_0.loc43, %specific_fn.loc43 [concrete = constants.%bound_method]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc43: init %i32 = call %bound_method.loc43_24.2(%int_0.loc43) [concrete = constants.%int_0.6a9]
+// CHECK:STDOUT:   %.loc43_24.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc43 [concrete = constants.%int_0.6a9]
+// CHECK:STDOUT:   %.loc43_24.2: %i32 = converted %int_0.loc43, %.loc43_24.1 [concrete = constants.%int_0.6a9]
+// CHECK:STDOUT:   %.loc43_25: ref %i32 = array_index %n.ref.loc43, %.loc43_24.2
+// CHECK:STDOUT:   %addr.loc43: %ptr.235 = addr_of %.loc43_25
+// CHECK:STDOUT:   %Cpp.ref.loc49_3: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %TakesFunction.ref.loc49: %.319 = name_ref TakesFunction, imports.%.a02 [concrete = constants.%empty_struct.45a]
+// CHECK:STDOUT:   %Cpp.ref.loc49_21: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %nullptr.ref.loc49: <error> = name_ref nullptr, <error> [concrete = <error>]
+// CHECK:STDOUT:   %facet_value: %type_where = facet_value constants.%array_type, () [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %.loc10_3: %type_where = converted constants.%array_type, %facet_value [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound: <bound method> = bound_method %n.var, constants.%AggregateT.as_type.as.Destroy.impl.Op.91f
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT:   %bound_method.loc10: <bound method> = bound_method %n.var, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %addr.loc10: %ptr.830 = addr_of %n.var
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc10(%addr.loc10)
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 761 - 0
toolchain/check/testdata/interop/cpp/function/default_arg.carbon

@@ -0,0 +1,761 @@
+// 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-FILE: toolchain/testing/testdata/min_prelude/int.carbon
+// EXTRA-ARGS: --clang-arg=--std=c++23
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/function/default_arg.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/function/default_arg.carbon
+
+// --- functions.h
+
+void A(int a, int b, int c = 1, int d = 2);
+
+struct X {
+  void B(int a, int b = 1);
+  static void C(int a, int b = 1);
+  void D(this X, int a, int b = 1);
+};
+
+// --- call_without_default.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "functions.h";
+
+fn Call() {
+  //@dump-sem-ir-begin
+  Cpp.A(1, 2, 3, 4);
+  ({} as Cpp.X).B(1, 2);
+  Cpp.X.C(1, 2);
+  ({} as Cpp.X).D(1, 2);
+  //@dump-sem-ir-end
+}
+
+// --- call_with_default.carbon
+//@include-in-dumps
+library "[[@TEST_NAME]]";
+
+import Cpp library "functions.h";
+
+fn Call() {
+  // //@dump-sem-ir-begin
+  Cpp.A(1, 2);
+  Cpp.A(1, 2, 3);
+  ({} as Cpp.X).B(1);
+  Cpp.X.C(1);
+  ({} as Cpp.X).D(1);
+  // //@dump-sem-ir-end
+}
+
+// --- fail_call_too_few_args.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "functions.h";
+
+fn Call() {
+  //@dump-sem-ir-begin
+  // CHECK:STDERR: fail_call_too_few_args.carbon:[[@LINE+8]]:10: error: no matching function for call to 'A' [CppInteropParseError]
+  // CHECK:STDERR:    16 |   Cpp.A(1);
+  // CHECK:STDERR:       |          ^
+  // CHECK:STDERR: fail_call_too_few_args.carbon:[[@LINE-7]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./functions.h:2:6: note: candidate function not viable: requires at least 2 arguments, but 1 was provided [CppInteropParseNote]
+  // CHECK:STDERR:     2 | void A(int a, int b, int c = 1, int d = 2);
+  // CHECK:STDERR:       |      ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR:
+  Cpp.A(1);
+
+  // CHECK:STDERR: fail_call_too_few_args.carbon:[[@LINE+8]]:9: error: no matching function for call to 'A' [CppInteropParseError]
+  // CHECK:STDERR:    26 |   Cpp.A();
+  // CHECK:STDERR:       |         ^
+  // CHECK:STDERR: fail_call_too_few_args.carbon:[[@LINE-17]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./functions.h:2:6: note: candidate function not viable: requires at least 2 arguments, but 0 were provided [CppInteropParseNote]
+  // CHECK:STDERR:     2 | void A(int a, int b, int c = 1, int d = 2);
+  // CHECK:STDERR:       |      ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR:
+  Cpp.A();
+
+  // CHECK:STDERR: fail_call_too_few_args.carbon:[[@LINE+8]]:19: error: no matching function for call to 'B' [CppInteropParseError]
+  // CHECK:STDERR:    36 |   ({} as Cpp.X).B();
+  // CHECK:STDERR:       |                   ^
+  // CHECK:STDERR: fail_call_too_few_args.carbon:[[@LINE-27]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./functions.h:5:8: note: candidate function not viable: requires at least argument 'a', but no arguments were provided [CppInteropParseNote]
+  // CHECK:STDERR:     5 |   void B(int a, int b = 1);
+  // CHECK:STDERR:       |        ^ ~~~~~~~~~~~~~~~~
+  // CHECK:STDERR:
+  ({} as Cpp.X).B();
+
+  // CHECK:STDERR: fail_call_too_few_args.carbon:[[@LINE+8]]:11: error: no matching function for call to 'C' [CppInteropParseError]
+  // CHECK:STDERR:    46 |   Cpp.X.C();
+  // CHECK:STDERR:       |           ^
+  // CHECK:STDERR: fail_call_too_few_args.carbon:[[@LINE-37]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./functions.h:6:15: note: candidate function not viable: requires at least argument 'a', but no arguments were provided [CppInteropParseNote]
+  // CHECK:STDERR:     6 |   static void C(int a, int b = 1);
+  // CHECK:STDERR:       |               ^ ~~~~~~~~~~~~~~~~
+  // CHECK:STDERR:
+  Cpp.X.C();
+
+  // CHECK:STDERR: fail_call_too_few_args.carbon:[[@LINE+8]]:19: error: no matching function for call to 'D' [CppInteropParseError]
+  // CHECK:STDERR:    56 |   ({} as Cpp.X).D();
+  // CHECK:STDERR:       |                   ^
+  // CHECK:STDERR: fail_call_too_few_args.carbon:[[@LINE-47]]:10: in file included here [InCppInclude]
+  // CHECK:STDERR: ./functions.h:7:8: note: candidate function not viable: requires at least non-object argument 'a', but no arguments were provided [CppInteropParseNote]
+  // CHECK:STDERR:     7 |   void D(this X, int a, int b = 1);
+  // CHECK:STDERR:       |        ^ ~~~~~~~~~~~~~~~~~~~~~~~~
+  // CHECK:STDERR:
+  ({} as Cpp.X).D();
+  //@dump-sem-ir-end
+}
+
+// CHECK:STDOUT: --- call_without_default.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %.490: type = cpp_overload_set_type @Int.as.ImplicitAs.impl.Convert [concrete]
+// CHECK:STDOUT:   %empty_struct.a89: %.490 = struct_value () [concrete]
+// CHECK:STDOUT:   %int_1.5b8: Core.IntLiteral = int_value 1 [concrete]
+// CHECK:STDOUT:   %int_2.ecc: Core.IntLiteral = int_value 2 [concrete]
+// CHECK:STDOUT:   %int_3.1ba: Core.IntLiteral = int_value 3 [concrete]
+// CHECK:STDOUT:   %int_4.0c1: Core.IntLiteral = int_value 4 [concrete]
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
+// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
+// CHECK:STDOUT:   %A.type: type = fn_type @A [concrete]
+// CHECK:STDOUT:   %A: %A.type = struct_value () [concrete]
+// CHECK:STDOUT:   %ImplicitAs.type.d14: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
+// CHECK:STDOUT:   %ImplicitAs.Convert.type.1b6: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%i32) [concrete]
+// CHECK:STDOUT:   %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.1c0: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340 = struct_value () [symbolic]
+// CHECK:STDOUT:   %ImplicitAs.impl_witness.204: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.9e9, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.584: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.584 = struct_value () [concrete]
+// CHECK:STDOUT:   %ImplicitAs.facet: %ImplicitAs.type.d14 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.204) [concrete]
+// CHECK:STDOUT:   %.1df: type = fn_type_with_self_type %ImplicitAs.Convert.type.1b6, %ImplicitAs.facet [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.abf: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0 [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
+// CHECK:STDOUT:   %bound_method.c11: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %int_1.5d2: %i32 = int_value 1 [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.b82: <bound method> = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0 [concrete]
+// CHECK:STDOUT:   %bound_method.8bd: <bound method> = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %int_2.ef8: %i32 = int_value 2 [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.595: <bound method> = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0 [concrete]
+// CHECK:STDOUT:   %bound_method.f36: <bound method> = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %int_3.822: %i32 = int_value 3 [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.5bb: <bound method> = bound_method %int_4.0c1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0 [concrete]
+// CHECK:STDOUT:   %bound_method.9cd: <bound method> = bound_method %int_4.0c1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %int_4.940: %i32 = int_value 4 [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %X: type = class_type @X [concrete]
+// CHECK:STDOUT:   %X.val: %X = struct_value () [concrete]
+// CHECK:STDOUT:   %.ed9: type = cpp_overload_set_type @X.B [concrete]
+// CHECK:STDOUT:   %empty_struct.61d: %.ed9 = struct_value () [concrete]
+// CHECK:STDOUT:   %ptr.1f9: type = ptr_type %X [concrete]
+// CHECK:STDOUT:   %X.B.type: type = fn_type @X.B [concrete]
+// CHECK:STDOUT:   %X.B: %X.B.type = struct_value () [concrete]
+// CHECK:STDOUT:   %.ce5: type = cpp_overload_set_type @X.C [concrete]
+// CHECK:STDOUT:   %empty_struct.a7d: %.ce5 = struct_value () [concrete]
+// CHECK:STDOUT:   %X.C.type: type = fn_type @X.C [concrete]
+// CHECK:STDOUT:   %X.C: %X.C.type = struct_value () [concrete]
+// CHECK:STDOUT:   %.c13: type = cpp_overload_set_type @X.D [concrete]
+// CHECK:STDOUT:   %empty_struct.576: %.c13 = struct_value () [concrete]
+// CHECK:STDOUT:   %D__carbon_thunk.type: type = fn_type @D__carbon_thunk [concrete]
+// CHECK:STDOUT:   %D__carbon_thunk: %D__carbon_thunk.type = struct_value () [concrete]
+// CHECK:STDOUT:   %type_where: type = facet_type <type where .Self impls <CanAggregateDestroy>> [concrete]
+// CHECK:STDOUT:   %facet_value: %type_where = facet_value %X, () [concrete]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.type.d91: type = fn_type @AggregateT.as_type.as.Destroy.impl.Op, @AggregateT.as_type.as.Destroy.impl(%facet_value) [concrete]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.55b: %AggregateT.as_type.as.Destroy.impl.Op.type.d91 = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .A = %.7c0
+// CHECK:STDOUT:     .X = %X.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.7c0: %.490 = cpp_overload_set_value @Int.as.ImplicitAs.impl.Convert [concrete = constants.%empty_struct.a89]
+// CHECK:STDOUT:   %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import_ref.ee7: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340) = import_ref Core//prelude/parts/int, loc23_39, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.1c0)]
+// CHECK:STDOUT:   %ImplicitAs.impl_witness_table.9e9 = impl_witness_table (%Core.import_ref.ee7), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
+// CHECK:STDOUT:   %X.decl: type = class_decl @X [concrete = constants.%X] {} {}
+// CHECK:STDOUT:   %.8ab: %.ed9 = cpp_overload_set_value @X.B [concrete = constants.%empty_struct.61d]
+// CHECK:STDOUT:   %X.B.decl: %X.B.type = fn_decl @X.B [concrete = constants.%X.B] {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.7af: %.ce5 = cpp_overload_set_value @X.C [concrete = constants.%empty_struct.a7d]
+// CHECK:STDOUT:   %X.C.decl: %X.C.type = fn_decl @X.C [concrete = constants.%X.C] {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.7b7: %.c13 = cpp_overload_set_value @X.D [concrete = constants.%empty_struct.576]
+// CHECK:STDOUT:   %D__carbon_thunk.decl: %D__carbon_thunk.type = fn_decl @D__carbon_thunk [concrete = constants.%D__carbon_thunk] {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Call() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Cpp.ref.loc8: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %A.ref: %.490 = name_ref A, imports.%.7c0 [concrete = constants.%empty_struct.a89]
+// CHECK:STDOUT:   %int_1.loc8: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
+// CHECK:STDOUT:   %int_2.loc8: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc]
+// CHECK:STDOUT:   %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba]
+// CHECK:STDOUT:   %int_4: Core.IntLiteral = int_value 4 [concrete = constants.%int_4.0c1]
+// CHECK:STDOUT:   %impl.elem0.loc8_9: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc8_9.1: <bound method> = bound_method %int_1.loc8, %impl.elem0.loc8_9 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.abf]
+// CHECK:STDOUT:   %specific_fn.loc8_9: <specific function> = specific_function %impl.elem0.loc8_9, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc8_9.2: <bound method> = bound_method %int_1.loc8, %specific_fn.loc8_9 [concrete = constants.%bound_method.c11]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_9: init %i32 = call %bound_method.loc8_9.2(%int_1.loc8) [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc8_9.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_9 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc8_9.2: %i32 = converted %int_1.loc8, %.loc8_9.1 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %impl.elem0.loc8_12: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc8_12.1: <bound method> = bound_method %int_2.loc8, %impl.elem0.loc8_12 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.b82]
+// CHECK:STDOUT:   %specific_fn.loc8_12: <specific function> = specific_function %impl.elem0.loc8_12, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc8_12.2: <bound method> = bound_method %int_2.loc8, %specific_fn.loc8_12 [concrete = constants.%bound_method.8bd]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_12: init %i32 = call %bound_method.loc8_12.2(%int_2.loc8) [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:   %.loc8_12.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_12 [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:   %.loc8_12.2: %i32 = converted %int_2.loc8, %.loc8_12.1 [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:   %impl.elem0.loc8_15: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc8_15.1: <bound method> = bound_method %int_3, %impl.elem0.loc8_15 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.595]
+// CHECK:STDOUT:   %specific_fn.loc8_15: <specific function> = specific_function %impl.elem0.loc8_15, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc8_15.2: <bound method> = bound_method %int_3, %specific_fn.loc8_15 [concrete = constants.%bound_method.f36]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_15: init %i32 = call %bound_method.loc8_15.2(%int_3) [concrete = constants.%int_3.822]
+// CHECK:STDOUT:   %.loc8_15.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_15 [concrete = constants.%int_3.822]
+// CHECK:STDOUT:   %.loc8_15.2: %i32 = converted %int_3, %.loc8_15.1 [concrete = constants.%int_3.822]
+// CHECK:STDOUT:   %impl.elem0.loc8_18: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc8_18.1: <bound method> = bound_method %int_4, %impl.elem0.loc8_18 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.5bb]
+// CHECK:STDOUT:   %specific_fn.loc8_18: <specific function> = specific_function %impl.elem0.loc8_18, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc8_18.2: <bound method> = bound_method %int_4, %specific_fn.loc8_18 [concrete = constants.%bound_method.9cd]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_18: init %i32 = call %bound_method.loc8_18.2(%int_4) [concrete = constants.%int_4.940]
+// CHECK:STDOUT:   %.loc8_18.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_18 [concrete = constants.%int_4.940]
+// CHECK:STDOUT:   %.loc8_18.2: %i32 = converted %int_4, %.loc8_18.1 [concrete = constants.%int_4.940]
+// CHECK:STDOUT:   %A.call: init %empty_tuple.type = call imports.%A.decl(%.loc8_9.2, %.loc8_12.2, %.loc8_15.2, %.loc8_18.2)
+// CHECK:STDOUT:   %.loc9_5.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %Cpp.ref.loc9: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %X.ref.loc9: type = name_ref X, imports.%X.decl [concrete = constants.%X]
+// CHECK:STDOUT:   %.loc9_5.2: ref %X = temporary_storage
+// CHECK:STDOUT:   %.loc9_5.3: init %X = class_init (), %.loc9_5.2 [concrete = constants.%X.val]
+// CHECK:STDOUT:   %.loc9_5.4: ref %X = temporary %.loc9_5.2, %.loc9_5.3
+// CHECK:STDOUT:   %.loc9_7: ref %X = converted %.loc9_5.1, %.loc9_5.4
+// CHECK:STDOUT:   %B.ref: %.ed9 = name_ref B, imports.%.8ab [concrete = constants.%empty_struct.61d]
+// CHECK:STDOUT:   %bound_method.loc9_16: <bound method> = bound_method %.loc9_7, %B.ref
+// CHECK:STDOUT:   %int_1.loc9: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
+// CHECK:STDOUT:   %int_2.loc9: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc]
+// CHECK:STDOUT:   %addr.loc9_7: %ptr.1f9 = addr_of %.loc9_7
+// CHECK:STDOUT:   %impl.elem0.loc9_19: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc9_19.1: <bound method> = bound_method %int_1.loc9, %impl.elem0.loc9_19 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.abf]
+// CHECK:STDOUT:   %specific_fn.loc9_19: <specific function> = specific_function %impl.elem0.loc9_19, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc9_19.2: <bound method> = bound_method %int_1.loc9, %specific_fn.loc9_19 [concrete = constants.%bound_method.c11]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_19: init %i32 = call %bound_method.loc9_19.2(%int_1.loc9) [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc9_19.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_19 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc9_19.2: %i32 = converted %int_1.loc9, %.loc9_19.1 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %impl.elem0.loc9_22: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc9_22.1: <bound method> = bound_method %int_2.loc9, %impl.elem0.loc9_22 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.b82]
+// CHECK:STDOUT:   %specific_fn.loc9_22: <specific function> = specific_function %impl.elem0.loc9_22, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc9_22.2: <bound method> = bound_method %int_2.loc9, %specific_fn.loc9_22 [concrete = constants.%bound_method.8bd]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_22: init %i32 = call %bound_method.loc9_22.2(%int_2.loc9) [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:   %.loc9_22.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_22 [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:   %.loc9_22.2: %i32 = converted %int_2.loc9, %.loc9_22.1 [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:   %X.B.call: init %empty_tuple.type = call imports.%X.B.decl(%addr.loc9_7, %.loc9_19.2, %.loc9_22.2)
+// CHECK:STDOUT:   %Cpp.ref.loc10: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %X.ref.loc10: type = name_ref X, imports.%X.decl [concrete = constants.%X]
+// CHECK:STDOUT:   %C.ref: %.ce5 = name_ref C, imports.%.7af [concrete = constants.%empty_struct.a7d]
+// CHECK:STDOUT:   %int_1.loc10: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
+// CHECK:STDOUT:   %int_2.loc10: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc]
+// CHECK:STDOUT:   %impl.elem0.loc10_11: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc10_11.1: <bound method> = bound_method %int_1.loc10, %impl.elem0.loc10_11 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.abf]
+// CHECK:STDOUT:   %specific_fn.loc10_11: <specific function> = specific_function %impl.elem0.loc10_11, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc10_11.2: <bound method> = bound_method %int_1.loc10, %specific_fn.loc10_11 [concrete = constants.%bound_method.c11]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_11: init %i32 = call %bound_method.loc10_11.2(%int_1.loc10) [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc10_11.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_11 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc10_11.2: %i32 = converted %int_1.loc10, %.loc10_11.1 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %impl.elem0.loc10_14: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc10_14.1: <bound method> = bound_method %int_2.loc10, %impl.elem0.loc10_14 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.b82]
+// CHECK:STDOUT:   %specific_fn.loc10_14: <specific function> = specific_function %impl.elem0.loc10_14, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc10_14.2: <bound method> = bound_method %int_2.loc10, %specific_fn.loc10_14 [concrete = constants.%bound_method.8bd]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_14: init %i32 = call %bound_method.loc10_14.2(%int_2.loc10) [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:   %.loc10_14.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_14 [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:   %.loc10_14.2: %i32 = converted %int_2.loc10, %.loc10_14.1 [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:   %X.C.call: init %empty_tuple.type = call imports.%X.C.decl(%.loc10_11.2, %.loc10_14.2)
+// CHECK:STDOUT:   %.loc11_5.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %Cpp.ref.loc11: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %X.ref.loc11: type = name_ref X, imports.%X.decl [concrete = constants.%X]
+// CHECK:STDOUT:   %.loc11_5.2: ref %X = temporary_storage
+// CHECK:STDOUT:   %.loc11_5.3: init %X = class_init (), %.loc11_5.2 [concrete = constants.%X.val]
+// CHECK:STDOUT:   %.loc11_5.4: ref %X = temporary %.loc11_5.2, %.loc11_5.3
+// CHECK:STDOUT:   %.loc11_7.1: ref %X = converted %.loc11_5.1, %.loc11_5.4
+// CHECK:STDOUT:   %D.ref: %.c13 = name_ref D, imports.%.7b7 [concrete = constants.%empty_struct.576]
+// CHECK:STDOUT:   %bound_method.loc11_16: <bound method> = bound_method %.loc11_7.1, %D.ref
+// CHECK:STDOUT:   %int_1.loc11: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
+// CHECK:STDOUT:   %int_2.loc11: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc]
+// CHECK:STDOUT:   %.loc11_7.2: %X = bind_value %.loc11_7.1
+// CHECK:STDOUT:   %impl.elem0.loc11_19: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc11_19.1: <bound method> = bound_method %int_1.loc11, %impl.elem0.loc11_19 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.abf]
+// CHECK:STDOUT:   %specific_fn.loc11_19: <specific function> = specific_function %impl.elem0.loc11_19, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc11_19.2: <bound method> = bound_method %int_1.loc11, %specific_fn.loc11_19 [concrete = constants.%bound_method.c11]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_19: init %i32 = call %bound_method.loc11_19.2(%int_1.loc11) [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc11_19.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_19 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc11_19.2: %i32 = converted %int_1.loc11, %.loc11_19.1 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %impl.elem0.loc11_22: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc11_22.1: <bound method> = bound_method %int_2.loc11, %impl.elem0.loc11_22 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.b82]
+// CHECK:STDOUT:   %specific_fn.loc11_22: <specific function> = specific_function %impl.elem0.loc11_22, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc11_22.2: <bound method> = bound_method %int_2.loc11, %specific_fn.loc11_22 [concrete = constants.%bound_method.8bd]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_22: init %i32 = call %bound_method.loc11_22.2(%int_2.loc11) [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:   %.loc11_22.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_22 [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:   %.loc11_22.2: %i32 = converted %int_2.loc11, %.loc11_22.1 [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:   %.loc11_7.3: ref %X = value_as_ref %.loc11_7.2
+// CHECK:STDOUT:   %addr.loc11_23: %ptr.1f9 = addr_of %.loc11_7.3
+// CHECK:STDOUT:   %D__carbon_thunk.call: init %empty_tuple.type = call imports.%D__carbon_thunk.decl(%addr.loc11_23, %.loc11_19.2, %.loc11_22.2)
+// CHECK:STDOUT:   %facet_value.loc11: %type_where = facet_value constants.%X, () [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %.loc11_5.5: %type_where = converted constants.%X, %facet_value.loc11 [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc11: <bound method> = bound_method %.loc11_5.4, constants.%AggregateT.as_type.as.Destroy.impl.Op.55b
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT:   %bound_method.loc11_5: <bound method> = bound_method %.loc11_5.4, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.1
+// CHECK:STDOUT:   %addr.loc11_5: %ptr.1f9 = addr_of %.loc11_5.4
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc11: init %empty_tuple.type = call %bound_method.loc11_5(%addr.loc11_5)
+// CHECK:STDOUT:   %facet_value.loc9: %type_where = facet_value constants.%X, () [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %.loc9_5.5: %type_where = converted constants.%X, %facet_value.loc9 [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc9: <bound method> = bound_method %.loc9_5.4, constants.%AggregateT.as_type.as.Destroy.impl.Op.55b
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT:   %bound_method.loc9_5: <bound method> = bound_method %.loc9_5.4, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.2
+// CHECK:STDOUT:   %addr.loc9_5: %ptr.1f9 = addr_of %.loc9_5.4
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9_5(%addr.loc9_5)
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- call_with_default.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Call.type: type = fn_type @Call [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %Call: %Call.type = struct_value () [concrete]
+// CHECK:STDOUT:   %.490: type = cpp_overload_set_type @Core.IntLiteral.as.ImplicitAs.impl.Convert [concrete]
+// CHECK:STDOUT:   %empty_struct.a89: %.490 = struct_value () [concrete]
+// CHECK:STDOUT:   %int_1.5b8: Core.IntLiteral = int_value 1 [concrete]
+// CHECK:STDOUT:   %int_2.ecc: Core.IntLiteral = int_value 2 [concrete]
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
+// CHECK:STDOUT:   %Int.type: type = generic_class_type @Int [concrete]
+// CHECK:STDOUT:   %Int.generic: %Int.type = struct_value () [concrete]
+// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
+// CHECK:STDOUT:   %pattern_type.7ce: type = pattern_type %i32 [concrete]
+// CHECK:STDOUT:   %A__carbon_thunk.type.f1e093.1: type = fn_type @A__carbon_thunk.1 [concrete]
+// CHECK:STDOUT:   %A__carbon_thunk.8f654e.1: %A__carbon_thunk.type.f1e093.1 = struct_value () [concrete]
+// CHECK:STDOUT:   %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete]
+// CHECK:STDOUT:   %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete]
+// CHECK:STDOUT:   %ImplicitAs.type.d14: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
+// CHECK:STDOUT:   %ImplicitAs.Convert.type.1b6: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%i32) [concrete]
+// CHECK:STDOUT:   %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.1c0: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340 = struct_value () [symbolic]
+// CHECK:STDOUT:   %ImplicitAs.impl_witness.204: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.9e9, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.584: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.584 = struct_value () [concrete]
+// CHECK:STDOUT:   %ImplicitAs.facet: %ImplicitAs.type.d14 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.204) [concrete]
+// CHECK:STDOUT:   %.1df: type = fn_type_with_self_type %ImplicitAs.Convert.type.1b6, %ImplicitAs.facet [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.abf: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0 [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
+// CHECK:STDOUT:   %bound_method.c11: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %int_1.5d2: %i32 = int_value 1 [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.b82: <bound method> = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0 [concrete]
+// CHECK:STDOUT:   %bound_method.8bd: <bound method> = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %int_2.ef8: %i32 = int_value 2 [concrete]
+// CHECK:STDOUT:   %int_3.1ba: Core.IntLiteral = int_value 3 [concrete]
+// CHECK:STDOUT:   %A__carbon_thunk.type.f1e093.2: type = fn_type @A__carbon_thunk.2 [concrete]
+// CHECK:STDOUT:   %A__carbon_thunk.8f654e.2: %A__carbon_thunk.type.f1e093.2 = struct_value () [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.595: <bound method> = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0 [concrete]
+// CHECK:STDOUT:   %bound_method.f36: <bound method> = bound_method %int_3.1ba, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %int_3.822: %i32 = int_value 3 [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %X: type = class_type @X [concrete]
+// CHECK:STDOUT:   %complete_type.357: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %X.val: %X = struct_value () [concrete]
+// CHECK:STDOUT:   %.ed9: type = cpp_overload_set_type @Int.as.ImplicitAs.impl.Convert [concrete]
+// CHECK:STDOUT:   %empty_struct.61d: %.ed9 = struct_value () [concrete]
+// CHECK:STDOUT:   %ptr.1f9: type = ptr_type %X [concrete]
+// CHECK:STDOUT:   %pattern_type.45c: type = pattern_type %ptr.1f9 [concrete]
+// CHECK:STDOUT:   %B__carbon_thunk.type: type = fn_type @B__carbon_thunk [concrete]
+// CHECK:STDOUT:   %B__carbon_thunk: %B__carbon_thunk.type = struct_value () [concrete]
+// CHECK:STDOUT:   %.ce5: type = cpp_overload_set_type @A.2 [concrete]
+// CHECK:STDOUT:   %empty_struct.a7d: %.ce5 = struct_value () [concrete]
+// CHECK:STDOUT:   %C__carbon_thunk.type: type = fn_type @C__carbon_thunk [concrete]
+// CHECK:STDOUT:   %C__carbon_thunk: %C__carbon_thunk.type = struct_value () [concrete]
+// CHECK:STDOUT:   %.c13: type = cpp_overload_set_type @A__carbon_thunk.2 [concrete]
+// CHECK:STDOUT:   %empty_struct.576: %.c13 = struct_value () [concrete]
+// CHECK:STDOUT:   %D__carbon_thunk.type: type = fn_type @D__carbon_thunk [concrete]
+// CHECK:STDOUT:   %D__carbon_thunk: %D__carbon_thunk.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Destroy.type: type = facet_type <@Destroy> [concrete]
+// CHECK:STDOUT:   %type_where: type = facet_type <type where .Self impls <CanAggregateDestroy>> [concrete]
+// CHECK:STDOUT:   %facet_value: %type_where = facet_value %X, () [concrete]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.type.d91: type = fn_type @AggregateT.as_type.as.Destroy.impl.Op, @AggregateT.as_type.as.Destroy.impl(%facet_value) [concrete]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.55b: %AggregateT.as_type.as.Destroy.impl.Op.type.d91 = struct_value () [concrete]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.specific_fn: <specific function> = specific_function %AggregateT.as_type.as.Destroy.impl.Op.55b, @AggregateT.as_type.as.Destroy.impl.Op(%facet_value) [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [concrete] {
+// CHECK:STDOUT:     .Int = %Core.Int
+// CHECK:STDOUT:     .ImplicitAs = %Core.ImplicitAs
+// CHECK:STDOUT:     .Destroy = %Core.Destroy
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .A = %.7c0
+// CHECK:STDOUT:     .X = %X.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.7c0: %.490 = cpp_overload_set_value @Core.IntLiteral.as.ImplicitAs.impl.Convert [concrete = constants.%empty_struct.a89]
+// CHECK:STDOUT:   %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic]
+// CHECK:STDOUT:   %A__carbon_thunk.decl.713db8.1: %A__carbon_thunk.type.f1e093.1 = fn_decl @A__carbon_thunk.1 [concrete = constants.%A__carbon_thunk.8f654e.1] {
+// CHECK:STDOUT:     %a.patt: %pattern_type.7ce = binding_pattern a [concrete]
+// CHECK:STDOUT:     %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt, call_param0 [concrete]
+// CHECK:STDOUT:     %b.patt: %pattern_type.7ce = binding_pattern b [concrete]
+// CHECK:STDOUT:     %b.param_patt: %pattern_type.7ce = value_param_pattern %b.patt, call_param1 [concrete]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %a.param: %i32 = value_param call_param0
+// CHECK:STDOUT:     %.1: type = splice_block %i32.2 [concrete = constants.%i32] {
+// CHECK:STDOUT:       %int_32.2: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:       %i32.2: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %a: %i32 = bind_name a, %a.param
+// CHECK:STDOUT:     %b.param: %i32 = value_param call_param1
+// CHECK:STDOUT:     %.2: type = splice_block %i32.1 [concrete = constants.%i32] {
+// CHECK:STDOUT:       %int_32.1: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:       %i32.1: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %b: %i32 = bind_name b, %b.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/parts/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic]
+// CHECK:STDOUT:   %Core.import_ref.ee7: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340) = import_ref Core//prelude/parts/int, loc23_39, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.1c0)]
+// CHECK:STDOUT:   %ImplicitAs.impl_witness_table.9e9 = impl_witness_table (%Core.import_ref.ee7), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
+// CHECK:STDOUT:   %A__carbon_thunk.decl.713db8.2: %A__carbon_thunk.type.f1e093.2 = fn_decl @A__carbon_thunk.2 [concrete = constants.%A__carbon_thunk.8f654e.2] {
+// CHECK:STDOUT:     %a.patt: %pattern_type.7ce = binding_pattern a [concrete]
+// CHECK:STDOUT:     %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt, call_param0 [concrete]
+// CHECK:STDOUT:     %b.patt: %pattern_type.7ce = binding_pattern b [concrete]
+// CHECK:STDOUT:     %b.param_patt: %pattern_type.7ce = value_param_pattern %b.patt, call_param1 [concrete]
+// CHECK:STDOUT:     %c.patt: %pattern_type.7ce = binding_pattern c [concrete]
+// CHECK:STDOUT:     %c.param_patt: %pattern_type.7ce = value_param_pattern %c.patt, call_param2 [concrete]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %a.param: %i32 = value_param call_param0
+// CHECK:STDOUT:     %.1: type = splice_block %i32.3 [concrete = constants.%i32] {
+// CHECK:STDOUT:       %int_32.3: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:       %i32.3: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %a: %i32 = bind_name a, %a.param
+// CHECK:STDOUT:     %b.param: %i32 = value_param call_param1
+// CHECK:STDOUT:     %.2: type = splice_block %i32.2 [concrete = constants.%i32] {
+// CHECK:STDOUT:       %int_32.2: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:       %i32.2: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %b: %i32 = bind_name b, %b.param
+// CHECK:STDOUT:     %c.param: %i32 = value_param call_param2
+// CHECK:STDOUT:     %.3: type = splice_block %i32.1 [concrete = constants.%i32] {
+// CHECK:STDOUT:       %int_32.1: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
+// CHECK:STDOUT:       %i32.1: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %c: %i32 = bind_name c, %c.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %X.decl: type = class_decl @X [concrete = constants.%X] {} {}
+// CHECK:STDOUT:   %.8ab: %.ed9 = cpp_overload_set_value @Int.as.ImplicitAs.impl.Convert [concrete = constants.%empty_struct.61d]
+// CHECK:STDOUT:   %B__carbon_thunk.decl: %B__carbon_thunk.type = fn_decl @B__carbon_thunk [concrete = constants.%B__carbon_thunk] {
+// CHECK:STDOUT:     %this.patt: %pattern_type.45c = binding_pattern this [concrete]
+// CHECK:STDOUT:     %this.param_patt: %pattern_type.45c = value_param_pattern %this.patt, call_param0 [concrete]
+// CHECK:STDOUT:     %a.patt: %pattern_type.7ce = binding_pattern a [concrete]
+// CHECK:STDOUT:     %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt, call_param1 [concrete]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %this.param: %ptr.1f9 = value_param call_param0
+// CHECK:STDOUT:     %this: %ptr.1f9 = bind_name this, %this.param
+// CHECK:STDOUT:     %a.param: %i32 = value_param call_param1
+// CHECK:STDOUT:     %.1: type = splice_block %i32 [concrete = constants.%i32] {
+// 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:     }
+// CHECK:STDOUT:     %a: %i32 = bind_name a, %a.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.7af: %.ce5 = cpp_overload_set_value @A.2 [concrete = constants.%empty_struct.a7d]
+// CHECK:STDOUT:   %C__carbon_thunk.decl: %C__carbon_thunk.type = fn_decl @C__carbon_thunk [concrete = constants.%C__carbon_thunk] {
+// CHECK:STDOUT:     %a.patt: %pattern_type.7ce = binding_pattern a [concrete]
+// CHECK:STDOUT:     %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt, call_param0 [concrete]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %a.param: %i32 = value_param call_param0
+// CHECK:STDOUT:     %.1: type = splice_block %i32 [concrete = constants.%i32] {
+// 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:     }
+// CHECK:STDOUT:     %a: %i32 = bind_name a, %a.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.7b7: %.c13 = cpp_overload_set_value @A__carbon_thunk.2 [concrete = constants.%empty_struct.576]
+// CHECK:STDOUT:   %D__carbon_thunk.decl: %D__carbon_thunk.type = fn_decl @D__carbon_thunk [concrete = constants.%D__carbon_thunk] {
+// CHECK:STDOUT:     %_.patt: %pattern_type.45c = binding_pattern _ [concrete]
+// CHECK:STDOUT:     %_.param_patt: %pattern_type.45c = value_param_pattern %_.patt, call_param0 [concrete]
+// CHECK:STDOUT:     %a.patt: %pattern_type.7ce = binding_pattern a [concrete]
+// CHECK:STDOUT:     %a.param_patt: %pattern_type.7ce = value_param_pattern %a.patt, call_param1 [concrete]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %_.param: %ptr.1f9 = value_param call_param0
+// CHECK:STDOUT:     %_: %ptr.1f9 = bind_name _, %_.param
+// CHECK:STDOUT:     %a.param: %i32 = value_param call_param1
+// CHECK:STDOUT:     %.1: type = splice_block %i32 [concrete = constants.%i32] {
+// 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:     }
+// CHECK:STDOUT:     %a: %i32 = bind_name a, %a.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.Destroy: type = import_ref Core//prelude/parts/destroy, Destroy, loaded [concrete = constants.%Destroy.type]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .Call = %Call.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "functions.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Call.decl: %Call.type = fn_decl @Call [concrete = constants.%Call] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @X {
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
+// CHECK:STDOUT:   complete_type_witness = %complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .B = imports.%.8ab
+// CHECK:STDOUT:   .C = imports.%.7af
+// CHECK:STDOUT:   .D = imports.%.7b7
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Call() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Cpp.ref.loc8: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %A.ref.loc8: %.490 = name_ref A, imports.%.7c0 [concrete = constants.%empty_struct.a89]
+// CHECK:STDOUT:   %int_1.loc8: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
+// CHECK:STDOUT:   %int_2.loc8: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc]
+// CHECK:STDOUT:   %impl.elem0.loc8_9: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc8_9.1: <bound method> = bound_method %int_1.loc8, %impl.elem0.loc8_9 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.abf]
+// CHECK:STDOUT:   %specific_fn.loc8_9: <specific function> = specific_function %impl.elem0.loc8_9, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc8_9.2: <bound method> = bound_method %int_1.loc8, %specific_fn.loc8_9 [concrete = constants.%bound_method.c11]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_9: init %i32 = call %bound_method.loc8_9.2(%int_1.loc8) [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc8_9.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_9 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc8_9.2: %i32 = converted %int_1.loc8, %.loc8_9.1 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %impl.elem0.loc8_12: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc8_12.1: <bound method> = bound_method %int_2.loc8, %impl.elem0.loc8_12 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.b82]
+// CHECK:STDOUT:   %specific_fn.loc8_12: <specific function> = specific_function %impl.elem0.loc8_12, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc8_12.2: <bound method> = bound_method %int_2.loc8, %specific_fn.loc8_12 [concrete = constants.%bound_method.8bd]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_12: init %i32 = call %bound_method.loc8_12.2(%int_2.loc8) [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:   %.loc8_12.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc8_12 [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:   %.loc8_12.2: %i32 = converted %int_2.loc8, %.loc8_12.1 [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:   %A__carbon_thunk.call.loc8: init %empty_tuple.type = call imports.%A__carbon_thunk.decl.713db8.1(%.loc8_9.2, %.loc8_12.2)
+// CHECK:STDOUT:   %Cpp.ref.loc9: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %A.ref.loc9: %.490 = name_ref A, imports.%.7c0 [concrete = constants.%empty_struct.a89]
+// CHECK:STDOUT:   %int_1.loc9: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
+// CHECK:STDOUT:   %int_2.loc9: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc]
+// CHECK:STDOUT:   %int_3: Core.IntLiteral = int_value 3 [concrete = constants.%int_3.1ba]
+// CHECK:STDOUT:   %impl.elem0.loc9_9: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc9_9.1: <bound method> = bound_method %int_1.loc9, %impl.elem0.loc9_9 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.abf]
+// CHECK:STDOUT:   %specific_fn.loc9_9: <specific function> = specific_function %impl.elem0.loc9_9, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc9_9.2: <bound method> = bound_method %int_1.loc9, %specific_fn.loc9_9 [concrete = constants.%bound_method.c11]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_9: init %i32 = call %bound_method.loc9_9.2(%int_1.loc9) [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc9_9.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_9 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc9_9.2: %i32 = converted %int_1.loc9, %.loc9_9.1 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %impl.elem0.loc9_12: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc9_12.1: <bound method> = bound_method %int_2.loc9, %impl.elem0.loc9_12 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.b82]
+// CHECK:STDOUT:   %specific_fn.loc9_12: <specific function> = specific_function %impl.elem0.loc9_12, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc9_12.2: <bound method> = bound_method %int_2.loc9, %specific_fn.loc9_12 [concrete = constants.%bound_method.8bd]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_12: init %i32 = call %bound_method.loc9_12.2(%int_2.loc9) [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:   %.loc9_12.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_12 [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:   %.loc9_12.2: %i32 = converted %int_2.loc9, %.loc9_12.1 [concrete = constants.%int_2.ef8]
+// CHECK:STDOUT:   %impl.elem0.loc9_15: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc9_15.1: <bound method> = bound_method %int_3, %impl.elem0.loc9_15 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.595]
+// CHECK:STDOUT:   %specific_fn.loc9_15: <specific function> = specific_function %impl.elem0.loc9_15, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc9_15.2: <bound method> = bound_method %int_3, %specific_fn.loc9_15 [concrete = constants.%bound_method.f36]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_15: init %i32 = call %bound_method.loc9_15.2(%int_3) [concrete = constants.%int_3.822]
+// CHECK:STDOUT:   %.loc9_15.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9_15 [concrete = constants.%int_3.822]
+// CHECK:STDOUT:   %.loc9_15.2: %i32 = converted %int_3, %.loc9_15.1 [concrete = constants.%int_3.822]
+// CHECK:STDOUT:   %A__carbon_thunk.call.loc9: init %empty_tuple.type = call imports.%A__carbon_thunk.decl.713db8.2(%.loc9_9.2, %.loc9_12.2, %.loc9_15.2)
+// CHECK:STDOUT:   %.loc10_5.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %Cpp.ref.loc10: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %X.ref.loc10: type = name_ref X, imports.%X.decl [concrete = constants.%X]
+// CHECK:STDOUT:   %.loc10_5.2: ref %X = temporary_storage
+// CHECK:STDOUT:   %.loc10_5.3: init %X = class_init (), %.loc10_5.2 [concrete = constants.%X.val]
+// CHECK:STDOUT:   %.loc10_5.4: ref %X = temporary %.loc10_5.2, %.loc10_5.3
+// CHECK:STDOUT:   %.loc10_7: ref %X = converted %.loc10_5.1, %.loc10_5.4
+// CHECK:STDOUT:   %B.ref: %.ed9 = name_ref B, imports.%.8ab [concrete = constants.%empty_struct.61d]
+// CHECK:STDOUT:   %bound_method.loc10_16: <bound method> = bound_method %.loc10_7, %B.ref
+// CHECK:STDOUT:   %int_1.loc10: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
+// CHECK:STDOUT:   %addr.loc10_7: %ptr.1f9 = addr_of %.loc10_7
+// CHECK:STDOUT:   %impl.elem0.loc10: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc10_19.1: <bound method> = bound_method %int_1.loc10, %impl.elem0.loc10 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.abf]
+// CHECK:STDOUT:   %specific_fn.loc10: <specific function> = specific_function %impl.elem0.loc10, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc10_19.2: <bound method> = bound_method %int_1.loc10, %specific_fn.loc10 [concrete = constants.%bound_method.c11]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10: init %i32 = call %bound_method.loc10_19.2(%int_1.loc10) [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc10_19.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc10_19.2: %i32 = converted %int_1.loc10, %.loc10_19.1 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %B__carbon_thunk.call: init %empty_tuple.type = call imports.%B__carbon_thunk.decl(%addr.loc10_7, %.loc10_19.2)
+// CHECK:STDOUT:   %Cpp.ref.loc11: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %X.ref.loc11: type = name_ref X, imports.%X.decl [concrete = constants.%X]
+// CHECK:STDOUT:   %C.ref: %.ce5 = name_ref C, imports.%.7af [concrete = constants.%empty_struct.a7d]
+// CHECK:STDOUT:   %int_1.loc11: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
+// CHECK:STDOUT:   %impl.elem0.loc11: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc11_11.1: <bound method> = bound_method %int_1.loc11, %impl.elem0.loc11 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.abf]
+// CHECK:STDOUT:   %specific_fn.loc11: <specific function> = specific_function %impl.elem0.loc11, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc11_11.2: <bound method> = bound_method %int_1.loc11, %specific_fn.loc11 [concrete = constants.%bound_method.c11]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11: init %i32 = call %bound_method.loc11_11.2(%int_1.loc11) [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc11_11.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc11_11.2: %i32 = converted %int_1.loc11, %.loc11_11.1 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %C__carbon_thunk.call: init %empty_tuple.type = call imports.%C__carbon_thunk.decl(%.loc11_11.2)
+// CHECK:STDOUT:   %.loc12_5.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %Cpp.ref.loc12: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %X.ref.loc12: type = name_ref X, imports.%X.decl [concrete = constants.%X]
+// CHECK:STDOUT:   %.loc12_5.2: ref %X = temporary_storage
+// CHECK:STDOUT:   %.loc12_5.3: init %X = class_init (), %.loc12_5.2 [concrete = constants.%X.val]
+// CHECK:STDOUT:   %.loc12_5.4: ref %X = temporary %.loc12_5.2, %.loc12_5.3
+// CHECK:STDOUT:   %.loc12_7.1: ref %X = converted %.loc12_5.1, %.loc12_5.4
+// CHECK:STDOUT:   %D.ref: %.c13 = name_ref D, imports.%.7b7 [concrete = constants.%empty_struct.576]
+// CHECK:STDOUT:   %bound_method.loc12_16: <bound method> = bound_method %.loc12_7.1, %D.ref
+// CHECK:STDOUT:   %int_1.loc12: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
+// CHECK:STDOUT:   %.loc12_7.2: %X = bind_value %.loc12_7.1
+// CHECK:STDOUT:   %impl.elem0.loc12: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc12_19.1: <bound method> = bound_method %int_1.loc12, %impl.elem0.loc12 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.abf]
+// CHECK:STDOUT:   %specific_fn.loc12: <specific function> = specific_function %impl.elem0.loc12, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc12_19.2: <bound method> = bound_method %int_1.loc12, %specific_fn.loc12 [concrete = constants.%bound_method.c11]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc12: init %i32 = call %bound_method.loc12_19.2(%int_1.loc12) [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc12_19.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc12 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc12_19.2: %i32 = converted %int_1.loc12, %.loc12_19.1 [concrete = constants.%int_1.5d2]
+// CHECK:STDOUT:   %.loc12_7.3: ref %X = value_as_ref %.loc12_7.2
+// CHECK:STDOUT:   %addr.loc12_20: %ptr.1f9 = addr_of %.loc12_7.3
+// CHECK:STDOUT:   %D__carbon_thunk.call: init %empty_tuple.type = call imports.%D__carbon_thunk.decl(%addr.loc12_20, %.loc12_19.2)
+// CHECK:STDOUT:   %facet_value.loc12: %type_where = facet_value constants.%X, () [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %.loc12_5.5: %type_where = converted constants.%X, %facet_value.loc12 [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc12: <bound method> = bound_method %.loc12_5.4, constants.%AggregateT.as_type.as.Destroy.impl.Op.55b
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.1: <specific function> = specific_function constants.%AggregateT.as_type.as.Destroy.impl.Op.55b, @AggregateT.as_type.as.Destroy.impl.Op(constants.%facet_value) [concrete = constants.%AggregateT.as_type.as.Destroy.impl.Op.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc12_5: <bound method> = bound_method %.loc12_5.4, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.1
+// CHECK:STDOUT:   %addr.loc12_5: %ptr.1f9 = addr_of %.loc12_5.4
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc12: init %empty_tuple.type = call %bound_method.loc12_5(%addr.loc12_5)
+// CHECK:STDOUT:   %facet_value.loc10: %type_where = facet_value constants.%X, () [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %.loc10_5.5: %type_where = converted constants.%X, %facet_value.loc10 [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc10: <bound method> = bound_method %.loc10_5.4, constants.%AggregateT.as_type.as.Destroy.impl.Op.55b
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.2: <specific function> = specific_function constants.%AggregateT.as_type.as.Destroy.impl.Op.55b, @AggregateT.as_type.as.Destroy.impl.Op(constants.%facet_value) [concrete = constants.%AggregateT.as_type.as.Destroy.impl.Op.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc10_5: <bound method> = bound_method %.loc10_5.4, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.2
+// CHECK:STDOUT:   %addr.loc10_5: %ptr.1f9 = addr_of %.loc10_5.4
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc10: init %empty_tuple.type = call %bound_method.loc10_5(%addr.loc10_5)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @A.1(%a.param: %i32, %b.param: %i32);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @A__carbon_thunk.1(%a.param: %i32, %b.param: %i32);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @A.2(%a.param: %i32, %b.param: %i32, %c.param: %i32);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @A__carbon_thunk.2(%a.param: %i32, %b.param: %i32, %c.param: %i32);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @X.B(%self.param: %ptr.1f9, %a.param: %i32);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @B__carbon_thunk(%this.param: %ptr.1f9, %a.param: %i32);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @X.C(%a.param: %i32);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @C__carbon_thunk(%a.param: %i32);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @X.D(%self.param: %X, %a.param: %i32);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @D__carbon_thunk(%_.param: %ptr.1f9, %a.param: %i32);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_call_too_few_args.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %.490: type = cpp_overload_set_type @const.as.Destroy.impl.Op [concrete]
+// CHECK:STDOUT:   %empty_struct.a89: %.490 = struct_value () [concrete]
+// CHECK:STDOUT:   %int_1: Core.IntLiteral = int_value 1 [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %X: type = class_type @X [concrete]
+// CHECK:STDOUT:   %X.val: %X = struct_value () [concrete]
+// CHECK:STDOUT:   %.ed9: type = cpp_overload_set_type @<null name> [concrete]
+// CHECK:STDOUT:   %empty_struct.61d: %.ed9 = struct_value () [concrete]
+// CHECK:STDOUT:   %.ce5: type = cpp_overload_set_type @<null name> [concrete]
+// CHECK:STDOUT:   %empty_struct.a7d: %.ce5 = struct_value () [concrete]
+// CHECK:STDOUT:   %.c13: type = cpp_overload_set_type @<null name> [concrete]
+// CHECK:STDOUT:   %empty_struct.576: %.c13 = struct_value () [concrete]
+// CHECK:STDOUT:   %type_where: type = facet_type <type where .Self impls <CanAggregateDestroy>> [concrete]
+// CHECK:STDOUT:   %facet_value: %type_where = facet_value %X, () [concrete]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.type.d91: type = fn_type @AggregateT.as_type.as.Destroy.impl.Op, @AggregateT.as_type.as.Destroy.impl(%facet_value) [concrete]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.55b: %AggregateT.as_type.as.Destroy.impl.Op.type.d91 = struct_value () [concrete]
+// CHECK:STDOUT:   %ptr.1f9: type = ptr_type %X [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .A = %.7c0
+// CHECK:STDOUT:     .X = %X.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.7c0: %.490 = cpp_overload_set_value @const.as.Destroy.impl.Op [concrete = constants.%empty_struct.a89]
+// CHECK:STDOUT:   %X.decl: type = class_decl @X [concrete = constants.%X] {} {}
+// CHECK:STDOUT:   %.8ab: %.ed9 = cpp_overload_set_value @<null name> [concrete = constants.%empty_struct.61d]
+// CHECK:STDOUT:   %.7af: %.ce5 = cpp_overload_set_value @<null name> [concrete = constants.%empty_struct.a7d]
+// CHECK:STDOUT:   %.7b7: %.c13 = cpp_overload_set_value @<null name> [concrete = constants.%empty_struct.576]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Call() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Cpp.ref.loc16: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %A.ref.loc16: %.490 = name_ref A, imports.%.7c0 [concrete = constants.%empty_struct.a89]
+// CHECK:STDOUT:   %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1]
+// CHECK:STDOUT:   %Cpp.ref.loc26: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %A.ref.loc26: %.490 = name_ref A, imports.%.7c0 [concrete = constants.%empty_struct.a89]
+// CHECK:STDOUT:   %.loc36_5.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %Cpp.ref.loc36: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %X.ref.loc36: type = name_ref X, imports.%X.decl [concrete = constants.%X]
+// CHECK:STDOUT:   %.loc36_5.2: ref %X = temporary_storage
+// CHECK:STDOUT:   %.loc36_5.3: init %X = class_init (), %.loc36_5.2 [concrete = constants.%X.val]
+// CHECK:STDOUT:   %.loc36_5.4: ref %X = temporary %.loc36_5.2, %.loc36_5.3
+// CHECK:STDOUT:   %.loc36_7: ref %X = converted %.loc36_5.1, %.loc36_5.4
+// CHECK:STDOUT:   %B.ref: %.ed9 = name_ref B, imports.%.8ab [concrete = constants.%empty_struct.61d]
+// CHECK:STDOUT:   %bound_method.loc36_16: <bound method> = bound_method %.loc36_7, %B.ref
+// CHECK:STDOUT:   %Cpp.ref.loc46: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %X.ref.loc46: type = name_ref X, imports.%X.decl [concrete = constants.%X]
+// CHECK:STDOUT:   %C.ref: %.ce5 = name_ref C, imports.%.7af [concrete = constants.%empty_struct.a7d]
+// CHECK:STDOUT:   %.loc56_5.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %Cpp.ref.loc56: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %X.ref.loc56: type = name_ref X, imports.%X.decl [concrete = constants.%X]
+// CHECK:STDOUT:   %.loc56_5.2: ref %X = temporary_storage
+// CHECK:STDOUT:   %.loc56_5.3: init %X = class_init (), %.loc56_5.2 [concrete = constants.%X.val]
+// CHECK:STDOUT:   %.loc56_5.4: ref %X = temporary %.loc56_5.2, %.loc56_5.3
+// CHECK:STDOUT:   %.loc56_7: ref %X = converted %.loc56_5.1, %.loc56_5.4
+// CHECK:STDOUT:   %D.ref: %.c13 = name_ref D, imports.%.7b7 [concrete = constants.%empty_struct.576]
+// CHECK:STDOUT:   %bound_method.loc56_16: <bound method> = bound_method %.loc56_7, %D.ref
+// CHECK:STDOUT:   %facet_value.loc56: %type_where = facet_value constants.%X, () [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %.loc56_5.5: %type_where = converted constants.%X, %facet_value.loc56 [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc56: <bound method> = bound_method %.loc56_5.4, constants.%AggregateT.as_type.as.Destroy.impl.Op.55b
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT:   %bound_method.loc56_5: <bound method> = bound_method %.loc56_5.4, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.1
+// CHECK:STDOUT:   %addr.loc56: %ptr.1f9 = addr_of %.loc56_5.4
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc56: init %empty_tuple.type = call %bound_method.loc56_5(%addr.loc56)
+// CHECK:STDOUT:   %facet_value.loc36: %type_where = facet_value constants.%X, () [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %.loc36_5.5: %type_where = converted constants.%X, %facet_value.loc36 [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.bound.loc36: <bound method> = bound_method %.loc36_5.4, constants.%AggregateT.as_type.as.Destroy.impl.Op.55b
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT:   %bound_method.loc36_5: <bound method> = bound_method %.loc36_5.4, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn.2
+// CHECK:STDOUT:   %addr.loc36: %ptr.1f9 = addr_of %.loc36_5.4
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call.loc36: init %empty_tuple.type = call %bound_method.loc36_5(%addr.loc36)
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

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

@@ -1453,13 +1453,6 @@ fn F() {
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {}
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: class @S {
-// CHECK:STDOUT:   complete_type_witness = invalid
-// CHECK:STDOUT:
-// CHECK:STDOUT: !members:
-// CHECK:STDOUT:   import Cpp//...
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: fn @F() {
 // CHECK:STDOUT: fn @F() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]

+ 147 - 0
toolchain/check/testdata/interop/cpp/function/qualified_param.carbon

@@ -0,0 +1,147 @@
+// 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-FILE: toolchain/testing/testdata/min_prelude/int.carbon
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/function/qualified_param.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/function/qualified_param.carbon
+
+// --- qualifiers.h
+
+void TakesConstInt(const int);
+
+struct S {};
+
+void TakesConstS(const S);
+
+// --- call_qualifiers.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "qualifiers.h";
+
+fn F() {
+  //@dump-sem-ir-begin
+  Cpp.TakesConstInt(42);
+
+  var s: Cpp.S = {};
+  Cpp.TakesConstS(s);
+  //@dump-sem-ir-end
+}
+
+// CHECK:STDOUT: --- call_qualifiers.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %.fc4: type = cpp_overload_set_type @ImplicitAs.Convert [concrete]
+// CHECK:STDOUT:   %empty_struct.cca: %.fc4 = struct_value () [concrete]
+// CHECK:STDOUT:   %int_42.20e: Core.IntLiteral = int_value 42 [concrete]
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
+// CHECK:STDOUT:   %i32: type = class_type @Int, @Int(%int_32) [concrete]
+// CHECK:STDOUT:   %TakesConstInt.type: type = fn_type @TakesConstInt [concrete]
+// CHECK:STDOUT:   %TakesConstInt: %TakesConstInt.type = struct_value () [concrete]
+// CHECK:STDOUT:   %ImplicitAs.type.d14: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
+// CHECK:STDOUT:   %ImplicitAs.Convert.type.1b6: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%i32) [concrete]
+// CHECK:STDOUT:   %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.1c0: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340 = struct_value () [symbolic]
+// CHECK:STDOUT:   %ImplicitAs.impl_witness.204: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.9e9, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.584: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.584 = struct_value () [concrete]
+// CHECK:STDOUT:   %ImplicitAs.facet: %ImplicitAs.type.d14 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.204) [concrete]
+// CHECK:STDOUT:   %.1df: type = fn_type_with_self_type %ImplicitAs.Convert.type.1b6, %ImplicitAs.facet [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %int_42.20e, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0 [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %int_42.20e, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %int_42.c68: %i32 = int_value 42 [concrete]
+// CHECK:STDOUT:   %S: type = class_type @S [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %pattern_type.7da: type = pattern_type %S [concrete]
+// CHECK:STDOUT:   %S.val: %S = struct_value () [concrete]
+// CHECK:STDOUT:   %.289: type = cpp_overload_set_type @Core.IntLiteral.as.ImplicitAs.impl.Convert [concrete]
+// CHECK:STDOUT:   %empty_struct.269: %.289 = struct_value () [concrete]
+// CHECK:STDOUT:   %const.e39: type = const_type %S [concrete]
+// CHECK:STDOUT:   %ptr.ff5: type = ptr_type %const.e39 [concrete]
+// CHECK:STDOUT:   %TakesConstS__carbon_thunk.type: type = fn_type @TakesConstS__carbon_thunk [concrete]
+// CHECK:STDOUT:   %TakesConstS__carbon_thunk: %TakesConstS__carbon_thunk.type = struct_value () [concrete]
+// CHECK:STDOUT:   %ptr.5c7: type = ptr_type %S [concrete]
+// CHECK:STDOUT:   %type_where: type = facet_type <type where .Self impls <CanAggregateDestroy>> [concrete]
+// CHECK:STDOUT:   %facet_value: %type_where = facet_value %S, () [concrete]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.type.df1: type = fn_type @AggregateT.as_type.as.Destroy.impl.Op, @AggregateT.as_type.as.Destroy.impl(%facet_value) [concrete]
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.cc2: %AggregateT.as_type.as.Destroy.impl.Op.type.df1 = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .TakesConstInt = %.6a4
+// CHECK:STDOUT:     .S = %S.decl
+// CHECK:STDOUT:     .TakesConstS = %.7c2
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.6a4: %.fc4 = cpp_overload_set_value @ImplicitAs.Convert [concrete = constants.%empty_struct.cca]
+// CHECK:STDOUT:   %TakesConstInt.decl: %TakesConstInt.type = fn_decl @TakesConstInt [concrete = constants.%TakesConstInt] {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import_ref.ee7: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.340) = import_ref Core//prelude/parts/int, loc23_39, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.1c0)]
+// CHECK:STDOUT:   %ImplicitAs.impl_witness_table.9e9 = impl_witness_table (%Core.import_ref.ee7), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
+// CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
+// CHECK:STDOUT:   %.7c2: %.289 = cpp_overload_set_value @Core.IntLiteral.as.ImplicitAs.impl.Convert [concrete = constants.%empty_struct.269]
+// CHECK:STDOUT:   %TakesConstS__carbon_thunk.decl: %TakesConstS__carbon_thunk.type = fn_decl @TakesConstS__carbon_thunk [concrete = constants.%TakesConstS__carbon_thunk] {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Cpp.ref.loc8: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %TakesConstInt.ref: %.fc4 = name_ref TakesConstInt, imports.%.6a4 [concrete = constants.%empty_struct.cca]
+// CHECK:STDOUT:   %int_42: Core.IntLiteral = int_value 42 [concrete = constants.%int_42.20e]
+// CHECK:STDOUT:   %impl.elem0: %.1df = impl_witness_access constants.%ImplicitAs.impl_witness.204, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0f0]
+// CHECK:STDOUT:   %bound_method.loc8_21.1: <bound method> = bound_method %int_42, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound]
+// CHECK:STDOUT:   %specific_fn: <specific function> = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc8_21.2: <bound method> = bound_method %int_42, %specific_fn [concrete = constants.%bound_method]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc8_21.2(%int_42) [concrete = constants.%int_42.c68]
+// CHECK:STDOUT:   %.loc8_21.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_42.c68]
+// CHECK:STDOUT:   %.loc8_21.2: %i32 = converted %int_42, %.loc8_21.1 [concrete = constants.%int_42.c68]
+// CHECK:STDOUT:   %TakesConstInt.call: init %empty_tuple.type = call imports.%TakesConstInt.decl(%.loc8_21.2)
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %s.patt: %pattern_type.7da = binding_pattern s [concrete]
+// CHECK:STDOUT:     %s.var_patt: %pattern_type.7da = var_pattern %s.patt [concrete]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %s.var: ref %S = var %s.var_patt
+// CHECK:STDOUT:   %.loc10_19.1: %empty_struct_type = struct_literal ()
+// CHECK:STDOUT:   %.loc10_19.2: init %S = class_init (), %s.var [concrete = constants.%S.val]
+// CHECK:STDOUT:   %.loc10_3.1: init %S = converted %.loc10_19.1, %.loc10_19.2 [concrete = constants.%S.val]
+// CHECK:STDOUT:   assign %s.var, %.loc10_3.1
+// CHECK:STDOUT:   %.loc10_13: type = splice_block %S.ref [concrete = constants.%S] {
+// CHECK:STDOUT:     %Cpp.ref.loc10: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// 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.loc11: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %TakesConstS.ref: %.289 = name_ref TakesConstS, imports.%.7c2 [concrete = constants.%empty_struct.269]
+// CHECK:STDOUT:   %s.ref: ref %S = name_ref s, %s
+// CHECK:STDOUT:   %.loc11_19.1: %S = bind_value %s.ref
+// CHECK:STDOUT:   %.loc11_19.2: ref %S = value_as_ref %.loc11_19.1
+// CHECK:STDOUT:   %addr.loc11: %ptr.5c7 = addr_of %.loc11_19.2
+// CHECK:STDOUT:   %.loc11_20.1: %ptr.ff5 = as_compatible %addr.loc11
+// CHECK:STDOUT:   %.loc11_20.2: %ptr.ff5 = converted %addr.loc11, %.loc11_20.1
+// CHECK:STDOUT:   %TakesConstS__carbon_thunk.call: init %empty_tuple.type = call imports.%TakesConstS__carbon_thunk.decl(%.loc11_20.2)
+// CHECK:STDOUT:   %facet_value: %type_where = facet_value constants.%S, () [concrete = constants.%facet_value]
+// CHECK:STDOUT:   %.loc10_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
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT:   %bound_method.loc10: <bound method> = bound_method %s.var, %AggregateT.as_type.as.Destroy.impl.Op.specific_fn
+// CHECK:STDOUT:   %addr.loc10: %ptr.5c7 = addr_of %s.var
+// CHECK:STDOUT:   %AggregateT.as_type.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc10(%addr.loc10)
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

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

@@ -402,10 +402,10 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %.1b9: type = cpp_overload_set_type @TakesRValue [concrete]
 // CHECK:STDOUT:   %.1b9: type = cpp_overload_set_type @TakesRValue [concrete]
 // CHECK:STDOUT:   %empty_struct: %.1b9 = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.1b9 = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %S.val: %S = struct_value () [concrete]
 // CHECK:STDOUT:   %S.val: %S = struct_value () [concrete]
 // CHECK:STDOUT:   %ptr.5c7: type = ptr_type %S [concrete]
 // CHECK:STDOUT:   %ptr.5c7: type = ptr_type %S [concrete]
 // CHECK:STDOUT:   %TakesRValue__carbon_thunk.type: type = fn_type @TakesRValue__carbon_thunk [concrete]
 // CHECK:STDOUT:   %TakesRValue__carbon_thunk.type: type = fn_type @TakesRValue__carbon_thunk [concrete]
@@ -422,8 +422,8 @@ fn F() {
 // CHECK:STDOUT:     .S = %S.decl
 // CHECK:STDOUT:     .S = %S.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
 // CHECK:STDOUT:   %.1f6: %.1b9 = cpp_overload_set_value @TakesRValue [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %.1f6: %.1b9 = cpp_overload_set_value @TakesRValue [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
 // CHECK:STDOUT:   %TakesRValue__carbon_thunk.decl: %TakesRValue__carbon_thunk.type = fn_decl @TakesRValue__carbon_thunk [concrete = constants.%TakesRValue__carbon_thunk] {
 // CHECK:STDOUT:   %TakesRValue__carbon_thunk.decl: %TakesRValue__carbon_thunk.type = fn_decl @TakesRValue__carbon_thunk [concrete = constants.%TakesRValue__carbon_thunk] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {

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

@@ -525,10 +525,10 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %S.val: %S = struct_value () [concrete]
 // CHECK:STDOUT:   %S.val: %S = struct_value () [concrete]
 // CHECK:STDOUT:   %ptr.5c7: type = ptr_type %S [concrete]
 // CHECK:STDOUT:   %ptr.5c7: type = ptr_type %S [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
@@ -545,8 +545,8 @@ fn F() {
 // CHECK:STDOUT:     .S = %S.decl
 // CHECK:STDOUT:     .S = %S.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
@@ -583,10 +583,10 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %S.val: %S = struct_value () [concrete]
 // CHECK:STDOUT:   %S.val: %S = struct_value () [concrete]
 // CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
 // CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
 // CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
 // CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
@@ -603,8 +603,8 @@ fn F() {
 // CHECK:STDOUT:     .S = %S.decl
 // CHECK:STDOUT:     .S = %S.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
 // CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {
 // CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
@@ -638,10 +638,10 @@ fn F() {
 // CHECK:STDOUT: --- fail_todo_import_definition_single_data_member_value_param_type.carbon
 // CHECK:STDOUT: --- fail_todo_import_definition_single_data_member_value_param_type.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @As.Convert [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @As.Convert [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT: imports {
@@ -650,8 +650,8 @@ fn F() {
 // CHECK:STDOUT:     .S = %S.decl
 // CHECK:STDOUT:     .S = %S.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @As.Convert [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @As.Convert [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F() {
 // CHECK:STDOUT: fn @F() {
@@ -668,10 +668,10 @@ fn F() {
 // CHECK:STDOUT: --- fail_todo_import_definition_multiple_data_members_value_param_type.carbon
 // CHECK:STDOUT: --- fail_todo_import_definition_multiple_data_members_value_param_type.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @As.Convert [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @As.Convert [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT: imports {
@@ -680,8 +680,8 @@ fn F() {
 // CHECK:STDOUT:     .S = %S.decl
 // CHECK:STDOUT:     .S = %S.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @As.Convert [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @As.Convert [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F() {
 // CHECK:STDOUT: fn @F() {
@@ -699,10 +699,10 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %S.val: %S = struct_value () [concrete]
 // CHECK:STDOUT:   %S.val: %S = struct_value () [concrete]
 // CHECK:STDOUT:   %pattern_type.cd8: type = pattern_type %S [concrete]
 // CHECK:STDOUT:   %pattern_type.cd8: type = pattern_type %S [concrete]
 // CHECK:STDOUT:   %ptr.edf: type = ptr_type %S [concrete]
 // CHECK:STDOUT:   %ptr.edf: type = ptr_type %S [concrete]
@@ -720,12 +720,12 @@ fn F() {
 // CHECK:STDOUT:     .N = %N
 // CHECK:STDOUT:     .N = %N
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %N: <namespace> = namespace [concrete] {
 // CHECK:STDOUT:   %N: <namespace> = namespace [concrete] {
 // CHECK:STDOUT:     .S = %S.decl
 // CHECK:STDOUT:     .S = %S.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
 // CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
-// CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
@@ -781,10 +781,10 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %.92e: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.92e: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.92e = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.92e = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %S.val: %S = struct_value () [concrete]
 // CHECK:STDOUT:   %S.val: %S = struct_value () [concrete]
 // CHECK:STDOUT:   %ptr.887: type = ptr_type %S [concrete]
 // CHECK:STDOUT:   %ptr.887: type = ptr_type %S [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
@@ -805,12 +805,12 @@ fn F() {
 // CHECK:STDOUT:     .N2 = %N2
 // CHECK:STDOUT:     .N2 = %N2
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.218: %.92e = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %N2: <namespace> = namespace [concrete] {
 // CHECK:STDOUT:   %N2: <namespace> = namespace [concrete] {
 // CHECK:STDOUT:     .S = %S.decl
 // CHECK:STDOUT:     .S = %S.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
 // CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
-// CHECK:STDOUT:   %.218: %.92e = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
@@ -850,11 +850,11 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %O: type = class_type @O [concrete]
-// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %O: type = class_type @O [concrete]
+// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %S.val: %S = struct_value () [concrete]
 // CHECK:STDOUT:   %S.val: %S = struct_value () [concrete]
 // CHECK:STDOUT:   %ptr.149: type = ptr_type %S [concrete]
 // CHECK:STDOUT:   %ptr.149: type = ptr_type %S [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
@@ -876,9 +876,9 @@ fn F() {
 // CHECK:STDOUT:     .O = %O.decl
 // CHECK:STDOUT:     .O = %O.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %O.decl: type = class_decl @O [concrete = constants.%O] {} {}
 // CHECK:STDOUT:   %O.decl: type = class_decl @O [concrete = constants.%O] {} {}
 // CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
 // CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
-// CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
@@ -1001,10 +1001,10 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %empty_struct.109: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct.109: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %S.val: %S = struct_value () [concrete]
 // CHECK:STDOUT:   %S.val: %S = struct_value () [concrete]
 // CHECK:STDOUT:   %ptr.5c7: type = ptr_type %S [concrete]
 // CHECK:STDOUT:   %ptr.5c7: type = ptr_type %S [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
@@ -1025,8 +1025,8 @@ fn F() {
 // CHECK:STDOUT:     .S = %S.decl
 // CHECK:STDOUT:     .S = %S.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo__carbon_thunk [concrete = constants.%empty_struct.109]
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo__carbon_thunk [concrete = constants.%empty_struct.109]
+// CHECK:STDOUT:   %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
@@ -1141,9 +1141,9 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
+// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %ptr.5c7: type = ptr_type %S [concrete]
 // CHECK:STDOUT:   %ptr.5c7: type = ptr_type %S [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
@@ -1188,9 +1188,9 @@ fn F() {
 // CHECK:STDOUT: --- import_decl_pointer_return_type.carbon
 // CHECK:STDOUT: --- import_decl_pointer_return_type.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
+// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %ptr: type = ptr_type %S [concrete]
 // CHECK:STDOUT:   %ptr: type = ptr_type %S [concrete]
 // CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
 // CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
 // CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
 // CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
@@ -1220,9 +1220,9 @@ fn F() {
 // CHECK:STDOUT: --- import_definition_pointer_return_type.carbon
 // CHECK:STDOUT: --- import_definition_pointer_return_type.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
+// CHECK:STDOUT:   %S: type = class_type @S [concrete]
 // CHECK:STDOUT:   %ptr: type = ptr_type %S [concrete]
 // CHECK:STDOUT:   %ptr: type = ptr_type %S [concrete]
 // CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
 // CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
 // CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
 // CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]

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

@@ -486,10 +486,10 @@ fn F() {
 // CHECK:STDOUT: --- fail_import_decl_value_param_type.carbon
 // CHECK:STDOUT: --- fail_import_decl_value_param_type.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @<null name> [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @<null name> [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT: imports {
@@ -498,8 +498,8 @@ fn F() {
 // CHECK:STDOUT:     .U = %U.decl
 // CHECK:STDOUT:     .U = %U.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %U.decl: type = class_decl @U [concrete = constants.%U] {} {}
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @<null name> [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @<null name> [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %U.decl: type = class_decl @U [concrete = constants.%U] {} {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F() {
 // CHECK:STDOUT: fn @F() {
@@ -516,10 +516,10 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %U.val: %U = struct_value () [concrete]
 // CHECK:STDOUT:   %U.val: %U = struct_value () [concrete]
 // CHECK:STDOUT:   %ptr.86f: type = ptr_type %U [concrete]
 // CHECK:STDOUT:   %ptr.86f: type = ptr_type %U [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
@@ -536,8 +536,8 @@ fn F() {
 // CHECK:STDOUT:     .U = %U.decl
 // CHECK:STDOUT:     .U = %U.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %U.decl: type = class_decl @U [concrete = constants.%U] {} {}
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %U.decl: type = class_decl @U [concrete = constants.%U] {} {}
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
@@ -573,10 +573,10 @@ fn F() {
 // CHECK:STDOUT: --- fail_import_definition_single_data_member_value_param_type.carbon
 // CHECK:STDOUT: --- fail_import_definition_single_data_member_value_param_type.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @As.Convert [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @As.Convert [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT: imports {
@@ -585,8 +585,8 @@ fn F() {
 // CHECK:STDOUT:     .U = %U.decl
 // CHECK:STDOUT:     .U = %U.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %U.decl: type = class_decl @U [concrete = constants.%U] {} {}
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @As.Convert [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @As.Convert [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %U.decl: type = class_decl @U [concrete = constants.%U] {} {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F() {
 // CHECK:STDOUT: fn @F() {
@@ -603,10 +603,10 @@ fn F() {
 // CHECK:STDOUT: --- fail_import_definition_multiple_data_members_value_param_type.carbon
 // CHECK:STDOUT: --- fail_import_definition_multiple_data_members_value_param_type.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @As.Convert [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @As.Convert [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
 // CHECK:STDOUT: imports {
@@ -615,8 +615,8 @@ fn F() {
 // CHECK:STDOUT:     .U = %U.decl
 // CHECK:STDOUT:     .U = %U.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %U.decl: type = class_decl @U [concrete = constants.%U] {} {}
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @As.Convert [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @As.Convert [concrete = constants.%empty_struct]
+// CHECK:STDOUT:   %U.decl: type = class_decl @U [concrete = constants.%U] {} {}
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F() {
 // CHECK:STDOUT: fn @F() {
@@ -634,10 +634,10 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %U.val: %U = struct_value () [concrete]
 // CHECK:STDOUT:   %U.val: %U = struct_value () [concrete]
 // CHECK:STDOUT:   %pattern_type.eb9: type = pattern_type %U [concrete]
 // CHECK:STDOUT:   %pattern_type.eb9: type = pattern_type %U [concrete]
 // CHECK:STDOUT:   %ptr.87e: type = ptr_type %U [concrete]
 // CHECK:STDOUT:   %ptr.87e: type = ptr_type %U [concrete]
@@ -655,12 +655,12 @@ fn F() {
 // CHECK:STDOUT:     .N = %N
 // CHECK:STDOUT:     .N = %N
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %N: <namespace> = namespace [concrete] {
 // CHECK:STDOUT:   %N: <namespace> = namespace [concrete] {
 // CHECK:STDOUT:     .U = %U.decl
 // CHECK:STDOUT:     .U = %U.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %U.decl: type = class_decl @U [concrete = constants.%U] {} {}
 // CHECK:STDOUT:   %U.decl: type = class_decl @U [concrete = constants.%U] {} {}
-// CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
@@ -716,10 +716,10 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %.92e: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.92e: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.92e = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.92e = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %U.val: %U = struct_value () [concrete]
 // CHECK:STDOUT:   %U.val: %U = struct_value () [concrete]
 // CHECK:STDOUT:   %ptr.8c1: type = ptr_type %U [concrete]
 // CHECK:STDOUT:   %ptr.8c1: type = ptr_type %U [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
@@ -740,12 +740,12 @@ fn F() {
 // CHECK:STDOUT:     .N2 = %N2
 // CHECK:STDOUT:     .N2 = %N2
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.218: %.92e = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %N2: <namespace> = namespace [concrete] {
 // CHECK:STDOUT:   %N2: <namespace> = namespace [concrete] {
 // CHECK:STDOUT:     .U = %U.decl
 // CHECK:STDOUT:     .U = %U.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %U.decl: type = class_decl @U [concrete = constants.%U] {} {}
 // CHECK:STDOUT:   %U.decl: type = class_decl @U [concrete = constants.%U] {} {}
-// CHECK:STDOUT:   %.218: %.92e = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
@@ -785,11 +785,11 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %O: type = class_type @O [concrete]
-// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %O: type = class_type @O [concrete]
+// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %U.val: %U = struct_value () [concrete]
 // CHECK:STDOUT:   %U.val: %U = struct_value () [concrete]
 // CHECK:STDOUT:   %ptr.a6c: type = ptr_type %U [concrete]
 // CHECK:STDOUT:   %ptr.a6c: type = ptr_type %U [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
@@ -811,9 +811,9 @@ fn F() {
 // CHECK:STDOUT:     .O = %O.decl
 // CHECK:STDOUT:     .O = %O.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %O.decl: type = class_decl @O [concrete = constants.%O] {} {}
 // CHECK:STDOUT:   %O.decl: type = class_decl @O [concrete = constants.%O] {} {}
 // CHECK:STDOUT:   %U.decl: type = class_decl @U [concrete = constants.%U] {} {}
 // CHECK:STDOUT:   %U.decl: type = class_decl @U [concrete = constants.%U] {} {}
-// CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo [concrete = constants.%empty_struct]
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
@@ -936,10 +936,10 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %empty_struct.109: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct.109: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
 // CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %U.val: %U = struct_value () [concrete]
 // CHECK:STDOUT:   %U.val: %U = struct_value () [concrete]
 // CHECK:STDOUT:   %ptr.86f: type = ptr_type %U [concrete]
 // CHECK:STDOUT:   %ptr.86f: type = ptr_type %U [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
@@ -960,8 +960,8 @@ fn F() {
 // CHECK:STDOUT:     .U = %U.decl
 // CHECK:STDOUT:     .U = %U.decl
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %U.decl: type = class_decl @U [concrete = constants.%U] {} {}
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo__carbon_thunk [concrete = constants.%empty_struct.109]
 // CHECK:STDOUT:   %.a21: %.c5d = cpp_overload_set_value @foo__carbon_thunk [concrete = constants.%empty_struct.109]
+// CHECK:STDOUT:   %U.decl: type = class_decl @U [concrete = constants.%U] {} {}
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:   %foo__carbon_thunk.decl: %foo__carbon_thunk.type = fn_decl @foo__carbon_thunk [concrete = constants.%foo__carbon_thunk] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:   } {
@@ -1076,9 +1076,9 @@ fn F() {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
+// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %ptr.86f: type = ptr_type %U [concrete]
 // CHECK:STDOUT:   %ptr.86f: type = ptr_type %U [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
@@ -1123,9 +1123,9 @@ fn F() {
 // CHECK:STDOUT: --- import_decl_pointer_return_type.carbon
 // CHECK:STDOUT: --- import_decl_pointer_return_type.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
+// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %ptr: type = ptr_type %U [concrete]
 // CHECK:STDOUT:   %ptr: type = ptr_type %U [concrete]
 // CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
 // CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
 // CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
 // CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
@@ -1155,9 +1155,9 @@ fn F() {
 // CHECK:STDOUT: --- import_definition_pointer_return_type.carbon
 // CHECK:STDOUT: --- import_definition_pointer_return_type.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
+// CHECK:STDOUT:   %U: type = class_type @U [concrete]
 // CHECK:STDOUT:   %ptr: type = ptr_type %U [concrete]
 // CHECK:STDOUT:   %ptr: type = ptr_type %U [concrete]
 // CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
 // CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
 // CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
 // CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]

+ 1 - 1
toolchain/check/testdata/interop/cpp/namespace.carbon

@@ -366,9 +366,9 @@ fn Use(y: Cpp.Y) -> i32 {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
-// CHECK:STDOUT:   %X: type = class_type @X [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %.c5d: type = cpp_overload_set_type @foo [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
 // CHECK:STDOUT:   %empty_struct: %.c5d = struct_value () [concrete]
+// CHECK:STDOUT:   %X: type = class_type @X [concrete]
 // CHECK:STDOUT:   %ptr.13d: type = ptr_type %X [concrete]
 // CHECK:STDOUT:   %ptr.13d: type = ptr_type %X [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk.type: type = fn_type @foo__carbon_thunk [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]
 // CHECK:STDOUT:   %foo__carbon_thunk: %foo__carbon_thunk.type = struct_value () [concrete]

+ 2 - 2
toolchain/check/testdata/interop/cpp/stdlib/string_view.carbon

@@ -60,14 +60,14 @@ fn G() -> str {
 // CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
 // CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
 // CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
 // CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
+// CHECK:STDOUT:   %.fd2: type = cpp_overload_set_type @Consume__carbon_thunk [concrete]
+// CHECK:STDOUT:   %empty_struct.c28: %.fd2 = struct_value () [concrete]
 // CHECK:STDOUT:   %str.ee0: type = class_type @String [concrete]
 // CHECK:STDOUT:   %str.ee0: type = class_type @String [concrete]
 // CHECK:STDOUT:   %int_64: Core.IntLiteral = int_value 64 [concrete]
 // CHECK:STDOUT:   %int_64: Core.IntLiteral = int_value 64 [concrete]
 // CHECK:STDOUT:   %u64: type = class_type @UInt, @UInt(%int_64) [concrete]
 // CHECK:STDOUT:   %u64: type = class_type @UInt, @UInt(%int_64) [concrete]
 // CHECK:STDOUT:   %int_8: Core.IntLiteral = int_value 8 [concrete]
 // CHECK:STDOUT:   %int_8: Core.IntLiteral = int_value 8 [concrete]
 // CHECK:STDOUT:   %u8: type = class_type @UInt, @UInt(%int_8) [concrete]
 // CHECK:STDOUT:   %u8: type = class_type @UInt, @UInt(%int_8) [concrete]
 // CHECK:STDOUT:   %ptr.3e8: type = ptr_type %u8 [concrete]
 // CHECK:STDOUT:   %ptr.3e8: type = ptr_type %u8 [concrete]
-// CHECK:STDOUT:   %.fd2: type = cpp_overload_set_type @Consume__carbon_thunk [concrete]
-// CHECK:STDOUT:   %empty_struct.c28: %.fd2 = struct_value () [concrete]
 // CHECK:STDOUT:   %str.3b1: %ptr.3e8 = string_literal "hello" [concrete]
 // CHECK:STDOUT:   %str.3b1: %ptr.3e8 = string_literal "hello" [concrete]
 // CHECK:STDOUT:   %int_5: %u64 = int_value 5 [concrete]
 // CHECK:STDOUT:   %int_5: %u64 = int_value 5 [concrete]
 // CHECK:STDOUT:   %String.val: %str.ee0 = struct_value (%str.3b1, %int_5) [concrete]
 // CHECK:STDOUT:   %String.val: %str.ee0 = struct_value (%str.3b1, %int_5) [concrete]

+ 1 - 0
toolchain/driver/testdata/stdin.carbon

@@ -30,6 +30,7 @@
 // CHECK:STDOUT:     ir0:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:     ir0:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:     ir1:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:     ir1:             {decl_id: inst<none>, is_export: false}
 // CHECK:STDOUT:   import_ir_insts: {}
 // CHECK:STDOUT:   import_ir_insts: {}
+// CHECK:STDOUT:   clang_decls:     {}
 // CHECK:STDOUT:   name_scopes:
 // CHECK:STDOUT:   name_scopes:
 // CHECK:STDOUT:     name_scope0:     {inst: inst14, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {}}
 // CHECK:STDOUT:     name_scope0:     {inst: inst14, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {}}
 // CHECK:STDOUT:   entity_names:    {}
 // CHECK:STDOUT:   entity_names:    {}

+ 15 - 1
toolchain/lower/file_context.cpp

@@ -437,6 +437,20 @@ auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id,
     return nullptr;
     return nullptr;
   }
   }
 
 
+  // Don't lower C++ functions that use a thunk. We will never reference them
+  // directly, and their signatures would not be expected to match the
+  // corresponding C++ function anyway.
+  if (function.special_function_kind ==
+      SemIR::Function::SpecialFunctionKind::HasCppThunk) {
+    // Make sure Clang emits this function.
+    // TODO: This shouldn't be necessary: Clang should emit definitions of
+    // functions that it emits calls to. But this doesn't currently work.
+    auto clang_decl_id = sem_ir().functions().Get(function_id).clang_decl_id;
+    HandleReferencedCppFunction(cast<clang::FunctionDecl>(
+        sem_ir().clang_decls().Get(clang_decl_id).key.decl));
+    return nullptr;
+  }
+
   // TODO: Consider tracking whether the function has been used, and only
   // TODO: Consider tracking whether the function has been used, and only
   // lowering it if it's needed.
   // lowering it if it's needed.
 
 
@@ -467,7 +481,7 @@ auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id,
     CARBON_CHECK(!specific_id.has_value(),
     CARBON_CHECK(!specific_id.has_value(),
                  "Specific functions cannot have C++ definitions");
                  "Specific functions cannot have C++ definitions");
     HandleReferencedCppFunction(
     HandleReferencedCppFunction(
-        sem_ir().clang_decls().Get(clang_decl_id).decl->getAsFunction());
+        sem_ir().clang_decls().Get(clang_decl_id).key.decl->getAsFunction());
     // TODO: Check that the signature and mangling generated by Clang and the
     // TODO: Check that the signature and mangling generated by Clang and the
     // one we generated are the same.
     // one we generated are the same.
   }
   }

+ 6 - 3
toolchain/lower/mangler.cpp

@@ -151,8 +151,11 @@ auto Mangler::Mangle(SemIR::FunctionId function_id,
     return "main";
     return "main";
   }
   }
   if (function.clang_decl_id.has_value()) {
   if (function.clang_decl_id.has_value()) {
-    return MangleCppClang(cast<clang::NamedDecl>(
-        sem_ir().clang_decls().Get(function.clang_decl_id).decl));
+    CARBON_CHECK(function.special_function_kind !=
+                     SemIR::Function::SpecialFunctionKind::HasCppThunk,
+                 "Shouldn't mangle C++ function that uses a thunk");
+    const auto& clang_decl = sem_ir().clang_decls().Get(function.clang_decl_id);
+    return MangleCppClang(cast<clang::NamedDecl>(clang_decl.key.decl));
   }
   }
   RawStringOstream os;
   RawStringOstream os;
   os << "_C";
   os << "_C";
@@ -208,7 +211,7 @@ auto Mangler::MangleGlobalVariable(SemIR::InstId pattern_id) -> std::string {
   auto var_name = sem_ir().entity_names().Get(var_name_id);
   auto var_name = sem_ir().entity_names().Get(var_name_id);
   if (var_name.clang_decl_id.has_value()) {
   if (var_name.clang_decl_id.has_value()) {
     return MangleCppClang(cast<clang::NamedDecl>(
     return MangleCppClang(cast<clang::NamedDecl>(
-        sem_ir().clang_decls().Get(var_name.clang_decl_id).decl));
+        sem_ir().clang_decls().Get(var_name.clang_decl_id).key.decl));
   }
   }
 
 
   RawStringOstream os;
   RawStringOstream os;

+ 15 - 15
toolchain/lower/testdata/interop/cpp/constructor.carbon

@@ -57,8 +57,18 @@ fn F() {
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #0
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #0
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
+// CHECK:STDOUT: define dso_local void @_ZN1CC1Ev.carbon_thunk(ptr %return) #1 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %return.addr = alloca ptr, align 8
+// CHECK:STDOUT:   store ptr %return, ptr %return.addr, align 8
+// CHECK:STDOUT:   %0 = load ptr, ptr %return.addr, align 8
+// CHECK:STDOUT:   call void @_ZN1CC1Ev(ptr nonnull align 4 dereferenceable(8) %0)
+// CHECK:STDOUT:   ret void
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: mustprogress noinline optnone
 // CHECK:STDOUT: ; Function Attrs: mustprogress noinline optnone
-// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN1CC1Ev(ptr nonnull align 4 dereferenceable(8) %this) #1 comdat align 2 {
+// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN1CC1Ev(ptr nonnull align 4 dereferenceable(8) %this) unnamed_addr #2 comdat align 2 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %this.addr = alloca ptr, align 8
 // CHECK:STDOUT:   %this.addr = alloca ptr, align 8
 // CHECK:STDOUT:   store ptr %this, ptr %this.addr, align 8
 // CHECK:STDOUT:   store ptr %this, ptr %this.addr, align 8
@@ -68,7 +78,7 @@ fn F() {
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: mustprogress noinline nounwind optnone
 // CHECK:STDOUT: ; Function Attrs: mustprogress noinline nounwind optnone
-// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN1CC2Ev(ptr nonnull align 4 dereferenceable(8) %this) unnamed_addr #2 comdat align 2 {
+// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN1CC2Ev(ptr nonnull align 4 dereferenceable(8) %this) unnamed_addr #3 comdat align 2 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %this.addr = alloca ptr, align 8
 // CHECK:STDOUT:   %this.addr = alloca ptr, align 8
 // CHECK:STDOUT:   store ptr %this, ptr %this.addr, align 8
 // CHECK:STDOUT:   store ptr %this, ptr %this.addr, align 8
@@ -80,20 +90,10 @@ fn F() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
-// CHECK:STDOUT: define dso_local void @_ZN1CC1Ev.carbon_thunk(ptr %return) #3 {
-// CHECK:STDOUT: entry:
-// CHECK:STDOUT:   %return.addr = alloca ptr, align 8
-// CHECK:STDOUT:   store ptr %return, ptr %return.addr, align 8
-// CHECK:STDOUT:   %0 = load ptr, ptr %return.addr, align 8
-// CHECK:STDOUT:   call void @_ZN1CC1Ev(ptr nonnull align 4 dereferenceable(8) %0)
-// CHECK:STDOUT:   ret void
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
-// CHECK:STDOUT: attributes #1 = { mustprogress noinline optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
-// CHECK:STDOUT: attributes #2 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
-// CHECK:STDOUT: attributes #3 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT: attributes #2 = { mustprogress noinline optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT: attributes #3 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}

+ 6 - 4
toolchain/lower/testdata/interop/cpp/enum.carbon

@@ -120,8 +120,6 @@ fn Call() {
 // CHECK:STDOUT:   ret void, !dbg !24
 // CHECK:STDOUT:   ret void, !dbg !24
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_ZN1C1FENS_1EE(i16)
-// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #0
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #0
 // CHECK:STDOUT:
 // CHECK:STDOUT:
@@ -136,11 +134,14 @@ fn Call() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_ZN1C1FENS_1EE(i16 signext) #2
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
 // CHECK:STDOUT: ; uselistorder directives
 // CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 6, 5, 4, 3, 2, 1, 0 }
 // CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 6, 5, 4, 3, 2, 1, 0 }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT: attributes #2 = { "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}
@@ -198,8 +199,6 @@ fn Call() {
 // CHECK:STDOUT:   ret void, !dbg !18
 // CHECK:STDOUT:   ret void, !dbg !18
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_ZN1C1FENS_1EE(i16)
-// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #0
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #0
 // CHECK:STDOUT:
 // CHECK:STDOUT:
@@ -214,8 +213,11 @@ fn Call() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_ZN1C1FENS_1EE(i16 signext) #2
+// CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT: attributes #2 = { "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}

+ 3 - 2
toolchain/lower/testdata/interop/cpp/extern_c.carbon

@@ -153,8 +153,6 @@ fn MyF() {
 // CHECK:STDOUT:   ret void, !dbg !11
 // CHECK:STDOUT:   ret void, !dbg !11
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_Zpl1XS_(ptr sret({}), ptr, ptr)
-// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: define dso_local void @_Zpl1XS_.carbon_thunk(ptr %0, ptr %1, ptr %return) #0 {
 // CHECK:STDOUT: define dso_local void @_Zpl1XS_.carbon_thunk(ptr %0, ptr %1, ptr %return) #0 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
@@ -174,7 +172,10 @@ fn MyF() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_Zpl1XS_() #1
+// CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT: attributes #0 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT: attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}

+ 72 - 1
toolchain/lower/testdata/interop/cpp/function_decl.carbon

@@ -2,7 +2,7 @@
 // Exceptions. See /LICENSE for license information.
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //
-// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon
+// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon
 //
 //
 // AUTOUPDATE
 // AUTOUPDATE
 // TIP: To test this file alone, run:
 // TIP: To test this file alone, run:
@@ -106,6 +106,26 @@ fn MyF() {
   Cpp.foo2();
   Cpp.foo2();
 }
 }
 
 
+// ============================================================================
+// Default arguments
+// ============================================================================
+
+// --- with_default_args.h
+
+void F(int a = 1, int b = 2);
+
+// --- call_with_default_args.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "with_default_args.h";
+
+fn MyF() {
+  Cpp.F();
+  Cpp.F(3);
+  Cpp.F(3, 4);
+}
+
 // CHECK:STDOUT: ; ModuleID = 'import_function_decl.carbon'
 // CHECK:STDOUT: ; ModuleID = 'import_function_decl.carbon'
 // CHECK:STDOUT: source_filename = "import_function_decl.carbon"
 // CHECK:STDOUT: source_filename = "import_function_decl.carbon"
 // CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
 // CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
@@ -307,3 +327,54 @@ fn MyF() {
 // CHECK:STDOUT: !9 = !{}
 // CHECK:STDOUT: !9 = !{}
 // CHECK:STDOUT: !10 = !DILocation(line: 8, column: 3, scope: !7)
 // CHECK:STDOUT: !10 = !DILocation(line: 8, column: 3, scope: !7)
 // CHECK:STDOUT: !11 = !DILocation(line: 6, column: 1, scope: !7)
 // CHECK:STDOUT: !11 = !DILocation(line: 6, column: 1, scope: !7)
+// CHECK:STDOUT: ; ModuleID = 'call_with_default_args.carbon'
+// CHECK:STDOUT: source_filename = "call_with_default_args.carbon"
+// CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+// CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu"
+// CHECK:STDOUT:
+// CHECK:STDOUT: define void @_CMyF.Main() !dbg !7 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   call void @_Z1Fii.carbon_thunk0(), !dbg !10
+// CHECK:STDOUT:   call void @_Z1Fii.carbon_thunk1(i32 3), !dbg !11
+// CHECK:STDOUT:   call void @_Z1Fii(i32 3, i32 4), !dbg !12
+// CHECK:STDOUT:   ret void, !dbg !13
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_Z1Fii(i32, i32)
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
+// CHECK:STDOUT: define dso_local void @_Z1Fii.carbon_thunk0() #0 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   call void @_Z1Fii(i32 1, i32 2)
+// CHECK:STDOUT:   ret void
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
+// CHECK:STDOUT: define dso_local void @_Z1Fii.carbon_thunk1(i32 %a) #0 {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %a.addr = alloca i32, align 4
+// CHECK:STDOUT:   store i32 %a, ptr %a.addr, align 4
+// CHECK:STDOUT:   %0 = load i32, ptr %a.addr, align 4
+// CHECK:STDOUT:   call void @_Z1Fii(i32 %0, i32 2)
+// CHECK:STDOUT:   ret void
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT:
+// CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
+// CHECK:STDOUT: !llvm.dbg.cu = !{!5}
+// CHECK:STDOUT:
+// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5}
+// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3}
+// CHECK:STDOUT: !2 = !{i32 1, !"wchar_size", i32 4}
+// CHECK:STDOUT: !3 = !{i32 8, !"PIC Level", i32 0}
+// CHECK:STDOUT: !4 = !{i32 7, !"PIE Level", i32 2}
+// CHECK:STDOUT: !5 = distinct !DICompileUnit(language: DW_LANG_C, file: !6, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+// CHECK:STDOUT: !6 = !DIFile(filename: "call_with_default_args.carbon", directory: "")
+// CHECK:STDOUT: !7 = distinct !DISubprogram(name: "MyF", linkageName: "_CMyF.Main", scope: null, file: !6, line: 6, type: !8, spFlags: DISPFlagDefinition, unit: !5)
+// CHECK:STDOUT: !8 = !DISubroutineType(types: !9)
+// CHECK:STDOUT: !9 = !{}
+// CHECK:STDOUT: !10 = !DILocation(line: 7, column: 3, scope: !7)
+// CHECK:STDOUT: !11 = !DILocation(line: 8, column: 3, scope: !7)
+// CHECK:STDOUT: !12 = !DILocation(line: 9, column: 3, scope: !7)
+// CHECK:STDOUT: !13 = !DILocation(line: 6, column: 1, scope: !7)

+ 18 - 17
toolchain/lower/testdata/interop/cpp/method.carbon

@@ -89,29 +89,29 @@ fn Call(n: Cpp.NeedThunk) {
 // CHECK:STDOUT:   ret i32 %by_val__carbon_thunk.call, !dbg !11
 // CHECK:STDOUT:   ret i32 %by_val__carbon_thunk.call, !dbg !11
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: ; Function Attrs: mustprogress noinline nounwind optnone
-// CHECK:STDOUT: define linkonce_odr dso_local i32 @_ZNK1A6by_valEv(ptr nonnull align 8 dereferenceable(12) %this) #0 comdat align 2 {
+// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
+// CHECK:STDOUT: define dso_local i32 @_ZNK1A6by_valEv.carbon_thunk(ptr %this) #0 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %this.addr = alloca ptr, align 8
 // CHECK:STDOUT:   %this.addr = alloca ptr, align 8
 // CHECK:STDOUT:   store ptr %this, ptr %this.addr, align 8
 // CHECK:STDOUT:   store ptr %this, ptr %this.addr, align 8
-// CHECK:STDOUT:   %this1 = load ptr, ptr %this.addr, align 8
-// CHECK:STDOUT:   %n = getelementptr inbounds nuw %struct.A, ptr %this1, i32 0, i32 1
-// CHECK:STDOUT:   %0 = load i32, ptr %n, align 8
-// CHECK:STDOUT:   ret i32 %0
+// CHECK:STDOUT:   %0 = load ptr, ptr %this.addr, align 8
+// CHECK:STDOUT:   %call = call i32 @_ZNK1A6by_valEv(ptr nonnull align 8 dereferenceable(12) %0)
+// CHECK:STDOUT:   ret i32 %call
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
-// CHECK:STDOUT: define dso_local i32 @_ZNK1A6by_valEv.carbon_thunk(ptr %this) #1 {
+// CHECK:STDOUT: ; Function Attrs: mustprogress noinline nounwind optnone
+// CHECK:STDOUT: define linkonce_odr dso_local i32 @_ZNK1A6by_valEv(ptr nonnull align 8 dereferenceable(12) %this) #1 comdat align 2 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %this.addr = alloca ptr, align 8
 // CHECK:STDOUT:   %this.addr = alloca ptr, align 8
 // CHECK:STDOUT:   store ptr %this, ptr %this.addr, align 8
 // CHECK:STDOUT:   store ptr %this, ptr %this.addr, align 8
-// CHECK:STDOUT:   %0 = load ptr, ptr %this.addr, align 8
-// CHECK:STDOUT:   %call = call i32 @_ZNK1A6by_valEv(ptr nonnull align 8 dereferenceable(12) %0)
-// CHECK:STDOUT:   ret i32 %call
+// CHECK:STDOUT:   %this1 = load ptr, ptr %this.addr, align 8
+// CHECK:STDOUT:   %n = getelementptr inbounds nuw %struct.A, ptr %this1, i32 0, i32 1
+// CHECK:STDOUT:   %0 = load i32, ptr %n, align 8
+// CHECK:STDOUT:   ret i32 %0
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: attributes #0 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
-// CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT: attributes #0 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT: attributes #1 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}
@@ -231,10 +231,6 @@ fn Call(n: Cpp.NeedThunk) {
 // CHECK:STDOUT:   ret void, !dbg !14
 // CHECK:STDOUT:   ret void, !dbg !14
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_ZNK9NeedThunk8ImplicitEa(ptr, i8)
-// CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_ZNH9NeedThunk8ExplicitES_a(ptr, i8)
-// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #0
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #0
 // CHECK:STDOUT:
 // CHECK:STDOUT:
@@ -252,6 +248,8 @@ fn Call(n: Cpp.NeedThunk) {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_ZNK9NeedThunk8ImplicitEa(ptr nonnull align 1 dereferenceable(1), i8 signext) #2
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: define dso_local void @_ZNH9NeedThunk8ExplicitES_a.carbon_thunk(ptr %0, ptr %c) #1 {
 // CHECK:STDOUT: define dso_local void @_ZNH9NeedThunk8ExplicitES_a.carbon_thunk(ptr %0, ptr %c) #1 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
@@ -267,11 +265,14 @@ fn Call(n: Cpp.NeedThunk) {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_ZNH9NeedThunk8ExplicitES_a(i8 signext) #2
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
 // CHECK:STDOUT: ; uselistorder directives
 // CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
 // CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT: attributes #2 = { "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}

+ 12 - 10
toolchain/lower/testdata/interop/cpp/parameters.carbon

@@ -140,10 +140,6 @@ fn PassValueExpr(y: Cpp.Y) {
 // CHECK:STDOUT:   ret void, !dbg !18
 // CHECK:STDOUT:   ret void, !dbg !18
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_Z11pass_signedasil(i8, i16, i32, i64)
-// CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_Z13pass_unsignedhtjm(i8, i16, i32, i64)
-// CHECK:STDOUT:
 // CHECK:STDOUT: declare i16 @_CMakeShort.Main()
 // CHECK:STDOUT: declare i16 @_CMakeShort.Main()
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @_CPassShort.Main(i16 %a, ptr %b) !dbg !19 {
 // CHECK:STDOUT: define void @_CPassShort.Main(i16 %a, ptr %b) !dbg !19 {
@@ -170,8 +166,6 @@ fn PassValueExpr(y: Cpp.Y) {
 // CHECK:STDOUT:   ret void, !dbg !28
 // CHECK:STDOUT:   ret void, !dbg !28
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_Z10pass_shorts(i16)
-// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #0
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #0
 // CHECK:STDOUT:
 // CHECK:STDOUT:
@@ -196,6 +190,8 @@ fn PassValueExpr(y: Cpp.Y) {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_Z11pass_signedasil(i8 signext, i16 signext, i32, i64) #2
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: define dso_local void @_Z13pass_unsignedhtjm.carbon_thunk(ptr %0, ptr %1, ptr %2, ptr %3) #1 {
 // CHECK:STDOUT: define dso_local void @_Z13pass_unsignedhtjm.carbon_thunk(ptr %0, ptr %1, ptr %2, ptr %3) #1 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
@@ -219,6 +215,8 @@ fn PassValueExpr(y: Cpp.Y) {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_Z13pass_unsignedhtjm(i8 zeroext, i16 zeroext, i32, i64) #2
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: define dso_local void @_Z10pass_shorts.carbon_thunk(ptr %0) #1 {
 // CHECK:STDOUT: define dso_local void @_Z10pass_shorts.carbon_thunk(ptr %0) #1 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
@@ -230,11 +228,14 @@ fn PassValueExpr(y: Cpp.Y) {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_Z10pass_shorts(i16 signext) #2
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
 // CHECK:STDOUT: ; uselistorder directives
 // CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }
 // CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT: attributes #2 = { "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}
@@ -289,8 +290,6 @@ fn PassValueExpr(y: Cpp.Y) {
 // CHECK:STDOUT:   ret void, !dbg !15
 // CHECK:STDOUT:   ret void, !dbg !15
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_Z11pass_struct1X(ptr)
-// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #0
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #0
 // CHECK:STDOUT:
 // CHECK:STDOUT:
@@ -315,12 +314,15 @@ fn PassValueExpr(y: Cpp.Y) {
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #2
 // CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #2
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_Z11pass_struct1X(i64, i32) #3
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
 // CHECK:STDOUT: ; uselistorder directives
 // CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 }
 // CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT: attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
+// CHECK:STDOUT: attributes #3 = { "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}
@@ -362,8 +364,6 @@ fn PassValueExpr(y: Cpp.Y) {
 // CHECK:STDOUT:   ret void, !dbg !15
 // CHECK:STDOUT:   ret void, !dbg !15
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_Z11pass_struct1Y(ptr)
-// CHECK:STDOUT:
 // CHECK:STDOUT: declare void @_CMake.Main(ptr sret([12 x i8]))
 // CHECK:STDOUT: declare void @_CMake.Main(ptr sret([12 x i8]))
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @_CPassInitExpr.Main() !dbg !16 {
 // CHECK:STDOUT: define void @_CPassInitExpr.Main() !dbg !16 {
@@ -398,6 +398,8 @@ fn PassValueExpr(y: Cpp.Y) {
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: declare void @_ZN1YC1ERKS_(ptr nonnull align 4 dereferenceable(12), ptr nonnull align 4 dereferenceable(12)) unnamed_addr #2
 // CHECK:STDOUT: declare void @_ZN1YC1ERKS_(ptr nonnull align 4 dereferenceable(12), ptr nonnull align 4 dereferenceable(12)) unnamed_addr #2
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_Z11pass_struct1Y(ptr dead_on_return) #2
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
 // CHECK:STDOUT: ; uselistorder directives
 // CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
 // CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
 // CHECK:STDOUT:
 // CHECK:STDOUT:

+ 13 - 12
toolchain/lower/testdata/interop/cpp/reference.carbon

@@ -68,18 +68,6 @@ fn PassRefs() {
 // CHECK:STDOUT:   ret void, !dbg !20
 // CHECK:STDOUT:   ret void, !dbg !20
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_Z8TakeCRefR1C(ptr)
-// CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_Z9TakeCRRefO1C(ptr)
-// CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_Z13TakeConstCRefRK1C(ptr)
-// CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_Z10TakeIntRefRi(ptr)
-// CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_Z11TakeIntRRefOi(i32)
-// CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_Z15TakeConstIntRefRKi(ptr)
-// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #0
 // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #0
 // CHECK:STDOUT:
 // CHECK:STDOUT:
@@ -96,6 +84,8 @@ fn PassRefs() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_Z8TakeCRefR1C(ptr nonnull align 1 dereferenceable(1)) #3
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: define dso_local void @_Z9TakeCRRefO1C.carbon_thunk(ptr %0) #2 {
 // CHECK:STDOUT: define dso_local void @_Z9TakeCRRefO1C.carbon_thunk(ptr %0) #2 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
@@ -106,6 +96,8 @@ fn PassRefs() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_Z9TakeCRRefO1C(ptr nonnull align 1 dereferenceable(1)) #3
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: define dso_local void @_Z13TakeConstCRefRK1C.carbon_thunk(ptr %0) #2 {
 // CHECK:STDOUT: define dso_local void @_Z13TakeConstCRefRK1C.carbon_thunk(ptr %0) #2 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
@@ -116,6 +108,8 @@ fn PassRefs() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_Z13TakeConstCRefRK1C(ptr nonnull align 1 dereferenceable(1)) #3
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: define dso_local void @_Z10TakeIntRefRi.carbon_thunk(ptr %0) #2 {
 // CHECK:STDOUT: define dso_local void @_Z10TakeIntRefRi.carbon_thunk(ptr %0) #2 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
@@ -126,6 +120,8 @@ fn PassRefs() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_Z10TakeIntRefRi(ptr nonnull align 4 dereferenceable(4)) #3
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: define dso_local void @_Z11TakeIntRRefOi.carbon_thunk(ptr %0) #2 {
 // CHECK:STDOUT: define dso_local void @_Z11TakeIntRRefOi.carbon_thunk(ptr %0) #2 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
@@ -136,6 +132,8 @@ fn PassRefs() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_Z11TakeIntRRefOi(ptr nonnull align 4 dereferenceable(4)) #3
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: define dso_local void @_Z15TakeConstIntRefRKi.carbon_thunk(ptr %0) #2 {
 // CHECK:STDOUT: define dso_local void @_Z15TakeConstIntRefRKi.carbon_thunk(ptr %0) #2 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
@@ -146,12 +144,15 @@ fn PassRefs() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_Z15TakeConstIntRefRKi(ptr nonnull align 4 dereferenceable(4)) #3
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
 // CHECK:STDOUT: ; uselistorder directives
 // CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 3, 2, 1, 0 }
 // CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 3, 2, 1, 0 }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #2 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT: attributes #2 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT: attributes #3 = { "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}

+ 12 - 10
toolchain/lower/testdata/interop/cpp/return.carbon

@@ -111,8 +111,6 @@ fn Var() {
 // CHECK:STDOUT:   ret i8 %.loc6_40.2, !dbg !11
 // CHECK:STDOUT:   ret i8 %.loc6_40.2, !dbg !11
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: declare i8 @_Z8ReturnU8v()
-// CHECK:STDOUT:
 // CHECK:STDOUT: define i16 @_CGetU16.Main() !dbg !12 {
 // CHECK:STDOUT: define i16 @_CGetU16.Main() !dbg !12 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc7_43.1.temp = alloca i16, align 2, !dbg !13
 // CHECK:STDOUT:   %.loc7_43.1.temp = alloca i16, align 2, !dbg !13
@@ -122,8 +120,6 @@ fn Var() {
 // CHECK:STDOUT:   ret i16 %.loc7_43.2, !dbg !14
 // CHECK:STDOUT:   ret i16 %.loc7_43.2, !dbg !14
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: declare i16 @_Z9ReturnU16v()
-// CHECK:STDOUT:
 // CHECK:STDOUT: define i32 @_CGetU32.Main() !dbg !15 {
 // CHECK:STDOUT: define i32 @_CGetU32.Main() !dbg !15 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %ReturnU32.call = call i32 @_Z9ReturnU32v(), !dbg !16
 // CHECK:STDOUT:   %ReturnU32.call = call i32 @_Z9ReturnU32v(), !dbg !16
@@ -149,8 +145,6 @@ fn Var() {
 // CHECK:STDOUT:   ret i8 %.loc11_40.2, !dbg !23
 // CHECK:STDOUT:   ret i8 %.loc11_40.2, !dbg !23
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: declare i8 @_Z8ReturnI8v()
-// CHECK:STDOUT:
 // CHECK:STDOUT: define i16 @_CGetI16.Main() !dbg !24 {
 // CHECK:STDOUT: define i16 @_CGetI16.Main() !dbg !24 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc12_43.1.temp = alloca i16, align 2, !dbg !25
 // CHECK:STDOUT:   %.loc12_43.1.temp = alloca i16, align 2, !dbg !25
@@ -160,8 +154,6 @@ fn Var() {
 // CHECK:STDOUT:   ret i16 %.loc12_43.2, !dbg !26
 // CHECK:STDOUT:   ret i16 %.loc12_43.2, !dbg !26
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: declare i16 @_Z9ReturnI16v()
-// CHECK:STDOUT:
 // CHECK:STDOUT: define i32 @_CGetI32.Main() !dbg !27 {
 // CHECK:STDOUT: define i32 @_CGetI32.Main() !dbg !27 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %ReturnI32.call = call i32 @_Z9ReturnI32v(), !dbg !28
 // CHECK:STDOUT:   %ReturnI32.call = call i32 @_Z9ReturnI32v(), !dbg !28
@@ -278,6 +270,8 @@ fn Var() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare zeroext i8 @_Z8ReturnU8v() #2
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: define dso_local void @_Z9ReturnU16v.carbon_thunk(ptr %return) #1 {
 // CHECK:STDOUT: define dso_local void @_Z9ReturnU16v.carbon_thunk(ptr %return) #1 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
@@ -289,6 +283,8 @@ fn Var() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare zeroext i16 @_Z9ReturnU16v() #2
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: define dso_local void @_Z8ReturnI8v.carbon_thunk(ptr %return) #1 {
 // CHECK:STDOUT: define dso_local void @_Z8ReturnI8v.carbon_thunk(ptr %return) #1 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
@@ -300,6 +296,8 @@ fn Var() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare signext i8 @_Z8ReturnI8v() #2
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: define dso_local void @_Z9ReturnI16v.carbon_thunk(ptr %return) #1 {
 // CHECK:STDOUT: define dso_local void @_Z9ReturnI16v.carbon_thunk(ptr %return) #1 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
@@ -311,11 +309,14 @@ fn Var() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare signext i16 @_Z9ReturnI16v() #2
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
 // CHECK:STDOUT: ; uselistorder directives
 // CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }
 // CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT: attributes #2 = { "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}
@@ -404,8 +405,6 @@ fn Var() {
 // CHECK:STDOUT:   ret void, !dbg !11
 // CHECK:STDOUT:   ret void, !dbg !11
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_Z4Makev(ptr sret([16 x i8]))
-// CHECK:STDOUT:
 // CHECK:STDOUT: define void @_CLet.Main() !dbg !12 {
 // CHECK:STDOUT: define void @_CLet.Main() !dbg !12 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %.loc9_27.1.temp = alloca [16 x i8], align 1, !dbg !13
 // CHECK:STDOUT:   %.loc9_27.1.temp = alloca [16 x i8], align 1, !dbg !13
@@ -435,11 +434,14 @@ fn Var() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: declare void @_Z4Makev(ptr dead_on_unwind writable sret(%struct.X) align 8) #2
+// CHECK:STDOUT:
 // CHECK:STDOUT: ; uselistorder directives
 // CHECK:STDOUT: ; uselistorder directives
 // CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
 // CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
 // CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT: attributes #2 = { "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}

+ 7 - 7
toolchain/lower/testdata/interop/cpp/template.carbon

@@ -62,13 +62,6 @@ fn PassClass(a: Cpp.Class) -> Cpp.Class {
 // CHECK:STDOUT:   ret i32 %0
 // CHECK:STDOUT:   ret i32 %0
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
-// CHECK:STDOUT: ; Function Attrs: mustprogress noinline nounwind optnone
-// CHECK:STDOUT: define linkonce_odr dso_local void @_Z8identityI5ClassET_S1_() #0 comdat {
-// CHECK:STDOUT: entry:
-// CHECK:STDOUT:   %x = alloca %class.Class, align 1
-// CHECK:STDOUT:   ret void
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
 // CHECK:STDOUT: define dso_local void @_Z8identityI5ClassET_S1_.carbon_thunk(ptr %x, ptr %return) #1 {
 // CHECK:STDOUT: define dso_local void @_Z8identityI5ClassET_S1_.carbon_thunk(ptr %x, ptr %return) #1 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT: entry:
@@ -84,6 +77,13 @@ fn PassClass(a: Cpp.Class) -> Cpp.Class {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: ; Function Attrs: mustprogress noinline nounwind optnone
+// CHECK:STDOUT: define linkonce_odr dso_local void @_Z8identityI5ClassET_S1_() #0 comdat {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %x = alloca %class.Class, align 1
+// CHECK:STDOUT:   ret void
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT: attributes #0 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT:
 // CHECK:STDOUT:

+ 1 - 0
toolchain/sem_ir/BUILD

@@ -55,6 +55,7 @@ cc_library(
         ":typed_insts",
         ":typed_insts",
         "//common:hashtable_key_context",
         "//common:hashtable_key_context",
         "//common:ostream",
         "//common:ostream",
+        "//common:raw_string_ostream",
         "//toolchain/base:canonical_value_store",
         "//toolchain/base:canonical_value_store",
         "@llvm-project//clang:ast",
         "@llvm-project//clang:ast",
     ],
     ],

+ 22 - 3
toolchain/sem_ir/clang_decl.cpp

@@ -5,13 +5,32 @@
 #include "toolchain/sem_ir/clang_decl.h"
 #include "toolchain/sem_ir/clang_decl.h"
 
 
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclBase.h"
+#include "clang/AST/TextNodeDumper.h"
+#include "common/ostream.h"
+#include "common/raw_string_ostream.h"
 
 
 namespace Carbon::SemIR {
 namespace Carbon::SemIR {
 
 
+auto ClangDeclKey::Print(llvm::raw_ostream& out) const -> void {
+  RawStringOstream decl_stream;
+  auto policy = decl->getASTContext().getPrintingPolicy();
+  policy.TerseOutput = true;
+  if (isa<clang::TranslationUnitDecl>(decl)) {
+    decl_stream << "<translation unit>";
+  } else {
+    decl->print(decl_stream, policy);
+  }
+
+  if (num_params != -1) {
+    out << "{decl: \"" << FormatEscaped(decl_stream.TakeStr())
+        << "\", num_params: " << num_params << "}";
+  } else {
+    out << "\"" << FormatEscaped(decl_stream.TakeStr()) << "\"";
+  }
+}
+
 auto ClangDecl::Print(llvm::raw_ostream& out) const -> void {
 auto ClangDecl::Print(llvm::raw_ostream& out) const -> void {
-  out << "{decl: ";
-  decl->print(out);
-  out << ", inst_id: " << inst_id << "}";
+  out << "{key: " << key << ", inst_id: " << inst_id << "}";
 }
 }
 
 
 }  // namespace Carbon::SemIR
 }  // namespace Carbon::SemIR

+ 73 - 34
toolchain/sem_ir/clang_decl.h

@@ -5,50 +5,59 @@
 #ifndef CARBON_TOOLCHAIN_SEM_IR_CLANG_DECL_H_
 #ifndef CARBON_TOOLCHAIN_SEM_IR_CLANG_DECL_H_
 #define CARBON_TOOLCHAIN_SEM_IR_CLANG_DECL_H_
 #define CARBON_TOOLCHAIN_SEM_IR_CLANG_DECL_H_
 
 
+#include <concepts>
+
+#include "clang/AST/Decl.h"
 #include "common/hashtable_key_context.h"
 #include "common/hashtable_key_context.h"
 #include "common/ostream.h"
 #include "common/ostream.h"
 #include "toolchain/base/canonical_value_store.h"
 #include "toolchain/base/canonical_value_store.h"
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/ids.h"
 
 
-// NOLINTNEXTLINE(readability-identifier-naming)
-namespace clang {
-
-// Forward declare indexed types, for integration with ValueStore.
-class Decl;
-
-}  // namespace clang
-
 namespace Carbon::SemIR {
 namespace Carbon::SemIR {
 
 
-// A Clang declaration mapped to a Carbon instruction.
-//
-// Note that Clang's AST uses address-identity for nodes, which means the
-// pointer is the canonical way to represent a specific AST node and is expected
-// to be sufficient for comparison, hashing, etc.
+// A key describing a Clang declaration that can be looked up in the value
+// store. This is a `clang::Decl*` pointing to a canonical declaration, plus any
+// other information that affects the mapping into Carbon. Currently this
+// includes the number of imported parameters for a function with default
+// arguments.
 //
 //
-// This type is specifically designed for use in a `CanonicalValueStore` and
-// provide a single canonical access from SemIR to each `clang::Decl*` used.
-// This also ensures that a given `clang::Decl*` is associated with exactly one
-// instruction, and the `inst_id` here provides access to that instruction from
-// either the `ClangDeclId` or the `clang::Decl*`.
-struct ClangDecl : public Printable<ClangDecl> {
-  auto Print(llvm::raw_ostream& out) const -> void;
+// A canonical declaration pointer is used so that we can perform direct address
+// comparisons and hash this structure based on its contents.
+struct ClangDeclKey : public Printable<ClangDeclKey> {
+  // For declaration classes that are unrelated to FunctionDecl, no parameter
+  // count is expected.
+  template <typename DeclT>
+    requires(std::derived_from<DeclT, clang::Decl> &&
+             !std::derived_from<clang::FunctionDecl, DeclT> &&
+             !std::derived_from<DeclT, clang::FunctionDecl>)
+  explicit ClangDeclKey(DeclT* decl) : ClangDeclKey(decl, -1, UncheckedTag()) {}
+
+  // For declaration classes that are derived from FunctionDecl, a parameter
+  // count is required.
+  static auto ForFunctionDecl(clang::FunctionDecl* decl, int num_params)
+      -> ClangDeclKey {
+    return ClangDeclKey(decl, num_params, UncheckedTag());
+  }
 
 
-  // Equality comparison uses the address-identity property of the Clang AST and
-  // just compares the `decl` pointers. The `inst_id` is always the same due to
-  // the canonicalization.
-  auto operator==(const ClangDecl& rhs) const -> bool {
-    return decl == rhs.decl;
+  // Factory function for clang declaration that is dynamically known to not be
+  // a function declaration.
+  static auto ForNonFunctionDecl(clang::Decl* decl) -> ClangDeclKey {
+    CARBON_CHECK(!isa<clang::FunctionDecl>(decl));
+    return ClangDeclKey(decl, -1, UncheckedTag());
   }
   }
-  // Support direct comparison with the Clang AST node pointer.
-  auto operator==(const clang::Decl* rhs_decl) const -> bool {
-    return decl == rhs_decl;
+
+  auto Print(llvm::raw_ostream& out) const -> void;
+
+  auto operator==(const ClangDeclKey& rhs) const -> bool {
+    return decl == rhs.decl && num_params == rhs.num_params;
   }
   }
 
 
   // Hashing for ClangDecl. See common/hashing.h.
   // Hashing for ClangDecl. See common/hashing.h.
-  friend auto CarbonHashValue(const ClangDecl& value, uint64_t seed)
+  friend auto CarbonHashValue(const ClangDeclKey& value, uint64_t seed)
       -> HashCode {
       -> HashCode {
-    return HashValue(value.decl, seed);
+    // Manual hashing support is required because this type has tail padding in
+    // 64-bit compilations.
+    return HashValue(std::pair{value.decl, value.num_params}, seed);
   }
   }
 
 
   // The Clang declaration pointing to the Clang AST.
   // The Clang declaration pointing to the Clang AST.
@@ -56,10 +65,40 @@ struct ClangDecl : public Printable<ClangDecl> {
   // `clang::LazyDeclPtr`.
   // `clang::LazyDeclPtr`.
   clang::Decl* decl = nullptr;
   clang::Decl* decl = nullptr;
 
 
+  // The number of parameters to import for a function declaration. Excludes the
+  // implicit object parameter, if there is one. Always -1 for a non-function
+  // declaration.
+  int32_t num_params = -1;
+
+ private:
+  struct UncheckedTag {
+    explicit UncheckedTag() = default;
+  };
+  ClangDeclKey(clang::Decl* decl, int num_params, UncheckedTag /*_*/)
+      : decl(decl->getCanonicalDecl()), num_params(num_params) {}
+};
+
+// A Clang declaration mapped to a Carbon instruction.
+//
+// Instances of this type are managed by a `ClangDeclStore`, which ensures that
+// a single `ClangDecl` exists for each `ClangDeclKey` used.
+struct ClangDecl : public Printable<ClangDecl> {
+  // Comparison against ClangDeclKey, required by CanonicalValueStore.
+  auto operator==(const ClangDeclKey& rhs) const -> bool { return key == rhs; }
+  auto operator==(const ClangDecl& rhs) const -> bool { return key == rhs.key; }
+
+  // Hashing for ClangDecl. See common/hashing.h.
+  friend auto CarbonHashValue(const ClangDecl& value, uint64_t seed)
+      -> HashCode {
+    return HashValue(value.key, seed);
+  }
+
+  auto Print(llvm::raw_ostream& out) const -> void;
+
+  // The key by which this declaration can be looked up.
+  ClangDeclKey key;
+
   // The instruction the Clang declaration is mapped to.
   // The instruction the Clang declaration is mapped to.
-  //
-  // This is stored along side the `decl` pointer to avoid having to lookup both
-  // the pointer and the instruction ID in two separate areas of storage.
   InstId inst_id;
   InstId inst_id;
 };
 };
 
 
@@ -79,7 +118,7 @@ struct ClangDeclId : public IdBase<ClangDeclId> {
 
 
 // Use the AST node pointer directly when doing `Lookup` to find an ID.
 // Use the AST node pointer directly when doing `Lookup` to find an ID.
 using ClangDeclStore =
 using ClangDeclStore =
-    CanonicalValueStore<ClangDeclId, clang::Decl*, ClangDecl>;
+    CanonicalValueStore<ClangDeclId, ClangDeclKey, ClangDecl>;
 
 
 }  // namespace Carbon::SemIR
 }  // namespace Carbon::SemIR
 
 

+ 2 - 0
toolchain/sem_ir/file.cpp

@@ -101,6 +101,7 @@ auto File::OutputYaml(bool include_singletons) const -> Yaml::OutputMapping {
     map.Add("sem_ir", Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) {
     map.Add("sem_ir", Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) {
               map.Add("import_irs", import_irs_.OutputYaml());
               map.Add("import_irs", import_irs_.OutputYaml());
               map.Add("import_ir_insts", import_ir_insts_.OutputYaml());
               map.Add("import_ir_insts", import_ir_insts_.OutputYaml());
+              map.Add("clang_decls", clang_decls_.OutputYaml());
               map.Add("name_scopes", name_scopes_.OutputYaml());
               map.Add("name_scopes", name_scopes_.OutputYaml());
               map.Add("entity_names", entity_names_.OutputYaml());
               map.Add("entity_names", entity_names_.OutputYaml());
               map.Add("functions", functions_.OutputYaml());
               map.Add("functions", functions_.OutputYaml());
@@ -139,6 +140,7 @@ auto File::CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const
   mem_usage.Collect(MemUsage::ConcatLabel(label, "import_irs_"), import_irs_);
   mem_usage.Collect(MemUsage::ConcatLabel(label, "import_irs_"), import_irs_);
   mem_usage.Collect(MemUsage::ConcatLabel(label, "import_ir_insts_"),
   mem_usage.Collect(MemUsage::ConcatLabel(label, "import_ir_insts_"),
                     import_ir_insts_);
                     import_ir_insts_);
+  mem_usage.Collect(MemUsage::ConcatLabel(label, "clang_decls_"), clang_decls_);
   mem_usage.Collect(MemUsage::ConcatLabel(label, "struct_type_fields_"),
   mem_usage.Collect(MemUsage::ConcatLabel(label, "struct_type_fields_"),
                     struct_type_fields_);
                     struct_type_fields_);
   mem_usage.Collect(MemUsage::ConcatLabel(label, "insts_"), insts_);
   mem_usage.Collect(MemUsage::ConcatLabel(label, "insts_"), insts_);

+ 1 - 0
toolchain/sem_ir/yaml_test.cpp

@@ -58,6 +58,7 @@ TEST(SemIRTest, Yaml) {
   auto file = Yaml::Mapping(ElementsAre(
   auto file = Yaml::Mapping(ElementsAre(
       Pair("import_irs", Yaml::Mapping(SizeIs(2))),
       Pair("import_irs", Yaml::Mapping(SizeIs(2))),
       Pair("import_ir_insts", Yaml::Mapping(SizeIs(0))),
       Pair("import_ir_insts", Yaml::Mapping(SizeIs(0))),
+      Pair("clang_decls", Yaml::Mapping(SizeIs(0))),
       Pair("name_scopes", Yaml::Mapping(SizeIs(1))),
       Pair("name_scopes", Yaml::Mapping(SizeIs(1))),
       Pair("entity_names", Yaml::Mapping(SizeIs(1))),
       Pair("entity_names", Yaml::Mapping(SizeIs(1))),
       Pair("functions", Yaml::Mapping(SizeIs(1))),
       Pair("functions", Yaml::Mapping(SizeIs(1))),