Remove Class::fields_exported (#7586)

In preparation for exporting specific classes, remove `fields_exported`.
A class's fields will be exported for each specific, so a single boolean
won't work.

Instead, check in `ExportAllFieldsToCpp` if `clang_decls` already has an
export for this field, and skip if so. Once specifics are supported,
this lookup will use both the field's `InstId` and `SpecificId`.

The above changes aren't quite sufficient, because if a field fails to
be exported, it will keep being attempted on each call to
`ExportAllFieldsToCpp` resulting in multiple errors. Fix this by storing
an invalid `FieldDecl` in `ClangDeclStore` if an error occurs. This lets
`ExportAllFieldsToCpp` know not to reattempt export, and
`ExportFieldToCpp` returns `nullptr` for invalid fields same as before.
This commit is contained in:
Nicholas Bishop
2026-07-29 19:27:05 +00:00
committed by GitHub
parent 3a37b89da2
commit f4e7a84df2
2 changed files with 35 additions and 11 deletions
+35 -8
View File
@@ -8,6 +8,7 @@
#include <string_view>
#include "clang/AST/ASTConsumer.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/EnterExpressionEvaluationContext.h"
#include "clang/Sema/Sema.h"
#include "llvm/Support/Casting.h"
@@ -251,11 +252,25 @@ static auto CreateCppFieldDecl(Context& context,
return cpp_field_decl;
}
auto ExportAllFieldsToCpp(Context& context, SemIR::Class& class_info) -> void {
if (class_info.fields_exported) {
return;
}
// Create an invalid `clang::FieldDecl`. This is only used as an error marker
// to indicate that a Carbon field has already been unsuccessfully exported.
static auto CreateInvalidFieldDecl(Context& context,
clang::DeclContext* decl_context)
-> clang::FieldDecl* {
clang::SourceLocation clang_loc;
auto* identifier_info =
context.clang_sema().getPreprocessor().getIdentifierInfo("invalid_field");
auto cpp_type = context.ast_context().IntTy;
auto* field_decl = clang::FieldDecl::Create(
context.ast_context(), decl_context, /*StartLoc=*/clang_loc,
/*IdLoc=*/clang_loc, identifier_info, cpp_type, /*TInfo=*/nullptr,
/*BW=*/nullptr,
/*Mutable=*/true, clang::ICIS_NoInit);
field_decl->setInvalidDecl();
return field_decl;
}
auto ExportAllFieldsToCpp(Context& context, SemIR::Class& class_info) -> void {
const auto& class_scope = context.name_scopes().Get(class_info.scope_id);
for (const auto& struct_field :
@@ -266,6 +281,13 @@ auto ExportAllFieldsToCpp(Context& context, SemIR::Class& class_info) -> void {
continue;
}
// Return early if the field is already exported. Since fields are always
// exported as a group, this indicates all fields have been exported so
// there's no need to continue to the rest.
if (context.clang_decls().Lookup(class_field->inst_id)) {
return;
}
// Map the parent scope into the C++ AST.
auto* decl_context = ExportNameScopeToCpp(
context, SemIR::LocId(class_field->inst_id), class_info.scope_id);
@@ -276,16 +298,19 @@ auto ExportAllFieldsToCpp(Context& context, SemIR::Class& class_info) -> void {
auto* cpp_field_decl = CreateCppFieldDecl(
context, class_scope, cast<clang::CXXRecordDecl>(decl_context),
class_field->inst_id, class_field->inst);
// If the field cannot be exported, create an invalid `FieldDecl` to store
// in `clang_decls`. This marks the field as unsuccessfully exported, so
// that we know not to attempt export again (which could create duplicate
// error diagnostics).
if (!cpp_field_decl) {
continue;
cpp_field_decl = CreateInvalidFieldDecl(context, decl_context);
}
// 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,
@@ -303,7 +328,9 @@ auto ExportFieldToCpp(Context& context, SemIR::InstId field_inst_id,
// Get the exported `clang::FieldDecl`.
if (const auto* clang_decl = context.clang_decls().Lookup(field_inst_id)) {
return cast<clang::FieldDecl>(clang_decl->decl());
if (!clang_decl->decl()->isInvalidDecl()) {
return cast<clang::FieldDecl>(clang_decl->decl());
}
}
return nullptr;
}