Skip to content

Commit 2dcf154

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

File tree

7 files changed

+162
-4
lines changed

7 files changed

+162
-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: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,14 +2472,32 @@ 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+
if (auto *cxxRecordDecl = const_cast<clang::CXXRecordDecl *>(
2487+
dyn_cast<clang::CXXRecordDecl>(decl))) {
2488+
if (auto generatedCxxMethodDecl = SwiftDeclSynthesizer::
2489+
synthesizeStaticFactoryForCXXForeignRef(
2490+
cxxRecordDecl, cxxRecordDecl->getASTContext(),
2491+
Impl.getClangSema())) {
2492+
if (Decl *importedInitDecl =
2493+
VisitCXXMethodDecl(generatedCxxMethodDecl))
2494+
result->addMember(importedInitDecl);
2495+
}
2496+
} else {
2497+
// TODO: Synthesize the static factory for C foreign reference types
2498+
// rdar://147532507
2499+
}
2500+
}
24832501
}
24842502

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

lib/ClangImporter/SwiftDeclSynthesizer.cpp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2529,3 +2529,111 @@ 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+
// TODO/DOUBT: How to add `_Nonnull` as a qualifier on cxxRecordPtrTy
2541+
// return type ??
2542+
clang::QualType cxxRecordTy = clangCtx.getRecordType(cxxRecordDecl);
2543+
clang::QualType cxxRecordPtrTy = clangCtx.getPointerType(cxxRecordTy);
2544+
2545+
clang::FunctionDecl *operatorNew = nullptr;
2546+
clang::FunctionDecl *operatorDelete = nullptr;
2547+
bool passAlignment = false;
2548+
clangSema.FindAllocationFunctions(
2549+
cxxRecordDecl->getLocation(), clang::SourceRange(), clang::Sema::AFS_Both,
2550+
clang::Sema::AFS_Both, cxxRecordTy,
2551+
/*IsArray*/ false, passAlignment, clang::MultiExprArg(), operatorNew,
2552+
operatorDelete);
2553+
if (!operatorNew || operatorNew->isDeleted() ||
2554+
operatorNew->getAccess() == clang::AS_private ||
2555+
operatorNew->getAccess() == clang::AS_protected)
2556+
return nullptr;
2557+
2558+
clang::CXXConstructorDecl *defaultCtorDecl = nullptr;
2559+
for (clang::CXXConstructorDecl *ctor : cxxRecordDecl->ctors()) {
2560+
if (ctor->isDefaultConstructor() && !ctor->isDeleted() &&
2561+
ctor->getAccess() != clang::AS_private &&
2562+
ctor->getAccess() != clang::AS_protected) {
2563+
defaultCtorDecl = ctor;
2564+
break;
2565+
}
2566+
}
2567+
if (!defaultCtorDecl)
2568+
return nullptr;
2569+
2570+
clang::IdentifierTable &clangIdents = clangCtx.Idents;
2571+
clang::IdentifierInfo *funcNameToSynthesize = &clangIdents.get(
2572+
("__returns_" + cxxRecordDecl->getNameAsString()).c_str());
2573+
clang::FunctionProtoType::ExtProtoInfo EPI;
2574+
clang::QualType funcTypeToSynthesize =
2575+
clangCtx.getFunctionType(cxxRecordPtrTy, {}, EPI);
2576+
2577+
clang::CXXMethodDecl *synthesizedCxxMethodDecl = clang::CXXMethodDecl::Create(
2578+
clangCtx, cxxRecordDecl, cxxRecordDecl->getLocation(),
2579+
clang::DeclarationNameInfo(funcNameToSynthesize,
2580+
cxxRecordDecl->getLocation()),
2581+
funcTypeToSynthesize, /*TypeSourceInfo=*/nullptr, clang::SC_Static,
2582+
/*UsesFPIntrin=*/false, /*isInline=*/true,
2583+
clang::ConstexprSpecKind::Unspecified, cxxRecordDecl->getLocation());
2584+
assert(synthesizedCxxMethodDecl &&
2585+
"Unable to synthesize static factory for c++ foreign reference type");
2586+
synthesizedCxxMethodDecl->setAccess(clang::AccessSpecifier::AS_public);
2587+
2588+
clang::SwiftAttrAttr *returnsRetainedAttrForSynthesizedCxxMethodDecl =
2589+
clang::SwiftAttrAttr::Create(clangCtx, "returns_retained");
2590+
synthesizedCxxMethodDecl->addAttr(
2591+
returnsRetainedAttrForSynthesizedCxxMethodDecl);
2592+
clang::SwiftNameAttr *swiftNameInitAttrForSynthesizedCxxMethodDecl =
2593+
clang::SwiftNameAttr::Create(clangCtx, "init()");
2594+
synthesizedCxxMethodDecl->addAttr(
2595+
swiftNameInitAttrForSynthesizedCxxMethodDecl);
2596+
2597+
clang::ExprResult synthesizedConstructExprResult =
2598+
clangSema.BuildCXXConstructExpr(
2599+
clang::SourceLocation(), cxxRecordTy, defaultCtorDecl,
2600+
/*Elidable=*/false, clang::MultiExprArg(),
2601+
/*HadMultipleCandidates=*/false,
2602+
/*IsListInitialization=*/false,
2603+
/*IsStdInitListInitialization=*/false,
2604+
/*RequiresZeroInit=*/false, clang::CXXConstructionKind::Complete,
2605+
clang::SourceRange());
2606+
assert(!synthesizedConstructExprResult.isInvalid() &&
2607+
"Unable to synthesize constructor expression for c++ foreign "
2608+
"reference type");
2609+
clang::CXXConstructExpr *synthesizedConstructExpr =
2610+
cast<clang::CXXConstructExpr>(synthesizedConstructExprResult.get());
2611+
2612+
clang::ExprResult synthesizedNewExprResult = clangSema.BuildCXXNew(
2613+
clang::SourceRange(), /*UseGlobal=*/false, clang::SourceLocation(), {},
2614+
clang::SourceLocation(), clang::SourceRange(), cxxRecordTy,
2615+
clangCtx.getTrivialTypeSourceInfo(cxxRecordTy), std::nullopt,
2616+
clang::SourceRange(), synthesizedConstructExpr);
2617+
assert(
2618+
!synthesizedNewExprResult.isInvalid() &&
2619+
"Unable to synthesize `new` expression for c++ foreign reference type");
2620+
clang::CXXNewExpr *synthesizedNewExpr =
2621+
cast<clang::CXXNewExpr>(synthesizedNewExprResult.get());
2622+
2623+
clang::ReturnStmt *synthesizedRetStmt =
2624+
clang::ReturnStmt::Create(clangCtx, clang::SourceLocation(),
2625+
synthesizedNewExpr, /*VarDecl=*/nullptr);
2626+
assert(synthesizedRetStmt && "Unable to synthesize return statement for "
2627+
"static factory of c++ foreign reference type");
2628+
2629+
clang::CompoundStmt *synthesizedFuncBody = clang::CompoundStmt::Create(
2630+
clangCtx, {synthesizedRetStmt}, clang::FPOptionsOverride(),
2631+
clang::SourceLocation(), clang::SourceLocation());
2632+
assert(synthesizedRetStmt && "Unable to synthesize function body for static "
2633+
"factory of c++ foreign reference type");
2634+
2635+
synthesizedCxxMethodDecl->setBody(synthesizedFuncBody);
2636+
synthesizedCxxMethodDecl->addAttr(
2637+
clang::NoDebugAttr::CreateImplicit(clangCtx));
2638+
return synthesizedCxxMethodDecl;
2639+
}

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)