Skip to content

Commit c225a0a

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

File tree

3 files changed

+128
-2
lines changed

3 files changed

+128
-2
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2486,14 +2486,114 @@ namespace {
24862486
ctors.push_back(valueCtor);
24872487
}
24882488

2489-
// Do not allow Swift to construct foreign reference types (at least, not
2490-
// yet).
24912489
if (isa<StructDecl>(result)) {
2490+
// For c++ value types, import all the constructors
24922491
for (auto ctor : ctors) {
24932492
// Add ctors directly as they cannot always be looked up from the
24942493
// clang decl (some are synthesized by Swift).
24952494
result->addMember(ctor);
24962495
}
2496+
} else if (isa<ClassDecl>(result)) {
2497+
// For C++ foreign reference types, generate a static factory method to
2498+
// construct the object and import it as a Swift initializer.
2499+
2500+
// TODO: better/safer way to do this casting
2501+
clang::CXXRecordDecl *cxxRecordDecl =
2502+
(clang::CXXRecordDecl *)cast<clang::CXXRecordDecl>(decl);
2503+
2504+
ASTContext &ctx = result->getASTContext();
2505+
clang::ASTContext &clangCtx = cxxRecordDecl->getASTContext();
2506+
2507+
// Creating a name for the new synthesized function
2508+
clang::IdentifierTable &idents = clangCtx.Idents;
2509+
clang::IdentifierInfo *generatedFuncName =
2510+
&idents.get(("returns" + cxxRecordDecl->getNameAsString()).c_str());
2511+
2512+
// Creating the return type for the synthesized function to be the
2513+
// pointe to cxxRecordDecl
2514+
clang::QualType cxxRecordTy = clangCtx.getRecordType(cxxRecordDecl);
2515+
clang::QualType cxxRecordPtrTy = clangCtx.getPointerType(cxxRecordTy);
2516+
// TODO/DOUBT: How to add `_Nonnull` as a qualifier on cxxRecordPtrTy
2517+
// return type ??
2518+
2519+
// Creating the type for the synthesized function
2520+
clang::FunctionProtoType::ExtProtoInfo EPI;
2521+
clang::QualType generatedFuncTy =
2522+
clangCtx.getFunctionType(cxxRecordPtrTy, {}, EPI);
2523+
2524+
clang::CXXMethodDecl *generatedCxxMethodDecl =
2525+
clang::CXXMethodDecl::Create(
2526+
clangCtx, cxxRecordDecl, clang::SourceLocation(),
2527+
clang::DeclarationNameInfo(generatedFuncName,
2528+
clang::SourceLocation()),
2529+
generatedFuncTy, nullptr, clang::SC_Static, false, true,
2530+
clang::ConstexprSpecKind::Unspecified, clang::SourceLocation());
2531+
2532+
// Create and attach "returns_retained" attribute to synthesized method
2533+
clang::SwiftAttrAttr *returnsRetainedAttr =
2534+
clang::SwiftAttrAttr::Create(clangCtx, "returns_retained");
2535+
generatedCxxMethodDecl->addAttr(returnsRetainedAttr);
2536+
generatedCxxMethodDecl->setAccess(clang::AccessSpecifier::AS_public);
2537+
2538+
// Getting the default constructor
2539+
clang::CXXConstructorDecl *ctorDecl = nullptr;
2540+
for (clang::CXXConstructorDecl *ctor : cxxRecordDecl->ctors()) {
2541+
if (ctor->isDefaultConstructor() && !ctor->isDeleted()) {
2542+
ctorDecl = ctor;
2543+
break;
2544+
}
2545+
}
2546+
2547+
if (ctorDecl) {
2548+
// Build the constructor expression using the default constructor decl
2549+
clang::ExprResult generatedConstructExpr =
2550+
Impl.getClangSema().BuildCXXConstructExpr(
2551+
clang::SourceLocation(), cxxRecordTy, ctorDecl, false,
2552+
clang::MultiExprArg(), false, false, false,
2553+
true, // TODO: fix the propagation of this zeroinit flag to
2554+
// BuildCXXNew
2555+
clang::CXXConstructionKind::Complete, clang::SourceRange());
2556+
2557+
if (!generatedConstructExpr.isInvalid()) {
2558+
// Build the new expr by passing the constructor expression as an
2559+
// initializer
2560+
clang::ExprResult generatedNewExprResult =
2561+
Impl.getClangSema().BuildCXXNew(
2562+
clang::SourceRange(), false, clang::SourceLocation(), {},
2563+
clang::SourceLocation(), clang::SourceRange(), cxxRecordTy,
2564+
clangCtx.getTrivialTypeSourceInfo(cxxRecordTy),
2565+
std::nullopt, clang::SourceRange(),
2566+
generatedConstructExpr.get());
2567+
2568+
if (!generatedNewExprResult.isInvalid()) {
2569+
// synthesizing the body of the generatedCxxMethodDecl
2570+
clang::CXXNewExpr *generatedNewExpr =
2571+
cast<clang::CXXNewExpr>(generatedNewExprResult.get());
2572+
clang::ReturnStmt *generatedRetStmt = clang::ReturnStmt::Create(
2573+
clangCtx, clang::SourceLocation(), generatedNewExpr, nullptr);
2574+
clang::CompoundStmt *generatedFuncBody =
2575+
clang::CompoundStmt::Create(
2576+
clangCtx, {generatedRetStmt}, clang::FPOptionsOverride(),
2577+
clang::SourceLocation(), clang::SourceLocation());
2578+
generatedCxxMethodDecl->setBody(generatedFuncBody);
2579+
2580+
// Getting the appropriate decl context
2581+
DeclBaseName initBaseName = DeclBaseName::createConstructor();
2582+
DeclName importedInitName(ctx, initBaseName,
2583+
llvm::ArrayRef<Identifier>{});
2584+
EffectiveClangContext effectiveContext(
2585+
generatedCxxMethodDecl->getDeclContext()->getRedeclContext());
2586+
auto dc = Impl.importDeclContextOf(decl, effectiveContext);
2587+
2588+
// Importing the synthesised method as Swift initializer and
2589+
// adding it to result class
2590+
Decl *importedInitDecl = importGlobalAsInitializer(
2591+
generatedCxxMethodDecl, importedInitName, dc,
2592+
CtorInitializerKind::Designated, std::nullopt);
2593+
result->addMember(importedInitDecl);
2594+
}
2595+
}
2596+
}
24972597
}
24982598

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

