Initial support for C++ function pointers (#7788)

This change introduces a new inst kind `CppFunctionPointerType`, which
represents an imported C++ function pointer that can be invoked from
Carbon (support for forming such a pointer from a Carbon function is in
a follow-up PR). This is implemented by treating the operation of
invoking a function pointer in C++ as if it were a call to an `__invoke`
method on the function pointer type, and extending the existing
function-import logic to support importing this fictitious method.

---------

Co-authored-by: Nicholas Bishop <nbishop@nbishop.net>
This commit is contained in:
Geoff Romer
2026-09-30 00:06:06 +00:00
committed by GitHub
co-authored by Nicholas Bishop
parent f65572a1e6
commit 12e1afeb15
41 changed files with 2030 additions and 200 deletions
+287 -149
View File
@@ -440,10 +440,22 @@ static auto ClangLookupName(Context& context, SemIR::NameScopeId scope_id,
return lookup;
}
// Returns whether `decl` already mapped to an instruction.
static auto IsClangDeclImported(Context& context, SemIR::ClangDeclKey key)
-> bool {
return context.clang_decls().LookupId(key).has_value();
namespace {
// A key that identifies a C++ declaration to import. A `ClangDeclKey`
// represents an ordinary clang declaration, and a `clang::Type*` represents
// that function pointer type's notional `__invoke` method, which has no clang
// declaration.
using ImportKey = std::variant<SemIR::ClangDeclKey, const clang::Type*>;
} // namespace
// Returns whether `key` already mapped to an instruction.
static auto IsImported(Context& context, ImportKey key) -> bool {
CARBON_KIND_SWITCH(key) {
case CARBON_KIND(SemIR::ClangDeclKey decl_key):
return context.clang_decls().LookupId(decl_key).has_value();
case CARBON_KIND(const clang::Type* type):
return context.clang_function_pointer_types().Lookup(type).has_value();
}
}
// If `decl` already mapped to an instruction, returns that instruction.
@@ -1073,7 +1085,7 @@ static auto ImportEnumConstantDecl(Context& context,
clang::EnumConstantDecl* enumerator_decl)
-> SemIR::InstId {
auto key = SemIR::ClangDeclKey(enumerator_decl);
CARBON_CHECK(!IsClangDeclImported(context, key));
CARBON_CHECK(!IsImported(context, key));
// Find the enclosing enum type.
auto enum_key = SemIR::ClangDeclKey(
@@ -1311,6 +1323,20 @@ static auto MapTagType(Context& context, const clang::TagType& type)
.type_id = context.types().GetTypeIdForTypeInstId(record_type_inst_id)};
}
static auto MapFunctionPointerType(Context& context, SemIR::LocId /*loc_id*/,
clang::QualType type) -> TypeExpr {
CARBON_CHECK(type->isFunctionPointerType());
auto clang_type_id = context.clang_function_pointer_types().Lookup(
type.getCanonicalType().getTypePtr());
CARBON_CHECK(clang_type_id.has_value());
// TODO: wrap the pointer type in `Optional` unless it was marked
// non-nullable.
return TypeExpr::ForUnsugared(
context, GetCppFunctionPointerType(context, clang_type_id));
}
// Maps a C++ type that is not a wrapper type such as a pointer to a Carbon
// type.
// TODO: Support more types.
@@ -1324,6 +1350,10 @@ static auto MapNonWrapperType(Context& context, SemIR::LocId loc_id,
return MapTagType(context, *tag_type);
}
if (type->isFunctionPointerType()) {
return MapFunctionPointerType(context, loc_id, type);
}
CARBON_CHECK(!type.hasQualifiers() && !type->isPointerType(),
"Should not see wrapper types here");
@@ -1378,11 +1408,11 @@ static auto MakeOptionalType(Context& context, SemIR::LocId loc_id,
return ExprAsType(context, loc_id, call_id);
}
// Maps a C++ pointer type to a Carbon pointer type.
static auto MapPointerType(Context& context, SemIR::LocId loc_id,
clang::QualType type, TypeExpr pointee_type_expr)
-> TypeExpr {
CARBON_CHECK(type->isPointerType());
// Maps a C++ object pointer type to a Carbon pointer type.
static auto MapObjectPointerType(Context& context, SemIR::LocId loc_id,
clang::QualType type,
TypeExpr pointee_type_expr) -> TypeExpr {
CARBON_CHECK(type->isObjectPointerType());
bool optional =
!IsClangTypeNonNull(type) &&
@@ -1444,7 +1474,7 @@ static auto MapType(Context& context, SemIR::LocId loc_id, clang::QualType type)
clang::QualType orig_type = type;
if (type.hasQualifiers()) {
type = ClangGetUnqualifiedTypePreserveNonNull(context, type);
} else if (type->isPointerType()) {
} else if (type->isObjectPointerType()) {
type = type->getPointeeType();
} else if (type->isReferenceType()) {
type = type.getNonReferenceType();
@@ -1466,8 +1496,8 @@ static auto MapType(Context& context, SemIR::LocId loc_id, clang::QualType type)
if (wrapper.hasQualifiers()) {
mapped = MapQualifiedType(context, wrapper, mapped);
} else if (wrapper->isPointerType()) {
mapped = MapPointerType(context, loc_id, wrapper, mapped);
} else if (wrapper->isObjectPointerType()) {
mapped = MapObjectPointerType(context, loc_id, wrapper, mapped);
} else if (wrapper->isReferenceType()) {
mapped = MapReferenceType(context, wrapper, mapped);
} else if (const auto* array_type = wrapper->getAsArrayTypeUnsafe()) {
@@ -1956,6 +1986,50 @@ static auto ImportFunction(Context& context, SemIR::LocId loc_id,
return decl_id;
}
// Given a C++ callee and its imported Carbon function (as produced by
// `ImportFunction`), this builds a simple-ABI thunk that invokes the callee,
// and defines the imported function as calling it.
static auto DefineAsThunkCall(Context& context, SemIR::LocId loc_id,
const CalleeFunctionInfo& callee_info,
SemIR::Function& function) -> void {
Diagnostics::AnnotationScope annotate_diagnostics(
&context.emitter(), [&](auto& builder) {
CARBON_DIAGNOSTIC(InCppThunk, Note,
"in thunk for C++ function used here");
builder.Note(loc_id, InCppThunk);
});
clang::FunctionDecl* thunk_clang_decl = BuildCppThunk(context, callee_info);
if (thunk_clang_decl == nullptr) {
return;
}
SemIR::ClangDeclSignature thunk_signature;
thunk_signature.kind = SemIR::ClangDeclSignature::Normal;
thunk_signature.num_params =
static_cast<int32_t>(thunk_clang_decl->getNumParams());
thunk_signature.passing_modes.assign(
thunk_signature.num_params,
SemIR::ClangDeclSignature::PassingMode::ByValue);
SemIR::ClangDeclSignatureId thunk_signature_id =
context.clang_decl_signatures().Add(std::move(thunk_signature));
CalleeFunctionInfo thunk_callee_info(context, thunk_clang_decl,
thunk_signature_id);
auto thunk_decl_id = ImportFunction(context, loc_id, thunk_callee_info);
if (thunk_decl_id == std::nullopt) {
return;
}
context.clang_decls().Add({.key = SemIR::ClangDeclKey::ForFunctionDecl(
thunk_clang_decl, thunk_signature_id),
.inst_id = *thunk_decl_id,
.is_imported = true});
auto thunk_function_id =
context.insts().GetAs<SemIR::FunctionDecl>(*thunk_decl_id).function_id;
auto& thunk_function = context.functions().Get(thunk_function_id);
thunk_function.SetCppThunk(function.first_owning_decl_id);
function.SetHasCppThunk(*thunk_decl_id);
}
// Imports a C++ function, returning a corresponding Carbon function.
// `signature` specifies how to convert the C++ function signature to the Carbon
// function signature. `signature.num_params` may be less than the number of
@@ -2001,41 +2075,7 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
context.insts().GetAs<SemIR::FunctionDecl>(*function_decl_id).function_id;
SemIR::Function& imported_function = context.functions().Get(function_id);
if (IsCppThunkRequired(context, callee_info)) {
Diagnostics::AnnotationScope annotate_diagnostics(
&context.emitter(), [&](auto& builder) {
CARBON_DIAGNOSTIC(InCppThunk, Note,
"in thunk for C++ function used here");
builder.Note(loc_id, InCppThunk);
});
if (clang::FunctionDecl* thunk_clang_decl =
BuildCppThunk(context, callee_info)) {
SemIR::ClangDeclSignature thunk_signature;
thunk_signature.kind = SemIR::ClangDeclSignature::Normal;
thunk_signature.num_params =
static_cast<int32_t>(thunk_clang_decl->getNumParams());
thunk_signature.passing_modes.assign(
thunk_signature.num_params,
SemIR::ClangDeclSignature::PassingMode::ByValue);
SemIR::ClangDeclSignatureId thunk_signature_id =
context.clang_decl_signatures().Add(std::move(thunk_signature));
CalleeFunctionInfo thunk_callee_info(context, thunk_clang_decl,
thunk_signature_id);
if (auto thunk_decl_id =
ImportFunction(context, loc_id, thunk_callee_info)) {
context.clang_decls().Add({.key = SemIR::ClangDeclKey::ForFunctionDecl(
thunk_clang_decl, thunk_signature_id),
.inst_id = *thunk_decl_id,
.is_imported = true});
auto thunk_function_id = context.insts()
.GetAs<SemIR::FunctionDecl>(*thunk_decl_id)
.function_id;
auto& thunk_function = context.functions().Get(thunk_function_id);
thunk_function.SetCppThunk(imported_function.first_owning_decl_id);
imported_function.SetHasCppThunk(*thunk_decl_id);
}
}
DefineAsThunkCall(context, loc_id, callee_info, imported_function);
} else {
// Inform Clang that the function has been referenced. This will trigger
// instantiation if needed.
@@ -2059,13 +2099,70 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
return *function_decl_id;
}
// Imports a C++ function pointer type, and prepares to import its `__invoke`
// function. The function itself must be imported separately with
// `ImportFunctionPointerInvoke` when it's called, because that requires the
// types in the signature to be complete.
static auto ImportFunctionPointer(Context& context,
const clang::Type* pointer_type)
-> SemIR::InstId {
CARBON_CHECK(pointer_type->isFunctionPointerType());
// Allocate an ID for the function pointer type and return it.
pointer_type = clang::QualType(pointer_type, /*Quals=*/0)
.getCanonicalType()
.getTypePtr();
auto clang_type_id =
context.clang_function_pointer_types().Lookup(pointer_type);
if (!clang_type_id.has_value()) {
clang_type_id = context.clang_function_pointer_types().Add(
{.clang_type = pointer_type,
.decl_id = SemIR::InstId::None,
.function_id = SemIR::FunctionId::None});
}
return context.types().GetTypeInstId(
GetCppFunctionPointerType(context, clang_type_id));
}
auto ImportFunctionPointerInvoke(
Context& context, SemIR::LocId loc_id,
SemIR::ClangFunctionPointerTypeId clang_type_id)
-> SemIR::ClangFunctionPointerTypeInfo {
const auto& info = context.clang_function_pointer_types().Get(clang_type_id);
if (info.decl_id.has_value()) {
return info;
}
Diagnostics::AnnotationScope annotate_diagnostics(
&context.emitter(), [&](auto& builder) {
CARBON_DIAGNOSTIC(InCppFunctionPointerThunk, Note,
"in thunk for C++ function pointer type used here");
builder.Note(loc_id, InCppFunctionPointerThunk);
});
CalleeFunctionInfo callee_info(context, info.clang_type);
SemIR::ClangFunctionPointerTypeInfo result = {
.clang_type = info.clang_type,
.decl_id = SemIR::ErrorInst::InstId,
.function_id = SemIR::FunctionId::None};
auto function_decl_id = ImportFunction(context, loc_id, callee_info);
if (function_decl_id.has_value()) {
result.decl_id = *function_decl_id;
result.function_id = context.insts()
.GetAs<SemIR::FunctionDecl>(*function_decl_id)
.function_id;
DefineAsThunkCall(context, loc_id, callee_info,
context.functions().Get(result.function_id));
}
context.clang_function_pointer_types().Update(clang_type_id, result);
return result;
}
namespace {
// An item to be imported in an import worklist.
// TODO: If worklists ever become particularly large, consider changing this
// to use a `PointerIntPair`.
// to use a `PointerUnion` and/or `PointerIntPair`.
struct ImportItem {
// A declaration that we want to import.
SemIR::ClangDeclKey decl_key;
ImportKey key;
// Whether we have added `decl`'s dependencies to the worklist.
bool added_dependencies;
};
@@ -2073,20 +2170,27 @@ struct ImportItem {
using ImportWorklist = llvm::SmallVector<ImportItem>;
} // namespace
// Adds the given declaration to our list of declarations to import.
static auto AddDependentDecl(Context& context, SemIR::ClangDeclKey decl,
ImportWorklist& worklist) -> void {
if (!IsClangDeclImported(context, decl)) {
worklist.push_back({.decl_key = decl, .added_dependencies = false});
// Adds the given declaration to the worklist. As an optimization, does nothing
// if the declaration has already been imported.
static auto PushDecl(Context& context, SemIR::ClangDeclKey decl,
ImportWorklist& worklist) -> void {
if (!IsImported(context, decl)) {
worklist.push_back({.key = decl, .added_dependencies = false});
}
}
// Finds all decls that need to be imported before importing the given type and
// adds them to the given set.
static auto AddDependentUnimportedTypeDecls(Context& context,
clang::QualType type,
ImportWorklist& worklist) -> void {
// Adds the declarations that the given type directly depends on to the
// worklist.
static auto PushType(Context& context, clang::QualType type,
ImportWorklist& worklist) -> void {
while (true) {
if (type->isFunctionPointerType()) {
const clang::Type* type_ptr = type.getCanonicalType().getTypePtr();
if (!IsImported(context, type_ptr)) {
worklist.push_back({.key = type_ptr, .added_dependencies = false});
}
return;
}
if (type->isPointerType() || type->isReferenceType()) {
type = type->getPointeeType();
} else if (const clang::ArrayType* array_type =
@@ -2098,52 +2202,68 @@ static auto AddDependentUnimportedTypeDecls(Context& context,
}
if (const auto* tag_type = type->getAs<clang::TagType>()) {
AddDependentDecl(context, SemIR::ClangDeclKey(tag_type->getDecl()),
worklist);
PushDecl(context, SemIR::ClangDeclKey(tag_type->getDecl()), worklist);
}
}
// Finds all decls that need to be imported before importing the given function
// and adds them to the given set.
static auto AddDependentUnimportedFunctionDecls(
Context& context, const clang::FunctionDecl& clang_decl,
SemIR::ClangDeclSignatureId signature_id, ImportWorklist& worklist)
// Finds all decls that need to be imported before importing the given type,
// which must be a `FunctionProtoType`, and adds them to the worklist. If
// `num_params` is non-negative, only the first `num_params` parameter types
// will be added.
static auto AddUnimportedDependenciesForFunctionType(Context& context,
clang::QualType type,
int num_params,
ImportWorklist& worklist)
-> void {
const auto& signature = context.clang_decl_signatures().Get(signature_id);
const auto* function_type =
clang_decl.getType()->castAs<clang::FunctionProtoType>();
for (int i : llvm::seq(clang_decl.hasCXXExplicitFunctionObjectParameter() +
signature.num_params)) {
AddDependentUnimportedTypeDecls(context, function_type->getParamType(i),
worklist);
const auto* function_type = type->castAs<clang::FunctionProtoType>();
if (num_params < 0) {
num_params = function_type->getNumParams();
}
AddDependentUnimportedTypeDecls(context, clang_decl.getReturnType(),
worklist);
for (int i : llvm::seq(num_params)) {
PushType(context, function_type->getParamType(i), worklist);
}
PushType(context, function_type->getReturnType(), worklist);
}
// Finds all decls that need to be imported before importing the given
// declaration and adds them to the given set.
static auto AddDependentUnimportedDecls(Context& context,
SemIR::ClangDeclKey key,
ImportWorklist& worklist) -> void {
clang::Decl* clang_decl = key.decl;
if (auto* clang_function_decl = clang_decl->getAsFunction()) {
AddDependentUnimportedFunctionDecls(context, *clang_function_decl,
key.signature_id, worklist);
} else if (auto* type_decl = dyn_cast<clang::TypeDecl>(clang_decl)) {
if (!isa<clang::TagDecl>(clang_decl)) {
AddDependentUnimportedTypeDecls(
context, type_decl->getASTContext().getTypeDeclType(type_decl),
worklist);
// declaration and adds them to the worklist.
static auto AddUnimportedDependencies(Context& context, ImportKey key,
ImportWorklist& worklist) -> void {
CARBON_KIND_SWITCH(key) {
case CARBON_KIND(SemIR::ClangDeclKey decl_key): {
clang::Decl* clang_decl = decl_key.decl;
if (auto* clang_function_decl = clang_decl->getAsFunction()) {
const auto& signature =
context.clang_decl_signatures().Get(decl_key.signature_id);
int num_params =
clang_function_decl->hasCXXExplicitFunctionObjectParameter() +
signature.num_params;
AddUnimportedDependenciesForFunctionType(
context, clang_function_decl->getType(), num_params, worklist);
} else if (auto* type_decl = dyn_cast<clang::TypeDecl>(clang_decl)) {
if (!isa<clang::TagDecl>(clang_decl)) {
PushType(context,
type_decl->getASTContext().getTypeDeclType(type_decl),
worklist);
}
} else if (auto* var_decl = dyn_cast<clang::VarDecl>(clang_decl)) {
PushType(context, var_decl->getType(), worklist);
}
auto* parent = GetParentDecl(clang_decl);
if (llvm::isa_and_nonnull<clang::TagDecl, clang::NamespaceDecl,
clang::TranslationUnitDecl>(parent)) {
PushDecl(context, SemIR::ClangDeclKey::ForNonFunctionDecl(parent),
worklist);
}
break;
}
case CARBON_KIND(const clang::Type* type): {
if (type->isFunctionPointerType()) {
AddUnimportedDependenciesForFunctionType(
context, type->getPointeeType(), -1, worklist);
}
break;
}
} else if (auto* var_decl = dyn_cast<clang::VarDecl>(clang_decl)) {
AddDependentUnimportedTypeDecls(context, var_decl->getType(), worklist);
}
auto* parent = GetParentDecl(clang_decl);
if (llvm::isa_and_nonnull<clang::TagDecl, clang::NamespaceDecl,
clang::TranslationUnitDecl>(parent)) {
AddDependentDecl(context, SemIR::ClangDeclKey::ForNonFunctionDecl(parent),
worklist);
}
}
@@ -2243,54 +2363,72 @@ static auto ImportTemplateDecl(Context& context,
// Imports a declaration from Clang to Carbon. Returns the instruction for the
// new Carbon declaration, which will be an ErrorInst on failure. Assumes all
// dependencies have already been imported.
static auto ImportDeclAfterDependencies(Context& context, SemIR::LocId loc_id,
SemIR::ClangDeclKey key)
-> SemIR::InstId {
clang::Decl* clang_decl = key.decl;
if (auto* clang_function_decl = clang_decl->getAsFunction()) {
return ImportFunctionDecl(context, loc_id, clang_function_decl,
key.signature_id);
}
if (auto* clang_namespace_decl = dyn_cast<clang::NamespaceDecl>(clang_decl)) {
return ImportNamespaceDecl(context, clang_namespace_decl);
}
if (auto* type_decl = dyn_cast<clang::TypeDecl>(clang_decl)) {
auto type = clang_decl->getASTContext().getTypeDeclType(type_decl);
auto type_inst_id = MapType(context, loc_id, type).inst_id;
if (!type_inst_id.has_value()) {
context.TODO(AddImportIRInst(context.sem_ir(), type_decl->getLocation()),
llvm::formatv("Unsupported: Type declaration: {0}",
type.getAsString()));
static auto ImportAfterDependencies(Context& context, SemIR::LocId loc_id,
ImportKey key) -> SemIR::InstId {
CARBON_KIND_SWITCH(key) {
case CARBON_KIND(SemIR::ClangDeclKey decl_key): {
clang::Decl* clang_decl = decl_key.decl;
if (auto* clang_function_decl = clang_decl->getAsFunction()) {
return ImportFunctionDecl(context, loc_id, clang_function_decl,
decl_key.signature_id);
}
if (auto* clang_namespace_decl =
dyn_cast<clang::NamespaceDecl>(clang_decl)) {
return ImportNamespaceDecl(context, clang_namespace_decl);
}
if (auto* type_decl = dyn_cast<clang::TypeDecl>(clang_decl)) {
auto type = clang_decl->getASTContext().getTypeDeclType(type_decl);
auto type_inst_id = MapType(context, loc_id, type).inst_id;
if (!type_inst_id.has_value()) {
context.TODO(
AddImportIRInst(context.sem_ir(), type_decl->getLocation()),
llvm::formatv("Unsupported: Type declaration: {0}",
type.getAsString()));
return SemIR::ErrorInst::InstId;
}
context.clang_decls().Add(
{.key = decl_key, .inst_id = type_inst_id, .is_imported = true});
return type_inst_id;
}
if (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, decl_key);
existing_inst_id.has_value()) {
return existing_inst_id;
}
context.TODO(
AddImportIRInst(context.sem_ir(), clang_decl->getLocation()),
"Unsupported: field declaration has unhandled type or kind");
return SemIR::ErrorInst::InstId;
}
if (auto* enum_const_decl =
dyn_cast<clang::EnumConstantDecl>(clang_decl)) {
return ImportEnumConstantDecl(context, enum_const_decl);
}
if (auto* var_decl = dyn_cast<clang::VarDecl>(clang_decl)) {
return ImportVarDecl(context, loc_id, var_decl);
}
if (auto* template_decl = dyn_cast<clang::TemplateDecl>(clang_decl)) {
return ImportTemplateDecl(context, template_decl);
}
context.TODO(AddImportIRInst(context.sem_ir(), clang_decl->getLocation()),
llvm::formatv("Unsupported: Declaration type {0}",
clang_decl->getDeclKindName()));
return SemIR::ErrorInst::InstId;
}
context.clang_decls().Add(
{.key = key, .inst_id = type_inst_id, .is_imported = true});
return type_inst_id;
}
if (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, key);
existing_inst_id.has_value()) {
return existing_inst_id;
}
context.TODO(AddImportIRInst(context.sem_ir(), clang_decl->getLocation()),
"Unsupported: field declaration has unhandled type or kind");
return SemIR::ErrorInst::InstId;
}
if (auto* enum_const_decl = dyn_cast<clang::EnumConstantDecl>(clang_decl)) {
return ImportEnumConstantDecl(context, enum_const_decl);
}
if (auto* var_decl = dyn_cast<clang::VarDecl>(clang_decl)) {
return ImportVarDecl(context, loc_id, var_decl);
}
if (auto* template_decl = dyn_cast<clang::TemplateDecl>(clang_decl)) {
return ImportTemplateDecl(context, template_decl);
}
case CARBON_KIND(const clang::Type* type): {
if (type->isFunctionPointerType()) {
return ImportFunctionPointer(context, type);
}
context.TODO(AddImportIRInst(context.sem_ir(), clang_decl->getLocation()),
llvm::formatv("Unsupported: Declaration type {0}",
clang_decl->getDeclKindName()));
return SemIR::ErrorInst::InstId;
context.TODO(loc_id,
llvm::formatv("Unsupported: type {0}",
clang::QualType(type, 0).getAsString()));
return SemIR::ErrorInst::InstId;
}
}
}
// Attempts to import a set of declarations. Returns `false` if an error was
@@ -2306,7 +2444,7 @@ static auto ImportDeclSet(Context& context, SemIR::LocId loc_id,
// adding the item to the worklist, but it might have been added to the
// worklist twice before the first time we visited it. For example, this
// happens for `fn F(a: Cpp.T, b: Cpp.T)`.
if (IsClangDeclImported(context, item.decl_key)) {
if (IsImported(context, item.key)) {
worklist.pop_back();
continue;
}
@@ -2314,17 +2452,17 @@ static auto ImportDeclSet(Context& context, SemIR::LocId loc_id,
// First time visiting this declaration (preorder): add its dependencies
// to the work list.
item.added_dependencies = true;
AddDependentUnimportedDecls(context, item.decl_key, worklist);
AddUnimportedDependencies(context, item.key, worklist);
} else {
// Second time visiting this declaration (postorder): its dependencies are
// already imported, so we can import it now.
auto decl_key = worklist.pop_back_val().decl_key;
auto inst_id = ImportDeclAfterDependencies(context, loc_id, decl_key);
auto decl_key = worklist.pop_back_val().key;
auto inst_id = ImportAfterDependencies(context, loc_id, decl_key);
CARBON_CHECK(inst_id.has_value());
if (inst_id == SemIR::ErrorInst::InstId) {
return false;
}
CARBON_CHECK(IsClangDeclImported(context, decl_key));
CARBON_CHECK(IsImported(context, decl_key));
}
}
@@ -2335,7 +2473,7 @@ auto ImportCppDecl(Context& context, SemIR::LocId loc_id,
SemIR::ClangDeclKey key) -> SemIR::InstId {
// Collect dependencies by walking the dependency graph in depth-first order.
ImportWorklist worklist;
AddDependentDecl(context, key, worklist);
PushDecl(context, key, worklist);
if (!ImportDeclSet(context, loc_id, worklist)) {
return SemIR::ErrorInst::InstId;
}
@@ -2346,7 +2484,7 @@ auto ImportCppType(Context& context, SemIR::LocId loc_id, clang::QualType type)
-> TypeExpr {
// Collect dependencies by walking the dependency graph in depth-first order.
ImportWorklist worklist;
AddDependentUnimportedTypeDecls(context, type, worklist);
PushType(context, type, worklist);
if (!ImportDeclSet(context, loc_id, worklist)) {
return {.inst_id = SemIR::ErrorInst::TypeInstId,
.type_id = SemIR::ErrorInst::TypeId};