Skip to content

🍒 [6.2] [cxx-interop] Import public constructor of C++ foreign reference types as Swift initializers #80715

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ EXPERIMENTAL_FEATURE(ImportNonPublicCxxMembers, true)

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

// Isolated deinit
SUPPRESSIBLE_LANGUAGE_FEATURE(IsolatedDeinit, 371, "isolated deinit")
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/FeatureSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ UNINTERESTING_FEATURE(StrictMemorySafety)
UNINTERESTING_FEATURE(SafeInteropWrappers)
UNINTERESTING_FEATURE(AssumeResilientCxxTypes)
UNINTERESTING_FEATURE(ImportNonPublicCxxMembers)
UNINTERESTING_FEATURE(CXXForeignReferenceTypeInitializers)
UNINTERESTING_FEATURE(SuppressCXXForeignReferenceTypeInitializers)
UNINTERESTING_FEATURE(CoroutineAccessorsUnwindOnCallerError)
UNINTERESTING_FEATURE(AllowRuntimeSymbolDeclarations)

Expand Down
26 changes: 19 additions & 7 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2497,8 +2497,8 @@ namespace {
result->addMember(ctor);
}
} else {
if (Impl.SwiftContext.LangOpts.hasFeature(
Feature::CXXForeignReferenceTypeInitializers)) {
if (!Impl.SwiftContext.LangOpts.hasFeature(
Feature::SuppressCXXForeignReferenceTypeInitializers)) {
assert(
isa<ClassDecl>(result) &&
"Expected result to be a ClassDecl as it cannot be a StructDecl");
Expand All @@ -2516,12 +2516,13 @@ namespace {
});
});
if (!hasUserProvidedStaticFactory) {
if (auto generatedCxxMethodDecl =
synthesizer.synthesizeStaticFactoryForCXXForeignRef(
cxxRecordDecl)) {
auto generatedCxxMethodDecls =
synthesizer.synthesizeStaticFactoryForCXXForeignRef(
cxxRecordDecl);
for (auto *methodDecl : generatedCxxMethodDecls) {
if (Decl *importedInitDecl =
Impl.SwiftContext.getClangModuleLoader()
->importDeclDirectly(generatedCxxMethodDecl))
->importDeclDirectly(methodDecl))
result->addMember(importedInitDecl);
}
}
Expand Down Expand Up @@ -3552,7 +3553,18 @@ namespace {
isa<clang::FunctionDecl>(decl)
? cast<clang::FunctionDecl>(decl)->getReturnType()
: cast<clang::ObjCMethodDecl>(decl)->getReturnType();
if (isForeignReferenceTypeWithoutImmortalAttrs(retType)) {
clang::QualType pointeeType = retType;
if (retType->isPointerType() || retType->isReferenceType()) {
pointeeType = retType->getPointeeType();
}

clang::RecordDecl *recordDecl = nullptr;
if (const auto *recordType = pointeeType->getAs<clang::RecordType>()) {
recordDecl = recordType->getDecl();
}

if (recordDecl && recordHasReferenceSemantics(recordDecl) &&
!hasImmortalAttrs(recordDecl)) {
if (returnsRetainedAttrIsPresent && returnsUnretainedAttrIsPresent) {
Impl.diagnose(loc, diag::both_returns_retained_returns_unretained,
decl);
Expand Down
264 changes: 168 additions & 96 deletions lib/ClangImporter/SwiftDeclSynthesizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2534,128 +2534,200 @@ SwiftDeclSynthesizer::makeDefaultArgument(const clang::ParmVarDecl *param,

// MARK: C++ foreign reference type constructors

clang::CXXMethodDecl *
llvm::SmallVector<clang::CXXMethodDecl *, 4>
SwiftDeclSynthesizer::synthesizeStaticFactoryForCXXForeignRef(
const clang::CXXRecordDecl *cxxRecordDecl) {

if (cxxRecordDecl->isAbstract())
return {};

clang::ASTContext &clangCtx = cxxRecordDecl->getASTContext();
clang::Sema &clangSema = ImporterImpl.getClangSema();

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

clang::CXXConstructorDecl *defaultCtorDecl = nullptr;
for (clang::CXXConstructorDecl *ctor : cxxRecordDecl->ctors()) {
if (ctor->parameters().empty() && !ctor->isDeleted() &&
ctor->getAccess() != clang::AS_private &&
ctor->getAccess() != clang::AS_protected) {
defaultCtorDecl = ctor;
break;
}
llvm::SmallVector<clang::CXXConstructorDecl *, 4> ctorDeclsForSynth;
for (clang::CXXConstructorDecl *ctorDecl : cxxRecordDecl->ctors()) {
if (ctorDecl->isDeleted() || ctorDecl->getAccess() == clang::AS_private ||
ctorDecl->getAccess() == clang::AS_protected ||
ctorDecl->isCopyOrMoveConstructor() || ctorDecl->isVariadic())
continue;

bool hasDefaultArg = !ctorDecl->parameters().empty() &&
ctorDecl->parameters().back()->hasDefaultArg();
// TODO: Add support for default args in ctors for C++ foreign reference
// types.
if (hasDefaultArg)
continue;
ctorDeclsForSynth.push_back(ctorDecl);
}
if (!defaultCtorDecl)
return nullptr;

if (ctorDeclsForSynth.empty())
return {};

clang::FunctionDecl *operatorNew = nullptr;
clang::FunctionDecl *operatorDelete = nullptr;
bool passAlignment = false;
clang::Sema::SFINAETrap trap(clangSema);
bool findingAllocFuncFailed = clangSema.FindAllocationFunctions(
cxxRecordDecl->getLocation(), clang::SourceRange(), clang::Sema::AFS_Both,
clang::Sema::AFS_Both, cxxRecordTy,
/*IsArray*/ false, passAlignment, clang::MultiExprArg(), operatorNew,
operatorDelete, /*Diagnose*/ false);
if (findingAllocFuncFailed || !operatorNew || operatorNew->isDeleted() ||
cxxRecordDeclLoc, clang::SourceRange(), clang::Sema::AFS_Both,
clang::Sema::AFS_Both, cxxRecordTy, /*IsArray=*/false, passAlignment,
clang::MultiExprArg(), operatorNew, operatorDelete,
/*Diagnose=*/false);
if (trap.hasErrorOccurred() || findingAllocFuncFailed || !operatorNew ||
operatorNew->isDeleted() ||
operatorNew->getAccess() == clang::AS_private ||
operatorNew->getAccess() == clang::AS_protected)
return nullptr;
return {};

clang::QualType cxxRecordPtrTy = clangCtx.getPointerType(cxxRecordTy);
// Adding `_Nonnull` to the return type of synthesized static factory
bool nullabilityCannotBeAdded =
clangSema.CheckImplicitNullabilityTypeSpecifier(
cxxRecordPtrTy, clang::NullabilityKind::NonNull,
cxxRecordDecl->getLocation(),
/*isParam=*/false,
/*OverrideExisting=*/true);
cxxRecordPtrTy, clang::NullabilityKind::NonNull, cxxRecordDeclLoc,
/*isParam=*/false, /*OverrideExisting=*/true);
assert(!nullabilityCannotBeAdded &&
"Failed to add _Nonnull specifier to synthesized "
"static factory's return type");

clang::IdentifierTable &clangIdents = clangCtx.Idents;
clang::IdentifierInfo *funcNameToSynthesize = &clangIdents.get(
("__returns_" + cxxRecordDecl->getNameAsString()).c_str());
clang::FunctionProtoType::ExtProtoInfo EPI;
clang::QualType funcTypeToSynthesize =
clangCtx.getFunctionType(cxxRecordPtrTy, {}, EPI);

clang::CXXMethodDecl *synthesizedCxxMethodDecl = clang::CXXMethodDecl::Create(
clangCtx, const_cast<clang::CXXRecordDecl *>(cxxRecordDecl),
cxxRecordDecl->getLocation(),
clang::DeclarationNameInfo(funcNameToSynthesize,
cxxRecordDecl->getLocation()),
funcTypeToSynthesize,
clangCtx.getTrivialTypeSourceInfo(funcTypeToSynthesize), clang::SC_Static,
/*UsesFPIntrin=*/false, /*isInline=*/true,
clang::ConstexprSpecKind::Unspecified, cxxRecordDecl->getLocation());
assert(synthesizedCxxMethodDecl &&
"Unable to synthesize static factory for c++ foreign reference type");
synthesizedCxxMethodDecl->setAccess(clang::AccessSpecifier::AS_public);

if (!hasImmortalAttrs(cxxRecordDecl)) {
clang::SwiftAttrAttr *returnsRetainedAttrForSynthesizedCxxMethodDecl =
clang::SwiftAttrAttr::Create(clangCtx, "returns_retained");
synthesizedCxxMethodDecl->addAttr(
returnsRetainedAttrForSynthesizedCxxMethodDecl);

llvm::SmallVector<clang::CXXMethodDecl *, 4> synthesizedFactories;
unsigned int selectedCtorDeclCounter = 0;
for (clang::CXXConstructorDecl *selectedCtorDecl : ctorDeclsForSynth) {
unsigned int ctorParamCount = selectedCtorDecl->getNumParams();
selectedCtorDeclCounter++;

std::string funcName = "__returns_" + cxxRecordDecl->getNameAsString();
if (ctorParamCount > 0)
funcName += "_" + std::to_string(ctorParamCount) + "_params";
funcName += "_" + std::to_string(selectedCtorDeclCounter);
clang::IdentifierInfo *funcNameToSynth = &clangIdents.get(funcName);

auto ctorFunctionProtoTy =
selectedCtorDecl->getType()->getAs<clang::FunctionProtoType>();
clang::ArrayRef<clang::QualType> paramTypes =
ctorFunctionProtoTy->getParamTypes();
clang::FunctionProtoType::ExtProtoInfo EPI;
clang::QualType funcTypeToSynth =
clangCtx.getFunctionType(cxxRecordPtrTy, paramTypes, EPI);

clang::CXXMethodDecl *synthCxxMethodDecl = clang::CXXMethodDecl::Create(
clangCtx, const_cast<clang::CXXRecordDecl *>(cxxRecordDecl),
cxxRecordDeclLoc,
clang::DeclarationNameInfo(funcNameToSynth, cxxRecordDeclLoc),
funcTypeToSynth, clangCtx.getTrivialTypeSourceInfo(funcTypeToSynth),
clang::SC_Static, /*UsesFPIntrin=*/false, /*isInline=*/true,
clang::ConstexprSpecKind::Unspecified, cxxRecordDeclLoc);
assert(
synthCxxMethodDecl &&
"Unable to synthesize static factory for c++ foreign reference type");
synthCxxMethodDecl->setAccess(clang::AccessSpecifier::AS_public);

llvm::SmallVector<clang::ParmVarDecl *, 4> synthParams;
for (unsigned int i = 0; i < ctorParamCount; ++i) {
auto *origParam = selectedCtorDecl->getParamDecl(i);
clang::IdentifierInfo *paramIdent = origParam->getIdentifier();
if (!paramIdent) {
std::string dummyName = "__unnamed_param_" + std::to_string(i);
paramIdent = &clangIdents.get(dummyName);
}
auto *param = clang::ParmVarDecl::Create(
clangCtx, synthCxxMethodDecl, cxxRecordDeclLoc, cxxRecordDeclLoc,
paramIdent, origParam->getType(),
clangCtx.getTrivialTypeSourceInfo(origParam->getType()),
clang::SC_None, /*DefArg=*/nullptr);
param->setIsUsed();
synthParams.push_back(param);
}
synthCxxMethodDecl->setParams(synthParams);

if (!hasImmortalAttrs(cxxRecordDecl)) {
synthCxxMethodDecl->addAttr(
clang::SwiftAttrAttr::Create(clangCtx, "returns_retained"));
}

std::string swiftInitStr = "init(";
for (unsigned i = 0; i < ctorParamCount; ++i) {
auto paramType = selectedCtorDecl->getParamDecl(i)->getType();
if (paramType->isRValueReferenceType()) {
swiftInitStr += "consuming:";
} else {
swiftInitStr += "_:";
}
}
swiftInitStr += ")";
synthCxxMethodDecl->addAttr(
clang::SwiftNameAttr::Create(clangCtx, swiftInitStr));

llvm::SmallVector<clang::Expr *, 4> ctorArgs;
for (auto *param : synthParams) {
clang::QualType paramTy = param->getType();
clang::QualType exprTy = paramTy.getNonReferenceType();
clang::Expr *argExpr = clang::DeclRefExpr::Create(
clangCtx, clang::NestedNameSpecifierLoc(), cxxRecordDeclLoc, param,
/*RefersToEnclosingVariableOrCapture=*/false, cxxRecordDeclLoc,
exprTy, clang::VK_LValue);
if (paramTy->isRValueReferenceType()) {
argExpr = clangSema
.BuildCXXNamedCast(
cxxRecordDeclLoc, clang::tok::kw_static_cast,
clangCtx.getTrivialTypeSourceInfo(paramTy), argExpr,
clang::SourceRange(), clang::SourceRange())
.get();
}
ctorArgs.push_back(argExpr);
}
llvm::SmallVector<clang::Expr *, 4> ctorArgsToAdd;

if (clangSema.CompleteConstructorCall(selectedCtorDecl, cxxRecordTy,
ctorArgs, cxxRecordDeclLoc,
ctorArgsToAdd))
continue;

clang::ExprResult synthCtorExprResult = clangSema.BuildCXXConstructExpr(
cxxRecordDeclLoc, cxxRecordTy, selectedCtorDecl,
/*Elidable=*/false, ctorArgsToAdd,
/*HadMultipleCandidates=*/false,
/*IsListInitialization=*/false,
/*IsStdInitListInitialization=*/false,
/*RequiresZeroInit=*/false, clang::CXXConstructionKind::Complete,
clang::SourceRange(cxxRecordDeclLoc, cxxRecordDeclLoc));
assert(!synthCtorExprResult.isInvalid() &&
"Unable to synthesize constructor expression for c++ foreign "
"reference type");
clang::Expr *synthCtorExpr = synthCtorExprResult.get();

clang::ExprResult synthNewExprResult = clangSema.BuildCXXNew(
clang::SourceRange(), /*UseGlobal=*/false, clang::SourceLocation(), {},
clang::SourceLocation(), clang::SourceRange(), cxxRecordTy,
clangCtx.getTrivialTypeSourceInfo(cxxRecordTy), std::nullopt,
clang::SourceRange(cxxRecordDeclLoc, cxxRecordDeclLoc), synthCtorExpr);
assert(
!synthNewExprResult.isInvalid() &&
"Unable to synthesize `new` expression for c++ foreign reference type");
auto *synthNewExpr = cast<clang::CXXNewExpr>(synthNewExprResult.get());

clang::ReturnStmt *synthRetStmt = clang::ReturnStmt::Create(
clangCtx, cxxRecordDeclLoc, synthNewExpr, /*NRVOCandidate=*/nullptr);
assert(synthRetStmt && "Unable to synthesize return statement for "
"static factory of c++ foreign reference type");

clang::CompoundStmt *synthFuncBody = clang::CompoundStmt::Create(
clangCtx, {synthRetStmt}, clang::FPOptionsOverride(), cxxRecordDeclLoc,
cxxRecordDeclLoc);
assert(synthRetStmt && "Unable to synthesize function body for static "
"factory of c++ foreign reference type");

synthCxxMethodDecl->setBody(synthFuncBody);
synthCxxMethodDecl->addAttr(clang::NoDebugAttr::CreateImplicit(clangCtx));

synthCxxMethodDecl->setImplicit();
synthCxxMethodDecl->setImplicitlyInline();

synthesizedFactories.push_back(synthCxxMethodDecl);
}

clang::SwiftNameAttr *swiftNameInitAttrForSynthesizedCxxMethodDecl =
clang::SwiftNameAttr::Create(clangCtx, "init()");
synthesizedCxxMethodDecl->addAttr(
swiftNameInitAttrForSynthesizedCxxMethodDecl);

clang::ExprResult synthesizedConstructExprResult =
clangSema.BuildCXXConstructExpr(
clang::SourceLocation(), cxxRecordTy, defaultCtorDecl,
/*Elidable=*/false, clang::MultiExprArg(),
/*HadMultipleCandidates=*/false,
/*IsListInitialization=*/false,
/*IsStdInitListInitialization=*/false,
/*RequiresZeroInit=*/false, clang::CXXConstructionKind::Complete,
clang::SourceRange());
assert(!synthesizedConstructExprResult.isInvalid() &&
"Unable to synthesize constructor expression for c++ foreign "
"reference type");
clang::CXXConstructExpr *synthesizedConstructExpr =
cast<clang::CXXConstructExpr>(synthesizedConstructExprResult.get());

clang::ExprResult synthesizedNewExprResult = clangSema.BuildCXXNew(
clang::SourceRange(), /*UseGlobal=*/false, clang::SourceLocation(), {},
clang::SourceLocation(), clang::SourceRange(), cxxRecordTy,
clangCtx.getTrivialTypeSourceInfo(cxxRecordTy), std::nullopt,
clang::SourceRange(), synthesizedConstructExpr);
assert(
!synthesizedNewExprResult.isInvalid() &&
"Unable to synthesize `new` expression for c++ foreign reference type");
clang::CXXNewExpr *synthesizedNewExpr =
cast<clang::CXXNewExpr>(synthesizedNewExprResult.get());

clang::ReturnStmt *synthesizedRetStmt =
clang::ReturnStmt::Create(clangCtx, clang::SourceLocation(),
synthesizedNewExpr, /*VarDecl=*/nullptr);
assert(synthesizedRetStmt && "Unable to synthesize return statement for "
"static factory of c++ foreign reference type");

clang::CompoundStmt *synthesizedFuncBody = clang::CompoundStmt::Create(
clangCtx, {synthesizedRetStmt}, clang::FPOptionsOverride(),
clang::SourceLocation(), clang::SourceLocation());
assert(synthesizedRetStmt && "Unable to synthesize function body for static "
"factory of c++ foreign reference type");

synthesizedCxxMethodDecl->setBody(synthesizedFuncBody);
synthesizedCxxMethodDecl->addAttr(
clang::NoDebugAttr::CreateImplicit(clangCtx));

synthesizedCxxMethodDecl->setImplicit();
synthesizedCxxMethodDecl->setImplicitlyInline();

return synthesizedCxxMethodDecl;
return synthesizedFactories;
}
3 changes: 2 additions & 1 deletion lib/ClangImporter/SwiftDeclSynthesizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,8 @@ class SwiftDeclSynthesizer {
/// Synthesize a static factory method for a C++ foreign reference type,
/// returning a `CXXMethodDecl*` or `nullptr` if the required constructor or
/// allocation function is not found.
clang::CXXMethodDecl *synthesizeStaticFactoryForCXXForeignRef(
llvm::SmallVector<clang::CXXMethodDecl *, 4>
synthesizeStaticFactoryForCXXForeignRef(
const clang::CXXRecordDecl *cxxRecordDecl);

private:
Expand Down
Loading