Skip to content

Commit 9694cc8

Browse files
authored
[cxx-interop] Import default public ctor of C++ foreign ref types as Swift Initializer (#79986)
Building on top of PR #79288, this update synthesizes a static factory method using the default new operator, with a call to the default constructor expression for C++ foreign reference types, and imports them as Swift initializers. rdar://147529406
1 parent 3b18849 commit 9694cc8

File tree

10 files changed

+487
-41
lines changed

10 files changed

+487
-41
lines changed

include/swift/Basic/Features.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,10 @@ EXPERIMENTAL_FEATURE(AssumeResilientCxxTypes, true)
462462
/// Import inherited non-public members when importing C++ classes.
463463
EXPERIMENTAL_FEATURE(ImportNonPublicCxxMembers, true)
464464

465+
/// Synthesize static factory methods for C++ foreign reference types and import
466+
/// them as Swift initializers.
467+
EXPERIMENTAL_FEATURE(CXXForeignReferenceTypeInitializers, true)
468+
465469
// Isolated deinit
466470
SUPPRESSIBLE_LANGUAGE_FEATURE(IsolatedDeinit, 371, "isolated deinit")
467471

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ UNINTERESTING_FEATURE(StrictMemorySafety)
403403
UNINTERESTING_FEATURE(SafeInteropWrappers)
404404
UNINTERESTING_FEATURE(AssumeResilientCxxTypes)
405405
UNINTERESTING_FEATURE(ImportNonPublicCxxMembers)
406+
UNINTERESTING_FEATURE(CXXForeignReferenceTypeInitializers)
406407
UNINTERESTING_FEATURE(CoroutineAccessorsUnwindOnCallerError)
407408
UNINTERESTING_FEATURE(AllowRuntimeSymbolDeclarations)
408409

lib/ClangImporter/ClangImporter.cpp

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7813,17 +7813,6 @@ static bool hasImportAsRefAttr(const clang::RecordDecl *decl) {
78137813
});
78147814
}
78157815

