Explicitly mark as unsupported instead of crashing mapping a parameter record type defined not in a namespace (#5777)

Before this change we crash in this case when trying to use the outer
type.
After this change we diagnose a TODO.
Will add the missing support for this in a separate PR.

Part of #5533.
This commit is contained in:
Boaz Brickner
2025-07-18 02:42:45 +00:00
committed by GitHub
parent 61290fdee9
commit f4f521de7f
4 changed files with 268 additions and 2 deletions
+11 -2
View File
@@ -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<clang::TranslationUnitDecl>(decl_context) &&
!clang::isa<clang::NamespaceDecl>(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<SemIR::Namespace>(parent_inst_id).name_scope_id;
SemIR::NameId record_name_id =
@@ -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: <elided>
// 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> = 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: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// 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: <namespace> = 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(<error>)
// 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: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: <elided>
// 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> = bound_method %x.var, constants.%Op.2df
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: %bound_method: <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: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- import_definition_and_static_method_call_before.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
@@ -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: <elided>
// 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> = 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: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// 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: <namespace> = 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(<error>)
// 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: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: <elided>
// 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> = bound_method %x.var, constants.%Op.2df
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: %bound_method: <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: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- import_definition_and_static_method_call_before.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
@@ -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: <elided>
// 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> = 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: <elided>
// CHECK:STDOUT: } {
// CHECK:STDOUT: <elided>
// 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: <namespace> = 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(<error>)
// 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: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: <elided>
// 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> = bound_method %x.var, constants.%Op.2df
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: %bound_method: <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: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- import_definition_and_static_method_call_before.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {