소스 검색

Support C++ import for anonymous struct and union members. (#5855)

Richard Smith 9 달 전
부모
커밋
0d74162e2a
3개의 변경된 파일235개의 추가작업 그리고 8개의 파일을 삭제
  1. 44 8
      toolchain/check/import_cpp.cpp
  2. 128 0
      toolchain/check/testdata/interop/cpp/class/field.carbon
  3. 63 0
      toolchain/lower/testdata/interop/cpp/field.carbon

+ 44 - 8
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<clang::FieldDecl>(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<clang::NamedDecl*> 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<clang::IndirectFieldDecl>(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});
+
+    // Compute the offset to the field that appears directly in the class.
+    uint64_t offset = clang_layout.getFieldOffset(
+        clang::cast<clang::FieldDecl>(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<clang::FieldDecl>(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(clang_layout.getFieldOffset(
-                             field->getFieldIndex()))
-                         .getQuantity());
+    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::FieldDecl>(clang_decl)) {
+  if (clang::isa<clang::FieldDecl, clang::IndirectFieldDecl>(clang_decl)) {
     // Usable fields get imported as a side effect of importing the class.
     if (SemIR::InstId existing_inst_id =
             LookupClangDeclInstId(context, clang_decl);

+ 128 - 0
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: <witness> = 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> = 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> = 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> = 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: <namespace> = 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: <witness> = 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 {

+ 63 - 0
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)