diff --git a/toolchain/check/cpp/export.cpp b/toolchain/check/cpp/export.cpp index 580aa3f08940..6534fadc231b 100644 --- a/toolchain/check/cpp/export.cpp +++ b/toolchain/check/cpp/export.cpp @@ -167,6 +167,166 @@ auto ExportClassToCpp(Context& context, SemIR::LocId loc_id, return record_decl; } +// Get the `StructTypeField`s from a class's object repr. +static auto GetStructTypeFields(Context& context, + const SemIR::Class& class_info) + -> llvm::ArrayRef { + if (class_info.adapt_id.has_value()) { + // The representation of an adapter won't necessarily be a + // struct. Return an empty array since adapters can't declare + // fields. + return {}; + } + + auto object_repr_type_id = + class_info.GetObjectRepr(context.sem_ir(), SemIR::SpecificId::None); + auto struct_type = + context.types().GetAs(object_repr_type_id); + return context.struct_type_fields().Get(struct_type.fields_id); +} + +static auto LookupClassFieldByStructField( + const Context& context, const SemIR::NameScope& class_scope, + const SemIR::StructTypeField& struct_field) + -> std::optional> { + if (auto entry_id = class_scope.Lookup(struct_field.name_id)) { + auto field_inst_id = + class_scope.GetEntry(*entry_id).result.target_inst_id(); + return context.insts().TryGetAsWithId(field_inst_id); + } + return std::nullopt; +} + +// Creates a `clang::FieldDecl` for a Carbon class field. Returns +// nullptr if an error occurs. +static auto CreateCppFieldDecl(Context& context, + clang::CXXRecordDecl* record_decl, + SemIR::InstId field_inst_id, + const SemIR::FieldDecl& field_decl) + -> clang::FieldDecl* { + // Get the field's C++ type. + auto unbound_element_type = + context.types().GetAs(field_decl.type_id); + auto cpp_type = + MapToCppType(context, context.types().GetTypeIdForTypeInstId( + unbound_element_type.element_type_inst_id)); + if (cpp_type.isNull()) { + context.TODO(field_inst_id, "failed to map Carbon type to C++"); + return nullptr; + } + + // Get the field's C++ identifier. + auto* identifier_info = GetClangIdentifierInfo(context, field_decl.name_id); + CARBON_CHECK(identifier_info, "field with non-identifier name {0}", + field_decl.name_id); + + // Create the `clang::FieldDecl`. + auto clang_loc = GetCppLocation(context, SemIR::LocId(field_inst_id)); + auto* cpp_field_decl = clang::FieldDecl::Create( + context.ast_context(), record_decl, /*StartLoc=*/clang_loc, + /*IdLoc=*/clang_loc, identifier_info, cpp_type, /*TInfo=*/nullptr, + /*BW=*/nullptr, + /*Mutable=*/true, clang::ICIS_NoInit); + cpp_field_decl->setAccess(clang::AS_public); + record_decl->addHiddenDecl(cpp_field_decl); + + return cpp_field_decl; +} + +auto ExportAllFieldsToCpp(Context& context, SemIR::Class& class_info) -> void { + if (class_info.fields_exported) { + return; + } + + const auto& class_scope = context.name_scopes().Get(class_info.scope_id); + + for (const auto& struct_field : GetStructTypeFields(context, class_info)) { + auto class_field = + LookupClassFieldByStructField(context, class_scope, struct_field); + if (!class_field) { + continue; + } + + // Map the parent scope into the C++ AST. + auto* decl_context = ExportNameScopeToCpp( + context, SemIR::LocId(class_field->inst_id), class_info.scope_id); + if (!decl_context) { + continue; + } + + auto* cpp_field_decl = + CreateCppFieldDecl(context, cast(decl_context), + class_field->inst_id, class_field->inst); + if (!cpp_field_decl) { + continue; + } + + // Create and store the `ClangDeclId`. + auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(cpp_field_decl); + context.clang_decls().Add({.key = key, .inst_id = class_field->inst_id}); + } + + class_info.fields_exported = true; +} + +auto ExportFieldToCpp(Context& context, SemIR::InstId field_inst_id, + SemIR::FieldDecl field_decl) -> clang::FieldDecl* { + // Get the `SemIR::Class` that contains the `field_decl`. + auto unbound_element_type = + context.types().GetAs(field_decl.type_id); + SemIR::TypeId class_type_id = context.types().GetTypeIdForTypeInstId( + unbound_element_type.class_type_inst_id); + auto class_type = context.types().GetAs(class_type_id); + auto& class_info = context.classes().Get(class_type.class_id); + + // If the class's fields haven't already been exported, do so now. + ExportAllFieldsToCpp(context, class_info); + + // Get the exported `clang::FieldDecl`. + auto clang_decl_id = context.clang_decls().Lookup(field_inst_id); + if (clang_decl_id == SemIR::ClangDeclId::None) { + return nullptr; + } + return cast( + context.clang_decls().Get(clang_decl_id).key.decl); +} + +auto CalculateCppFieldOffsets( + Context& context, SemIR::ClassId class_id, + llvm::DenseMap& field_offsets) -> bool { + auto class_info = context.classes().Get(class_id); + const auto& class_scope = context.name_scopes().Get(class_info.scope_id); + + auto class_layout = SemIR::ObjectLayout::Empty(); + for (const auto& struct_field : GetStructTypeFields(context, class_info)) { + auto field_type_id = context.sem_ir().types().GetTypeIdForTypeInstId( + struct_field.type_inst_id); + auto field_layout = context.sem_ir() + .types() + .GetCompleteTypeInfo(field_type_id) + .object_layout; + + // Use the field's name to look up the corresponding entry in the + // class. If it's a `FieldDecl`, write out the offset of the + // corresponding `clang::FieldDecl`. + auto class_field = + LookupClassFieldByStructField(context, class_scope, struct_field); + if (class_field) { + auto* cpp_field_decl = + ExportFieldToCpp(context, class_field->inst_id, class_field->inst); + if (!cpp_field_decl) { + return false; + } + field_offsets.insert( + {cpp_field_decl, class_layout.FieldOffset(field_layout).bits()}); + } + + class_layout.AppendField(field_layout); + } + + return true; +} + namespace { struct FunctionInfo { struct Param { diff --git a/toolchain/check/cpp/export.h b/toolchain/check/cpp/export.h index c1f1e6b18e7f..ae690661c802 100644 --- a/toolchain/check/cpp/export.h +++ b/toolchain/check/cpp/export.h @@ -31,6 +31,29 @@ auto ExportClassToCpp(Context& context, SemIR::LocId loc_id, SemIR::InstId class_inst_id, SemIR::ClassType class_type) -> clang::TagDecl*; +// Export all `SemIR::FieldDecl`s in the class body as `clang::FieldDecl`s. +auto ExportAllFieldsToCpp(Context& context, SemIR::Class& class_info) -> void; + +// Exports a Carbon class field into C++. +// +// If the field has already been exported, returns the existing C++ +// field. +// +// If the field has not already been exported, *all* fields of the class +// are exported, and then the requested C++ field is returned. +// +// Returns nullptr if the class could not be exported and an error was +// diagnosed. +auto ExportFieldToCpp(Context& context, SemIR::InstId field_inst_id, + SemIR::FieldDecl field_decl) -> clang::FieldDecl*; + +// Get the field offset for each field in a class. +// +// Returns true on success, false if any error occurs. +auto CalculateCppFieldOffsets( + Context& context, SemIR::ClassId class_id, + llvm::DenseMap& field_offsets) -> bool; + // Get a `clang::FunctionDecl` that can be used to call a Carbon function. auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_id, SemIR::FunctionId function_id) -> clang::FunctionDecl*; diff --git a/toolchain/check/cpp/generate_ast.cpp b/toolchain/check/cpp/generate_ast.cpp index b9d16635c9b3..43b2c29ba475 100644 --- a/toolchain/check/cpp/generate_ast.cpp +++ b/toolchain/check/cpp/generate_ast.cpp @@ -436,6 +436,9 @@ auto CarbonExternalASTSource::MapInstIdToClangDeclOrType(LookupResult lookup) return ExportFunctionToCpp(*context_, SemIR::LocId(target_inst_id), callee_function->function_id); } + case CARBON_KIND(SemIR::FieldDecl field_decl): { + return ExportFieldToCpp(*context_, target_inst_id, field_decl); + } default: return nullptr; } @@ -609,7 +612,7 @@ auto CarbonExternalASTSource::CompleteType(clang::TagDecl* tag_decl) -> void { return; } - const auto& class_info = context_->classes().Get(class_type.class_id); + auto& class_info = context_->classes().Get(class_type.class_id); class_decl->startDefinition(); CARBON_CHECK(class_decl->hasDefinition()); @@ -636,8 +639,9 @@ auto CarbonExternalASTSource::CompleteType(clang::TagDecl* tag_decl) -> void { } } - // TODO: Import fields, plus any special member functions that affect class - // properties. + ExportAllFieldsToCpp(*context_, class_info); + + // TODO: Import any special member functions that affect class properties. class_decl->completeDefinition(); } @@ -668,8 +672,8 @@ auto CarbonExternalASTSource::layoutRecordType( size = layout.size.bytes() * 8; alignment = layout.alignment.bits(); - // TODO: Add field offsets once we import fields. - static_cast(field_offsets); + // Fill in `field_offsets`. + CalculateCppFieldOffsets(*context_, class_type.class_id, field_offsets); // Add offset for base class, if any. if (const auto* class_decl = dyn_cast(record_decl); diff --git a/toolchain/check/testdata/interop/cpp/class/export/field.carbon b/toolchain/check/testdata/interop/cpp/class/export/field.carbon new file mode 100644 index 000000000000..263e167ca031 --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/class/export/field.carbon @@ -0,0 +1,123 @@ +// 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-FILE: toolchain/testing/testdata/min_prelude/int.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/class/export/field.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/class/export/field.carbon + +// --- field.carbon +library "[[@TEST_NAME]]"; +import Cpp; + +class A { + var x: i32; + var y: i32; +} + +inline Cpp ''' +void F() { + Carbon::A a; + a.x = 12; + a.y = 34; +} +'''; + +// --- field_inheritence.carbon +library "[[@TEST_NAME]]"; +import Cpp; + +base class A { + var x: i32; +} + +class B { + extend base: A; + var y: i32; +} + +inline Cpp ''' +void F() { + Carbon::B b; + b.x = 12; + b.y = 34; +} +'''; + +// --- fail_nonexistent_field.carbon +library "[[@TEST_NAME]]"; +import Cpp; + +class A {} + +inline Cpp ''' +void F() { + Carbon::A a; + // CHECK:STDERR: fail_nonexistent_field.carbon:[[@LINE+4]]:5: error: no member named 'x' in 'Carbon::A' [CppInteropParseError] + // CHECK:STDERR: 13 | a.x = 12; + // CHECK:STDERR: | ~ ^ + // CHECK:STDERR: + a.x = 12; + // CHECK:STDERR: fail_nonexistent_field.carbon:[[@LINE+4]]:5: error: no member named 'y' in 'Carbon::A' [CppInteropParseError] + // CHECK:STDERR: 18 | a.y = 34; + // CHECK:STDERR: | ~ ^ + // CHECK:STDERR: + a.y = 34; +} +'''; + +// --- fail_unsupported_field_type.carbon +library "[[@TEST_NAME]]"; +import Cpp; + +class A { + // CHECK:STDERR: fail_unsupported_field_type.carbon:[[@LINE+4]]:7: error: semantics TODO: `failed to map Carbon type to C++` [SemanticsTodo] + // CHECK:STDERR: var x: (); + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + var x: (); + // CHECK:STDERR: fail_unsupported_field_type.carbon:[[@LINE+4]]:7: error: semantics TODO: `failed to map Carbon type to C++` [SemanticsTodo] + // CHECK:STDERR: var y: (); + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + var y: (); +} + +inline Cpp ''' +void F() { + Carbon::A a; + // CHECK:STDERR: fail_unsupported_field_type.carbon:[[@LINE+4]]:5: error: no member named 'x' in 'Carbon::A' [CppInteropParseError] + // CHECK:STDERR: 24 | a.x; + // CHECK:STDERR: | ~ ^ + // CHECK:STDERR: + a.x; + // CHECK:STDERR: fail_unsupported_field_type.carbon:[[@LINE+4]]:5: error: no member named 'y' in 'Carbon::A' [CppInteropParseError] + // CHECK:STDERR: 29 | a.y; + // CHECK:STDERR: | ~ ^ + // CHECK:STDERR: + a.y; +} +'''; + +// --- fail_adapter.carbon +library "[[@TEST_NAME]]"; +import Cpp; + +class A { + adapt (); +} + +inline Cpp ''' +void F() { + Carbon::A a; + // CHECK:STDERR: fail_adapter.carbon:[[@LINE+4]]:5: error: no member named 'x' in 'Carbon::A' [CppInteropParseError] + // CHECK:STDERR: 15 | a.x = 12; + // CHECK:STDERR: | ~ ^ + // CHECK:STDERR: + a.x = 12; +} +'''; diff --git a/toolchain/check/testdata/interop/cpp/class/export/layout.carbon b/toolchain/check/testdata/interop/cpp/class/export/layout.carbon index 4a3cd66c3168..c9e46bbeff4b 100644 --- a/toolchain/check/testdata/interop/cpp/class/export/layout.carbon +++ b/toolchain/check/testdata/interop/cpp/class/export/layout.carbon @@ -32,7 +32,7 @@ library "[[@TEST_NAME]]"; import Cpp; class C { - var a: ()*; + var a: i32*; var b: i32; } diff --git a/toolchain/lower/testdata/interop/cpp/reverse/class.carbon b/toolchain/lower/testdata/interop/cpp/reverse/class.carbon index 31fbed42e129..719a20274299 100644 --- a/toolchain/lower/testdata/interop/cpp/reverse/class.carbon +++ b/toolchain/lower/testdata/interop/cpp/reverse/class.carbon @@ -34,7 +34,7 @@ library "[[@TEST_NAME]]"; import Cpp; base class A { - var x: ()*; + var x: i32*; var y: i32; } @@ -70,6 +70,29 @@ void h() { } '''; +// --- field.carbon + +library "[[@TEST_NAME]]"; + +import Cpp; + +class C { + var a: i32; + var b: i32; +} + +inline Cpp ''' +void F(Carbon::C& c) { + c.a = 12; + c.b = 34; +} +'''; + +fn G() { + var c: C = {.a = 0, .b = 0}; + Cpp.F(ref c); +} + // CHECK:STDOUT: ; ModuleID = 'pointer.carbon' // CHECK:STDOUT: source_filename = "pointer.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" @@ -129,9 +152,9 @@ void h() { // 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: %"class.Carbon::A" = type { [12 x i8] } -// CHECK:STDOUT: %"class.Carbon::B" = type { [16 x i8] } -// CHECK:STDOUT: %"class.Carbon::C" = type { [16 x i8] } +// CHECK:STDOUT: %"class.Carbon::A" = type <{ ptr, i32 }> +// CHECK:STDOUT: %"class.Carbon::B" = type { %"class.Carbon::A", i32 } +// CHECK:STDOUT: %"class.Carbon::C" = type { %"class.Carbon::A", i32 } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: mustprogress uwtable // CHECK:STDOUT: define dso_local void @_Z1fv() #0 { @@ -269,3 +292,116 @@ void h() { // CHECK:STDOUT: !30 = !{!31} // CHECK:STDOUT: !31 = !DILocalVariable(arg: 1, scope: !29, type: !21) // CHECK:STDOUT: !32 = !DILocation(line: 23, column: 1, scope: !29) +// CHECK:STDOUT: ; ModuleID = 'field.carbon' +// CHECK:STDOUT: source_filename = "field.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: %"class.Carbon::C" = type { i32, i32 } +// CHECK:STDOUT: +// CHECK:STDOUT: @C.val.loc19_3 = internal constant { i32, i32 } zeroinitializer +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: mustprogress nounwind uwtable +// CHECK:STDOUT: define dso_local void @_Z1FRN6Carbon1CE(ptr noundef nonnull align 4 dereferenceable(8) %c) #0 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %c.addr = alloca ptr, align 8 +// CHECK:STDOUT: store ptr %c, ptr %c.addr, align 8, !tbaa !11 +// CHECK:STDOUT: %0 = load ptr, ptr %c.addr, align 8, !tbaa !11, !nonnull !14, !align !15 +// CHECK:STDOUT: %a = getelementptr inbounds nuw %"class.Carbon::C", ptr %0, i32 0, i32 0 +// CHECK:STDOUT: store i32 12, ptr %a, align 4, !tbaa !16 +// CHECK:STDOUT: %1 = load ptr, ptr %c.addr, align 8, !tbaa !11, !nonnull !14, !align !15 +// CHECK:STDOUT: %b = getelementptr inbounds nuw %"class.Carbon::C", ptr %1, i32 0, i32 1 +// CHECK:STDOUT: store i32 34, ptr %b, align 4, !tbaa !18 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CG.Main() #1 !dbg !19 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %c.var = alloca { i32, i32 }, align 8, !dbg !22 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %c.var), !dbg !22 +// CHECK:STDOUT: %.loc19_29.3.a = getelementptr inbounds nuw { i32, i32 }, ptr %c.var, i32 0, i32 0, !dbg !23 +// CHECK:STDOUT: %.loc19_29.6.b = getelementptr inbounds nuw { i32, i32 }, ptr %c.var, i32 0, i32 1, !dbg !23 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %c.var, ptr align 4 @C.val.loc19_3, i64 8, i1 false), !dbg !22 +// CHECK:STDOUT: call void @_Z1FRN6Carbon1CE(ptr %c.var), !dbg !24 +// CHECK:STDOUT: call void @"_COp.71e25083d63c4d6c:core.Destroy.Core"(ptr %c.var), !dbg !22 +// CHECK:STDOUT: ret void, !dbg !25 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.7e389eab4a7e5487:core.Destroy.Core"(ptr %self) #1 !dbg !26 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !32 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.fb32c4dfca65e378:core.Destroy.Core"(ptr %self) #1 !dbg !33 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !39 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.71e25083d63c4d6c:core.Destroy.Core"(ptr %self) #1 !dbg !40 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !43 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #2 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #3 +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { mustprogress nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +// CHECK:STDOUT: attributes #1 = { nounwind } +// CHECK:STDOUT: attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4} +// CHECK:STDOUT: !llvm.dbg.cu = !{!5} +// CHECK:STDOUT: !llvm.errno.tbaa = !{!7} +// 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 8, !"PIC Level", i32 2} +// CHECK:STDOUT: !3 = !{i32 7, !"PIE Level", i32 2} +// CHECK:STDOUT: !4 = !{i32 7, !"uwtable", i32 2} +// CHECK:STDOUT: !5 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !6, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !6 = !DIFile(filename: "field.carbon", directory: "") +// CHECK:STDOUT: !7 = !{!8, !8, i64 0} +// CHECK:STDOUT: !8 = !{!"int", !9, i64 0} +// CHECK:STDOUT: !9 = !{!"omnipotent char", !10, i64 0} +// CHECK:STDOUT: !10 = !{!"Simple C++ TBAA"} +// CHECK:STDOUT: !11 = !{!12, !12, i64 0} +// CHECK:STDOUT: !12 = !{!"p1 _ZTSN6Carbon1CE", !13, i64 0} +// CHECK:STDOUT: !13 = !{!"any pointer", !9, i64 0} +// CHECK:STDOUT: !14 = !{} +// CHECK:STDOUT: !15 = !{i64 4} +// CHECK:STDOUT: !16 = !{!17, !8, i64 0} +// CHECK:STDOUT: !17 = !{!"_ZTSN6Carbon1CE", !8, i64 0, !8, i64 4} +// CHECK:STDOUT: !18 = !{!17, !8, i64 4} +// CHECK:STDOUT: !19 = distinct !DISubprogram(name: "G", linkageName: "_CG.Main", scope: null, file: !6, line: 18, type: !20, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !20 = !DISubroutineType(types: !21) +// CHECK:STDOUT: !21 = !{null} +// CHECK:STDOUT: !22 = !DILocation(line: 19, column: 3, scope: !19) +// CHECK:STDOUT: !23 = !DILocation(line: 19, column: 14, scope: !19) +// CHECK:STDOUT: !24 = !DILocation(line: 20, column: 3, scope: !19) +// CHECK:STDOUT: !25 = !DILocation(line: 18, column: 1, scope: !19) +// CHECK:STDOUT: !26 = distinct !DISubprogram(name: "Op", linkageName: "_COp.7e389eab4a7e5487:core.Destroy.Core", scope: null, file: !6, line: 19, type: !27, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !30) +// CHECK:STDOUT: !27 = !DISubroutineType(types: !28) +// CHECK:STDOUT: !28 = !{null, !29} +// CHECK:STDOUT: !29 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +// CHECK:STDOUT: !30 = !{!31} +// CHECK:STDOUT: !31 = !DILocalVariable(arg: 1, scope: !26, type: !29) +// CHECK:STDOUT: !32 = !DILocation(line: 19, column: 3, scope: !26) +// CHECK:STDOUT: !33 = distinct !DISubprogram(name: "Op", linkageName: "_COp.fb32c4dfca65e378:core.Destroy.Core", scope: null, file: !6, line: 19, type: !34, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !37) +// CHECK:STDOUT: !34 = !DISubroutineType(types: !35) +// CHECK:STDOUT: !35 = !{null, !36} +// CHECK:STDOUT: !36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !37 = !{!38} +// CHECK:STDOUT: !38 = !DILocalVariable(arg: 1, scope: !33, type: !36) +// CHECK:STDOUT: !39 = !DILocation(line: 19, column: 3, scope: !33) +// CHECK:STDOUT: !40 = distinct !DISubprogram(name: "Op", linkageName: "_COp.71e25083d63c4d6c:core.Destroy.Core", scope: null, file: !6, line: 19, type: !34, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !41) +// CHECK:STDOUT: !41 = !{!42} +// CHECK:STDOUT: !42 = !DILocalVariable(arg: 1, scope: !40, type: !36) +// CHECK:STDOUT: !43 = !DILocation(line: 19, column: 3, scope: !40) diff --git a/toolchain/sem_ir/clang_decl.cpp b/toolchain/sem_ir/clang_decl.cpp index 729c647d07e1..dcdf591a2643 100644 --- a/toolchain/sem_ir/clang_decl.cpp +++ b/toolchain/sem_ir/clang_decl.cpp @@ -41,4 +41,34 @@ auto ClangDecl::Print(llvm::raw_ostream& out) const -> void { out << "{key: " << key << ", inst_id: " << inst_id << "}"; } +ClangDeclStore::ClangDeclStore(CheckIRId check_ir_id) : values_(check_ir_id) {} + +auto ClangDeclStore::Add(ClangDecl value) -> ClangDeclId { + auto id = values_.Add(value); + inst_id_to_clang_decl_id_.Insert(value.inst_id, id); + return id; +} + +auto ClangDeclStore::Lookup(ClangDeclKey key) const -> ClangDeclId { + return values_.Lookup(key); +} + +auto ClangDeclStore::Lookup(InstId inst_id) const -> ClangDeclId { + if (auto result = inst_id_to_clang_decl_id_.Lookup(inst_id)) { + return result.value(); + } + return ClangDeclId::None; +} + +auto ClangDeclStore::OutputYaml() const -> Yaml::OutputMapping { + return values_.OutputYaml(); +} + +auto ClangDeclStore::CollectMemUsage(MemUsage& mem_usage, + llvm::StringRef label) const -> void { + values_.CollectMemUsage(mem_usage, label); + mem_usage.Collect(MemUsage::ConcatLabel(label, "inst_id_to_clang_decl_id_"), + inst_id_to_clang_decl_id_); +} + } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/clang_decl.h b/toolchain/sem_ir/clang_decl.h index 6b65d16233bd..4a3a99ec9ff1 100644 --- a/toolchain/sem_ir/clang_decl.h +++ b/toolchain/sem_ir/clang_decl.h @@ -118,9 +118,38 @@ struct ClangDecl : public Printable { auto GetAsKey() const -> ClangDeclKey { return key; } }; -// Use the AST node pointer directly when doing `Lookup` to find an ID. -using ClangDeclStore = - CanonicalValueStore, ClangDecl>; +// Canonical storage for `ClangDecl`s. Provides bidirectional mapping +// between `ClangDeclId`s and `InstId`s. +class ClangDeclStore { + public: + explicit ClangDeclStore(CheckIRId check_ir_id); + + // Adds a `ClangDecl`, returning an ID to reference it. + auto Add(ClangDecl value) -> ClangDeclId; + + // Looks up a `ClangDecl` by `ClangDeclId`. + auto Get(ClangDeclId id) const -> const ClangDecl& { return values_.Get(id); } + + // Looks up a `ClangDeclId` by `ClangDeclKey`. + auto Lookup(ClangDeclKey key) const -> ClangDeclId; + + // Looks up a `ClangDeclId` by `InstId`. + auto Lookup(InstId inst_id) const -> ClangDeclId; + + auto OutputYaml() const -> Yaml::OutputMapping; + + auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const + -> void; + + private: + // Canonical storage for `ClangDecl`s. Allows mapping from a + // `ClangDeclId` to an `InstId`. + CanonicalValueStore, ClangDecl> + values_; + + // Map from `InstId` to `ClangDeclId`. + Map inst_id_to_clang_decl_id_; +}; } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/class.h b/toolchain/sem_ir/class.h index 641aee9b0132..29e22f9d2f72 100644 --- a/toolchain/sem_ir/class.h +++ b/toolchain/sem_ir/class.h @@ -5,6 +5,7 @@ #ifndef CARBON_TOOLCHAIN_SEM_IR_CLASS_H_ #define CARBON_TOOLCHAIN_SEM_IR_CLASS_H_ +#include "common/map.h" #include "toolchain/base/value_store.h" #include "toolchain/sem_ir/entity_with_params_base.h" #include "toolchain/sem_ir/ids.h" @@ -34,6 +35,9 @@ struct ClassFields { // Whether this class or any base class has at least one virtual function. bool is_dynamic = false; + // Whether the class's fields have been exported to C++. + bool fields_exported = false; + // The following members are set at the `{` of the class definition. // The class scope. diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index 5f0022c6147c..aa8cdf21a093 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -381,7 +381,7 @@ class File : public Printable { // Clang AST declarations pointing to the AST and their mapped Carbon // instructions. When calling `Lookup()`, `inst_id` is ignored. `Add()` will // not add multiple entries with the same `decl` and different `inst_id`. - ClangDeclStore clang_decls_; + ClangDeclStore clang_decls_ = ClangDeclStore(check_ir_id()); // All instructions. The first entries will always be the singleton // instructions.