Skip to content

Commit c37700b

Browse files
authored
Merge pull request #80715 from swiftlang/fahadnayyar/6.2/cxx-frt-ctor
🍒 [6.2] [cxx-interop] Import public constructor of C++ foreign reference types as Swift initializers
2 parents 7a3393c + f3ecb7e commit c37700b

14 files changed

+439
-173
lines changed

include/swift/Basic/Features.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ EXPERIMENTAL_FEATURE(ImportNonPublicCxxMembers, true)
467467

468468
/// Synthesize static factory methods for C++ foreign reference types and import
469469
/// them as Swift initializers.
470-
EXPERIMENTAL_FEATURE(CXXForeignReferenceTypeInitializers, true)
470+
EXPERIMENTAL_FEATURE(SuppressCXXForeignReferenceTypeInitializers, true)
471471

472472
// Isolated deinit
473473
SUPPRESSIBLE_LANGUAGE_FEATURE(IsolatedDeinit, 371, "isolated deinit")

lib/AST/FeatureSet.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ UNINTERESTING_FEATURE(StrictMemorySafety)
426426
UNINTERESTING_FEATURE(SafeInteropWrappers)
427427
UNINTERESTING_FEATURE(AssumeResilientCxxTypes)
428428
UNINTERESTING_FEATURE(ImportNonPublicCxxMembers)
429-
UNINTERESTING_FEATURE(CXXForeignReferenceTypeInitializers)
429+
UNINTERESTING_FEATURE(SuppressCXXForeignReferenceTypeInitializers)
430430
UNINTERESTING_FEATURE(CoroutineAccessorsUnwindOnCallerError)
431431
UNINTERESTING_FEATURE(AllowRuntimeSymbolDeclarations)
432432

