From c06165d3e0fa60269c952c0f351bc9c14b606804 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 19 Aug 2026 17:45:34 +0000 Subject: [PATCH] Require exported field types to be complete in the clang AST. (#7653) It's not enough for field types of Carbon classes to be complete in SemIR. If the field is exported to Clang, we also need the type to be complete in Clang's AST, since Clang assumes it has a definition available for the types of all fields of a complete class. --- toolchain/check/cpp/export.cpp | 16 +++++++++++++++- .../interop/cpp/class/export/field.carbon | 18 ++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/toolchain/check/cpp/export.cpp b/toolchain/check/cpp/export.cpp index bf8d85c013da..cffce8ade0ad 100644 --- a/toolchain/check/cpp/export.cpp +++ b/toolchain/check/cpp/export.cpp @@ -487,18 +487,32 @@ static auto CreateCppFieldDecl(Context& context, return nullptr; } + auto clang_loc = GetCppLocation(context, SemIR::LocId(field_inst_id)); + bool invalid = false; + + // The field is required to have a complete type in the Clang AST, not just in + // SemIR. + if (context.clang_sema().RequireCompleteSizedType( + clang_loc, cpp_type, clang::diag::err_field_incomplete_or_sizeless)) { + // Follow Clang in marking the class as invalid if it contains a field with + // an incomplete type. In Clang this prevents record layout; it's unclear if + // it's necessary for us. + record_decl->setInvalidDecl(); + invalid = true; + } + // 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->setInvalidDecl(invalid); SetCppClassMemberAccess(class_scope, field_decl.name_id, cpp_field_decl); diff --git a/toolchain/check/testdata/interop/cpp/class/export/field.carbon b/toolchain/check/testdata/interop/cpp/class/export/field.carbon index 449bd7f92696..c584567e1746 100644 --- a/toolchain/check/testdata/interop/cpp/class/export/field.carbon +++ b/toolchain/check/testdata/interop/cpp/class/export/field.carbon @@ -48,6 +48,24 @@ void F() { } '''; +// --- class_field.carbon +library "[[@TEST_NAME]]"; +import Cpp; + +class ClassField {} + +class ClassWithClassField { + var c: ClassField; +} + +inline Cpp ''' +void g() { + // In order for this to work, we need both class definitions to be exported to + // Clang. + Carbon::ClassWithClassField c; +} +'''; + // --- fail_nonexistent_field.carbon library "[[@TEST_NAME]]"; import Cpp;