Explorar el Código

C++ Interop: Basic C++ record (class/struct/union) import support (#5156)

Focus: Calling static C++ function defined in C++ classes.

Limitations:
* Ignores visibility (public / protected / private).
* No support for: dynamic classes, member methods, data members,
declarations without definitions, importing inheritance.

Based on #5142.

C++ Interop Demo with a class:

```c++
// hello_world.h

namespace some_namespace {

class MyClass {
 public:
  static void hello_world();
};

}  // namespace some_namespace
```

```c++
// hello_world.cpp

#include "hello_world.h"

#include <cstdio>

namespace some_namespace {

void MyClass::hello_world() { printf("Hello World!\n"); }

}  // namespace some_namespace
```

```carbon
// main.carbon

library "Main";

import Cpp library "hello_world.h";

fn Run() -> i32 {
  Cpp.some_namespace.MyClass.hello_world();
  return 0;
}
```

```shell
$ clang -c hello_world.cpp
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link hello_world.o main.o --output=demo
$ ./demo
Hello World!
```

Part of #5150.
Boaz Brickner hace 1 año
padre
commit
68111a994c

+ 2 - 0
toolchain/check/BUILD

@@ -17,6 +17,7 @@ cc_library(
     srcs = [
         "action.cpp",
         "call.cpp",
+        "class.cpp",
         "context.cpp",
         "control_flow.cpp",
         "convert.cpp",
@@ -53,6 +54,7 @@ cc_library(
     hdrs = [
         "action.h",
         "call.h",
+        "class.h",
         "context.h",
         "control_flow.h",
         "convert.h",

+ 260 - 0
toolchain/check/class.cpp

@@ -0,0 +1,260 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "toolchain/check/class.h"
+
+#include "toolchain/check/context.h"
+#include "toolchain/check/eval.h"
+#include "toolchain/check/function.h"
+#include "toolchain/check/import_ref.h"
+#include "toolchain/check/inst.h"
+#include "toolchain/check/type.h"
+
+namespace Carbon::Check {
+
+auto TryGetAsClass(Context& context, SemIR::TypeId type_id) -> SemIR::Class* {
+  auto class_type = context.types().TryGetAs<SemIR::ClassType>(type_id);
+  if (!class_type) {
+    return nullptr;
+  }
+  return &context.classes().Get(class_type->class_id);
+}
+
+auto SetClassSelfType(Context& context, SemIR::ClassId class_id) -> void {
+  auto& class_info = context.classes().Get(class_id);
+  auto specific_id = context.generics().GetSelfSpecific(class_info.generic_id);
+  class_info.self_type_id = GetClassType(context, class_id, specific_id);
+}
+
+auto StartClassDefinition(Context& context, SemIR::Class& class_info,
+                          SemIR::InstId definition_id) -> void {
+  // Track that this declaration is the definition.
+  CARBON_CHECK(!class_info.has_definition_started());
+  class_info.definition_id = definition_id;
+  class_info.scope_id = context.name_scopes().Add(
+      definition_id, SemIR::NameId::None, class_info.parent_scope_id);
+
+  // Introduce `Self`.
+  context.name_scopes().AddRequiredName(
+      class_info.scope_id, SemIR::NameId::SelfType,
+      context.types().GetInstId(class_info.self_type_id));
+}
+
+// Checks that the specified finished adapter definition is valid and builds and
+// returns a corresponding complete type witness instruction.
+static auto CheckCompleteAdapterClassType(
+    Context& context, Parse::NodeId node_id, SemIR::ClassId class_id,
+    llvm::ArrayRef<SemIR::InstId> field_decls,
+    llvm::ArrayRef<SemIR::InstId> body) -> SemIR::InstId {
+  const auto& class_info = context.classes().Get(class_id);
+  if (class_info.base_id.has_value()) {
+    CARBON_DIAGNOSTIC(AdaptWithBase, Error, "adapter with base class");
+    CARBON_DIAGNOSTIC(AdaptWithBaseHere, Note, "`base` declaration is here");
+    context.emitter()
+        .Build(class_info.adapt_id, AdaptWithBase)
+        .Note(class_info.base_id, AdaptWithBaseHere)
+        .Emit();
+    return SemIR::ErrorInst::SingletonInstId;
+  }
+
+  if (!field_decls.empty()) {
+    CARBON_DIAGNOSTIC(AdaptWithFields, Error, "adapter with fields");
+    CARBON_DIAGNOSTIC(AdaptWithFieldHere, Note,
+                      "first field declaration is here");
+    context.emitter()
+        .Build(class_info.adapt_id, AdaptWithFields)
+        .Note(field_decls.front(), AdaptWithFieldHere)
+        .Emit();
+    return SemIR::ErrorInst::SingletonInstId;
+  }
+
+  for (auto inst_id : body) {
+    if (auto function_decl =
+            context.insts().TryGetAs<SemIR::FunctionDecl>(inst_id)) {
+      auto& function = context.functions().Get(function_decl->function_id);
+      if (function.virtual_modifier ==
+          SemIR::Function::VirtualModifier::Virtual) {
+        CARBON_DIAGNOSTIC(AdaptWithVirtual, Error,
+                          "adapter with virtual function");
+        CARBON_DIAGNOSTIC(AdaptWithVirtualHere, Note,
+                          "first virtual function declaration is here");
+        context.emitter()
+            .Build(class_info.adapt_id, AdaptWithVirtual)
+            .Note(inst_id, AdaptWithVirtualHere)
+            .Emit();
+        return SemIR::ErrorInst::SingletonInstId;
+      }
+    }
+  }
+
+  // The object representation of the adapter is the object representation
+  // of the adapted type.
+  auto adapted_type_id =
+      class_info.GetAdaptedType(context.sem_ir(), SemIR::SpecificId::None);
+  auto object_repr_id = context.types().GetObjectRepr(adapted_type_id);
+
+  return AddInst<SemIR::CompleteTypeWitness>(
+      context, node_id,
+      {.type_id =
+           GetSingletonType(context, SemIR::WitnessType::SingletonInstId),
+       .object_repr_id = object_repr_id});
+}
+
+static auto AddStructTypeFields(
+    Context& context,
+    llvm::SmallVector<SemIR::StructTypeField>& struct_type_fields,
+    llvm::ArrayRef<SemIR::InstId> field_decls) -> SemIR::StructTypeFieldsId {
+  for (auto field_decl_id : field_decls) {
+    auto field_decl = context.insts().GetAs<SemIR::FieldDecl>(field_decl_id);
+    field_decl.index =
+        SemIR::ElementIndex{static_cast<int>(struct_type_fields.size())};
+    ReplaceInstPreservingConstantValue(context, field_decl_id, field_decl);
+    if (field_decl.type_id == SemIR::ErrorInst::SingletonTypeId) {
+      struct_type_fields.push_back(
+          {.name_id = field_decl.name_id,
+           .type_id = SemIR::ErrorInst::SingletonTypeId});
+      continue;
+    }
+    auto unbound_element_type =
+        context.sem_ir().types().GetAs<SemIR::UnboundElementType>(
+            field_decl.type_id);
+    struct_type_fields.push_back(
+        {.name_id = field_decl.name_id,
+         .type_id = context.types().GetTypeIdForTypeInstId(
+             unbound_element_type.element_type_inst_id)});
+  }
+  auto fields_id =
+      context.struct_type_fields().AddCanonical(struct_type_fields);
+  return fields_id;
+}
+
+// Builds and returns a vtable for the current class. Assumes that the virtual
+// functions for the class are listed as the top element of the `vtable_stack`.
+static auto BuildVtable(Context& context, Parse::NodeId node_id,
+                        SemIR::InstId base_vtable_id,
+                        llvm::ArrayRef<SemIR::InstId> vtable_contents)
+    -> SemIR::InstId {
+  llvm::SmallVector<SemIR::InstId> vtable;
+  if (base_vtable_id.has_value()) {
+    LoadImportRef(context, base_vtable_id);
+    auto canonical_base_vtable_id =
+        context.constant_values().GetConstantInstId(base_vtable_id);
+    if (canonical_base_vtable_id == SemIR::ErrorInst::SingletonInstId) {
+      return SemIR::ErrorInst::SingletonInstId;
+    }
+    auto base_vtable_inst_block = context.inst_blocks().Get(
+        context.insts()
+            .GetAs<SemIR::Vtable>(canonical_base_vtable_id)
+            .virtual_functions_id);
+    // TODO: Avoid quadratic search. Perhaps build a map from `NameId` to the
+    // elements of the top of `vtable_stack`.
+    for (auto fn_decl_id : base_vtable_inst_block) {
+      auto fn_decl = GetCalleeFunction(context.sem_ir(), fn_decl_id);
+      const auto& fn = context.functions().Get(fn_decl.function_id);
+      for (auto override_fn_decl_id : vtable_contents) {
+        auto override_fn_decl =
+            context.insts().GetAs<SemIR::FunctionDecl>(override_fn_decl_id);
+        const auto& override_fn =
+            context.functions().Get(override_fn_decl.function_id);
+        if (override_fn.virtual_modifier ==
+                SemIR::FunctionFields::VirtualModifier::Impl &&
+            override_fn.name_id == fn.name_id) {
+          // TODO: Support generic base classes, rather than passing
+          // `SpecificId::None`.
+          CheckFunctionTypeMatches(context, override_fn, fn,
+                                   SemIR::SpecificId::None,
+                                   /*check_syntax=*/false,
+                                   /*check_self=*/false);
+          fn_decl_id = override_fn_decl_id;
+        }
+      }
+      vtable.push_back(fn_decl_id);
+    }
+  }
+
+  for (auto inst_id : vtable_contents) {
+    auto fn_decl = context.insts().GetAs<SemIR::FunctionDecl>(inst_id);
+    const auto& fn = context.functions().Get(fn_decl.function_id);
+    if (fn.virtual_modifier != SemIR::FunctionFields::VirtualModifier::Impl) {
+      vtable.push_back(inst_id);
+    }
+  }
+  return AddInst<SemIR::Vtable>(
+      context, node_id,
+      {.type_id = GetSingletonType(context, SemIR::VtableType::SingletonInstId),
+       .virtual_functions_id = context.inst_blocks().Add(vtable)});
+}
+
+// Checks that the specified finished class definition is valid and builds and
+// returns a corresponding complete type witness instruction.
+static auto CheckCompleteClassType(
+    Context& context, Parse::NodeId node_id, SemIR::ClassId class_id,
+    llvm::ArrayRef<SemIR::InstId> field_decls,
+    llvm::ArrayRef<SemIR::InstId> vtable_contents,
+    llvm::ArrayRef<SemIR::InstId> body) -> SemIR::InstId {
+  auto& class_info = context.classes().Get(class_id);
+  if (class_info.adapt_id.has_value()) {
+    return CheckCompleteAdapterClassType(context, node_id, class_id,
+                                         field_decls, body);
+  }
+
+  bool defining_vptr = class_info.is_dynamic;
+  auto base_type_id =
+      class_info.GetBaseType(context.sem_ir(), SemIR::SpecificId::None);
+  SemIR::Class* base_class_info = nullptr;
+  if (base_type_id.has_value()) {
+    // TODO: If the base class is template dependent, we will need to decide
+    // whether to add a vptr as part of instantiation.
+    base_class_info = TryGetAsClass(context, base_type_id);
+    if (base_class_info && base_class_info->is_dynamic) {
+      defining_vptr = false;
+    }
+  }
+
+  llvm::SmallVector<SemIR::StructTypeField> struct_type_fields;
+  struct_type_fields.reserve(defining_vptr + class_info.base_id.has_value() +
+                             field_decls.size());
+  if (defining_vptr) {
+    struct_type_fields.push_back(
+        {.name_id = SemIR::NameId::Vptr,
+         .type_id =
+             GetPointerType(context, SemIR::VtableType::SingletonInstId)});
+  }
+  if (base_type_id.has_value()) {
+    auto base_decl = context.insts().GetAs<SemIR::BaseDecl>(class_info.base_id);
+    base_decl.index =
+        SemIR::ElementIndex{static_cast<int>(struct_type_fields.size())};
+    ReplaceInstPreservingConstantValue(context, class_info.base_id, base_decl);
+    struct_type_fields.push_back(
+        {.name_id = SemIR::NameId::Base, .type_id = base_type_id});
+  }
+
+  if (class_info.is_dynamic) {
+    class_info.vtable_id = BuildVtable(
+        context, node_id,
+        defining_vptr ? SemIR::InstId::None : base_class_info->vtable_id,
+        vtable_contents);
+  }
+
+  return AddInst<SemIR::CompleteTypeWitness>(
+      context, node_id,
+      {.type_id =
+           GetSingletonType(context, SemIR::WitnessType::SingletonInstId),
+       .object_repr_id = GetStructType(
+           context,
+           AddStructTypeFields(context, struct_type_fields, field_decls))});
+}
+
+auto ComputeClassObjectRepr(Context& context, Parse::NodeId node_id,
+                            SemIR::ClassId class_id,
+                            llvm::ArrayRef<SemIR::InstId> field_decls,
+                            llvm::ArrayRef<SemIR::InstId> vtable_contents,
+                            llvm::ArrayRef<SemIR::InstId> body) -> void {
+  auto complete_type_witness_id = CheckCompleteClassType(
+      context, node_id, class_id, field_decls, vtable_contents, body);
+  auto& class_info = context.classes().Get(class_id);
+  class_info.complete_type_witness_id = complete_type_witness_id;
+}
+
+}  // namespace Carbon::Check

+ 32 - 0
toolchain/check/class.h

@@ -0,0 +1,32 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef CARBON_TOOLCHAIN_CHECK_CLASS_H_
+#define CARBON_TOOLCHAIN_CHECK_CLASS_H_
+
+#include "toolchain/check/context.h"
+
+namespace Carbon::Check {
+
+// If `type_id` is a class type, get its corresponding `SemIR::Class` object.
+// Otherwise returns `nullptr`.
+auto TryGetAsClass(Context& context, SemIR::TypeId type_id) -> SemIR::Class*;
+
+// Sets the `Self` type for the class.
+auto SetClassSelfType(Context& context, SemIR::ClassId class_id) -> void;
+
+// Starts the class definition, adding `Self` to name lookup.
+auto StartClassDefinition(Context& context, SemIR::Class& class_info,
+                          SemIR::InstId definition_id) -> void;
+
+// Computes the object representation for a fully defined class.
+auto ComputeClassObjectRepr(Context& context, Parse::NodeId node_id,
+                            SemIR::ClassId class_id,
+                            llvm::ArrayRef<SemIR::InstId> field_decls,
+                            llvm::ArrayRef<SemIR::InstId> vtable_contents,
+                            llvm::ArrayRef<SemIR::InstId> body) -> void;
+
+}  // namespace Carbon::Check
+
+#endif  // CARBON_TOOLCHAIN_CHECK_CLASS_H_

+ 8 - 233
toolchain/check/handle_class.cpp

@@ -3,6 +3,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
 #include "toolchain/base/kind_switch.h"
+#include "toolchain/check/class.h"
 #include "toolchain/check/context.h"
 #include "toolchain/check/convert.h"
 #include "toolchain/check/decl_name_stack.h"
@@ -27,17 +28,6 @@
 
 namespace Carbon::Check {
 
-// If `type_id` is a class type, get its corresponding `SemIR::Class` object.
-// Otherwise returns `nullptr`.
-static auto TryGetAsClass(Context& context, SemIR::TypeId type_id)
-    -> SemIR::Class* {
-  auto class_type = context.types().TryGetAs<SemIR::ClassType>(type_id);
-  if (!class_type) {
-    return nullptr;
-  }
-  return &context.classes().Get(class_type->class_id);
-}
-
 auto HandleParseNode(Context& context, Parse::ClassIntroducerId node_id)
     -> bool {
   // Create an instruction block to hold the instructions created as part of the
@@ -260,14 +250,9 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
   ReplaceInstBeforeConstantUse(context, class_decl_id, class_decl);
 
   if (is_new_class) {
-    // Build the `Self` type using the resulting type constant.
     // TODO: Form this as part of building the definition, not as part of the
     // declaration.
-    auto& class_info = context.classes().Get(class_decl.class_id);
-    auto specific_id =
-        context.generics().GetSelfSpecific(class_info.generic_id);
-    class_info.self_type_id =
-        GetClassType(context, class_decl.class_id, specific_id);
+    SetClassSelfType(context, class_decl.class_id);
   }
 
   if (!is_definition && context.sem_ir().is_impl() && !is_extern) {
@@ -288,12 +273,7 @@ auto HandleParseNode(Context& context, Parse::ClassDefinitionStartId node_id)
   auto [class_id, class_decl_id] =
       BuildClassDecl(context, node_id, /*is_definition=*/true);
   auto& class_info = context.classes().Get(class_id);
-
-  // Track that this declaration is the definition.
-  CARBON_CHECK(!class_info.has_definition_started());
-  class_info.definition_id = class_decl_id;
-  class_info.scope_id = context.name_scopes().Add(
-      class_decl_id, SemIR::NameId::None, class_info.parent_scope_id);
+  StartClassDefinition(context, class_info, class_decl_id);
 
   // Enter the class scope.
   context.scope_stack().Push(
@@ -301,11 +281,6 @@ auto HandleParseNode(Context& context, Parse::ClassDefinitionStartId node_id)
       context.generics().GetSelfSpecific(class_info.generic_id));
   StartGenericDefinition(context);
 
-  // Introduce `Self`.
-  context.name_scopes().AddRequiredName(
-      class_info.scope_id, SemIR::NameId::SelfType,
-      context.types().GetInstId(class_info.self_type_id));
-
   context.inst_block_stack().Push();
   context.node_stack().Push(node_id, class_id);
   context.field_decls_stack().PushArray();
@@ -578,222 +553,22 @@ auto HandleParseNode(Context& context, Parse::BaseDeclId node_id) -> bool {
   return true;
 }
 
-// Checks that the specified finished adapter definition is valid and builds and
-// returns a corresponding complete type witness instruction.
-static auto CheckCompleteAdapterClassType(Context& context,
-                                          Parse::NodeId node_id,
-                                          SemIR::ClassId class_id)
-    -> SemIR::InstId {
-  const auto& class_info = context.classes().Get(class_id);
-  if (class_info.base_id.has_value()) {
-    CARBON_DIAGNOSTIC(AdaptWithBase, Error, "adapter with base class");
-    CARBON_DIAGNOSTIC(AdaptWithBaseHere, Note, "`base` declaration is here");
-    context.emitter()
-        .Build(class_info.adapt_id, AdaptWithBase)
-        .Note(class_info.base_id, AdaptWithBaseHere)
-        .Emit();
-    return SemIR::ErrorInst::SingletonInstId;
-  }
-
-  auto field_decls = context.field_decls_stack().PeekArray();
-  if (!field_decls.empty()) {
-    CARBON_DIAGNOSTIC(AdaptWithFields, Error, "adapter with fields");
-    CARBON_DIAGNOSTIC(AdaptWithFieldHere, Note,
-                      "first field declaration is here");
-    context.emitter()
-        .Build(class_info.adapt_id, AdaptWithFields)
-        .Note(field_decls.front(), AdaptWithFieldHere)
-        .Emit();
-    return SemIR::ErrorInst::SingletonInstId;
-  }
-
-  for (auto inst_id : context.inst_block_stack().PeekCurrentBlockContents()) {
-    if (auto function_decl =
-            context.insts().TryGetAs<SemIR::FunctionDecl>(inst_id)) {
-      auto& function = context.functions().Get(function_decl->function_id);
-      if (function.virtual_modifier ==
-          SemIR::Function::VirtualModifier::Virtual) {
-        CARBON_DIAGNOSTIC(AdaptWithVirtual, Error,
-                          "adapter with virtual function");
-        CARBON_DIAGNOSTIC(AdaptWithVirtualHere, Note,
-                          "first virtual function declaration is here");
-        context.emitter()
-            .Build(class_info.adapt_id, AdaptWithVirtual)
-            .Note(inst_id, AdaptWithVirtualHere)
-            .Emit();
-        return SemIR::ErrorInst::SingletonInstId;
-      }
-    }
-  }
-
-  // The object representation of the adapter is the object representation
-  // of the adapted type.
-  auto adapted_type_id =
-      class_info.GetAdaptedType(context.sem_ir(), SemIR::SpecificId::None);
-  auto object_repr_id = context.types().GetObjectRepr(adapted_type_id);
-
-  return AddInst<SemIR::CompleteTypeWitness>(
-      context, node_id,
-      {.type_id =
-           GetSingletonType(context, SemIR::WitnessType::SingletonInstId),
-       .object_repr_id = object_repr_id});
-}
-
-static auto AddStructTypeFields(
-    Context& context,
-    llvm::SmallVector<SemIR::StructTypeField>& struct_type_fields)
-    -> SemIR::StructTypeFieldsId {
-  for (auto field_decl_id : context.field_decls_stack().PeekArray()) {
-    auto field_decl = context.insts().GetAs<SemIR::FieldDecl>(field_decl_id);
-    field_decl.index =
-        SemIR::ElementIndex{static_cast<int>(struct_type_fields.size())};
-    ReplaceInstPreservingConstantValue(context, field_decl_id, field_decl);
-    if (field_decl.type_id == SemIR::ErrorInst::SingletonTypeId) {
-      struct_type_fields.push_back(
-          {.name_id = field_decl.name_id,
-           .type_id = SemIR::ErrorInst::SingletonTypeId});
-      continue;
-    }
-    auto unbound_element_type =
-        context.sem_ir().types().GetAs<SemIR::UnboundElementType>(
-            field_decl.type_id);
-    struct_type_fields.push_back(
-        {.name_id = field_decl.name_id,
-         .type_id = context.types().GetTypeIdForTypeInstId(
-             unbound_element_type.element_type_inst_id)});
-  }
-  auto fields_id =
-      context.struct_type_fields().AddCanonical(struct_type_fields);
-  return fields_id;
-}
-
-// Builds and returns a vtable for the current class. Assumes that the virtual
-// functions for the class are listed as the top element of the `vtable_stack`.
-static auto BuildVtable(Context& context, Parse::NodeId node_id,
-                        SemIR::InstId base_vtable_id) -> SemIR::InstId {
-  llvm::SmallVector<SemIR::InstId> vtable;
-  if (base_vtable_id.has_value()) {
-    LoadImportRef(context, base_vtable_id);
-    auto canonical_base_vtable_id =
-        context.constant_values().GetConstantInstId(base_vtable_id);
-    if (canonical_base_vtable_id == SemIR::ErrorInst::SingletonInstId) {
-      return SemIR::ErrorInst::SingletonInstId;
-    }
-    auto base_vtable_inst_block = context.inst_blocks().Get(
-        context.insts()
-            .GetAs<SemIR::Vtable>(canonical_base_vtable_id)
-            .virtual_functions_id);
-    // TODO: Avoid quadratic search. Perhaps build a map from `NameId` to the
-    // elements of the top of `vtable_stack`.
-    for (auto fn_decl_id : base_vtable_inst_block) {
-      auto fn_decl = GetCalleeFunction(context.sem_ir(), fn_decl_id);
-      const auto& fn = context.functions().Get(fn_decl.function_id);
-      for (auto override_fn_decl_id :
-           context.vtable_stack().PeekCurrentBlockContents()) {
-        auto override_fn_decl =
-            context.insts().GetAs<SemIR::FunctionDecl>(override_fn_decl_id);
-        const auto& override_fn =
-            context.functions().Get(override_fn_decl.function_id);
-        if (override_fn.virtual_modifier ==
-                SemIR::FunctionFields::VirtualModifier::Impl &&
-            override_fn.name_id == fn.name_id) {
-          // TODO: Support generic base classes, rather than passing
-          // `SpecificId::None`.
-          CheckFunctionTypeMatches(context, override_fn, fn,
-                                   SemIR::SpecificId::None,
-                                   /*check_syntax=*/false,
-                                   /*check_self=*/false);
-          fn_decl_id = override_fn_decl_id;
-        }
-      }
-      vtable.push_back(fn_decl_id);
-    }
-  }
-
-  for (auto inst_id : context.vtable_stack().PeekCurrentBlockContents()) {
-    auto fn_decl = context.insts().GetAs<SemIR::FunctionDecl>(inst_id);
-    const auto& fn = context.functions().Get(fn_decl.function_id);
-    if (fn.virtual_modifier != SemIR::FunctionFields::VirtualModifier::Impl) {
-      vtable.push_back(inst_id);
-    }
-  }
-  return AddInst<SemIR::Vtable>(
-      context, node_id,
-      {.type_id = GetSingletonType(context, SemIR::VtableType::SingletonInstId),
-       .virtual_functions_id = context.inst_blocks().Add(vtable)});
-}
-
-// Checks that the specified finished class definition is valid and builds and
-// returns a corresponding complete type witness instruction.
-static auto CheckCompleteClassType(Context& context, Parse::NodeId node_id,
-                                   SemIR::ClassId class_id) -> SemIR::InstId {
-  auto& class_info = context.classes().Get(class_id);
-  if (class_info.adapt_id.has_value()) {
-    return CheckCompleteAdapterClassType(context, node_id, class_id);
-  }
-
-  bool defining_vptr = class_info.is_dynamic;
-  auto base_type_id =
-      class_info.GetBaseType(context.sem_ir(), SemIR::SpecificId::None);
-  SemIR::Class* base_class_info = nullptr;
-  if (base_type_id.has_value()) {
-    // TODO: If the base class is template dependent, we will need to decide
-    // whether to add a vptr as part of instantiation.
-    base_class_info = TryGetAsClass(context, base_type_id);
-    if (base_class_info && base_class_info->is_dynamic) {
-      defining_vptr = false;
-    }
-  }
-
-  auto field_decls = context.field_decls_stack().PeekArray();
-  llvm::SmallVector<SemIR::StructTypeField> struct_type_fields;
-  struct_type_fields.reserve(defining_vptr + class_info.base_id.has_value() +
-                             field_decls.size());
-  if (defining_vptr) {
-    struct_type_fields.push_back(
-        {.name_id = SemIR::NameId::Vptr,
-         .type_id =
-             GetPointerType(context, SemIR::VtableType::SingletonInstId)});
-  }
-  if (base_type_id.has_value()) {
-    auto base_decl = context.insts().GetAs<SemIR::BaseDecl>(class_info.base_id);
-    base_decl.index =
-        SemIR::ElementIndex{static_cast<int>(struct_type_fields.size())};
-    ReplaceInstPreservingConstantValue(context, class_info.base_id, base_decl);
-    struct_type_fields.push_back(
-        {.name_id = SemIR::NameId::Base, .type_id = base_type_id});
-  }
-
-  if (class_info.is_dynamic) {
-    class_info.vtable_id = BuildVtable(
-        context, node_id,
-        defining_vptr ? SemIR::InstId::None : base_class_info->vtable_id);
-  }
-
-  return AddInst<SemIR::CompleteTypeWitness>(
-      context, node_id,
-      {.type_id =
-           GetSingletonType(context, SemIR::WitnessType::SingletonInstId),
-       .object_repr_id = GetStructType(
-           context, AddStructTypeFields(context, struct_type_fields))});
-}
-
 auto HandleParseNode(Context& context, Parse::ClassDefinitionId node_id)
     -> bool {
   auto class_id =
       context.node_stack().Pop<Parse::NodeKind::ClassDefinitionStart>();
 
   // The class type is now fully defined. Compute its object representation.
-  auto complete_type_witness_id =
-      CheckCompleteClassType(context, node_id, class_id);
-  auto& class_info = context.classes().Get(class_id);
-  class_info.complete_type_witness_id = complete_type_witness_id;
+  ComputeClassObjectRepr(context, node_id, class_id,
+                         context.field_decls_stack().PeekArray(),
+                         context.vtable_stack().PeekCurrentBlockContents(),
+                         context.inst_block_stack().PeekCurrentBlockContents());
 
   context.inst_block_stack().Pop();
   context.field_decls_stack().PopArray();
   context.vtable_stack().Pop();
 
-  FinishGenericDefinition(context, class_info.generic_id);
+  FinishGenericDefinition(context, context.classes().Get(class_id).generic_id);
 
   // The decl_name_stack and scopes are popped by `ProcessNodeIds`.
   return true;

+ 116 - 9
toolchain/check/import_cpp.cpp

@@ -15,9 +15,11 @@
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/raw_ostream.h"
+#include "toolchain/check/class.h"
 #include "toolchain/check/context.h"
 #include "toolchain/check/convert.h"
 #include "toolchain/check/diagnostic_helpers.h"
+#include "toolchain/check/eval.h"
 #include "toolchain/check/import.h"
 #include "toolchain/check/inst.h"
 #include "toolchain/check/literal.h"
@@ -218,8 +220,8 @@ auto ImportCppFiles(Context& context, llvm::StringRef importing_file_path,
 
 // Look ups the given name in the Clang AST in a specific scope. Returns the
 // lookup result if lookup was successful.
-static auto ClangLookup(Context& context, SemIR::LocId loc_id,
-                        SemIR::NameScopeId scope_id, SemIR::NameId name_id)
+static auto ClangLookup(Context& context, SemIR::NameScopeId scope_id,
+                        SemIR::NameId name_id)
     -> std::optional<clang::LookupResult> {
   std::optional<llvm::StringRef> name =
       context.names().GetAsStringIfIdentifier(name_id);
@@ -239,16 +241,14 @@ static auto ClangLookup(Context& context, SemIR::LocId loc_id,
               sema.getPreprocessor().getIdentifierInfo(*name)),
           clang::SourceLocation()),
       clang::Sema::LookupNameKind::LookupOrdinaryName);
+  // TODO: Diagnose on access and return the `AccessKind` for storage. We'll
+  // probably need a dedicated `DiagnosticConsumer` because
+  // `TextDiagnosticPrinter` assumes we're processing a C++ source file.
+  lookup.suppressDiagnostics();
 
   bool found = sema.LookupQualifiedName(
       lookup, context.name_scopes().Get(scope_id).cpp_decl_context());
 
-  if (lookup.isClassLookup()) {
-    // TODO: To support class lookup, also return the AccessKind for storage.
-    context.TODO(loc_id, "Unsupported: Lookup in Class");
-    return std::nullopt;
-  }
-
   if (!found) {
     return std::nullopt;
   }
@@ -433,6 +433,108 @@ static auto ImportNamespaceDecl(Context& context,
   return result.inst_id;
 }
 
+// Creates a class declaration for the given class name in the given scope.
+// Returns the `InstId` for the declaration.
+static auto BuildClassDecl(Context& context, SemIR::NameScopeId parent_scope_id,
+                           SemIR::NameId name_id)
+    -> std::tuple<SemIR::ClassId, SemIR::InstId> {
+  // Add the class declaration.
+  auto class_decl =
+      SemIR::ClassDecl{.type_id = SemIR::TypeType::SingletonTypeId,
+                       .class_id = SemIR::ClassId::None,
+                       .decl_block_id = SemIR::InstBlockId::None};
+  // TODO: Consider setting a proper location.
+  auto class_decl_id =
+      AddPlaceholderInst(context, SemIR::LocIdAndInst::NoLoc(class_decl));
+
+  SemIR::Class class_info = {
+      {.name_id = name_id,
+       .parent_scope_id = parent_scope_id,
+       .generic_id = SemIR::GenericId::None,
+       .first_param_node_id = Parse::NodeId::None,
+       .last_param_node_id = Parse::NodeId::None,
+       .pattern_block_id = SemIR::InstBlockId::None,
+       .implicit_param_patterns_id = SemIR::InstBlockId::None,
+       .param_patterns_id = SemIR::InstBlockId::None,
+       .is_extern = false,
+       .extern_library_id = SemIR::LibraryNameId::None,
+       .non_owning_decl_id = SemIR::InstId::None,
+       .first_owning_decl_id = class_decl_id},
+      {// `.self_type_id` depends on the ClassType, so is set below.
+       .self_type_id = SemIR::TypeId::None,
+       // TODO: Support Dynamic classes.
+       // TODO: Support Final classes.
+       .inheritance_kind = SemIR::Class::Base}};
+
+  class_decl.class_id = context.classes().Add(class_info);
+
+  // Write the class ID into the ClassDecl.
+  ReplaceInstBeforeConstantUse(context, class_decl_id, class_decl);
+
+  SetClassSelfType(context, class_decl.class_id);
+
+  return {class_decl.class_id, class_decl_id};
+}
+
+// Creates a class definition for the given class name in the given scope based
+// on the information in the given Clang declaration. Returns the `InstId` for
+// the declaration, which is assumed to be for a class definition. Returns the
+// new class id and instruction id.
+static auto BuildClassDefinition(Context& context,
+                                 SemIR::NameScopeId parent_scope_id,
+                                 SemIR::NameId name_id,
+                                 clang::CXXRecordDecl* clang_decl)
+    -> std::tuple<SemIR::ClassId, SemIR::InstId> {
+  auto [class_id, class_decl_id] =
+      BuildClassDecl(context, parent_scope_id, name_id);
+  auto& class_info = context.classes().Get(class_id);
+  StartClassDefinition(context, class_info, class_decl_id);
+
+  context.name_scopes()
+      .Get(class_info.scope_id)
+      .set_cpp_decl_context(clang_decl);
+
+  return {class_id, class_decl_id};
+}
+
+// Imports a record declaration from Clang to Carbon. If successful, returns
+// the new Carbon class declaration `InstId`.
+// TODO: Change `clang_decl` to `const &` when lookup is using `clang::DeclID`
+// and we don't need to store the decl for lookup context.
+static auto ImportCXXRecordDecl(Context& context, SemIR::LocId loc_id,
+                                SemIR::NameScopeId parent_scope_id,
+                                SemIR::NameId name_id,
+                                clang::CXXRecordDecl* clang_decl)
+    -> SemIR::InstId {
+  clang::CXXRecordDecl* clang_def = clang_decl->getDefinition();
+  if (!clang_def) {
+    context.TODO(loc_id,
+                 "Unsupported: Record declarations without a definition");
+    return SemIR::ErrorInst::SingletonInstId;
+  }
+
+  if (clang_def->isDynamicClass()) {
+    context.TODO(loc_id, "Unsupported: Dynamic Class");
+    return SemIR::ErrorInst::SingletonInstId;
+  }
+
+  auto [class_id, class_def_id] =
+      BuildClassDefinition(context, parent_scope_id, name_id, clang_def);
+
+  // The class type is now fully defined. Compute its object representation.
+  ComputeClassObjectRepr(context,
+                         // TODO: Consider having a proper location here.
+                         Parse::NodeId::None, class_id,
+                         // TODO: Set fields.
+                         /*field_decls=*/{},
+                         // TODO: Set vtable.
+                         /*vtable_contents=*/{},
+                         // TODO: Set block.
+                         /*body=*/{});
+
+  return class_def_id;
+}
+
 // Imports a declaration from Clang to Carbon. If successful, returns the
 // instruction for the new Carbon declaration.
 static auto ImportNameDecl(Context& context, SemIR::LocId loc_id,
@@ -448,6 +550,11 @@ static auto ImportNameDecl(Context& context, SemIR::LocId loc_id,
     return ImportNamespaceDecl(context, scope_id, name_id,
                                clang_namespace_decl);
   }
+  if (auto* clang_record_decl =
+          clang::dyn_cast<clang::CXXRecordDecl>(clang_decl)) {
+    return ImportCXXRecordDecl(context, loc_id, scope_id, name_id,
+                               clang_record_decl);
+  }
 
   context.TODO(loc_id, llvm::formatv("Unsupported: Declaration type {0}",
                                      clang_decl->getDeclKindName())
@@ -465,7 +572,7 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id,
         builder.Note(loc_id, InCppNameLookup, name_id);
       });
 
-  auto lookup = ClangLookup(context, loc_id, scope_id, name_id);
+  auto lookup = ClangLookup(context, scope_id, name_id);
   if (!lookup) {
     return SemIR::InstId::None;
   }

+ 1136 - 0
toolchain/check/testdata/interop/cpp/no_prelude/class.carbon

@@ -0,0 +1,1136 @@
+// 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
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/no_prelude/class.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/no_prelude/class.carbon
+
+// --- class_declaration.h
+
+class Bar;
+
+// --- fail_import_class_declaration.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "class_declaration.h";
+
+// CHECK:STDERR: fail_import_class_declaration.carbon:[[@LINE+7]]:13: error: semantics TODO: `Unsupported: Record declarations without a definition` [SemanticsTodo]
+// CHECK:STDERR: fn MyF(bar: Cpp.Bar*);
+// CHECK:STDERR:             ^~~~~~~
+// CHECK:STDERR: fail_import_class_declaration.carbon:[[@LINE+4]]:13: note: in `Cpp` name lookup for `Bar` [InCppNameLookup]
+// CHECK:STDERR: fn MyF(bar: Cpp.Bar*);
+// CHECK:STDERR:             ^~~~~~~
+// CHECK:STDERR:
+fn MyF(bar: Cpp.Bar*);
+
+// --- class_definition.h
+
+class Bar {};
+
+// --- import_class_definition.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "class_definition.h";
+
+fn MyF(bar: Cpp.Bar*);
+
+// --- class_declaration_and_definition.h
+
+class Bar;
+class Bar {};
+
+// --- import_class_declaration_and_definition.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "class_declaration_and_definition.h";
+
+fn MyF(bar: Cpp.Bar*);
+
+// --- class_public_static_member_function.h
+
+class Bar {
+ public:
+  static auto foo() -> void;
+};
+
+// --- import_class_public_static_member_function.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "class_public_static_member_function.h";
+
+fn MyF() {
+  Cpp.Bar.foo();
+}
+
+// --- class_private_static_member_function.h
+
+class Bar {
+ private:
+  static auto foo() -> void;
+};
+
+// --- todo_fail_import_class_private_static_member_function.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "class_private_static_member_function.h";
+
+fn MyF() {
+  Cpp.Bar.foo();
+}
+
+// --- class_public_member_function.h
+
+class Bar {
+ public:
+  auto foo() -> void;
+};
+
+// --- fail_import_class_public_member_function.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "class_public_member_function.h";
+
+fn MyF(bar : Cpp.Bar*) {
+  // CHECK:STDERR: fail_import_class_public_member_function.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: Non-global function` [SemanticsTodo]
+  // CHECK:STDERR:   bar->foo();
+  // CHECK:STDERR:   ^~~~~~~~
+  // CHECK:STDERR: fail_import_class_public_member_function.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
+  // CHECK:STDERR:   bar->foo();
+  // CHECK:STDERR:   ^~~~~~~~
+  // CHECK:STDERR:
+  bar->foo();
+}
+
+// --- class_public_static_data_member.h
+
+class Bar {
+ public:
+  static Bar* foo;
+};
+
+// --- fail_import_class_public_static_data_member.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "class_public_static_data_member.h";
+
+fn MyF() {
+  // CHECK:STDERR: fail_import_class_public_static_data_member.carbon:[[@LINE+11]]:23: error: semantics TODO: `Unsupported: Declaration type Var` [SemanticsTodo]
+  // CHECK:STDERR:   let bar: Cpp.Bar* = Cpp.Bar.foo();
+  // CHECK:STDERR:                       ^~~~~~~~~~~
+  // CHECK:STDERR: fail_import_class_public_static_data_member.carbon:[[@LINE+8]]:23: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
+  // CHECK:STDERR:   let bar: Cpp.Bar* = Cpp.Bar.foo();
+  // CHECK:STDERR:                       ^~~~~~~~~~~
+  // CHECK:STDERR:
+  // CHECK:STDERR: fail_import_class_public_static_data_member.carbon:[[@LINE+4]]:23: error: member name `foo` not found in `Cpp.Bar` [MemberNameNotFoundInInstScope]
+  // CHECK:STDERR:   let bar: Cpp.Bar* = Cpp.Bar.foo();
+  // CHECK:STDERR:                       ^~~~~~~~~~~
+  // CHECK:STDERR:
+  let bar: Cpp.Bar* = Cpp.Bar.foo();
+}
+
+// --- class_public_data_member.h
+
+class Bar {
+ public:
+  Bar* foo;
+};
+
+// --- fail_import_class_public_data_member.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "class_public_static_data_member.h";
+
+fn MyF(bar : Cpp.Bar*) {
+  // CHECK:STDERR: fail_import_class_public_data_member.carbon:[[@LINE+11]]:27: error: semantics TODO: `Unsupported: Declaration type Var` [SemanticsTodo]
+  // CHECK:STDERR:   let foo_bar: Cpp.Bar* = bar->foo;
+  // CHECK:STDERR:                           ^~~~~~~~
+  // CHECK:STDERR: fail_import_class_public_data_member.carbon:[[@LINE+8]]:27: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
+  // CHECK:STDERR:   let foo_bar: Cpp.Bar* = bar->foo;
+  // CHECK:STDERR:                           ^~~~~~~~
+  // CHECK:STDERR:
+  // CHECK:STDERR: fail_import_class_public_data_member.carbon:[[@LINE+4]]:27: error: member name `foo` not found in `Cpp.Bar` [MemberNameNotFoundInInstScope]
+  // CHECK:STDERR:   let foo_bar: Cpp.Bar* = bar->foo;
+  // CHECK:STDERR:                           ^~~~~~~~
+  // CHECK:STDERR:
+  let foo_bar: Cpp.Bar* = bar->foo;
+}
+
+// --- class_inheritance_static.h
+
+class Bar1 {
+ public:
+  static auto foo1() -> void;
+};
+
+class Bar2 : public Bar1 {
+ public:
+  static auto foo2() -> void;
+};
+
+// --- import_class_inheritance_static.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "class_inheritance_static.h";
+
+fn MyF() {
+  Cpp.Bar1.foo1();
+  Cpp.Bar2.foo1();
+  Cpp.Bar2.foo2();
+}
+
+// --- class_inheritance_pointers.h
+
+class Bar1 {};
+class Bar2 : public Bar1 {};
+
+// --- fail_import_class_inheritance_pointers.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "class_inheritance_pointers.h";
+
+fn MyF1(bar : Cpp.Bar1*);
+// TODO: Support C++ inheritance.
+// CHECK:STDERR: fail_import_class_inheritance_pointers.carbon:[[@LINE+7]]:33: error: `Core.ImplicitAs` implicitly referenced here, but package `Core` not found [CoreNotFound]
+// CHECK:STDERR: fn MyF2(bar : Cpp.Bar2*) { MyF1(bar); }
+// CHECK:STDERR:                                 ^~~
+// CHECK:STDERR: fail_import_class_inheritance_pointers.carbon:[[@LINE-5]]:9: note: initializing function parameter [InCallToFunctionParam]
+// CHECK:STDERR: fn MyF1(bar : Cpp.Bar1*);
+// CHECK:STDERR:         ^~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn MyF2(bar : Cpp.Bar2*) { MyF1(bar); }
+
+// --- class_dynamic.h
+
+class Bar {
+ public:
+  virtual ~Bar();
+};
+
+// --- fail_import_class_dynamic.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "class_dynamic.h";
+
+// CHECK:STDERR: fail_import_class_dynamic.carbon:[[@LINE+7]]:14: error: semantics TODO: `Unsupported: Dynamic Class` [SemanticsTodo]
+// CHECK:STDERR: fn MyF(bar : Cpp.Bar*);
+// CHECK:STDERR:              ^~~~~~~
+// CHECK:STDERR: fail_import_class_dynamic.carbon:[[@LINE+4]]:14: note: in `Cpp` name lookup for `Bar` [InCppNameLookup]
+// CHECK:STDERR: fn MyF(bar : Cpp.Bar*);
+// CHECK:STDERR:              ^~~~~~~
+// CHECK:STDERR:
+fn MyF(bar : Cpp.Bar*);
+
+// --- class_to_inherit_public.h
+
+class Bar {
+ public:
+  static auto foo() -> void;
+};
+
+// --- import_class_to_inherit_public.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "class_to_inherit_public.h";
+
+class Derived {
+  extend base: Cpp.Bar;
+}
+
+fn MyF() {
+  Derived.foo();
+}
+
+// --- class_to_inherit_private.h
+
+class Bar {
+ private:
+  static auto foo() -> void;
+};
+
+// --- todo_fail_class_to_inherit_private.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "class_to_inherit_private.h";
+
+class Derived {
+  extend base: Cpp.Bar;
+}
+
+fn MyF() {
+  Derived.foo();
+}
+
+// --- class_template.h
+
+template<typename T>
+class Bar {};
+
+// --- fail_import_class_template.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "class_template.h";
+
+// CHECK:STDERR: fail_import_class_template.carbon:[[@LINE+11]]:13: error: semantics TODO: `Unsupported: Declaration type ClassTemplate` [SemanticsTodo]
+// CHECK:STDERR: fn MyF(bar: Cpp.Bar*);
+// CHECK:STDERR:             ^~~~~~~
+// CHECK:STDERR: fail_import_class_template.carbon:[[@LINE+8]]:13: note: in `Cpp` name lookup for `Bar` [InCppNameLookup]
+// CHECK:STDERR: fn MyF(bar: Cpp.Bar*);
+// CHECK:STDERR:             ^~~~~~~
+// CHECK:STDERR:
+// CHECK:STDERR: fail_import_class_template.carbon:[[@LINE+4]]:13: error: member name `Bar` not found in `Cpp` [MemberNameNotFoundInInstScope]
+// CHECK:STDERR: fn MyF(bar: Cpp.Bar*);
+// CHECK:STDERR:             ^~~~~~~
+// CHECK:STDERR:
+fn MyF(bar: Cpp.Bar*);
+
+// CHECK:STDOUT: --- fail_import_class_declaration.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = <error>
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "class_declaration.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: <error> = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: <error> = value_param_pattern %bar.patt, call_param0 [concrete = <error>]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: <error> = value_param call_param0
+// CHECK:STDOUT:     %.loc13: type = splice_block %ptr [concrete = <error>] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.ref: <error> = name_ref Bar, <error> [concrete = <error>]
+// CHECK:STDOUT:       %ptr: type = ptr_type <error> [concrete = <error>]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: <error> = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: <error>);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- import_class_definition.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %ptr: type = ptr_type %Bar [concrete]
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "class_definition.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: %ptr = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: %ptr = value_param_pattern %bar.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: %ptr = value_param call_param0
+// CHECK:STDOUT:     %.loc6: type = splice_block %ptr [concrete = constants.%ptr] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:       %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:       %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:       %ptr: type = ptr_type %Bar.ref [concrete = constants.%ptr]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: %ptr = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: %ptr);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- import_class_declaration_and_definition.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %ptr: type = ptr_type %Bar [concrete]
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "class_declaration_and_definition.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: %ptr = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: %ptr = value_param_pattern %bar.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: %ptr = value_param call_param0
+// CHECK:STDOUT:     %.loc6: type = splice_block %ptr [concrete = constants.%ptr] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:       %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:       %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:       %ptr: type = ptr_type %Bar.ref [concrete = constants.%ptr]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: %ptr = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: %ptr);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- import_class_public_static_member_function.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [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:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "class_public_static_member_function.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = @MyF.%foo.decl
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {} {}
+// CHECK:STDOUT:   %foo.ref: %foo.type = name_ref foo, %foo.decl [concrete = constants.%foo]
+// CHECK:STDOUT:   %foo.call: init %empty_tuple.type = call %foo.ref()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @foo[]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- todo_fail_import_class_private_static_member_function.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [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:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "class_private_static_member_function.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = @MyF.%foo.decl
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {} {}
+// CHECK:STDOUT:   %foo.ref: %foo.type = name_ref foo, %foo.decl [concrete = constants.%foo]
+// CHECK:STDOUT:   %foo.call: init %empty_tuple.type = call %foo.ref()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @foo[]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_class_public_member_function.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %ptr.f68: type = ptr_type %Bar [concrete]
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "class_public_member_function.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: %ptr.f68 = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: %ptr.f68 = value_param_pattern %bar.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: %ptr.f68 = value_param call_param0
+// CHECK:STDOUT:     %.loc6: type = splice_block %ptr [concrete = constants.%ptr.f68] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:       %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:       %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:       %ptr: type = ptr_type %Bar.ref [concrete = constants.%ptr.f68]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: %ptr.f68 = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = <error>
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: %ptr.f68) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %bar.ref: %ptr.f68 = name_ref bar, %bar
+// CHECK:STDOUT:   %.loc14: ref %Bar = deref %bar.ref
+// CHECK:STDOUT:   %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_class_public_static_data_member.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %ptr.f68: type = ptr_type %Bar [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "class_public_static_data_member.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = <poisoned>
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %bar.patt: %ptr.f68 = binding_pattern bar
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.ref.loc18_23: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar.ref.loc18_26: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:   %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
+// CHECK:STDOUT:   %.loc18: type = splice_block %ptr [concrete = constants.%ptr.f68] {
+// CHECK:STDOUT:     %Cpp.ref.loc18_12: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:     %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:     %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:     %Bar.ref.loc18_15: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:     %ptr: type = ptr_type %Bar.ref.loc18_15 [concrete = constants.%ptr.f68]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %bar: %ptr.f68 = bind_name bar, <error>
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_class_public_data_member.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %ptr.f68: type = ptr_type %Bar [concrete]
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "class_public_static_data_member.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: %ptr.f68 = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: %ptr.f68 = value_param_pattern %bar.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: %ptr.f68 = value_param call_param0
+// CHECK:STDOUT:     %.loc6: type = splice_block %ptr.loc6 [concrete = constants.%ptr.f68] {
+// CHECK:STDOUT:       %Cpp.ref.loc6: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:       %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:       %Bar.ref.loc6: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:       %ptr.loc6: type = ptr_type %Bar.ref.loc6 [concrete = constants.%ptr.f68]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: %ptr.f68 = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = <poisoned>
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: %ptr.f68) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %foo_bar.patt: %ptr.f68 = binding_pattern foo_bar
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %bar.ref: %ptr.f68 = name_ref bar, %bar
+// CHECK:STDOUT:   %.loc18_30: ref %Bar = deref %bar.ref
+// CHECK:STDOUT:   %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
+// CHECK:STDOUT:   %.loc18_23: type = splice_block %ptr.loc18 [concrete = constants.%ptr.f68] {
+// CHECK:STDOUT:     %Cpp.ref.loc18: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:     %Bar.ref.loc18: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:     %ptr.loc18: type = ptr_type %Bar.ref.loc18 [concrete = constants.%ptr.f68]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %foo_bar: %ptr.f68 = bind_name foo_bar, <error>
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- import_class_inheritance_static.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Bar1: type = class_type @Bar1 [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %foo1.type.148: type = fn_type @foo1.1 [concrete]
+// CHECK:STDOUT:   %foo1.8cd: %foo1.type.148 = struct_value () [concrete]
+// CHECK:STDOUT:   %Bar2: type = class_type @Bar2 [concrete]
+// CHECK:STDOUT:   %foo1.type.0b8: type = fn_type @foo1.2 [concrete]
+// CHECK:STDOUT:   %foo1.ba2: %foo1.type.0b8 = struct_value () [concrete]
+// CHECK:STDOUT:   %foo2.type: type = fn_type @foo2 [concrete]
+// CHECK:STDOUT:   %foo2: %foo2.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar1 = @MyF.%Bar1.decl
+// CHECK:STDOUT:     .Bar2 = @MyF.%Bar2.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "class_inheritance_static.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar1 {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type.1
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar1
+// CHECK:STDOUT:   .foo1 = @MyF.%foo1.decl.1
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar2 {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type.2
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar2
+// CHECK:STDOUT:   .foo1 = @MyF.%foo1.decl.2
+// CHECK:STDOUT:   .foo2 = @MyF.%foo2.decl
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Cpp.ref.loc7: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar1.decl: type = class_decl @Bar1 [concrete = constants.%Bar1] {} {}
+// CHECK:STDOUT:   %complete_type.1: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %Bar1.ref: type = name_ref Bar1, %Bar1.decl [concrete = constants.%Bar1]
+// CHECK:STDOUT:   %foo1.decl.1: %foo1.type.148 = fn_decl @foo1.1 [concrete = constants.%foo1.8cd] {} {}
+// CHECK:STDOUT:   %foo1.ref.loc7: %foo1.type.148 = name_ref foo1, %foo1.decl.1 [concrete = constants.%foo1.8cd]
+// CHECK:STDOUT:   %foo1.call.loc7: init %empty_tuple.type = call %foo1.ref.loc7()
+// CHECK:STDOUT:   %Cpp.ref.loc8: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar2.decl: type = class_decl @Bar2 [concrete = constants.%Bar2] {} {}
+// CHECK:STDOUT:   %complete_type.2: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %Bar2.ref.loc8: type = name_ref Bar2, %Bar2.decl [concrete = constants.%Bar2]
+// CHECK:STDOUT:   %foo1.decl.2: %foo1.type.0b8 = fn_decl @foo1.2 [concrete = constants.%foo1.ba2] {} {}
+// CHECK:STDOUT:   %foo1.ref.loc8: %foo1.type.0b8 = name_ref foo1, %foo1.decl.2 [concrete = constants.%foo1.ba2]
+// CHECK:STDOUT:   %foo1.call.loc8: init %empty_tuple.type = call %foo1.ref.loc8()
+// CHECK:STDOUT:   %Cpp.ref.loc9: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar2.ref.loc9: type = name_ref Bar2, %Bar2.decl [concrete = constants.%Bar2]
+// CHECK:STDOUT:   %foo2.decl: %foo2.type = fn_decl @foo2 [concrete = constants.%foo2] {} {}
+// CHECK:STDOUT:   %foo2.ref: %foo2.type = name_ref foo2, %foo2.decl [concrete = constants.%foo2]
+// CHECK:STDOUT:   %foo2.call: init %empty_tuple.type = call %foo2.ref()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @foo1.1[]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @foo1.2[]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @foo2[]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_class_inheritance_pointers.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Bar1: type = class_type @Bar1 [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %ptr.f68: type = ptr_type %Bar1 [concrete]
+// CHECK:STDOUT:   %MyF1.type: type = fn_type @MyF1 [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %MyF1: %MyF1.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Bar2: type = class_type @Bar2 [concrete]
+// CHECK:STDOUT:   %ptr.eca: type = ptr_type %Bar2 [concrete]
+// CHECK:STDOUT:   %MyF2.type: type = fn_type @MyF2 [concrete]
+// CHECK:STDOUT:   %MyF2: %MyF2.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar1 = @MyF1.%Bar1.decl
+// CHECK:STDOUT:     .Bar2 = @MyF2.%Bar2.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF1 = %MyF1.decl
+// CHECK:STDOUT:     .MyF2 = %MyF2.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "class_inheritance_pointers.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF1.decl: %MyF1.type = fn_decl @MyF1 [concrete = constants.%MyF1] {
+// CHECK:STDOUT:     %bar.patt: %ptr.f68 = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: %ptr.f68 = value_param_pattern %bar.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: %ptr.f68 = value_param call_param0
+// CHECK:STDOUT:     %.loc6: type = splice_block %ptr [concrete = constants.%ptr.f68] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar1.decl: type = class_decl @Bar1 [concrete = constants.%Bar1] {} {}
+// CHECK:STDOUT:       %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:       %Bar1.ref: type = name_ref Bar1, %Bar1.decl [concrete = constants.%Bar1]
+// CHECK:STDOUT:       %ptr: type = ptr_type %Bar1.ref [concrete = constants.%ptr.f68]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: %ptr.f68 = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF2.decl: %MyF2.type = fn_decl @MyF2 [concrete = constants.%MyF2] {
+// CHECK:STDOUT:     %bar.patt: %ptr.eca = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: %ptr.eca = value_param_pattern %bar.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: %ptr.eca = value_param call_param0
+// CHECK:STDOUT:     %.loc15_23: type = splice_block %ptr [concrete = constants.%ptr.eca] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar2.decl: type = class_decl @Bar2 [concrete = constants.%Bar2] {} {}
+// CHECK:STDOUT:       %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:       %Bar2.ref: type = name_ref Bar2, %Bar2.decl [concrete = constants.%Bar2]
+// CHECK:STDOUT:       %ptr: type = ptr_type %Bar2.ref [concrete = constants.%ptr.eca]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: %ptr.eca = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar1 {
+// CHECK:STDOUT:   complete_type_witness = @MyF1.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar1
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar2 {
+// CHECK:STDOUT:   complete_type_witness = @MyF2.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar2
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF1(%bar.param_patt: %ptr.f68);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF2(%bar.param_patt: %ptr.eca) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %MyF1.ref: %MyF1.type = name_ref MyF1, file.%MyF1.decl [concrete = constants.%MyF1]
+// CHECK:STDOUT:   %bar.ref: %ptr.eca = name_ref bar, %bar
+// CHECK:STDOUT:   %.loc15_33: %ptr.f68 = converted %bar.ref, <error> [concrete = <error>]
+// CHECK:STDOUT:   %MyF1.call: init %empty_tuple.type = call %MyF1.ref(<error>)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_class_dynamic.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = <error>
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "class_dynamic.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: <error> = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: <error> = value_param_pattern %bar.patt, call_param0 [concrete = <error>]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: <error> = value_param call_param0
+// CHECK:STDOUT:     %.loc13: type = splice_block %ptr [concrete = <error>] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.ref: <error> = name_ref Bar, <error> [concrete = <error>]
+// CHECK:STDOUT:       %ptr: type = ptr_type <error> [concrete = <error>]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: <error> = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: <error>);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- import_class_to_inherit_public.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Derived: type = class_type @Derived [concrete]
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type.357: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %Derived.elem: type = unbound_element_type %Derived, %Bar [concrete]
+// CHECK:STDOUT:   %struct_type.base.36d: type = struct_type {.base: %Bar} [concrete]
+// CHECK:STDOUT:   %complete_type.fff: <witness> = complete_type_witness %struct_type.base.36d [concrete]
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [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:     .Bar = @Derived.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .Derived = %Derived.decl
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "class_to_inherit_public.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {}
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Derived {
+// CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:   %complete_type.1: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
+// CHECK:STDOUT:   %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:   %.loc7: %Derived.elem = base_decl %Bar.ref, element0 [concrete]
+// CHECK:STDOUT:   %complete_type.loc8: <witness> = complete_type_witness %struct_type.base.36d [concrete = constants.%complete_type.fff]
+// CHECK:STDOUT:   complete_type_witness = %complete_type.loc8
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Derived
+// CHECK:STDOUT:   .Cpp = <poisoned>
+// CHECK:STDOUT:   .base = %.loc7
+// CHECK:STDOUT:   .foo = <poisoned>
+// CHECK:STDOUT:   extend %Bar.ref
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @Derived.%complete_type.1
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = @MyF.%foo.decl
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived]
+// CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {} {}
+// CHECK:STDOUT:   %foo.ref: %foo.type = name_ref foo, %foo.decl [concrete = constants.%foo]
+// CHECK:STDOUT:   %foo.call: init %empty_tuple.type = call %foo.ref()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @foo[]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- todo_fail_class_to_inherit_private.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Derived: type = class_type @Derived [concrete]
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type.357: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %Derived.elem: type = unbound_element_type %Derived, %Bar [concrete]
+// CHECK:STDOUT:   %struct_type.base.36d: type = struct_type {.base: %Bar} [concrete]
+// CHECK:STDOUT:   %complete_type.fff: <witness> = complete_type_witness %struct_type.base.36d [concrete]
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [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:     .Bar = @Derived.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .Derived = %Derived.decl
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "class_to_inherit_private.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {}
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Derived {
+// CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:   %complete_type.1: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
+// CHECK:STDOUT:   %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:   %.loc7: %Derived.elem = base_decl %Bar.ref, element0 [concrete]
+// CHECK:STDOUT:   %complete_type.loc8: <witness> = complete_type_witness %struct_type.base.36d [concrete = constants.%complete_type.fff]
+// CHECK:STDOUT:   complete_type_witness = %complete_type.loc8
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Derived
+// CHECK:STDOUT:   .Cpp = <poisoned>
+// CHECK:STDOUT:   .base = %.loc7
+// CHECK:STDOUT:   .foo = <poisoned>
+// CHECK:STDOUT:   extend %Bar.ref
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @Derived.%complete_type.1
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = @MyF.%foo.decl
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived]
+// CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {} {}
+// CHECK:STDOUT:   %foo.ref: %foo.type = name_ref foo, %foo.decl [concrete = constants.%foo]
+// CHECK:STDOUT:   %foo.call: init %empty_tuple.type = call %foo.ref()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @foo[]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_class_template.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = <poisoned>
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "class_template.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: <error> = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: <error> = value_param_pattern %bar.patt, call_param0 [concrete = <error>]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: <error> = value_param call_param0
+// CHECK:STDOUT:     %.loc17: type = splice_block %ptr [concrete = <error>] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.ref: <error> = name_ref Bar, <error> [concrete = <error>]
+// CHECK:STDOUT:       %ptr: type = ptr_type <error> [concrete = <error>]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: <error> = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: <error>);
+// CHECK:STDOUT:

+ 1126 - 0
toolchain/check/testdata/interop/cpp/no_prelude/struct.carbon

@@ -0,0 +1,1126 @@
+// 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
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/no_prelude/struct.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/no_prelude/struct.carbon
+
+// --- struct_declaration.h
+
+struct Bar;
+
+// --- fail_import_struct_declaration.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "struct_declaration.h";
+
+// CHECK:STDERR: fail_import_struct_declaration.carbon:[[@LINE+7]]:13: error: semantics TODO: `Unsupported: Record declarations without a definition` [SemanticsTodo]
+// CHECK:STDERR: fn MyF(bar: Cpp.Bar*);
+// CHECK:STDERR:             ^~~~~~~
+// CHECK:STDERR: fail_import_struct_declaration.carbon:[[@LINE+4]]:13: note: in `Cpp` name lookup for `Bar` [InCppNameLookup]
+// CHECK:STDERR: fn MyF(bar: Cpp.Bar*);
+// CHECK:STDERR:             ^~~~~~~
+// CHECK:STDERR:
+fn MyF(bar: Cpp.Bar*);
+
+// --- struct_definition.h
+
+struct Bar {};
+
+// --- import_struct_definition.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "struct_definition.h";
+
+fn MyF(bar: Cpp.Bar*);
+
+// --- struct_declaration_and_definition.h
+
+struct Bar;
+struct Bar {};
+
+// --- import_struct_declaration_and_definition.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "struct_declaration_and_definition.h";
+
+fn MyF(bar: Cpp.Bar*);
+
+// --- struct_public_static_member_function.h
+
+struct Bar {
+  static auto foo() -> void;
+};
+
+// --- import_struct_public_static_member_function.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "struct_public_static_member_function.h";
+
+fn MyF() {
+  Cpp.Bar.foo();
+}
+
+// --- struct_private_static_member_function.h
+
+struct Bar {
+ private:
+  static auto foo() -> void;
+};
+
+// --- todo_fail_import_struct_private_static_member_function.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "struct_private_static_member_function.h";
+
+fn MyF() {
+  Cpp.Bar.foo();
+}
+
+// --- struct_public_member_function.h
+
+struct Bar {
+  auto foo() -> void;
+};
+
+// --- fail_import_struct_public_member_function.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "struct_public_member_function.h";
+
+fn MyF(bar : Cpp.Bar*) {
+  // CHECK:STDERR: fail_import_struct_public_member_function.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: Non-global function` [SemanticsTodo]
+  // CHECK:STDERR:   bar->foo();
+  // CHECK:STDERR:   ^~~~~~~~
+  // CHECK:STDERR: fail_import_struct_public_member_function.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
+  // CHECK:STDERR:   bar->foo();
+  // CHECK:STDERR:   ^~~~~~~~
+  // CHECK:STDERR:
+  bar->foo();
+}
+
+// --- struct_public_static_data_member.h
+
+struct Bar {
+  static Bar* foo;
+};
+
+// --- fail_import_struct_public_static_data_member.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "struct_public_static_data_member.h";
+
+fn MyF() {
+  // CHECK:STDERR: fail_import_struct_public_static_data_member.carbon:[[@LINE+11]]:23: error: semantics TODO: `Unsupported: Declaration type Var` [SemanticsTodo]
+  // CHECK:STDERR:   let bar: Cpp.Bar* = Cpp.Bar.foo();
+  // CHECK:STDERR:                       ^~~~~~~~~~~
+  // CHECK:STDERR: fail_import_struct_public_static_data_member.carbon:[[@LINE+8]]:23: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
+  // CHECK:STDERR:   let bar: Cpp.Bar* = Cpp.Bar.foo();
+  // CHECK:STDERR:                       ^~~~~~~~~~~
+  // CHECK:STDERR:
+  // CHECK:STDERR: fail_import_struct_public_static_data_member.carbon:[[@LINE+4]]:23: error: member name `foo` not found in `Cpp.Bar` [MemberNameNotFoundInInstScope]
+  // CHECK:STDERR:   let bar: Cpp.Bar* = Cpp.Bar.foo();
+  // CHECK:STDERR:                       ^~~~~~~~~~~
+  // CHECK:STDERR:
+  let bar: Cpp.Bar* = Cpp.Bar.foo();
+}
+
+// --- struct_public_data_member.h
+
+struct Bar {
+  Bar* foo;
+};
+
+// --- fail_import_struct_public_data_member.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "struct_public_static_data_member.h";
+
+fn MyF(bar : Cpp.Bar*) {
+  // CHECK:STDERR: fail_import_struct_public_data_member.carbon:[[@LINE+11]]:27: error: semantics TODO: `Unsupported: Declaration type Var` [SemanticsTodo]
+  // CHECK:STDERR:   let foo_bar: Cpp.Bar* = bar->foo;
+  // CHECK:STDERR:                           ^~~~~~~~
+  // CHECK:STDERR: fail_import_struct_public_data_member.carbon:[[@LINE+8]]:27: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
+  // CHECK:STDERR:   let foo_bar: Cpp.Bar* = bar->foo;
+  // CHECK:STDERR:                           ^~~~~~~~
+  // CHECK:STDERR:
+  // CHECK:STDERR: fail_import_struct_public_data_member.carbon:[[@LINE+4]]:27: error: member name `foo` not found in `Cpp.Bar` [MemberNameNotFoundInInstScope]
+  // CHECK:STDERR:   let foo_bar: Cpp.Bar* = bar->foo;
+  // CHECK:STDERR:                           ^~~~~~~~
+  // CHECK:STDERR:
+  let foo_bar: Cpp.Bar* = bar->foo;
+}
+
+// --- struct_inheritance_static.h
+
+struct Bar1 {
+  static auto foo1() -> void;
+};
+
+struct Bar2 : public Bar1 {
+  static auto foo2() -> void;
+};
+
+// --- import_struct_inheritance_static.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "struct_inheritance_static.h";
+
+fn MyF() {
+  Cpp.Bar1.foo1();
+  Cpp.Bar2.foo1();
+  Cpp.Bar2.foo2();
+}
+
+// --- struct_inheritance_pointers.h
+
+struct Bar1 {};
+struct Bar2 : public Bar1 {};
+
+// --- fail_import_struct_inheritance_pointers.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "struct_inheritance_pointers.h";
+
+fn MyF1(bar : Cpp.Bar1*);
+// TODO: Support C++ inheritance.
+// CHECK:STDERR: fail_import_struct_inheritance_pointers.carbon:[[@LINE+7]]:33: error: `Core.ImplicitAs` implicitly referenced here, but package `Core` not found [CoreNotFound]
+// CHECK:STDERR: fn MyF2(bar : Cpp.Bar2*) { MyF1(bar); }
+// CHECK:STDERR:                                 ^~~
+// CHECK:STDERR: fail_import_struct_inheritance_pointers.carbon:[[@LINE-5]]:9: note: initializing function parameter [InCallToFunctionParam]
+// CHECK:STDERR: fn MyF1(bar : Cpp.Bar1*);
+// CHECK:STDERR:         ^~~~~~~~~~~~~~~
+// CHECK:STDERR:
+fn MyF2(bar : Cpp.Bar2*) { MyF1(bar); }
+
+// --- struct_dynamic.h
+
+struct Bar {
+  virtual ~Bar();
+};
+
+// --- fail_import_struct_dynamic.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "struct_dynamic.h";
+
+// CHECK:STDERR: fail_import_struct_dynamic.carbon:[[@LINE+7]]:14: error: semantics TODO: `Unsupported: Dynamic Class` [SemanticsTodo]
+// CHECK:STDERR: fn MyF(bar : Cpp.Bar*);
+// CHECK:STDERR:              ^~~~~~~
+// CHECK:STDERR: fail_import_struct_dynamic.carbon:[[@LINE+4]]:14: note: in `Cpp` name lookup for `Bar` [InCppNameLookup]
+// CHECK:STDERR: fn MyF(bar : Cpp.Bar*);
+// CHECK:STDERR:              ^~~~~~~
+// CHECK:STDERR:
+fn MyF(bar : Cpp.Bar*);
+
+// --- struct_to_inherit_public.h
+
+struct Bar { static auto foo() -> void; };
+
+// --- import_struct_to_inherit_public.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "struct_to_inherit_public.h";
+
+class Derived {
+  extend base: Cpp.Bar;
+}
+
+fn MyF() {
+  Derived.foo();
+}
+
+// --- struct_to_inherit_private.h
+
+struct Bar {
+ private:
+  static auto foo() -> void;
+};
+
+// --- todo_fail_struct_to_inherit_private.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "struct_to_inherit_private.h";
+
+class Derived {
+  extend base: Cpp.Bar;
+}
+
+fn MyF() {
+  Derived.foo();
+}
+
+// --- struct_template.h
+
+template<typename T>
+struct Bar {};
+
+// --- fail_import_struct_template.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "struct_template.h";
+
+// CHECK:STDERR: fail_import_struct_template.carbon:[[@LINE+11]]:13: error: semantics TODO: `Unsupported: Declaration type ClassTemplate` [SemanticsTodo]
+// CHECK:STDERR: fn MyF(bar: Cpp.Bar*);
+// CHECK:STDERR:             ^~~~~~~
+// CHECK:STDERR: fail_import_struct_template.carbon:[[@LINE+8]]:13: note: in `Cpp` name lookup for `Bar` [InCppNameLookup]
+// CHECK:STDERR: fn MyF(bar: Cpp.Bar*);
+// CHECK:STDERR:             ^~~~~~~
+// CHECK:STDERR:
+// CHECK:STDERR: fail_import_struct_template.carbon:[[@LINE+4]]:13: error: member name `Bar` not found in `Cpp` [MemberNameNotFoundInInstScope]
+// CHECK:STDERR: fn MyF(bar: Cpp.Bar*);
+// CHECK:STDERR:             ^~~~~~~
+// CHECK:STDERR:
+fn MyF(bar: Cpp.Bar*);
+
+// CHECK:STDOUT: --- fail_import_struct_declaration.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = <error>
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "struct_declaration.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: <error> = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: <error> = value_param_pattern %bar.patt, call_param0 [concrete = <error>]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: <error> = value_param call_param0
+// CHECK:STDOUT:     %.loc13: type = splice_block %ptr [concrete = <error>] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.ref: <error> = name_ref Bar, <error> [concrete = <error>]
+// CHECK:STDOUT:       %ptr: type = ptr_type <error> [concrete = <error>]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: <error> = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: <error>);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- import_struct_definition.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %ptr: type = ptr_type %Bar [concrete]
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "struct_definition.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: %ptr = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: %ptr = value_param_pattern %bar.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: %ptr = value_param call_param0
+// CHECK:STDOUT:     %.loc6: type = splice_block %ptr [concrete = constants.%ptr] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:       %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:       %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:       %ptr: type = ptr_type %Bar.ref [concrete = constants.%ptr]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: %ptr = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: %ptr);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- import_struct_declaration_and_definition.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %ptr: type = ptr_type %Bar [concrete]
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "struct_declaration_and_definition.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: %ptr = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: %ptr = value_param_pattern %bar.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: %ptr = value_param call_param0
+// CHECK:STDOUT:     %.loc6: type = splice_block %ptr [concrete = constants.%ptr] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:       %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:       %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:       %ptr: type = ptr_type %Bar.ref [concrete = constants.%ptr]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: %ptr = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: %ptr);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- import_struct_public_static_member_function.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [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:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "struct_public_static_member_function.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = @MyF.%foo.decl
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {} {}
+// CHECK:STDOUT:   %foo.ref: %foo.type = name_ref foo, %foo.decl [concrete = constants.%foo]
+// CHECK:STDOUT:   %foo.call: init %empty_tuple.type = call %foo.ref()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @foo[]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- todo_fail_import_struct_private_static_member_function.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [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:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "struct_private_static_member_function.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = @MyF.%foo.decl
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {} {}
+// CHECK:STDOUT:   %foo.ref: %foo.type = name_ref foo, %foo.decl [concrete = constants.%foo]
+// CHECK:STDOUT:   %foo.call: init %empty_tuple.type = call %foo.ref()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @foo[]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_struct_public_member_function.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %ptr.f68: type = ptr_type %Bar [concrete]
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "struct_public_member_function.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: %ptr.f68 = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: %ptr.f68 = value_param_pattern %bar.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: %ptr.f68 = value_param call_param0
+// CHECK:STDOUT:     %.loc6: type = splice_block %ptr [concrete = constants.%ptr.f68] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:       %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:       %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:       %ptr: type = ptr_type %Bar.ref [concrete = constants.%ptr.f68]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: %ptr.f68 = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = <error>
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: %ptr.f68) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %bar.ref: %ptr.f68 = name_ref bar, %bar
+// CHECK:STDOUT:   %.loc14: ref %Bar = deref %bar.ref
+// CHECK:STDOUT:   %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_struct_public_static_data_member.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %ptr.f68: type = ptr_type %Bar [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "struct_public_static_data_member.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = <poisoned>
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %bar.patt: %ptr.f68 = binding_pattern bar
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.ref.loc18_23: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar.ref.loc18_26: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:   %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
+// CHECK:STDOUT:   %.loc18: type = splice_block %ptr [concrete = constants.%ptr.f68] {
+// CHECK:STDOUT:     %Cpp.ref.loc18_12: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:     %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:     %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:     %Bar.ref.loc18_15: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:     %ptr: type = ptr_type %Bar.ref.loc18_15 [concrete = constants.%ptr.f68]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %bar: %ptr.f68 = bind_name bar, <error>
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_struct_public_data_member.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %ptr.f68: type = ptr_type %Bar [concrete]
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "struct_public_static_data_member.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: %ptr.f68 = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: %ptr.f68 = value_param_pattern %bar.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: %ptr.f68 = value_param call_param0
+// CHECK:STDOUT:     %.loc6: type = splice_block %ptr.loc6 [concrete = constants.%ptr.f68] {
+// CHECK:STDOUT:       %Cpp.ref.loc6: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:       %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:       %Bar.ref.loc6: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:       %ptr.loc6: type = ptr_type %Bar.ref.loc6 [concrete = constants.%ptr.f68]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: %ptr.f68 = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = <poisoned>
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: %ptr.f68) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %foo_bar.patt: %ptr.f68 = binding_pattern foo_bar
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %bar.ref: %ptr.f68 = name_ref bar, %bar
+// CHECK:STDOUT:   %.loc18_30: ref %Bar = deref %bar.ref
+// CHECK:STDOUT:   %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
+// CHECK:STDOUT:   %.loc18_23: type = splice_block %ptr.loc18 [concrete = constants.%ptr.f68] {
+// CHECK:STDOUT:     %Cpp.ref.loc18: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:     %Bar.ref.loc18: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:     %ptr.loc18: type = ptr_type %Bar.ref.loc18 [concrete = constants.%ptr.f68]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %foo_bar: %ptr.f68 = bind_name foo_bar, <error>
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- import_struct_inheritance_static.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Bar1: type = class_type @Bar1 [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %foo1.type.148: type = fn_type @foo1.1 [concrete]
+// CHECK:STDOUT:   %foo1.8cd: %foo1.type.148 = struct_value () [concrete]
+// CHECK:STDOUT:   %Bar2: type = class_type @Bar2 [concrete]
+// CHECK:STDOUT:   %foo1.type.0b8: type = fn_type @foo1.2 [concrete]
+// CHECK:STDOUT:   %foo1.ba2: %foo1.type.0b8 = struct_value () [concrete]
+// CHECK:STDOUT:   %foo2.type: type = fn_type @foo2 [concrete]
+// CHECK:STDOUT:   %foo2: %foo2.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar1 = @MyF.%Bar1.decl
+// CHECK:STDOUT:     .Bar2 = @MyF.%Bar2.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "struct_inheritance_static.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar1 {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type.1
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar1
+// CHECK:STDOUT:   .foo1 = @MyF.%foo1.decl.1
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar2 {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type.2
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar2
+// CHECK:STDOUT:   .foo1 = @MyF.%foo1.decl.2
+// CHECK:STDOUT:   .foo2 = @MyF.%foo2.decl
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Cpp.ref.loc7: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar1.decl: type = class_decl @Bar1 [concrete = constants.%Bar1] {} {}
+// CHECK:STDOUT:   %complete_type.1: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %Bar1.ref: type = name_ref Bar1, %Bar1.decl [concrete = constants.%Bar1]
+// CHECK:STDOUT:   %foo1.decl.1: %foo1.type.148 = fn_decl @foo1.1 [concrete = constants.%foo1.8cd] {} {}
+// CHECK:STDOUT:   %foo1.ref.loc7: %foo1.type.148 = name_ref foo1, %foo1.decl.1 [concrete = constants.%foo1.8cd]
+// CHECK:STDOUT:   %foo1.call.loc7: init %empty_tuple.type = call %foo1.ref.loc7()
+// CHECK:STDOUT:   %Cpp.ref.loc8: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar2.decl: type = class_decl @Bar2 [concrete = constants.%Bar2] {} {}
+// CHECK:STDOUT:   %complete_type.2: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %Bar2.ref.loc8: type = name_ref Bar2, %Bar2.decl [concrete = constants.%Bar2]
+// CHECK:STDOUT:   %foo1.decl.2: %foo1.type.0b8 = fn_decl @foo1.2 [concrete = constants.%foo1.ba2] {} {}
+// CHECK:STDOUT:   %foo1.ref.loc8: %foo1.type.0b8 = name_ref foo1, %foo1.decl.2 [concrete = constants.%foo1.ba2]
+// CHECK:STDOUT:   %foo1.call.loc8: init %empty_tuple.type = call %foo1.ref.loc8()
+// CHECK:STDOUT:   %Cpp.ref.loc9: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar2.ref.loc9: type = name_ref Bar2, %Bar2.decl [concrete = constants.%Bar2]
+// CHECK:STDOUT:   %foo2.decl: %foo2.type = fn_decl @foo2 [concrete = constants.%foo2] {} {}
+// CHECK:STDOUT:   %foo2.ref: %foo2.type = name_ref foo2, %foo2.decl [concrete = constants.%foo2]
+// CHECK:STDOUT:   %foo2.call: init %empty_tuple.type = call %foo2.ref()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @foo1.1[]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @foo1.2[]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @foo2[]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_struct_inheritance_pointers.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Bar1: type = class_type @Bar1 [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %ptr.f68: type = ptr_type %Bar1 [concrete]
+// CHECK:STDOUT:   %MyF1.type: type = fn_type @MyF1 [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %MyF1: %MyF1.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Bar2: type = class_type @Bar2 [concrete]
+// CHECK:STDOUT:   %ptr.eca: type = ptr_type %Bar2 [concrete]
+// CHECK:STDOUT:   %MyF2.type: type = fn_type @MyF2 [concrete]
+// CHECK:STDOUT:   %MyF2: %MyF2.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar1 = @MyF1.%Bar1.decl
+// CHECK:STDOUT:     .Bar2 = @MyF2.%Bar2.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF1 = %MyF1.decl
+// CHECK:STDOUT:     .MyF2 = %MyF2.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "struct_inheritance_pointers.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF1.decl: %MyF1.type = fn_decl @MyF1 [concrete = constants.%MyF1] {
+// CHECK:STDOUT:     %bar.patt: %ptr.f68 = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: %ptr.f68 = value_param_pattern %bar.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: %ptr.f68 = value_param call_param0
+// CHECK:STDOUT:     %.loc6: type = splice_block %ptr [concrete = constants.%ptr.f68] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar1.decl: type = class_decl @Bar1 [concrete = constants.%Bar1] {} {}
+// CHECK:STDOUT:       %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:       %Bar1.ref: type = name_ref Bar1, %Bar1.decl [concrete = constants.%Bar1]
+// CHECK:STDOUT:       %ptr: type = ptr_type %Bar1.ref [concrete = constants.%ptr.f68]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: %ptr.f68 = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF2.decl: %MyF2.type = fn_decl @MyF2 [concrete = constants.%MyF2] {
+// CHECK:STDOUT:     %bar.patt: %ptr.eca = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: %ptr.eca = value_param_pattern %bar.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: %ptr.eca = value_param call_param0
+// CHECK:STDOUT:     %.loc15_23: type = splice_block %ptr [concrete = constants.%ptr.eca] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar2.decl: type = class_decl @Bar2 [concrete = constants.%Bar2] {} {}
+// CHECK:STDOUT:       %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:       %Bar2.ref: type = name_ref Bar2, %Bar2.decl [concrete = constants.%Bar2]
+// CHECK:STDOUT:       %ptr: type = ptr_type %Bar2.ref [concrete = constants.%ptr.eca]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: %ptr.eca = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar1 {
+// CHECK:STDOUT:   complete_type_witness = @MyF1.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar1
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar2 {
+// CHECK:STDOUT:   complete_type_witness = @MyF2.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar2
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF1(%bar.param_patt: %ptr.f68);
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF2(%bar.param_patt: %ptr.eca) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %MyF1.ref: %MyF1.type = name_ref MyF1, file.%MyF1.decl [concrete = constants.%MyF1]
+// CHECK:STDOUT:   %bar.ref: %ptr.eca = name_ref bar, %bar
+// CHECK:STDOUT:   %.loc15_33: %ptr.f68 = converted %bar.ref, <error> [concrete = <error>]
+// CHECK:STDOUT:   %MyF1.call: init %empty_tuple.type = call %MyF1.ref(<error>)
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_struct_dynamic.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = <error>
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "struct_dynamic.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: <error> = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: <error> = value_param_pattern %bar.patt, call_param0 [concrete = <error>]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: <error> = value_param call_param0
+// CHECK:STDOUT:     %.loc13: type = splice_block %ptr [concrete = <error>] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.ref: <error> = name_ref Bar, <error> [concrete = <error>]
+// CHECK:STDOUT:       %ptr: type = ptr_type <error> [concrete = <error>]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: <error> = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: <error>);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- import_struct_to_inherit_public.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Derived: type = class_type @Derived [concrete]
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type.357: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %Derived.elem: type = unbound_element_type %Derived, %Bar [concrete]
+// CHECK:STDOUT:   %struct_type.base.36d: type = struct_type {.base: %Bar} [concrete]
+// CHECK:STDOUT:   %complete_type.fff: <witness> = complete_type_witness %struct_type.base.36d [concrete]
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [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:     .Bar = @Derived.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .Derived = %Derived.decl
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "struct_to_inherit_public.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {}
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Derived {
+// CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:   %complete_type.1: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
+// CHECK:STDOUT:   %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:   %.loc7: %Derived.elem = base_decl %Bar.ref, element0 [concrete]
+// CHECK:STDOUT:   %complete_type.loc8: <witness> = complete_type_witness %struct_type.base.36d [concrete = constants.%complete_type.fff]
+// CHECK:STDOUT:   complete_type_witness = %complete_type.loc8
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Derived
+// CHECK:STDOUT:   .Cpp = <poisoned>
+// CHECK:STDOUT:   .base = %.loc7
+// CHECK:STDOUT:   .foo = <poisoned>
+// CHECK:STDOUT:   extend %Bar.ref
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @Derived.%complete_type.1
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = @MyF.%foo.decl
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived]
+// CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {} {}
+// CHECK:STDOUT:   %foo.ref: %foo.type = name_ref foo, %foo.decl [concrete = constants.%foo]
+// CHECK:STDOUT:   %foo.call: init %empty_tuple.type = call %foo.ref()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @foo[]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- todo_fail_struct_to_inherit_private.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Derived: type = class_type @Derived [concrete]
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type.357: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %Derived.elem: type = unbound_element_type %Derived, %Bar [concrete]
+// CHECK:STDOUT:   %struct_type.base.36d: type = struct_type {.base: %Bar} [concrete]
+// CHECK:STDOUT:   %complete_type.fff: <witness> = complete_type_witness %struct_type.base.36d [concrete]
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [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:     .Bar = @Derived.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .Derived = %Derived.decl
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "struct_to_inherit_private.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {}
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Derived {
+// CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:   %complete_type.1: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
+// CHECK:STDOUT:   %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:   %.loc7: %Derived.elem = base_decl %Bar.ref, element0 [concrete]
+// CHECK:STDOUT:   %complete_type.loc8: <witness> = complete_type_witness %struct_type.base.36d [concrete = constants.%complete_type.fff]
+// CHECK:STDOUT:   complete_type_witness = %complete_type.loc8
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Derived
+// CHECK:STDOUT:   .Cpp = <poisoned>
+// CHECK:STDOUT:   .base = %.loc7
+// CHECK:STDOUT:   .foo = <poisoned>
+// CHECK:STDOUT:   extend %Bar.ref
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @Derived.%complete_type.1
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = @MyF.%foo.decl
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived]
+// CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {} {}
+// CHECK:STDOUT:   %foo.ref: %foo.type = name_ref foo, %foo.decl [concrete = constants.%foo]
+// CHECK:STDOUT:   %foo.call: init %empty_tuple.type = call %foo.ref()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @foo[]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_struct_template.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = <poisoned>
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "struct_template.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: <error> = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: <error> = value_param_pattern %bar.patt, call_param0 [concrete = <error>]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: <error> = value_param call_param0
+// CHECK:STDOUT:     %.loc17: type = splice_block %ptr [concrete = <error>] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.ref: <error> = name_ref Bar, <error> [concrete = <error>]
+// CHECK:STDOUT:       %ptr: type = ptr_type <error> [concrete = <error>]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: <error> = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: <error>);
+// CHECK:STDOUT:

+ 759 - 0
toolchain/check/testdata/interop/cpp/no_prelude/union.carbon

@@ -0,0 +1,759 @@
+// 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
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/no_prelude/union.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/no_prelude/union.carbon
+
+// --- union_declaration.h
+
+union Bar;
+
+// --- fail_import_union_declaration.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "union_declaration.h";
+
+// CHECK:STDERR: fail_import_union_declaration.carbon:[[@LINE+7]]:13: error: semantics TODO: `Unsupported: Record declarations without a definition` [SemanticsTodo]
+// CHECK:STDERR: fn MyF(bar: Cpp.Bar*);
+// CHECK:STDERR:             ^~~~~~~
+// CHECK:STDERR: fail_import_union_declaration.carbon:[[@LINE+4]]:13: note: in `Cpp` name lookup for `Bar` [InCppNameLookup]
+// CHECK:STDERR: fn MyF(bar: Cpp.Bar*);
+// CHECK:STDERR:             ^~~~~~~
+// CHECK:STDERR:
+fn MyF(bar: Cpp.Bar*);
+
+// --- union_definition.h
+
+union Bar {};
+
+// --- import_union_definition.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "union_definition.h";
+
+fn MyF(bar: Cpp.Bar*);
+
+// --- union_declaration_and_definition.h
+
+union Bar;
+union Bar {};
+
+// --- import_union_declaration_and_definition.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "union_declaration_and_definition.h";
+
+fn MyF(bar: Cpp.Bar*);
+
+// --- union_public_static_member_function.h
+
+union Bar {
+ public:
+  static auto foo() -> void;
+};
+
+// --- import_union_public_static_member_function.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "union_public_static_member_function.h";
+
+fn MyF() {
+  Cpp.Bar.foo();
+}
+
+// --- union_private_static_member_function.h
+
+union Bar {
+ private:
+  static auto foo() -> void;
+};
+
+// --- todo_fail_import_union_private_static_member_function.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "union_private_static_member_function.h";
+
+fn MyF() {
+  Cpp.Bar.foo();
+}
+
+// --- union_public_member_function.h
+
+union Bar {
+ public:
+  auto foo() -> void;
+};
+
+// --- fail_import_union_public_member_function.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "union_public_member_function.h";
+
+fn MyF(bar : Cpp.Bar*) {
+  // CHECK:STDERR: fail_import_union_public_member_function.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: Non-global function` [SemanticsTodo]
+  // CHECK:STDERR:   bar->foo();
+  // CHECK:STDERR:   ^~~~~~~~
+  // CHECK:STDERR: fail_import_union_public_member_function.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
+  // CHECK:STDERR:   bar->foo();
+  // CHECK:STDERR:   ^~~~~~~~
+  // CHECK:STDERR:
+  bar->foo();
+}
+
+// --- union_public_static_data_member.h
+
+union Bar {
+ public:
+  static Bar* foo;
+};
+
+// --- fail_import_union_public_static_data_member.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "union_public_static_data_member.h";
+
+fn MyF() {
+  // CHECK:STDERR: fail_import_union_public_static_data_member.carbon:[[@LINE+11]]:23: error: semantics TODO: `Unsupported: Declaration type Var` [SemanticsTodo]
+  // CHECK:STDERR:   let bar: Cpp.Bar* = Cpp.Bar.foo();
+  // CHECK:STDERR:                       ^~~~~~~~~~~
+  // CHECK:STDERR: fail_import_union_public_static_data_member.carbon:[[@LINE+8]]:23: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
+  // CHECK:STDERR:   let bar: Cpp.Bar* = Cpp.Bar.foo();
+  // CHECK:STDERR:                       ^~~~~~~~~~~
+  // CHECK:STDERR:
+  // CHECK:STDERR: fail_import_union_public_static_data_member.carbon:[[@LINE+4]]:23: error: member name `foo` not found in `Cpp.Bar` [MemberNameNotFoundInInstScope]
+  // CHECK:STDERR:   let bar: Cpp.Bar* = Cpp.Bar.foo();
+  // CHECK:STDERR:                       ^~~~~~~~~~~
+  // CHECK:STDERR:
+  let bar: Cpp.Bar* = Cpp.Bar.foo();
+}
+
+// --- union_public_data_member.h
+
+union Bar {
+ public:
+  Bar* foo;
+};
+
+// --- fail_import_union_public_data_member.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "union_public_static_data_member.h";
+
+fn MyF(bar : Cpp.Bar*) {
+  // CHECK:STDERR: fail_import_union_public_data_member.carbon:[[@LINE+11]]:27: error: semantics TODO: `Unsupported: Declaration type Var` [SemanticsTodo]
+  // CHECK:STDERR:   let foo_bar: Cpp.Bar* = bar->foo;
+  // CHECK:STDERR:                           ^~~~~~~~
+  // CHECK:STDERR: fail_import_union_public_data_member.carbon:[[@LINE+8]]:27: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
+  // CHECK:STDERR:   let foo_bar: Cpp.Bar* = bar->foo;
+  // CHECK:STDERR:                           ^~~~~~~~
+  // CHECK:STDERR:
+  // CHECK:STDERR: fail_import_union_public_data_member.carbon:[[@LINE+4]]:27: error: member name `foo` not found in `Cpp.Bar` [MemberNameNotFoundInInstScope]
+  // CHECK:STDERR:   let foo_bar: Cpp.Bar* = bar->foo;
+  // CHECK:STDERR:                           ^~~~~~~~
+  // CHECK:STDERR:
+  let foo_bar: Cpp.Bar* = bar->foo;
+}
+
+// --- union_to_inherit_public.h
+
+union Bar {
+ public:
+  static auto foo() -> void;
+};
+
+// --- todo_fail_import_union_to_inherit_public.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "union_to_inherit_public.h";
+
+class Derived {
+  extend base: Cpp.Bar;
+}
+
+fn MyF() {
+  Derived.foo();
+}
+
+// --- union_template.h
+
+template<typename T>
+union Bar {};
+
+// --- fail_import_union_template.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "union_template.h";
+
+// CHECK:STDERR: fail_import_union_template.carbon:[[@LINE+11]]:13: error: semantics TODO: `Unsupported: Declaration type ClassTemplate` [SemanticsTodo]
+// CHECK:STDERR: fn MyF(bar: Cpp.Bar*);
+// CHECK:STDERR:             ^~~~~~~
+// CHECK:STDERR: fail_import_union_template.carbon:[[@LINE+8]]:13: note: in `Cpp` name lookup for `Bar` [InCppNameLookup]
+// CHECK:STDERR: fn MyF(bar: Cpp.Bar*);
+// CHECK:STDERR:             ^~~~~~~
+// CHECK:STDERR:
+// CHECK:STDERR: fail_import_union_template.carbon:[[@LINE+4]]:13: error: member name `Bar` not found in `Cpp` [MemberNameNotFoundInInstScope]
+// CHECK:STDERR: fn MyF(bar: Cpp.Bar*);
+// CHECK:STDERR:             ^~~~~~~
+// CHECK:STDERR:
+fn MyF(bar: Cpp.Bar*);
+
+// CHECK:STDOUT: --- fail_import_union_declaration.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = <error>
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "union_declaration.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: <error> = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: <error> = value_param_pattern %bar.patt, call_param0 [concrete = <error>]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: <error> = value_param call_param0
+// CHECK:STDOUT:     %.loc13: type = splice_block %ptr [concrete = <error>] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.ref: <error> = name_ref Bar, <error> [concrete = <error>]
+// CHECK:STDOUT:       %ptr: type = ptr_type <error> [concrete = <error>]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: <error> = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: <error>);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- import_union_definition.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %ptr: type = ptr_type %Bar [concrete]
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "union_definition.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: %ptr = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: %ptr = value_param_pattern %bar.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: %ptr = value_param call_param0
+// CHECK:STDOUT:     %.loc6: type = splice_block %ptr [concrete = constants.%ptr] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:       %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:       %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:       %ptr: type = ptr_type %Bar.ref [concrete = constants.%ptr]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: %ptr = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: %ptr);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- import_union_declaration_and_definition.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %ptr: type = ptr_type %Bar [concrete]
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "union_declaration_and_definition.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: %ptr = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: %ptr = value_param_pattern %bar.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: %ptr = value_param call_param0
+// CHECK:STDOUT:     %.loc6: type = splice_block %ptr [concrete = constants.%ptr] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:       %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:       %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:       %ptr: type = ptr_type %Bar.ref [concrete = constants.%ptr]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: %ptr = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: %ptr);
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- import_union_public_static_member_function.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [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:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "union_public_static_member_function.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = @MyF.%foo.decl
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {} {}
+// CHECK:STDOUT:   %foo.ref: %foo.type = name_ref foo, %foo.decl [concrete = constants.%foo]
+// CHECK:STDOUT:   %foo.call: init %empty_tuple.type = call %foo.ref()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @foo[]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- todo_fail_import_union_private_static_member_function.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [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:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "union_private_static_member_function.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = @MyF.%foo.decl
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:   %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {} {}
+// CHECK:STDOUT:   %foo.ref: %foo.type = name_ref foo, %foo.decl [concrete = constants.%foo]
+// CHECK:STDOUT:   %foo.call: init %empty_tuple.type = call %foo.ref()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @foo[]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_union_public_member_function.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %ptr.f68: type = ptr_type %Bar [concrete]
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "union_public_member_function.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: %ptr.f68 = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: %ptr.f68 = value_param_pattern %bar.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: %ptr.f68 = value_param call_param0
+// CHECK:STDOUT:     %.loc6: type = splice_block %ptr [concrete = constants.%ptr.f68] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:       %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:       %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:       %ptr: type = ptr_type %Bar.ref [concrete = constants.%ptr.f68]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: %ptr.f68 = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = <error>
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: %ptr.f68) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %bar.ref: %ptr.f68 = name_ref bar, %bar
+// CHECK:STDOUT:   %.loc14: ref %Bar = deref %bar.ref
+// CHECK:STDOUT:   %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_union_public_static_data_member.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %ptr.f68: type = ptr_type %Bar [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "union_public_static_data_member.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = <poisoned>
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %bar.patt: %ptr.f68 = binding_pattern bar
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.ref.loc18_23: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar.ref.loc18_26: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:   %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
+// CHECK:STDOUT:   %.loc18: type = splice_block %ptr [concrete = constants.%ptr.f68] {
+// CHECK:STDOUT:     %Cpp.ref.loc18_12: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:     %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:     %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:     %Bar.ref.loc18_15: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:     %ptr: type = ptr_type %Bar.ref.loc18_15 [concrete = constants.%ptr.f68]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %bar: %ptr.f68 = bind_name bar, <error>
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_union_public_data_member.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %ptr.f68: type = ptr_type %Bar [concrete]
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = @MyF.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "union_public_static_data_member.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: %ptr.f68 = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: %ptr.f68 = value_param_pattern %bar.patt, call_param0
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: %ptr.f68 = value_param call_param0
+// CHECK:STDOUT:     %.loc6: type = splice_block %ptr.loc6 [concrete = constants.%ptr.f68] {
+// CHECK:STDOUT:       %Cpp.ref.loc6: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:       %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
+// CHECK:STDOUT:       %Bar.ref.loc6: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:       %ptr.loc6: type = ptr_type %Bar.ref.loc6 [concrete = constants.%ptr.f68]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: %ptr.f68 = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @MyF.%complete_type
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = <poisoned>
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: %ptr.f68) {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %foo_bar.patt: %ptr.f68 = binding_pattern foo_bar
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %bar.ref: %ptr.f68 = name_ref bar, %bar
+// CHECK:STDOUT:   %.loc18_30: ref %Bar = deref %bar.ref
+// CHECK:STDOUT:   %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
+// CHECK:STDOUT:   %.loc18_23: type = splice_block %ptr.loc18 [concrete = constants.%ptr.f68] {
+// CHECK:STDOUT:     %Cpp.ref.loc18: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:     %Bar.ref.loc18: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:     %ptr.loc18: type = ptr_type %Bar.ref.loc18 [concrete = constants.%ptr.f68]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %foo_bar: %ptr.f68 = bind_name foo_bar, <error>
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- todo_fail_import_union_to_inherit_public.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Derived: type = class_type @Derived [concrete]
+// CHECK:STDOUT:   %Bar: type = class_type @Bar [concrete]
+// CHECK:STDOUT:   %empty_struct_type: type = struct_type {} [concrete]
+// CHECK:STDOUT:   %complete_type.357: <witness> = complete_type_witness %empty_struct_type [concrete]
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %Derived.elem: type = unbound_element_type %Derived, %Bar [concrete]
+// CHECK:STDOUT:   %struct_type.base.36d: type = struct_type {.base: %Bar} [concrete]
+// CHECK:STDOUT:   %complete_type.fff: <witness> = complete_type_witness %struct_type.base.36d [concrete]
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [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:     .Bar = @Derived.%Bar.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .Derived = %Derived.decl
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "union_to_inherit_public.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Derived.decl: type = class_decl @Derived [concrete = constants.%Derived] {} {}
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {}
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Derived {
+// CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {}
+// CHECK:STDOUT:   %complete_type.1: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
+// CHECK:STDOUT:   %Bar.ref: type = name_ref Bar, %Bar.decl [concrete = constants.%Bar]
+// CHECK:STDOUT:   %.loc7: %Derived.elem = base_decl %Bar.ref, element0 [concrete]
+// CHECK:STDOUT:   %complete_type.loc8: <witness> = complete_type_witness %struct_type.base.36d [concrete = constants.%complete_type.fff]
+// CHECK:STDOUT:   complete_type_witness = %complete_type.loc8
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Derived
+// CHECK:STDOUT:   .Cpp = <poisoned>
+// CHECK:STDOUT:   .base = %.loc7
+// CHECK:STDOUT:   .foo = <poisoned>
+// CHECK:STDOUT:   extend %Bar.ref
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Bar {
+// CHECK:STDOUT:   complete_type_witness = @Derived.%complete_type.1
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%Bar
+// CHECK:STDOUT:   .foo = @MyF.%foo.decl
+// CHECK:STDOUT:   import Cpp//...
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived]
+// CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {} {}
+// CHECK:STDOUT:   %foo.ref: %foo.type = name_ref foo, %foo.decl [concrete = constants.%foo]
+// CHECK:STDOUT:   %foo.call: init %empty_tuple.type = call %foo.ref()
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @foo[]();
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_import_union_template.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %MyF.type: type = fn_type @MyF [concrete]
+// CHECK:STDOUT:   %MyF: %MyF.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .Bar = <poisoned>
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [concrete] {
+// CHECK:STDOUT:     .Cpp = imports.%Cpp
+// CHECK:STDOUT:     .MyF = %MyF.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.import_cpp = import_cpp {
+// CHECK:STDOUT:     import Cpp "union_template.h"
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {
+// CHECK:STDOUT:     %bar.patt: <error> = binding_pattern bar
+// CHECK:STDOUT:     %bar.param_patt: <error> = value_param_pattern %bar.patt, call_param0 [concrete = <error>]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %bar.param: <error> = value_param call_param0
+// CHECK:STDOUT:     %.loc17: type = splice_block %ptr [concrete = <error>] {
+// CHECK:STDOUT:       %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:       %Bar.ref: <error> = name_ref Bar, <error> [concrete = <error>]
+// CHECK:STDOUT:       %ptr: type = ptr_type <error> [concrete = <error>]
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %bar: <error> = bind_name bar, %bar.param
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @MyF(%bar.param_patt: <error>);
+// CHECK:STDOUT:

+ 2 - 2
toolchain/check/testdata/interop/cpp/no_prelude/unsupported_decl_type.carbon

@@ -10,7 +10,7 @@
 
 // --- unsupported_decl.h
 
-class foo;
+typedef int foo;
 
 // --- fail_import_unsupported_decl.carbon
 
@@ -19,7 +19,7 @@ library "[[@TEST_NAME]]";
 import Cpp library "unsupported_decl.h";
 
 fn F() {
-  // CHECK:STDERR: fail_import_unsupported_decl.carbon:[[@LINE+11]]:3: error: semantics TODO: `Unsupported: Declaration type CXXRecord` [SemanticsTodo]
+  // CHECK:STDERR: fail_import_unsupported_decl.carbon:[[@LINE+11]]:3: error: semantics TODO: `Unsupported: Declaration type Typedef` [SemanticsTodo]
   // CHECK:STDERR:   Cpp.foo();
   // CHECK:STDERR:   ^~~~~~~
   // CHECK:STDERR: fail_import_unsupported_decl.carbon:[[@LINE+8]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]