test/Interop/Cxx/foreign-reference/Inputs/constructor.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,23 @@ inline void release(ImportWithCtor * _Nonnull x) {
4040
if (!--x->value)
4141
delete x;
4242
}
43+
44+
namespace DefaultCtor {
45+
struct
46+
__attribute__((swift_attr("import_reference")))
47+
__attribute__((swift_attr("retain:retain")))
48+
__attribute__((swift_attr("release:release")))
49+
CxxRefTy{
50+
public:
51+
int val = 2;
52+
// TODO: resolve the conflict b/w swift_name("init()") and synthesized function
53+
// __attribute__((swift_name("init()")))
54+
// __attribute__((swift_attr("returns_retained")))
55+
// static CxxRefTy * _Nonnull returnsCxxRefTy() {
56+
// return new CxxRefTy{};
57+
// }
58+
};
59+
}
60+
61+
void retain(DefaultCtor::CxxRefTy * _Nonnull v) {};
62+
void release(DefaultCtor::CxxRefTy * _Nonnull v) {};

test/Interop/Cxx/foreign-reference/constructor-execution.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,10 @@ ForeignRefCtorSuite.test("ImportStaticFactoryAsInitializer") {
2222
expectEqual(z3.param2, 3)
2323
}
2424

25+
ForeignRefCtorSuite.test("SynthesizeAndImportStaticFactoryAsInitializer") {
26+
let x = DefaultCtor.CxxRefTy()
27+
// TODO: make this non optional
28+
expectEqual(x!.val, 2)
29+
}
30+
2531
runAllTests()

0 commit comments

Comments
 (0)