Skip to content

Commit 06bf551

Browse files
committed
[cxx-interop] Import default ctor of C++ foreign ref types as Swift Initializer
rdar://147529406
1 parent f6c9177 commit 06bf551

File tree

8 files changed

+456
-4
lines changed

8 files changed

+456
-4
lines changed

include/swift/Basic/Features.def

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

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

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ UNINTERESTING_FEATURE(StrictMemorySafety)
392392
UNINTERESTING_FEATURE(SafeInteropWrappers)
393393
UNINTERESTING_FEATURE(AssumeResilientCxxTypes)
394394
UNINTERESTING_FEATURE(ImportNonPublicCxxMembers)
395+
UNINTERESTING_FEATURE(CXXForeignReferenceTypeInitializers)
395396
UNINTERESTING_FEATURE(CoroutineAccessorsUnwindOnCallerError)
396397

397398
static bool usesFeatureSwiftSettings(const Decl *decl) {

lib/ClangImporter/ImportDecl.cpp

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,14 +2472,43 @@ namespace {
24722472
ctors.push_back(valueCtor);
24732473
}
24742474

2475-
// Do not allow Swift to construct foreign reference types (at least, not
2476-
// yet).
24772475
if (isa<StructDecl>(result)) {
24782476
for (auto ctor : ctors) {
24792477
// Add ctors directly as they cannot always be looked up from the
24802478
// clang decl (some are synthesized by Swift).
24812479
result->addMember(ctor);
24822480
}
2481+
} else {
2482+
if (Impl.SwiftContext.LangOpts.hasFeature(
2483+
Feature::CXXForeignReferenceTypeInitializers)) {
2484+
assert(
2485+
isa<ClassDecl>(result) &&
2486+
"Expected result to be a ClassDecl as it cannot be a StructDecl");
2487+
// When we add full support for C foreign reference types then we
2488+
// should synthesize static factories for them as well
2489+
if (auto *cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(decl)) {
2490+
bool hasUserProvidedStaticFactory =
2491+
llvm::any_of(cxxRecordDecl->methods(), [](const auto *method) {
2492+
return method->isStatic() &&
2493+
llvm::any_of(method->template specific_attrs<
2494+
clang::SwiftNameAttr>(),
2495+
[](const auto *attr) {
2496+
return attr->getName().str().find(
2497+
"init") != std::string::npos;
2498+
});
2499+
});
2500+
if (!hasUserProvidedStaticFactory) {
2501+
if (auto generatedCxxMethodDecl = SwiftDeclSynthesizer::
2502+
synthesizeStaticFactoryForCXXForeignRef(
2503+
cxxRecordDecl, cxxRecordDecl->getASTContext(),
2504+
Impl.getClangSema())) {
2505+
if (Decl *importedInitDecl =
2506+
VisitCXXMethodDecl(generatedCxxMethodDecl))
2507+
result->addMember(importedInitDecl);
2508+
}
2509+
}
2510+
}
2511+
}
24832512
}
24842513

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

lib/ClangImporter/SwiftDeclSynthesizer.cpp

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

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

lib/ClangImporter/SwiftDeclSynthesizer.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,13 @@ 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+
static clang::CXXMethodDecl *synthesizeStaticFactoryForCXXForeignRef(
341+
const clang::CXXRecordDecl *cxxRecordDecl, clang::ASTContext &clangCtx,
342+
clang::Sema &clangSema);
343+
337344
private:
338345
Type getConstantLiteralType(Type type, ConstantConvertKind convertKind);
339346
};

0 commit comments

Comments
 (0)