diff --git a/toolchain/check/import_cpp.cpp b/toolchain/check/import_cpp.cpp index f0b8ee526800..1e1125a22c00 100644 --- a/toolchain/check/import_cpp.cpp +++ b/toolchain/check/import_cpp.cpp @@ -700,8 +700,17 @@ static auto MapRecordType(Context& context, SemIR::LocId loc_id, // Check if the declaration is already mapped. SemIR::InstId record_inst_id = LookupClangDeclInstId(context, record_decl); if (!record_inst_id.has_value()) { - auto parent_inst_id = - AsCarbonNamespace(context, record_decl->getDeclContext()); + clang::DeclContext* decl_context = record_decl->getDeclContext(); + if (!clang::isa(decl_context) && + !clang::isa(decl_context)) { + context.TODO(loc_id, + "Unsupported mapping of a C++ record to a type within a " + "declaration context that is not the translation unit or " + "a namespace"); + return {.inst_id = SemIR::ErrorInst::TypeInstId, + .type_id = SemIR::ErrorInst::TypeId}; + } + auto parent_inst_id = AsCarbonNamespace(context, decl_context); auto parent_name_scope_id = context.insts().GetAs(parent_inst_id).name_scope_id; SemIR::NameId record_name_id = diff --git a/toolchain/check/testdata/interop/cpp/function/class.carbon b/toolchain/check/testdata/interop/cpp/function/class.carbon index c76b7e680f5d..dbd9e7dd828e 100644 --- a/toolchain/check/testdata/interop/cpp/function/class.carbon +++ b/toolchain/check/testdata/interop/cpp/function/class.carbon @@ -211,6 +211,39 @@ fn F() { //@dump-sem-ir-end } +// ============================================================================ +// Defined class in class +// ============================================================================ + +// --- definition_in_outer_definition.h + +class O { + public: + class C {}; +}; + +auto foo(O::C) -> void; + +// --- fail_todo_import_definition_in_outer_definition.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "definition_in_outer_definition.h"; + +fn F() { + //@dump-sem-ir-begin + // CHECK:STDERR: fail_todo_import_definition_in_outer_definition.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported mapping of a C++ record to a type within a declaration context that is not the translation unit or a namespace` [SemanticsTodo] + // CHECK:STDERR: Cpp.foo({}); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_todo_import_definition_in_outer_definition.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup] + // CHECK:STDERR: Cpp.foo({}); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + Cpp.foo({}); + var x: Cpp.O; + //@dump-sem-ir-end +} + // ============================================================================ // Defined class and explicitly used // ============================================================================ @@ -653,6 +686,59 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_import_definition_in_outer_definition.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %foo.type: type = fn_type @foo [concrete] +// CHECK:STDOUT: %foo: %foo.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %O: type = class_type @O [concrete] +// CHECK:STDOUT: %pattern_type.cff: type = pattern_type %O [concrete] +// CHECK:STDOUT: %Op.type.1b8: type = fn_type @Op.2, @Destroy.impl(%O) [concrete] +// CHECK:STDOUT: %Op.2df: %Op.type.1b8 = struct_value () [concrete] +// CHECK:STDOUT: %ptr.820: type = ptr_type %O [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .foo = %foo.decl +// CHECK:STDOUT: .O = %O.decl +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %O.decl: type = class_decl @O [concrete = constants.%O] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref.loc15: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %foo.ref: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo] +// CHECK:STDOUT: %.loc15: %empty_struct_type = struct_literal () +// CHECK:STDOUT: %foo.call: init %empty_tuple.type = call %foo.ref() +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %x.patt: %pattern_type.cff = binding_pattern x [concrete] +// CHECK:STDOUT: %x.var_patt: %pattern_type.cff = var_pattern %x.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x.var: ref %O = var %x.var_patt +// CHECK:STDOUT: %.loc16: type = splice_block %O.ref [concrete = constants.%O] { +// CHECK:STDOUT: %Cpp.ref.loc16: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: +// CHECK:STDOUT: %O.ref: type = name_ref O, imports.%O.decl [concrete = constants.%O] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x: ref %O = bind_name x, %x.var +// CHECK:STDOUT: %Op.bound: = bound_method %x.var, constants.%Op.2df +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method: = bound_method %x.var, %Op.specific_fn +// CHECK:STDOUT: %addr: %ptr.820 = addr_of %x.var +// CHECK:STDOUT: %no_op: init %empty_tuple.type = call %bound_method(%addr) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: --- import_definition_and_static_method_call_before.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { diff --git a/toolchain/check/testdata/interop/cpp/function/struct.carbon b/toolchain/check/testdata/interop/cpp/function/struct.carbon index 9b0a9bde7a08..e35e2f0a8bb8 100644 --- a/toolchain/check/testdata/interop/cpp/function/struct.carbon +++ b/toolchain/check/testdata/interop/cpp/function/struct.carbon @@ -211,6 +211,38 @@ fn F() { //@dump-sem-ir-end } +// ============================================================================ +// Defined struct in struct +// ============================================================================ + +// --- definition_in_outer_definition.h + +struct O { + struct S {}; +}; + +auto foo(O::S) -> void; + +// --- fail_todo_import_definition_in_outer_definition.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "definition_in_outer_definition.h"; + +fn F() { + //@dump-sem-ir-begin + // CHECK:STDERR: fail_todo_import_definition_in_outer_definition.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported mapping of a C++ record to a type within a declaration context that is not the translation unit or a namespace` [SemanticsTodo] + // CHECK:STDERR: Cpp.foo({}); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_todo_import_definition_in_outer_definition.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup] + // CHECK:STDERR: Cpp.foo({}); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + Cpp.foo({}); + var x: Cpp.O; + //@dump-sem-ir-end +} + // ============================================================================ // Defined struct and explicitly used // ============================================================================ @@ -653,6 +685,59 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_import_definition_in_outer_definition.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %foo.type: type = fn_type @foo [concrete] +// CHECK:STDOUT: %foo: %foo.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %O: type = class_type @O [concrete] +// CHECK:STDOUT: %pattern_type.cff: type = pattern_type %O [concrete] +// CHECK:STDOUT: %Op.type.1b8: type = fn_type @Op.2, @Destroy.impl(%O) [concrete] +// CHECK:STDOUT: %Op.2df: %Op.type.1b8 = struct_value () [concrete] +// CHECK:STDOUT: %ptr.820: type = ptr_type %O [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .foo = %foo.decl +// CHECK:STDOUT: .O = %O.decl +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %O.decl: type = class_decl @O [concrete = constants.%O] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref.loc15: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %foo.ref: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo] +// CHECK:STDOUT: %.loc15: %empty_struct_type = struct_literal () +// CHECK:STDOUT: %foo.call: init %empty_tuple.type = call %foo.ref() +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %x.patt: %pattern_type.cff = binding_pattern x [concrete] +// CHECK:STDOUT: %x.var_patt: %pattern_type.cff = var_pattern %x.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x.var: ref %O = var %x.var_patt +// CHECK:STDOUT: %.loc16: type = splice_block %O.ref [concrete = constants.%O] { +// CHECK:STDOUT: %Cpp.ref.loc16: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: +// CHECK:STDOUT: %O.ref: type = name_ref O, imports.%O.decl [concrete = constants.%O] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x: ref %O = bind_name x, %x.var +// CHECK:STDOUT: %Op.bound: = bound_method %x.var, constants.%Op.2df +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method: = bound_method %x.var, %Op.specific_fn +// CHECK:STDOUT: %addr: %ptr.820 = addr_of %x.var +// CHECK:STDOUT: %no_op: init %empty_tuple.type = call %bound_method(%addr) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: --- import_definition_and_static_method_call_before.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { diff --git a/toolchain/check/testdata/interop/cpp/function/union.carbon b/toolchain/check/testdata/interop/cpp/function/union.carbon index 8eca37d7049d..a9680f63e687 100644 --- a/toolchain/check/testdata/interop/cpp/function/union.carbon +++ b/toolchain/check/testdata/interop/cpp/function/union.carbon @@ -227,6 +227,39 @@ fn F() { //@dump-sem-ir-end } +// ============================================================================ +// Defined union in union +// ============================================================================ + +// --- definition_in_outer_definition.h + +union O { + public: + union U {}; +}; + +auto foo(O::U) -> void; + +// --- fail_todo_import_definition_in_outer_definition.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "definition_in_outer_definition.h"; + +fn F() { + //@dump-sem-ir-begin + // CHECK:STDERR: fail_todo_import_definition_in_outer_definition.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported mapping of a C++ record to a type within a declaration context that is not the translation unit or a namespace` [SemanticsTodo] + // CHECK:STDERR: Cpp.foo({}); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_todo_import_definition_in_outer_definition.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup] + // CHECK:STDERR: Cpp.foo({}); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + Cpp.foo({}); + var x: Cpp.O; + //@dump-sem-ir-end +} + // ============================================================================ // Defined union and explicitly used // ============================================================================ @@ -669,6 +702,59 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_import_definition_in_outer_definition.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %foo.type: type = fn_type @foo [concrete] +// CHECK:STDOUT: %foo: %foo.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %O: type = class_type @O [concrete] +// CHECK:STDOUT: %pattern_type.cff: type = pattern_type %O [concrete] +// CHECK:STDOUT: %Op.type.1b8: type = fn_type @Op.2, @Destroy.impl(%O) [concrete] +// CHECK:STDOUT: %Op.2df: %Op.type.1b8 = struct_value () [concrete] +// CHECK:STDOUT: %ptr.820: type = ptr_type %O [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .foo = %foo.decl +// CHECK:STDOUT: .O = %O.decl +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %O.decl: type = class_decl @O [concrete = constants.%O] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref.loc15: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %foo.ref: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo] +// CHECK:STDOUT: %.loc15: %empty_struct_type = struct_literal () +// CHECK:STDOUT: %foo.call: init %empty_tuple.type = call %foo.ref() +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %x.patt: %pattern_type.cff = binding_pattern x [concrete] +// CHECK:STDOUT: %x.var_patt: %pattern_type.cff = var_pattern %x.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x.var: ref %O = var %x.var_patt +// CHECK:STDOUT: %.loc16: type = splice_block %O.ref [concrete = constants.%O] { +// CHECK:STDOUT: %Cpp.ref.loc16: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: +// CHECK:STDOUT: %O.ref: type = name_ref O, imports.%O.decl [concrete = constants.%O] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x: ref %O = bind_name x, %x.var +// CHECK:STDOUT: %Op.bound: = bound_method %x.var, constants.%Op.2df +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method: = bound_method %x.var, %Op.specific_fn +// CHECK:STDOUT: %addr: %ptr.820 = addr_of %x.var +// CHECK:STDOUT: %no_op: init %empty_tuple.type = call %bound_method(%addr) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: --- import_definition_and_static_method_call_before.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants {