Skip to content

Commit 3f507ef

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

File tree

7 files changed

+198
-4
lines changed

7 files changed

+198
-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: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,14 +2472,48 @@ 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(isa<ClassDecl>(result) && "Expected result to be a ClassDecl "
2485+
"for C/C++ foreign reference types");
2486+
// When we add full support for C foreign reference types then we
2487+
// should synthesize static factories for them as well
2488+
if (auto *cxxRecordDecl = const_cast<clang::CXXRecordDecl *>(
2489+
dyn_cast<clang::CXXRecordDecl>(decl))) {
2490+
bool hasUserProvidedStaticFactory = false;
2491+
for (const auto *method : cxxRecordDecl->methods()) {
2492+
if (method->isStatic()) {
2493+
for (const auto *attr :
2494+
method->specific_attrs<clang::SwiftNameAttr>()) {
2495+
std::string attributeName = attr->getName().str();
2496+
if (attributeName.find("init") != std::string::npos) {
2497+
hasUserProvidedStaticFactory = true;
2498+
break;
2499+
}
2500+
}
2501+
}
2502+
if (hasUserProvidedStaticFactory)
2503+
break;
2504+
}
2505+
if (!hasUserProvidedStaticFactory) {
2506+
if (auto generatedCxxMethodDecl = SwiftDeclSynthesizer::
2507+
synthesizeStaticFactoryForCXXForeignRef(
2508+
cxxRecordDecl, cxxRecordDecl->getASTContext(),
2509+
Impl.getClangSema())) {
2510+
if (Decl *importedInitDecl =
2511+
VisitCXXMethodDecl(generatedCxxMethodDecl))
2512+
result->addMember(importedInitDecl);
2513+
}
2514+
}
2515+
}
2516+
}
24832517
}
24842518

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

lib/ClangImporter/SwiftDeclSynthesizer.cpp

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2529,3 +2529,131 @@ 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+
clang::CXXRecordDecl *cxxRecordDecl, clang::ASTContext &clangCtx,
2538+
clang::Sema &clangSema) {
2539+
2540+
clang::QualType cxxRecordTy = clangCtx.getRecordType(cxxRecordDecl);
2541+
2542+
clang::FunctionDecl *operatorNew = nullptr;
2543+
clang::FunctionDecl *operatorDelete = nullptr;
2544+
bool passAlignment = false;
2545+
clangSema.FindAllocationFunctions(
2546+
cxxRecordDecl->getLocation(), clang::SourceRange(), clang::Sema::AFS_Both,
2547+
clang::Sema::AFS_Both, cxxRecordTy,
2548+
/*IsArray*/ false, passAlignment, clang::MultiExprArg(), operatorNew,
2549+
operatorDelete);
2550+
if (!operatorNew || operatorNew->isDeleted() ||
2551+
operatorNew->getAccess() == clang::AS_private ||
2552+
operatorNew->getAccess() == clang::AS_protected)
2553+
return nullptr;
2554+
2555+
clang::CXXConstructorDecl *defaultCtorDecl = nullptr;
2556+
for (clang::CXXConstructorDecl *ctor : cxxRecordDecl->ctors()) {
2557+
if (ctor->isDefaultConstructor() && !ctor->isDeleted() &&
2558+
ctor->getAccess() != clang::AS_private &&
2559+
ctor->getAccess() != clang::AS_protected) {
2560+
bool ctorHasDefaultArgs = false;
2561+
for (const clang::ParmVarDecl *ctorParam : ctor->parameters()) {
2562+
if (ctorParam->hasDefaultArg()) {
2563+
ctorHasDefaultArgs = true;
2564+
}
2565+
}
2566+
if (!ctorHasDefaultArgs) {
2567+
defaultCtorDecl = ctor;
2568+
break;
2569+
}
2570+
}
2571+
}
2572+
if (!defaultCtorDecl)
2573+
return nullptr;
2574+
2575+
clang::QualType cxxRecordPtrTy = clangCtx.getPointerType(cxxRecordTy);
2576+
// Adding `_Nonnull` to the return type of synthesized static factory
2577+
clangSema.CheckImplicitNullabilityTypeSpecifier(
2578+
cxxRecordPtrTy, clang::NullabilityKind::NonNull,
2579+
cxxRecordDecl->getLocation(),
2580+
/*isParam=*/false,
2581+
/*OverrideExisting=*/true);
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, cxxRecordDecl, cxxRecordDecl->getLocation(),
2592+
clang::DeclarationNameInfo(funcNameToSynthesize,
2593+
cxxRecordDecl->getLocation()),
2594+
funcTypeToSynthesize, clangCtx.getTrivialTypeSourceInfo(cxxRecordTy),
2595+
clang::SC_Static,
2596+
/*UsesFPIntrin=*/false, /*isInline=*/true,
2597+
clang::ConstexprSpecKind::Unspecified, cxxRecordDecl->getLocation());
2598+
assert(synthesizedCxxMethodDecl &&
2599+
"Unable to synthesize static factory for c++ foreign reference type");
2600+
synthesizedCxxMethodDecl->setAccess(clang::AccessSpecifier::AS_public);
2601+
2602+
clang::SwiftAttrAttr *returnsRetainedAttrForSynthesizedCxxMethodDecl =
2603+
clang::SwiftAttrAttr::Create(clangCtx, "returns_retained");
2604+
synthesizedCxxMethodDecl->addAttr(
2605+
returnsRetainedAttrForSynthesizedCxxMethodDecl);
2606+
clang::SwiftNameAttr *swiftNameInitAttrForSynthesizedCxxMethodDecl =
2607+
clang::SwiftNameAttr::Create(clangCtx, "init()");
2608+
synthesizedCxxMethodDecl->addAttr(
2609+
swiftNameInitAttrForSynthesizedCxxMethodDecl);
2610+
2611+
clang::ExprResult synthesizedConstructExprResult =
2612+
clangSema.BuildCXXConstructExpr(
2613+
clang::SourceLocation(), cxxRecordTy, defaultCtorDecl,
2614+
/*Elidable=*/false, clang::MultiExprArg(),
2615+
/*HadMultipleCandidates=*/false,
2616+
/*IsListInitialization=*/false,
2617+
/*IsStdInitListInitialization=*/false,
2618+
/*RequiresZeroInit=*/false, clang::CXXConstructionKind::Complete,
2619+
clang::SourceRange());
2620+
assert(!synthesizedConstructExprResult.isInvalid() &&
2621+
"Unable to synthesize constructor expression for c++ foreign "
2622+
"reference type");
2623+
clang::CXXConstructExpr *synthesizedConstructExpr =
2624+
cast<clang::CXXConstructExpr>(synthesizedConstructExprResult.get());
2625+
2626+
clang::ExprResult synthesizedNewExprResult = clangSema.BuildCXXNew(
2627+
clang::SourceRange(), /*UseGlobal=*/false, clang::SourceLocation(), {},
2628+
clang::SourceLocation(), clang::SourceRange(), cxxRecordTy,
2629+
clangCtx.getTrivialTypeSourceInfo(cxxRecordTy), std::nullopt,
2630+
clang::SourceRange(), synthesizedConstructExpr);
2631+
assert(
2632+
!synthesizedNewExprResult.isInvalid() &&
2633+
"Unable to synthesize `new` expression for c++ foreign reference type");
2634+
clang::CXXNewExpr *synthesizedNewExpr =
2635+
cast<clang::CXXNewExpr>(synthesizedNewExprResult.get());
2636+
2637+
clang::ReturnStmt *synthesizedRetStmt =
2638+
clang::ReturnStmt::Create(clangCtx, clang::SourceLocation(),
2639+
synthesizedNewExpr, /*VarDecl=*/nullptr);
2640+
assert(synthesizedRetStmt && "Unable to synthesize return statement for "
2641+
"static factory of c++ foreign reference type");
2642+
2643+
clang::CompoundStmt *synthesizedFuncBody = clang::CompoundStmt::Create(
2644+
clangCtx, {synthesizedRetStmt}, clang::FPOptionsOverride(),
2645+
clang::SourceLocation(), clang::SourceLocation());
2646+
assert(synthesizedRetStmt && "Unable to synthesize function body for static "
2647+
"factory of c++ foreign reference type");
2648+
2649+
synthesizedCxxMethodDecl->setBody(synthesizedFuncBody);
2650+
synthesizedCxxMethodDecl->addAttr(
2651+
clang::NoDebugAttr::CreateImplicit(clangCtx));
2652+
2653+
// Marking the synthesized factory as implicit and implicitly inline
2654+
synthesizedCxxMethodDecl->setImplicit();
2655+
synthesizedCxxMethodDecl->addAttr(
2656+
clang::AlwaysInlineAttr::CreateImplicit(clangCtx));
2657+
2658+
return synthesizedCxxMethodDecl;
2659+
}

lib/ClangImporter/SwiftDeclSynthesizer.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,14 @@ 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 *
341+
synthesizeStaticFactoryForCXXForeignRef(clang::CXXRecordDecl *cxxRecordDecl,
342+
clang::ASTContext &clangCtx,
343+
clang::Sema &clangSema);
344+
337345
private:
338346
Type getConstantLiteralType(Type type, ConstantConvertKind convertKind);
339347
};

test/Interop/Cxx/class/Inputs/constructors.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,4 +176,16 @@ class Value {
176176
int x;
177177
};
178178

179+
namespace SynthesizeDefaultCtorFactoryForCxxRefTy {
180+
struct __attribute__((swift_attr("import_reference")))
181+
__attribute__((swift_attr("retain:Retain")))
182+
__attribute__((swift_attr("release:Release"))) CxxRefTy {
183+
public:
184+
int val = 2;
185+
};
186+
} // namespace SynthesizeDefaultCtorFactoryForCxxRefTy
187+
188+
void Retain(SynthesizeDefaultCtorFactoryForCxxRefTy::CxxRefTy *_Nonnull v) {};
189+
void Release(SynthesizeDefaultCtorFactoryForCxxRefTy::CxxRefTy *_Nonnull v) {};
190+
179191
#endif // TEST_INTEROP_CXX_CLASS_INPUTS_CONSTRUCTORS_H

test/Interop/Cxx/class/constructors-executable.swift

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-experimental-cxx-interop -Xfrontend -disable-availability-checking)
1+
// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -cxx-interoperability-mode=default -enable-experimental-feature CXXForeignReferenceTypeInitializers -Xfrontend -disable-availability-checking)
22
//
33
// REQUIRES: executable_test
44

5-
import StdlibUnittest
65
import Constructors
6+
import StdlibUnittest
77

88
var CxxConstructorTestSuite = TestSuite("CxxConstructors")
99

@@ -88,4 +88,11 @@ CxxConstructorTestSuite.test("ImportStaticFactoryAsInitializer") {
8888
expectEqual(v.getX(), 2)
8989
}
9090

91+
CxxConstructorTestSuite.test("SynthesizeAndImportStaticFactoryAsInitializer") {
92+
let x = SynthesizeDefaultCtorFactoryForCxxRefTy.CxxRefTy()
93+
expectEqual(x.val, 2)
94+
x.val = 3
95+
expectEqual(x.val, 3)
96+
}
97+
9198
runAllTests()

0 commit comments

Comments
 (0)