Skip to content

Commit 6c9b82a

Browse files
committed
[WIP] [cxx-interop] Import default/trivial ctors of C++ foreign ref types as Swift Initializer
1 parent f6c9177 commit 6c9b82a

File tree

5 files changed

+142
-3
lines changed

5 files changed

+142
-3
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(GenerateCxxRefTypeInit, 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(GenerateCxxRefTypeInit)
395396
UNINTERESTING_FEATURE(CoroutineAccessorsUnwindOnCallerError)
396397

397398
static bool usesFeatureSwiftSettings(const Decl *decl) {

lib/ClangImporter/ImportDecl.cpp

Lines changed: 115 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2041,6 +2041,105 @@ namespace {
20412041
fd->getAttrs().add(new (Impl.SwiftContext) UnsafeAttr(/*Implicit=*/true));
20422042
}
20432043

2044+
void generateStaticFactoryForCXXForeignRef(
2045+
clang::ASTContext &clangCtx, ClassDecl *result,
2046+
clang::CXXRecordDecl *cxxRecordDecl, clang::Sema &clangSema) {
2047+
2048+
clang::IdentifierTable &idents = clangCtx.Idents;
2049+
clang::IdentifierInfo *generatedFuncName = &idents.get(
2050+
("__returns_" + cxxRecordDecl->getNameAsString()).c_str());
2051+
2052+
// TODO/DOUBT: How to add `_Nonnull` as a qualifier on cxxRecordPtrTy
2053+
// return type ??
2054+
clang::QualType cxxRecordTy = clangCtx.getRecordType(cxxRecordDecl);
2055+
clang::QualType cxxRecordPtrTy = clangCtx.getPointerType(cxxRecordTy);
2056+
clang::FunctionProtoType::ExtProtoInfo EPI;
2057+
clang::QualType generatedFuncTy =
2058+
clangCtx.getFunctionType(cxxRecordPtrTy, {}, EPI);
2059+
2060+
// TODO: Use operatorNew directly instead of calling BuildCXXNew
2061+
clang::FunctionDecl *operatorNew = nullptr;
2062+
clang::FunctionDecl *operatorDelete = nullptr;
2063+
bool passAlignment = false;
2064+
bool allocationFound = clangSema.FindAllocationFunctions(
2065+
cxxRecordDecl->getLocation(), clang::SourceRange(),
2066+
clang::Sema::AFS_Both, clang::Sema::AFS_Both, cxxRecordTy,
2067+
/*IsArray*/ false, passAlignment, clang::MultiExprArg(), operatorNew,
2068+
operatorDelete);
2069+
2070+
llvm::errs() << "allocationFound: " << allocationFound << "\n";
2071+
if (!operatorNew || operatorNew->isDeleted() ||
2072+
operatorNew->getAccess() == clang::AS_private ||
2073+
operatorNew->getAccess() == clang::AS_protected) {
2074+
return;
2075+
}
2076+
2077+
clang::CXXConstructorDecl *defaultCtorDecl = nullptr;
2078+
for (clang::CXXConstructorDecl *ctor : cxxRecordDecl->ctors()) {
2079+
if (ctor->isDefaultConstructor() && !ctor->isDeleted()) {
2080+
defaultCtorDecl = ctor;
2081+
break;
2082+
}
2083+
}
2084+
2085+
if (!defaultCtorDecl)
2086+
return;
2087+
2088+
clang::CXXMethodDecl *generatedCxxMethodDecl =
2089+
clang::CXXMethodDecl::Create(
2090+
clangCtx, cxxRecordDecl, cxxRecordDecl->getLocation(),
2091+
clang::DeclarationNameInfo(generatedFuncName,
2092+
cxxRecordDecl->getLocation()),
2093+
generatedFuncTy, nullptr, clang::SC_Static,
2094+
/*UsesFPIntrin*/ false, /*isInline=*/true,
2095+
clang::ConstexprSpecKind::Unspecified,
2096+
cxxRecordDecl->getLocation());
2097+
2098+
generatedCxxMethodDecl->setAccess(clang::AccessSpecifier::AS_public);
2099+
2100+
clang::SwiftAttrAttr *generatedReturnsRetainedAttr =
2101+
clang::SwiftAttrAttr::Create(clangCtx, "returns_retained");
2102+
generatedCxxMethodDecl->addAttr(generatedReturnsRetainedAttr);
2103+
2104+
clang::SwiftNameAttr *generatedSwiftNameInitAttr =
2105+
clang::SwiftNameAttr::Create(clangCtx, "init()");
2106+
generatedCxxMethodDecl->addAttr(generatedSwiftNameInitAttr);
2107+
2108+
clang::ExprResult generatedConstructExpr =
2109+
clangSema.BuildCXXConstructExpr(
2110+
clang::SourceLocation(), cxxRecordTy, defaultCtorDecl,
2111+
/*Elidable*/ false, clang::MultiExprArg(),
2112+
/*HadMultipleCandidates,*/ false,
2113+
/*IsListInitialization*/ false,
2114+
/*IsStdInitListInitialization*/ false,
2115+
/*RequiresZeroInit*/ true, // TODO: fix the propagation of this
2116+
// zeroinit flag to BuildCXXNew
2117+
clang::CXXConstructionKind::Complete, clang::SourceRange());
2118+
2119+
if (generatedConstructExpr.isInvalid())
2120+
return;
2121+
clang::ExprResult generatedNewExprResult = clangSema.BuildCXXNew(
2122+
clang::SourceRange(), /*UseGlobal*/ false, clang::SourceLocation(),
2123+
{}, clang::SourceLocation(), clang::SourceRange(), cxxRecordTy,
2124+
clangCtx.getTrivialTypeSourceInfo(cxxRecordTy), std::nullopt,
2125+
clang::SourceRange(), generatedConstructExpr.get());
2126+
2127+
if (generatedNewExprResult.isInvalid())
2128+
return;
2129+
clang::CXXNewExpr *generatedNewExpr =
2130+
cast<clang::CXXNewExpr>(generatedNewExprResult.get());
2131+
clang::ReturnStmt *generatedRetStmt = clang::ReturnStmt::Create(
2132+
clangCtx, clang::SourceLocation(), generatedNewExpr, nullptr);
2133+
clang::CompoundStmt *generatedFuncBody = clang::CompoundStmt::Create(
2134+
clangCtx, {generatedRetStmt}, clang::FPOptionsOverride(),
2135+
clang::SourceLocation(), clang::SourceLocation());
2136+
generatedCxxMethodDecl->setBody(generatedFuncBody);
2137+
generatedCxxMethodDecl->addAttr(
2138+
clang::NoDebugAttr::CreateImplicit(clangCtx));
2139+
if (Decl *importedInitDecl = VisitCXXMethodDecl(generatedCxxMethodDecl))
2140+
result->addMember(importedInitDecl);
2141+
}
2142+
20442143
Decl *VisitRecordDecl(const clang::RecordDecl *decl) {
20452144
// Track whether this record contains fields we can't reference in Swift
20462145
// as stored properties.
@@ -2472,14 +2571,28 @@ namespace {
24722571
ctors.push_back(valueCtor);
24732572
}
24742573

2475-
// Do not allow Swift to construct foreign reference types (at least, not
2476-
// yet).
24772574
if (isa<StructDecl>(result)) {
24782575
for (auto ctor : ctors) {
24792576
// Add ctors directly as they cannot always be looked up from the
24802577
// clang decl (some are synthesized by Swift).
24812578
result->addMember(ctor);
24822579
}
2580+
} else {
2581+
// TODO: Make the feature flag GenerateCxxRefTypeInit actually work
2582+
if (result->getASTContext().LangOpts.hasFeature(
2583+
Feature::GenerateCxxRefTypeInit)) {
2584+
assert(isa<ClassDecl>(result) && "Expected result to be a ClassDecl "
2585+
"for C/C++ foreign reference types");
2586+
if (auto *cxxRecordDecl = const_cast<clang::CXXRecordDecl *>(
2587+
dyn_cast<clang::CXXRecordDecl>(decl))) {
2588+
generateStaticFactoryForCXXForeignRef(
2589+
cxxRecordDecl->getASTContext(), cast<ClassDecl>(result),
2590+
cxxRecordDecl, Impl.getClangSema());
2591+
} else {
2592+
// TODO: Synthesize the static factory for C foreign reference types
2593+
// rdar://147532507
2594+
}
2595+
}
24832596
}
24842597

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

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

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

179+
namespace DefaultCtor {
180+
// TODO: Do not synthesize static factories of C++ foreign ref types if a user
181+
// provided static factory already exists with `swift_name("init")` annotation
182+
// rdar://147532991
183+
struct __attribute__((swift_attr("import_reference")))
184+
__attribute__((swift_attr("retain:retain")))
185+
__attribute__((swift_attr("release:release"))) CxxRefTy {
186+
public:
187+
int val = 2;
188+
};
189+
} // namespace DefaultCtor
190+
191+
void retain(DefaultCtor::CxxRefTy *_Nonnull v) {};
192+
void release(DefaultCtor::CxxRefTy *_Nonnull v) {};
193+
179194
#endif // TEST_INTEROP_CXX_CLASS_INPUTS_CONSTRUCTORS_H

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
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,10 @@ CxxConstructorTestSuite.test("ImportStaticFactoryAsInitializer") {
8888
expectEqual(v.getX(), 2)
8989
}
9090

91+
ForeignRefCtorSuite.test("SynthesizeAndImportStaticFactoryAsInitializer") {
92+
let x = DefaultCtor.CxxRefTy()
93+
// TODO: make this non optional
94+
expectEqual(x!.val, 2)
95+
}
96+
9197
runAllTests()

0 commit comments

Comments
 (0)