lib/ClangImporter/ImportDecl.cpp

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2497,8 +2497,8 @@ namespace {
24972497
result->addMember(ctor);
24982498
}
24992499
} else {
2500-
if (Impl.SwiftContext.LangOpts.hasFeature(
2501-
Feature::CXXForeignReferenceTypeInitializers)) {
2500+
if (!Impl.SwiftContext.LangOpts.hasFeature(
2501+
Feature::SuppressCXXForeignReferenceTypeInitializers)) {
25022502
assert(
25032503
isa<ClassDecl>(result) &&
25042504
"Expected result to be a ClassDecl as it cannot be a StructDecl");
@@ -2516,12 +2516,13 @@ namespace {
25162516
});
25172517
});
25182518
if (!hasUserProvidedStaticFactory) {
2519-
if (auto generatedCxxMethodDecl =
2520-
synthesizer.synthesizeStaticFactoryForCXXForeignRef(
2521-
cxxRecordDecl)) {
2519+
auto generatedCxxMethodDecls =
2520+
synthesizer.synthesizeStaticFactoryForCXXForeignRef(
2521+
cxxRecordDecl);
2522+
for (auto *methodDecl : generatedCxxMethodDecls) {
25222523
if (Decl *importedInitDecl =
25232524
Impl.SwiftContext.getClangModuleLoader()
2524-
->importDeclDirectly(generatedCxxMethodDecl))
2525+
->importDeclDirectly(methodDecl))
25252526
result->addMember(importedInitDecl);
25262527
}
25272528
}
@@ -3552,7 +3553,18 @@ namespace {
35523553
isa<clang::FunctionDecl>(decl)
35533554
? cast<clang::FunctionDecl>(decl)->getReturnType()
35543555
: cast<clang::ObjCMethodDecl>(decl)->getReturnType();
3555-
if (isForeignReferenceTypeWithoutImmortalAttrs(retType)) {
3556+
clang::QualType pointeeType = retType;
3557+
if (retType->isPointerType() || retType->isReferenceType()) {
3558+
pointeeType = retType->getPointeeType();
3559+
}
3560+
3561+
clang::RecordDecl *recordDecl = nullptr;
3562+
if (const auto *recordType = pointeeType->getAs<clang::RecordType>()) {
3563+
recordDecl = recordType->getDecl();
3564+
}
3565+
3566+
if (recordDecl && recordHasReferenceSemantics(recordDecl) &&
3567+
!hasImmortalAttrs(recordDecl)) {
35563568
if (returnsRetainedAttrIsPresent && returnsUnretainedAttrIsPresent) {
35573569
Impl.diagnose(loc, diag::both_returns_retained_returns_unretained,
35583570
decl);

lib/ClangImporter/SwiftDeclSynthesizer.cpp

Lines changed: 168 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -2534,128 +2534,200 @@ SwiftDeclSynthesizer::makeDefaultArgument(const clang::ParmVarDecl *param,
25342534

25352535
// MARK: C++ foreign reference type constructors
25362536

2537-
clang::CXXMethodDecl *
2537+
llvm::SmallVector<clang::CXXMethodDecl *, 4>
25382538
SwiftDeclSynthesizer::synthesizeStaticFactoryForCXXForeignRef(
25392539
const clang::CXXRecordDecl *cxxRecordDecl) {
25402540

2541+
if (cxxRecordDecl->isAbstract())
2542+
return {};
2543+
25412544
clang::ASTContext &clangCtx = cxxRecordDecl->getASTContext();
25422545
clang::Sema &clangSema = ImporterImpl.getClangSema();
25432546

25442547
clang::QualType cxxRecordTy = clangCtx.getRecordType(cxxRecordDecl);
2548+
clang::SourceLocation cxxRecordDeclLoc = cxxRecordDecl->getLocation();
25452549

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-
}
2550+
llvm::SmallVector<clang::CXXConstructorDecl *, 4> ctorDeclsForSynth;
2551+
for (clang::CXXConstructorDecl *ctorDecl : cxxRecordDecl->ctors()) {
2552+
if (ctorDecl->isDeleted() || ctorDecl->getAccess() == clang::AS_private ||
2553+
ctorDecl->getAccess() == clang::AS_protected ||
2554+
ctorDecl->isCopyOrMoveConstructor() || ctorDecl->isVariadic())
2555+
continue;
2556+
2557+
bool hasDefaultArg = !ctorDecl->parameters().empty() &&
2558+
ctorDecl->parameters().back()->hasDefaultArg();
2559+
// TODO: Add support for default args in ctors for C++ foreign reference
2560+
// types.
2561+
if (hasDefaultArg)
2562+
continue;
2563+
ctorDeclsForSynth.push_back(ctorDecl);
25542564
}
2555-
if (!defaultCtorDecl)
2556-
return nullptr;
2565+
2566+
if (ctorDeclsForSynth.empty())
2567+
return {};
25572568

25582569
clang::FunctionDecl *operatorNew = nullptr;
25592570
clang::FunctionDecl *operatorDelete = nullptr;
25602571
bool passAlignment = false;
2572+
clang::Sema::SFINAETrap trap(clangSema);
25612573
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() ||
2574+
cxxRecordDeclLoc, clang::SourceRange(), clang::Sema::AFS_Both,
2575+
clang::Sema::AFS_Both, cxxRecordTy, /*IsArray=*/false, passAlignment,
2576+
clang::MultiExprArg(), operatorNew, operatorDelete,
2577+
/*Diagnose=*/false);
2578+
if (trap.hasErrorOccurred() || findingAllocFuncFailed || !operatorNew ||
2579+
operatorNew->isDeleted() ||
25672580
operatorNew->getAccess() == clang::AS_private ||
25682581
operatorNew->getAccess() == clang::AS_protected)
2569-
return nullptr;
2582+
return {};
25702583

25712584
clang::QualType cxxRecordPtrTy = clangCtx.getPointerType(cxxRecordTy);
25722585
// Adding `_Nonnull` to the return type of synthesized static factory
25732586
bool nullabilityCannotBeAdded =
25742587
clangSema.CheckImplicitNullabilityTypeSpecifier(
2575-
cxxRecordPtrTy, clang::NullabilityKind::NonNull,
2576-
cxxRecordDecl->getLocation(),
2577-
/*isParam=*/false,
2578-
/*OverrideExisting=*/true);
2588+
cxxRecordPtrTy, clang::NullabilityKind::NonNull, cxxRecordDeclLoc,
2589+
/*isParam=*/false, /*OverrideExisting=*/true);
25792590
assert(!nullabilityCannotBeAdded &&
25802591
"Failed to add _Nonnull specifier to synthesized "
25812592
"static factory's return type");
25822593

25832594
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);
2595+
2596+
llvm::SmallVector<clang::CXXMethodDecl *, 4> synthesizedFactories;
2597+
unsigned int selectedCtorDeclCounter = 0;
2598+
for (clang::CXXConstructorDecl *selectedCtorDecl : ctorDeclsForSynth) {
2599+
unsigned int ctorParamCount = selectedCtorDecl->getNumParams();
2600+
selectedCtorDeclCounter++;
2601+
2602+
std::string funcName = "__returns_" + cxxRecordDecl->getNameAsString();
2603+
if (ctorParamCount > 0)
2604+
funcName += "_" + std::to_string(ctorParamCount) + "_params";
2605+
funcName += "_" + std::to_string(selectedCtorDeclCounter);
2606+
clang::IdentifierInfo *funcNameToSynth = &clangIdents.get(funcName);
2607+
2608+
auto ctorFunctionProtoTy =
2609+
selectedCtorDecl->getType()->getAs<clang::FunctionProtoType>();
2610+
clang::ArrayRef<clang::QualType> paramTypes =
2611+
ctorFunctionProtoTy->getParamTypes();
2612+
clang::FunctionProtoType::ExtProtoInfo EPI;
2613+
clang::QualType funcTypeToSynth =
2614+
clangCtx.getFunctionType(cxxRecordPtrTy, paramTypes, EPI);
2615+
2616+
clang::CXXMethodDecl *synthCxxMethodDecl = clang::CXXMethodDecl::Create(
2617+
clangCtx, const_cast<clang::CXXRecordDecl *>(cxxRecordDecl),
2618+
cxxRecordDeclLoc,
2619+
clang::DeclarationNameInfo(funcNameToSynth, cxxRecordDeclLoc),
2620+
funcTypeToSynth, clangCtx.getTrivialTypeSourceInfo(funcTypeToSynth),
2621+
clang::SC_Static, /*UsesFPIntrin=*/false, /*isInline=*/true,
2622+
clang::ConstexprSpecKind::Unspecified, cxxRecordDeclLoc);
2623+
assert(
2624+
synthCxxMethodDecl &&
2625+
"Unable to synthesize static factory for c++ foreign reference type");
2626+
synthCxxMethodDecl->setAccess(clang::AccessSpecifier::AS_public);
2627+
2628+
llvm::SmallVector<clang::ParmVarDecl *, 4> synthParams;
2629+
for (unsigned int i = 0; i < ctorParamCount; ++i) {
2630+
auto *origParam = selectedCtorDecl->getParamDecl(i);
2631+
clang::IdentifierInfo *paramIdent = origParam->getIdentifier();
2632+
if (!paramIdent) {
2633+
std::string dummyName = "__unnamed_param_" + std::to_string(i);
2634+
paramIdent = &clangIdents.get(dummyName);
2635+
}
2636+
auto *param = clang::ParmVarDecl::Create(
2637+
clangCtx, synthCxxMethodDecl, cxxRecordDeclLoc, cxxRecordDeclLoc,
2638+
paramIdent, origParam->getType(),
2639+
clangCtx.getTrivialTypeSourceInfo(origParam->getType()),
2640+
clang::SC_None, /*DefArg=*/nullptr);
2641+
param->setIsUsed();
2642+
synthParams.push_back(param);
2643+
}
2644+
synthCxxMethodDecl->setParams(synthParams);
2645+
2646+
if (!hasImmortalAttrs(cxxRecordDecl)) {
2647+
synthCxxMethodDecl->addAttr(
2648+
clang::SwiftAttrAttr::Create(clangCtx, "returns_retained"));
2649+
}
2650+
2651+
std::string swiftInitStr = "init(";
2652+
for (unsigned i = 0; i < ctorParamCount; ++i) {
2653+
auto paramType = selectedCtorDecl->getParamDecl(i)->getType();
2654+
if (paramType->isRValueReferenceType()) {
2655+
swiftInitStr += "consuming:";
2656+
} else {
2657+
swiftInitStr += "_:";
2658+
}
2659+
}
2660+
swiftInitStr += ")";
2661+
synthCxxMethodDecl->addAttr(
2662+
clang::SwiftNameAttr::Create(clangCtx, swiftInitStr));
2663+
2664+
llvm::SmallVector<clang::Expr *, 4> ctorArgs;
2665+
for (auto *param : synthParams) {
2666+
clang::QualType paramTy = param->getType();
2667+
clang::QualType exprTy = paramTy.getNonReferenceType();
2668+
clang::Expr *argExpr = clang::DeclRefExpr::Create(
2669+
clangCtx, clang::NestedNameSpecifierLoc(), cxxRecordDeclLoc, param,
2670+
/*RefersToEnclosingVariableOrCapture=*/false, cxxRecordDeclLoc,
2671+
exprTy, clang::VK_LValue);
2672+
if (paramTy->isRValueReferenceType()) {
2673+
argExpr = clangSema
2674+
.BuildCXXNamedCast(
2675+
cxxRecordDeclLoc, clang::tok::kw_static_cast,
2676+
clangCtx.getTrivialTypeSourceInfo(paramTy), argExpr,
2677+
clang::SourceRange(), clang::SourceRange())
2678+
.get();
2679+
}
2680+
ctorArgs.push_back(argExpr);
2681+
}
2682+
llvm::SmallVector<clang::Expr *, 4> ctorArgsToAdd;
2683+
2684+
if (clangSema.CompleteConstructorCall(selectedCtorDecl, cxxRecordTy,
2685+
ctorArgs, cxxRecordDeclLoc,
2686+
ctorArgsToAdd))
2687+
continue;
2688+
2689+
clang::ExprResult synthCtorExprResult = clangSema.BuildCXXConstructExpr(
2690+
cxxRecordDeclLoc, cxxRecordTy, selectedCtorDecl,
2691+
/*Elidable=*/false, ctorArgsToAdd,
2692+
/*HadMultipleCandidates=*/false,
2693+
/*IsListInitialization=*/false,
2694+
/*IsStdInitListInitialization=*/false,
2695+
/*RequiresZeroInit=*/false, clang::CXXConstructionKind::Complete,
2696+
clang::SourceRange(cxxRecordDeclLoc, cxxRecordDeclLoc));
2697+
assert(!synthCtorExprResult.isInvalid() &&
2698+
"Unable to synthesize constructor expression for c++ foreign "
2699+
"reference type");
2700+
clang::Expr *synthCtorExpr = synthCtorExprResult.get();
2701+
2702+
clang::ExprResult synthNewExprResult = clangSema.BuildCXXNew(
2703+
clang::SourceRange(), /*UseGlobal=*/false, clang::SourceLocation(), {},
2704+
clang::SourceLocation(), clang::SourceRange(), cxxRecordTy,
2705+
clangCtx.getTrivialTypeSourceInfo(cxxRecordTy), std::nullopt,
2706+
clang::SourceRange(cxxRecordDeclLoc, cxxRecordDeclLoc), synthCtorExpr);
2707+
assert(
2708+
!synthNewExprResult.isInvalid() &&
2709+
"Unable to synthesize `new` expression for c++ foreign reference type");
2710+
auto *synthNewExpr = cast<clang::CXXNewExpr>(synthNewExprResult.get());
2711+
2712+
clang::ReturnStmt *synthRetStmt = clang::ReturnStmt::Create(
2713+
clangCtx, cxxRecordDeclLoc, synthNewExpr, /*NRVOCandidate=*/nullptr);
2714+
assert(synthRetStmt && "Unable to synthesize return statement for "
2715+
"static factory of c++ foreign reference type");
2716+
2717+
clang::CompoundStmt *synthFuncBody = clang::CompoundStmt::Create(
2718+
clangCtx, {synthRetStmt}, clang::FPOptionsOverride(), cxxRecordDeclLoc,
2719+
cxxRecordDeclLoc);
2720+
assert(synthRetStmt && "Unable to synthesize function body for static "
2721+
"factory of c++ foreign reference type");
2722+
2723+
synthCxxMethodDecl->setBody(synthFuncBody);
2724+
synthCxxMethodDecl->addAttr(clang::NoDebugAttr::CreateImplicit(clangCtx));
2725+
2726+
synthCxxMethodDecl->setImplicit();
2727+
synthCxxMethodDecl->setImplicitlyInline();
2728+
2729+
synthesizedFactories.push_back(synthCxxMethodDecl);
26082730
}
26092731

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;
2732+
return synthesizedFactories;
26612733
}

lib/ClangImporter/SwiftDeclSynthesizer.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,8 @@ class SwiftDeclSynthesizer {
337337
/// Synthesize a static factory method for a C++ foreign reference type,
338338
/// returning a `CXXMethodDecl*` or `nullptr` if the required constructor or
339339
/// allocation function is not found.
340-
clang::CXXMethodDecl *synthesizeStaticFactoryForCXXForeignRef(
340+
llvm::SmallVector<clang::CXXMethodDecl *, 4>
341+
synthesizeStaticFactoryForCXXForeignRef(
341342
const clang::CXXRecordDecl *cxxRecordDecl);
342343

343344
private:

0 commit comments

Comments
 (0)