7816-
// TODO: Move all these utility functions in a new file ClangImporterUtils.h
7817-
// rdar://138803759
7818-
static bool hasImmortalAtts(const clang::RecordDecl *decl) {
7819-
return decl->hasAttrs() && llvm::any_of(decl->getAttrs(), [](auto *attr) {
7820-
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
7821-
return swiftAttr->getAttribute() == "retain:immortal" ||
7822-
swiftAttr->getAttribute() == "release:immortal";
7823-
return false;
7824-
});
7825-
}
7826-
78277816
// Is this a pointer to a foreign reference type.
78287817
bool importer::isForeignReferenceTypeWithoutImmortalAttrs(const clang::QualType type) {
78297818
if (!type->isPointerType())
@@ -7835,7 +7824,7 @@ bool importer::isForeignReferenceTypeWithoutImmortalAttrs(const clang::QualType
78357824
return false;
78367825

78377826
return hasImportAsRefAttr(pointeeType->getDecl()) &&
7838-
!hasImmortalAtts(pointeeType->getDecl());
7827+
!hasImmortalAttrs(pointeeType->getDecl());
78397828
}
78407829

78417830
static bool hasDiamondInheritanceRefType(const clang::CXXRecordDecl *decl) {

lib/ClangImporter/ImportDecl.cpp

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,15 @@ bool importer::recordHasReferenceSemantics(
180180
return semanticsKind == CxxRecordSemanticsKind::Reference;
181181
}
182182

183+
bool importer::hasImmortalAttrs(const clang::RecordDecl *decl) {
184+
return decl->hasAttrs() && llvm::any_of(decl->getAttrs(), [](auto *attr) {
185+
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
186+
return swiftAttr->getAttribute() == "retain:immortal" ||
187+
swiftAttr->getAttribute() == "release:immortal";
188+
return false;
189+
});
190+
}
191+
183192
#ifndef NDEBUG
184193
static bool verifyNameMapping(MappedTypeNameKind NameMapping,
185194
StringRef left, StringRef right) {
@@ -2478,14 +2487,43 @@ namespace {
24782487
ctors.push_back(valueCtor);
24792488
}
24802489

2481-
// Do not allow Swift to construct foreign reference types (at least, not
2482-
// yet).
24832490
if (isa<StructDecl>(result)) {
24842491
for (auto ctor : ctors) {
24852492
// Add ctors directly as they cannot always be looked up from the
24862493
// clang decl (some are synthesized by Swift).
24872494
result->addMember(ctor);
24882495
}
2496+
} else {
2497+
if (Impl.SwiftContext.LangOpts.hasFeature(
2498+
Feature::CXXForeignReferenceTypeInitializers)) {
2499+
assert(
2500+
isa<ClassDecl>(result) &&
2501+
"Expected result to be a ClassDecl as it cannot be a StructDecl");
2502+
// When we add full support for C foreign reference types then we
2503+
// should synthesize static factories for them as well
2504+
if (auto *cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(decl)) {
2505+
bool hasUserProvidedStaticFactory = llvm::any_of(
2506+
cxxRecordDecl->methods(),
2507+
[](const clang::CXXMethodDecl *method) {
2508+
return method->isStatic() &&
2509+
llvm::any_of(
2510+
method->specific_attrs<clang::SwiftNameAttr>(),
2511+
[](const auto *attr) {
2512+
return attr->getName().starts_with("init(");
2513+
});
2514+
});
2515+
if (!hasUserProvidedStaticFactory) {
2516+
if (auto generatedCxxMethodDecl =
2517+
synthesizer.synthesizeStaticFactoryForCXXForeignRef(
2518+
cxxRecordDecl)) {
2519+
if (Decl *importedInitDecl =
2520+
Impl.SwiftContext.getClangModuleLoader()
2521+
->importDeclDirectly(generatedCxxMethodDecl))
2522+
result->addMember(importedInitDecl);
2523+
}
2524+
}
2525+
}
2526+
}
24892527
}
24902528

24912529
if (auto structResult = dyn_cast<StructDecl>(result)) {

lib/ClangImporter/ImporterImpl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,6 +1890,10 @@ namespace importer {
18901890
bool recordHasReferenceSemantics(const clang::RecordDecl *decl,
18911891
ClangImporter::Implementation *importerImpl);
18921892

1893+
/// Returns true if the given C/C++ reference type uses "immortal"
1894+
/// retain/release functions.
1895+
bool hasImmortalAttrs(const clang::RecordDecl *decl);
1896+
18931897
/// Whether this is a forward declaration of a type. We ignore forward
18941898
/// declarations in certain cases, and instead process the real declarations.
18951899
bool isForwardDeclOfType(const clang::Decl *decl);

lib/ClangImporter/SwiftDeclSynthesizer.cpp

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2531,3 +2531,131 @@ SwiftDeclSynthesizer::makeDefaultArgument(const clang::ParmVarDecl *param,
25312531

25322532
return callExpr;
25332533
}
2534+
2535+
// MARK: C++ foreign reference type constructors
2536+
2537+
clang::CXXMethodDecl *
2538+
SwiftDeclSynthesizer::synthesizeStaticFactoryForCXXForeignRef(
2539+
const clang::CXXRecordDecl *cxxRecordDecl) {
2540+
2541+
clang::ASTContext &clangCtx = cxxRecordDecl->getASTContext();
2542+
clang::Sema &clangSema = ImporterImpl.getClangSema();
2543+
2544+
clang::QualType cxxRecordTy = clangCtx.getRecordType(cxxRecordDecl);
2545+
2546+
clang::CXXConstructorDecl *defaultCtorDecl = nullptr;
2547+
for (clang::CXXConstructorDecl *ctor : cxxRecordDecl->ctors()) {
2548+
if (ctor->parameters().empty() && !ctor->isDeleted() &&
2549+
ctor->getAccess() != clang::AS_private &&
2550+
ctor->getAccess() != clang::AS_protected) {
2551+
defaultCtorDecl = ctor;
2552+
break;
2553+
}
2554+
}
2555+
if (!defaultCtorDecl)
2556+
return nullptr;
2557+
2558+
clang::FunctionDecl *operatorNew = nullptr;
2559+
clang::FunctionDecl *operatorDelete = nullptr;
2560+
bool passAlignment = false;
2561+
bool findingAllocFuncFailed = clangSema.FindAllocationFunctions(
2562+
cxxRecordDecl->getLocation(), clang::SourceRange(), clang::Sema::AFS_Both,
2563+
clang::Sema::AFS_Both, cxxRecordTy,
2564+
/*IsArray*/ false, passAlignment, clang::MultiExprArg(), operatorNew,
2565+
operatorDelete, /*Diagnose*/ false);
2566+
if (findingAllocFuncFailed || !operatorNew || operatorNew->isDeleted() ||
2567+
operatorNew->getAccess() == clang::AS_private ||
2568+
operatorNew->getAccess() == clang::AS_protected)
2569+
return nullptr;
2570+
2571+
clang::QualType cxxRecordPtrTy = clangCtx.getPointerType(cxxRecordTy);
2572+
// Adding `_Nonnull` to the return type of synthesized static factory
2573+
bool nullabilityCannotBeAdded =
2574+
clangSema.CheckImplicitNullabilityTypeSpecifier(
2575+
cxxRecordPtrTy, clang::NullabilityKind::NonNull,
2576+
cxxRecordDecl->getLocation(),
2577+
/*isParam=*/false,
2578+
/*OverrideExisting=*/true);
2579+
assert(!nullabilityCannotBeAdded &&
2580+
"Failed to add _Nonnull specifier to synthesized "
2581+
"static factory's return type");
2582+
2583+
clang::IdentifierTable &clangIdents = clangCtx.Idents;
2584+
clang::IdentifierInfo *funcNameToSynthesize = &clangIdents.get(
2585+
("__returns_" + cxxRecordDecl->getNameAsString()).c_str());
2586+
clang::FunctionProtoType::ExtProtoInfo EPI;
2587+
clang::QualType funcTypeToSynthesize =
2588+
clangCtx.getFunctionType(cxxRecordPtrTy, {}, EPI);
2589+
2590+
clang::CXXMethodDecl *synthesizedCxxMethodDecl = clang::CXXMethodDecl::Create(
2591+
clangCtx, const_cast<clang::CXXRecordDecl *>(cxxRecordDecl),
2592+
cxxRecordDecl->getLocation(),
2593+
clang::DeclarationNameInfo(funcNameToSynthesize,
2594+
cxxRecordDecl->getLocation()),
2595+
funcTypeToSynthesize,
2596+
clangCtx.getTrivialTypeSourceInfo(funcTypeToSynthesize), clang::SC_Static,
2597+
/*UsesFPIntrin=*/false, /*isInline=*/true,
2598+
clang::ConstexprSpecKind::Unspecified, cxxRecordDecl->getLocation());
2599+
assert(synthesizedCxxMethodDecl &&
2600+
"Unable to synthesize static factory for c++ foreign reference type");
2601+
synthesizedCxxMethodDecl->setAccess(clang::AccessSpecifier::AS_public);
2602+
2603+
if (!hasImmortalAttrs(cxxRecordDecl)) {
2604+
clang::SwiftAttrAttr *returnsRetainedAttrForSynthesizedCxxMethodDecl =
2605+
clang::SwiftAttrAttr::Create(clangCtx, "returns_retained");
2606+
synthesizedCxxMethodDecl->addAttr(
2607+
returnsRetainedAttrForSynthesizedCxxMethodDecl);
2608+
}
2609+
2610+
clang::SwiftNameAttr *swiftNameInitAttrForSynthesizedCxxMethodDecl =
2611+
clang::SwiftNameAttr::Create(clangCtx, "init()");
2612+
synthesizedCxxMethodDecl->addAttr(
2613+
swiftNameInitAttrForSynthesizedCxxMethodDecl);
2614+
2615+
clang::ExprResult synthesizedConstructExprResult =
2616+
clangSema.BuildCXXConstructExpr(
2617+
clang::SourceLocation(), cxxRecordTy, defaultCtorDecl,
2618+
/*Elidable=*/false, clang::MultiExprArg(),
2619+
/*HadMultipleCandidates=*/false,
2620+
/*IsListInitialization=*/false,
2621+
/*IsStdInitListInitialization=*/false,
2622+
/*RequiresZeroInit=*/false, clang::CXXConstructionKind::Complete,
2623+
clang::SourceRange());
2624+
assert(!synthesizedConstructExprResult.isInvalid() &&
2625+
"Unable to synthesize constructor expression for c++ foreign "
2626+
"reference type");
2627+
clang::CXXConstructExpr *synthesizedConstructExpr =
2628+
cast<clang::CXXConstructExpr>(synthesizedConstructExprResult.get());
2629+
2630+
clang::ExprResult synthesizedNewExprResult = clangSema.BuildCXXNew(
2631+
clang::SourceRange(), /*UseGlobal=*/false, clang::SourceLocation(), {},
2632+
clang::SourceLocation(), clang::SourceRange(), cxxRecordTy,
2633+
clangCtx.getTrivialTypeSourceInfo(cxxRecordTy), std::nullopt,
2634+
clang::SourceRange(), synthesizedConstructExpr);
2635+
assert(
2636+
!synthesizedNewExprResult.isInvalid() &&
2637+
"Unable to synthesize `new` expression for c++ foreign reference type");
2638+
clang::CXXNewExpr *synthesizedNewExpr =
2639+
cast<clang::CXXNewExpr>(synthesizedNewExprResult.get());
2640+
2641+
clang::ReturnStmt *synthesizedRetStmt =
2642+
clang::ReturnStmt::Create(clangCtx, clang::SourceLocation(),
2643+
synthesizedNewExpr, /*VarDecl=*/nullptr);
2644+
assert(synthesizedRetStmt && "Unable to synthesize return statement for "
2645+
"static factory of c++ foreign reference type");
2646+
2647+
clang::CompoundStmt *synthesizedFuncBody = clang::CompoundStmt::Create(
2648+
clangCtx, {synthesizedRetStmt}, clang::FPOptionsOverride(),
2649+
clang::SourceLocation(), clang::SourceLocation());
2650+
assert(synthesizedRetStmt && "Unable to synthesize function body for static "
2651+
"factory of c++ foreign reference type");
2652+
2653+
synthesizedCxxMethodDecl->setBody(synthesizedFuncBody);
2654+
synthesizedCxxMethodDecl->addAttr(
2655+
clang::NoDebugAttr::CreateImplicit(clangCtx));
2656+
2657+
synthesizedCxxMethodDecl->setImplicit();
2658+
synthesizedCxxMethodDecl->setImplicitlyInline();
2659+
2660+
return synthesizedCxxMethodDecl;
2661+
}

lib/ClangImporter/SwiftDeclSynthesizer.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,12 @@ class SwiftDeclSynthesizer {
334334
const swift::Type &swiftParamTy,
335335
SourceLoc paramLoc);
336336

337+
/// Synthesize a static factory method for a C++ foreign reference type,
338+
/// returning a `CXXMethodDecl*` or `nullptr` if the required constructor or
339+
/// allocation function is not found.
340+
clang::CXXMethodDecl *synthesizeStaticFactoryForCXXForeignRef(
341+
const clang::CXXRecordDecl *cxxRecordDecl);
342+
337343
private:
338344
Type getConstantLiteralType(Type type, ConstantConvertKind convertKind);
339345
};

0 commit comments

Comments
 (0)