diff --git a/toolchain/check/import_cpp.cpp b/toolchain/check/import_cpp.cpp index 439fb87af8a2..520932e5cf6a 100644 --- a/toolchain/check/import_cpp.cpp +++ b/toolchain/check/import_cpp.cpp @@ -543,19 +543,44 @@ static auto ImportClassObjectRepr(Context& context, // TODO: Import bases. // Import fields. - for (auto* field : clang_def->fields()) { + for (auto* decl : clang_def->decls()) { + auto* field = clang::dyn_cast(decl); + + // Track the chain of fields from the class to this field. This chain is + // only one element long unless the field is a member of an anonymous struct + // or union. + clang::NamedDecl* single_field_chain[1] = {field}; + llvm::ArrayRef chain = single_field_chain; + + // If this isn't a field, it might be an indirect field in an anonymous + // struct or union. + if (!field) { + auto* indirect_field = clang::dyn_cast(decl); + if (!indirect_field) { + continue; + } + chain = indirect_field->chain(); + field = indirect_field->getAnonField(); + } + if (field->isBitField()) { // TODO: Add a representation for named bitfield members. continue; } + if (field->isAnonymousStructOrUnion()) { - // TODO: Visit IndirectFieldDecls and add them to the layout. + // Fields within an anonymous structure or union will be added via their + // IndirectFieldDecls. continue; } auto field_name_id = AddIdentifierName(context, field->getName()); auto [field_type_inst_id, field_type_id] = MapType(context, import_ir_inst_id, field->getType()); + if (!field_type_inst_id.has_value()) { + // TODO: For now, just skip over fields whose types we can't map. + continue; + } // Create a field now, as we know the index to use. // TODO: Consider doing this lazily instead. @@ -568,12 +593,23 @@ static auto ImportClassObjectRepr(Context& context, .name_id = field_name_id, .index = SemIR::ElementIndex(fields.size())})); context.sem_ir().clang_decls().Add( - {.decl = field->getCanonicalDecl(), .inst_id = field_decl_id}); + {.decl = decl->getCanonicalDecl(), .inst_id = field_decl_id}); - layout.push_back(context.ast_context() - .toCharUnitsFromBits(clang_layout.getFieldOffset( - field->getFieldIndex())) - .getQuantity()); + // Compute the offset to the field that appears directly in the class. + uint64_t offset = clang_layout.getFieldOffset( + clang::cast(chain.front())->getFieldIndex()); + + // If this is an indirect field, walk the path and accumulate the offset to + // the named field. + for (auto* inner_decl : chain.drop_front()) { + auto* inner_field = clang::cast(inner_decl); + const auto& inner_layout = + context.ast_context().getASTRecordLayout(inner_field->getParent()); + offset += inner_layout.getFieldOffset(inner_field->getFieldIndex()); + } + + layout.push_back( + context.ast_context().toCharUnitsFromBits(offset).getQuantity()); fields.push_back( {.name_id = field_name_id, .type_inst_id = field_type_inst_id}); } @@ -1228,7 +1264,7 @@ static auto ImportDeclAfterDependencies(Context& context, SemIR::LocId loc_id, } return type_inst_id; } - if (clang::isa(clang_decl)) { + if (clang::isa(clang_decl)) { // Usable fields get imported as a side effect of importing the class. if (SemIR::InstId existing_inst_id = LookupClangDeclInstId(context, clang_decl); diff --git a/toolchain/check/testdata/interop/cpp/class/field.carbon b/toolchain/check/testdata/interop/cpp/class/field.carbon index 473436a1d6f4..5c2c503719b2 100644 --- a/toolchain/check/testdata/interop/cpp/class/field.carbon +++ b/toolchain/check/testdata/interop/cpp/class/field.carbon @@ -63,6 +63,33 @@ fn H(u: Cpp.Union) -> i32 { //@dump-sem-ir-end } +// --- anon_struct_union.h + +struct A { + union { + struct { + short a_0; + short b_2; + int c_4; + }; + struct { + char d_0; + char e_1; + int f_4; + short g_8; + }; + }; + int h_12; +}; + +// --- use_anon_struct_union.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "anon_struct_union.h"; + +fn GetF(a: Cpp.A) -> i32 { return a.f_4; } + // --- with_bitfields.h struct Struct { @@ -406,6 +433,107 @@ fn G(s: Cpp.Union) -> i32 { // CHECK:STDOUT: return %.loc20_10.2 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- use_anon_struct_union.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %A: type = class_type @A [concrete] +// CHECK:STDOUT: %int_16: Core.IntLiteral = int_value 16 [concrete] +// CHECK:STDOUT: %Int.type: type = generic_class_type @Int [concrete] +// CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] +// CHECK:STDOUT: %i16: type = class_type @Int, @Int(%int_16) [concrete] +// CHECK:STDOUT: %A.elem.543: type = unbound_element_type %A, %i16 [concrete] +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %A.elem.c3f: type = unbound_element_type %A, %i32 [concrete] +// CHECK:STDOUT: %.371: type = custom_layout_type {size=16, align=4, .a_0@0: %i16, .b_2@2: %i16, .c_4@4: %i32, .f_4@4: %i32, .g_8@8: %i16, .h_12@12: %i32} [concrete] +// CHECK:STDOUT: %complete_type.fa3: = complete_type_witness %.371 [concrete] +// CHECK:STDOUT: %pattern_type.9de: type = pattern_type %A [concrete] +// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: %GetF.type: type = fn_type @GetF [concrete] +// CHECK:STDOUT: %GetF: %GetF.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { +// CHECK:STDOUT: .Int = %Core.Int +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} +// CHECK:STDOUT: %Core.Int: %Int.type = import_ref Core//prelude/parts/int, Int, loaded [concrete = constants.%Int.generic] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: .GetF = %GetF.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "anon_struct_union.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %GetF.decl: %GetF.type = fn_decl @GetF [concrete = constants.%GetF] { +// CHECK:STDOUT: %a.patt: %pattern_type.9de = binding_pattern a [concrete] +// CHECK:STDOUT: %a.param_patt: %pattern_type.9de = value_param_pattern %a.patt, call_param0 [concrete] +// CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] +// CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt, call_param1 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] +// CHECK:STDOUT: %a.param: %A = value_param call_param0 +// CHECK:STDOUT: %.loc6_15: type = splice_block %A.ref [concrete = constants.%A] { +// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %A.ref: type = name_ref A, imports.%A.decl [concrete = constants.%A] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a: %A = bind_name a, %a.param +// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 +// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @A { +// CHECK:STDOUT: %int_16.1: Core.IntLiteral = int_value 16 [concrete = constants.%int_16] +// CHECK:STDOUT: %i16.1: type = class_type @Int, @Int(constants.%int_16) [concrete = constants.%i16] +// CHECK:STDOUT: %.1: %A.elem.543 = field_decl a_0, element0 [concrete] +// CHECK:STDOUT: %int_16.2: Core.IntLiteral = int_value 16 [concrete = constants.%int_16] +// CHECK:STDOUT: %i16.2: type = class_type @Int, @Int(constants.%int_16) [concrete = constants.%i16] +// CHECK:STDOUT: %.2: %A.elem.543 = field_decl b_2, element1 [concrete] +// CHECK:STDOUT: %int_32.1: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] +// CHECK:STDOUT: %i32.1: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] +// CHECK:STDOUT: %.3: %A.elem.c3f = field_decl c_4, element2 [concrete] +// CHECK:STDOUT: %int_32.2: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] +// CHECK:STDOUT: %i32.2: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] +// CHECK:STDOUT: %.4: %A.elem.c3f = field_decl f_4, element3 [concrete] +// CHECK:STDOUT: %int_16.3: Core.IntLiteral = int_value 16 [concrete = constants.%int_16] +// CHECK:STDOUT: %i16.3: type = class_type @Int, @Int(constants.%int_16) [concrete = constants.%i16] +// CHECK:STDOUT: %.5: %A.elem.543 = field_decl g_8, element4 [concrete] +// CHECK:STDOUT: %int_32.3: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] +// CHECK:STDOUT: %i32.3: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] +// CHECK:STDOUT: %.6: %A.elem.c3f = field_decl h_12, element5 [concrete] +// CHECK:STDOUT: %.7: type = custom_layout_type {size=16, align=4, .a_0@0: %i16, .b_2@2: %i16, .c_4@4: %i32, .f_4@4: %i32, .g_8@8: %i16, .h_12@12: %i32} [concrete = constants.%.371] +// CHECK:STDOUT: %complete_type: = complete_type_witness %.7 [concrete = constants.%complete_type.fa3] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%A +// CHECK:STDOUT: .f_4 = %.4 +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @GetF(%a.param: %A) -> %i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %a.ref: %A = name_ref a, %a +// CHECK:STDOUT: %f_4.ref: %A.elem.c3f = name_ref f_4, @A.%.4 [concrete = @A.%.4] +// CHECK:STDOUT: %.loc6_36.1: ref %i32 = class_element_access %a.ref, element3 +// CHECK:STDOUT: %.loc6_36.2: %i32 = bind_value %.loc6_36.1 +// CHECK:STDOUT: return %.loc6_36.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: --- use_non_bitfields_in_type_with_bitfields.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { diff --git a/toolchain/lower/testdata/interop/cpp/field.carbon b/toolchain/lower/testdata/interop/cpp/field.carbon index 401c3baceb1b..cea4d88caad5 100644 --- a/toolchain/lower/testdata/interop/cpp/field.carbon +++ b/toolchain/lower/testdata/interop/cpp/field.carbon @@ -84,6 +84,30 @@ fn AssignM(p: Cpp.A*) { p->m = 1; } +// --- anon_union.h + +struct A { + int x; + union { + int n; + int *_Nonnull p; + }; +}; + +// --- access_anon_union.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "anon_union.h"; + +fn AccessN(a: Cpp.A) -> i32 { + return a.n; +} + +fn AccessP(a: Cpp.A) -> i32 { + return *a.p; +} + // CHECK:STDOUT: ; ModuleID = 'access_struct.carbon' // CHECK:STDOUT: source_filename = "access_struct.carbon" // CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" @@ -232,3 +256,42 @@ fn AssignM(p: Cpp.A*) { // CHECK:STDOUT: !12 = distinct !DISubprogram(name: "AssignM", linkageName: "_CAssignM.Main", scope: null, file: !6, line: 10, type: !8, spFlags: DISPFlagDefinition, unit: !5) // CHECK:STDOUT: !13 = !DILocation(line: 11, column: 3, scope: !12) // CHECK:STDOUT: !14 = !DILocation(line: 10, column: 1, scope: !12) +// CHECK:STDOUT: ; ModuleID = 'access_anon_union.carbon' +// CHECK:STDOUT: source_filename = "access_anon_union.carbon" +// CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +// CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu" +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @_CAccessN.Main(ptr %a) !dbg !7 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc7_11.1.n = getelementptr inbounds nuw [16 x i8], ptr %a, i32 0, i32 8, !dbg !10 +// CHECK:STDOUT: %.loc7_11.2 = load i32, ptr %.loc7_11.1.n, align 4, !dbg !10 +// CHECK:STDOUT: ret i32 %.loc7_11.2, !dbg !11 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @_CAccessP.Main(ptr %a) !dbg !12 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc11_12.1.p = getelementptr inbounds nuw [16 x i8], ptr %a, i32 0, i32 8, !dbg !13 +// CHECK:STDOUT: %.loc11_12.2 = load ptr, ptr %.loc11_12.1.p, align 8, !dbg !13 +// CHECK:STDOUT: %.loc11_10.2 = load i32, ptr %.loc11_12.2, align 4, !dbg !14 +// CHECK:STDOUT: ret i32 %.loc11_10.2, !dbg !15 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4} +// CHECK:STDOUT: !llvm.dbg.cu = !{!5} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = !{i32 1, !"wchar_size", i32 4} +// CHECK:STDOUT: !3 = !{i32 8, !"PIC Level", i32 0} +// CHECK:STDOUT: !4 = !{i32 7, !"PIE Level", i32 2} +// CHECK:STDOUT: !5 = distinct !DICompileUnit(language: DW_LANG_C, file: !6, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !6 = !DIFile(filename: "access_anon_union.carbon", directory: "") +// CHECK:STDOUT: !7 = distinct !DISubprogram(name: "AccessN", linkageName: "_CAccessN.Main", scope: null, file: !6, line: 6, type: !8, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !8 = !DISubroutineType(types: !9) +// CHECK:STDOUT: !9 = !{} +// CHECK:STDOUT: !10 = !DILocation(line: 7, column: 10, scope: !7) +// CHECK:STDOUT: !11 = !DILocation(line: 7, column: 3, scope: !7) +// CHECK:STDOUT: !12 = distinct !DISubprogram(name: "AccessP", linkageName: "_CAccessP.Main", scope: null, file: !6, line: 10, type: !8, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !13 = !DILocation(line: 11, column: 11, scope: !12) +// CHECK:STDOUT: !14 = !DILocation(line: 11, column: 10, scope: !12) +// CHECK:STDOUT: !15 = !DILocation(line: 11, column: 3, scope: !12)