Skip to content

Add support for calling C++ constructors #30630

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 33 commits into from
Oct 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
a5e953b
Add support for calling C++ constructors.
martinboehme Feb 26, 2020
7ad2eef
Only import constructors marked `noexcept`.
martinboehme Mar 26, 2020
3848548
Revert "Only import constructors marked `noexcept`."
martinboehme Apr 7, 2020
fd00bc1
Move tests from `CXXInterop` to `Interop/Cxx`.
martinboehme May 13, 2020
e606727
Duplicate changes to GenClangType in ClangTypeConverter.
martinboehme May 14, 2020
b2c5a3e
Add a constructor thunk if required to add additional constructor
martinboehme May 11, 2020
3066e16
Remove redundant "cxx" from test names.
martinboehme May 15, 2020
5644137
Eliminate duplication of code for adding empty argument names.
martinboehme May 15, 2020
bed2603
Various changes after merging master:
martinboehme May 20, 2020
beaaa74
Don't put an `sret` attribute on the `this` argument.
martinboehme May 25, 2020
8416ccf
Rename constructors-ir.swift to constructors-irgen.swift.
martinboehme May 25, 2020
8f6042a
Add additional IR tests for a class without a virtual base class.
martinboehme May 25, 2020
c9405fb
Add Windows-name-mangled version of symbol to SIL test.
martinboehme May 26, 2020
cb4ddda
Avoid crashing if lldb imports C++ structs without enabling C++ interop.
martinboehme Jun 2, 2020
33e8c71
Update comment in VisitCXXRecordDecl().
martinboehme Jun 8, 2020
7e8ea12
Respond to review comments.
martinboehme Jun 10, 2020
1ce3753
Another response to review comments.
martinboehme Jun 10, 2020
faca489
Add a test that Objective-C types passed to a C++ constructor are
martinboehme Jun 10, 2020
83b51b9
Add SILGen and IRGen tests for passing Objective-C types to C++
martinboehme Jun 10, 2020
cc7564e
Rename constructors-sil.swift to constructors-silgen.swift.
martinboehme Jun 10, 2020
2713edb
[cxx-interop] Fix missing APIs and tests after rebase.
zoecarver Sep 21, 2020
7399c20
[cxx-interop] Revert changes to ClangTypeConverter::visitMetatypeType.
zoecarver Sep 24, 2020
9e2d03e
[cxx-interop] Skip void types in emitCXXConstructorThunkIfNeeded.
zoecarver Sep 29, 2020
7635443
[cxx-interop] Update ABIArgInfo::Indirect case in expandExternalSigna…
zoecarver Oct 3, 2020
f04de9f
[cxx-interop] Skip metatypes when lowering C++ constructor SIL functi…
zoecarver Oct 4, 2020
4364af3
[cxx-interop] Small fixes and cleanup based on review.
zoecarver Oct 4, 2020
5774610
[cxx-interop] Fix patch formatting with clang-format.
zoecarver Oct 4, 2020
4cb337a
[cxx-interop] Generate memberwise initializers for non-C++ types.
zoecarver Oct 10, 2020
20222bb
[cxx-interop] Bail if trying to convert call result to void.
zoecarver Oct 10, 2020
b2bb47d
[cxx-interop] Fix Windows tests for non-trivial C++ types.
zoecarver Oct 10, 2020
1d3b051
[cxx-interop] Remove logic around applying attributes.
zoecarver Oct 13, 2020
d9acaf3
[cxx-interop] Merge synthesized initializer tests into "constructors-…
zoecarver Oct 15, 2020
545b44e
[cxx-interop] Look through template decl to find constructor when imp…
zoecarver Oct 16, 2020
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
8 changes: 7 additions & 1 deletion lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3589,7 +3589,13 @@ void ClangImporter::getMangledName(raw_ostream &os,
if (!Impl.Mangler)
Impl.Mangler.reset(Impl.getClangASTContext().createMangleContext());

Impl.Mangler->mangleName(clangDecl, os);
if (auto ctor = dyn_cast<clang::CXXConstructorDecl>(clangDecl)) {
auto ctorGlobalDecl =
clang::GlobalDecl(ctor, clang::CXXCtorType::Ctor_Complete);
Impl.Mangler->mangleCXXName(ctorGlobalDecl, os);
} else {
Impl.Mangler->mangleName(clangDecl, os);
}
}

// ---------------------------------------------------------------------------
Expand Down
111 changes: 87 additions & 24 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1241,6 +1241,10 @@ synthesizeStructDefaultConstructorBody(AbstractFunctionDecl *afd,
ASTContext &ctx = constructor->getASTContext();
auto structDecl = static_cast<StructDecl *>(context);

// We should call into C++ constructors directly.
assert(!isa<clang::CXXRecordDecl>(structDecl->getClangDecl()) &&
"Should not synthesize a C++ object constructor.");

// Use a builtin to produce a zero initializer, and assign it to self.

// Construct the left-hand reference to self.
Expand Down Expand Up @@ -3378,6 +3382,11 @@ namespace {
continue;
}

if (auto CD = dyn_cast<ConstructorDecl>(member)) {
ctors.push_back(CD);
continue;
}

if (auto MD = dyn_cast<FuncDecl>(member)) {
methods.push_back(MD);
continue;
Expand Down Expand Up @@ -3434,12 +3443,17 @@ namespace {

bool hasReferenceableFields = !members.empty();

if (hasZeroInitializableStorage) {
// Add constructors for the struct.
const clang::CXXRecordDecl *cxxRecordDecl =
dyn_cast<clang::CXXRecordDecl>(decl);
if (hasZeroInitializableStorage && !cxxRecordDecl) {
// Add default constructor for the struct if compiling in C mode.
// If we're compiling for C++, we'll import the C++ default constructor
// (if there is one), so we don't need to synthesize one here.
ctors.push_back(createDefaultConstructor(Impl, result));
}

if (hasReferenceableFields && hasMemberwiseInitializer) {
bool isAggregate = !cxxRecordDecl || cxxRecordDecl->isAggregate();
if (hasReferenceableFields && hasMemberwiseInitializer && isAggregate) {
// The default zero initializer suppresses the implicit value
// constructor that would normally be formed, so we have to add that
// explicitly as well.
Expand Down Expand Up @@ -3470,7 +3484,7 @@ namespace {

result->setHasUnreferenceableStorage(hasUnreferenceableStorage);

if (auto cxxRecordDecl = dyn_cast<clang::CXXRecordDecl>(decl)) {
if (cxxRecordDecl) {
result->setIsCxxNonTrivial(!cxxRecordDecl->isTriviallyCopyable());

for (auto ctor : cxxRecordDecl->ctors()) {
Expand All @@ -3491,6 +3505,34 @@ namespace {
return result;
}

Decl *VisitCXXRecordDecl(const clang::CXXRecordDecl *decl) {
// This can be called from lldb without C++ interop being enabled: There
// may be C++ declarations in imported modules, but the interface for
// those modules may be a pure C or Objective-C interface.
// To avoid crashing in Clang's Sema, fall back to importing this as a
// plain RecordDecl.
if (!Impl.SwiftContext.LangOpts.EnableCXXInterop)
return VisitRecordDecl(decl);

auto &clangSema = Impl.getClangSema();
// Make Clang define the implicit default constructor if the class needs
// it. Make sure we only do this if the class has been fully defined and
// we're not in a dependent context (this is equivalent to the logic in
// CanDeclareSpecialMemberFunction in Clang's SemaLookup.cpp).
if (decl->getDefinition() && !decl->isBeingDefined() &&
!decl->isDependentContext() &&
decl->needsImplicitDefaultConstructor()) {
clang::CXXConstructorDecl *ctor =
clangSema.DeclareImplicitDefaultConstructor(
const_cast<clang::CXXRecordDecl *>(decl));
if (!ctor->isDeleted())
clangSema.DefineImplicitDefaultConstructor(clang::SourceLocation(),
ctor);
}

return VisitRecordDecl(decl);
}

Decl *VisitClassTemplateSpecializationDecl(
const clang::ClassTemplateSpecializationDecl *decl) {
// `Sema::isCompleteType` will try to instantiate the class template as a
Expand All @@ -3514,7 +3556,7 @@ namespace {
Impl.getClangSema().InstantiateClassTemplateSpecializationMembers(
def->getLocation(), def, clang::TSK_ExplicitInstantiationDefinition);

return VisitRecordDecl(def);
return VisitCXXRecordDecl(def);
}

Decl *VisitClassTemplatePartialSpecializationDecl(
Expand Down Expand Up @@ -3745,6 +3787,9 @@ namespace {
ImportedName importedName,
Optional<ImportedName> correctSwiftName,
Optional<AccessorInfo> accessorInfo) {
if (decl->isDeleted())
return nullptr;

auto dc =
Impl.importDeclContextOf(decl, importedName.getEffectiveContext());
if (!dc)
Expand All @@ -3753,7 +3798,6 @@ namespace {
DeclName name = accessorInfo ? DeclName() : importedName.getDeclName();
auto selfIdx = importedName.getSelfIndex();

FuncDecl *result = nullptr;
ImportedType importedType;
bool selfIsInOut = false;
ParameterList *bodyParams = nullptr;
Expand Down Expand Up @@ -3855,27 +3899,48 @@ namespace {
if (!importedType)
return nullptr;

auto resultTy = importedType.getType();
auto loc = Impl.importSourceLoc(decl->getLocation());

// FIXME: Poor location info.
auto nameLoc = Impl.importSourceLoc(decl->getLocation());
result = createFuncOrAccessor(
Impl.SwiftContext, loc, accessorInfo, name,
nameLoc, bodyParams, resultTy,
/*async*/ false, /*throws*/ false, dc, decl);

if (!dc->isModuleScopeContext()) {
if (selfIsInOut)
result->setSelfAccessKind(SelfAccessKind::Mutating);
else
result->setSelfAccessKind(SelfAccessKind::NonMutating);
if (selfIdx) {
result->setSelfIndex(selfIdx.getValue());
} else {
result->setStatic();
result->setImportAsStaticMember();

AbstractFunctionDecl *result = nullptr;
if (auto *ctordecl = dyn_cast<clang::CXXConstructorDecl>(decl)) {
// Don't import copy constructor or move constructor -- these will be
// provided through the value witness table.
if (ctordecl->isCopyConstructor() || ctordecl->isMoveConstructor())
return nullptr;

DeclName ctorName(Impl.SwiftContext, DeclBaseName::createConstructor(),
bodyParams);
result = Impl.createDeclWithClangNode<ConstructorDecl>(
decl, AccessLevel::Public, ctorName, loc, /*failable=*/false,
/*FailabilityLoc=*/SourceLoc(), /*Throws=*/false,
/*ThrowsLoc=*/SourceLoc(), bodyParams, /*GenericParams=*/nullptr,
dc);
} else {
auto resultTy = importedType.getType();

FuncDecl *func =
createFuncOrAccessor(Impl.SwiftContext, loc, accessorInfo, name,
nameLoc, bodyParams, resultTy,
/*async=*/false, /*throws=*/false, dc, decl);
result = func;

if (!dc->isModuleScopeContext()) {
if (selfIsInOut)
func->setSelfAccessKind(SelfAccessKind::Mutating);
else
func->setSelfAccessKind(SelfAccessKind::NonMutating);
if (selfIdx) {
func->setSelfIndex(selfIdx.getValue());
} else {
func->setStatic();
func->setImportAsStaticMember();
}
}
// Someday, maybe this will need to be 'open' for C++ virtual methods.
func->setAccess(AccessLevel::Public);
}

result->setIsObjC(false);
Expand All @@ -3889,8 +3954,6 @@ namespace {
result->getAttrs().add(new (Impl.SwiftContext)
FinalAttr(/*IsImplicit=*/true));

// Someday, maybe this will need to be 'open' for C++ virtual methods.
result->setAccess(AccessLevel::Public);
finishFuncDecl(decl, result);

// If this is a compatibility stub, mark it as such.
Expand Down
32 changes: 23 additions & 9 deletions lib/ClangImporter/ImportName.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1366,6 +1366,15 @@ static bool suppressFactoryMethodAsInit(const clang::ObjCMethodDecl *method,
initKind == CtorInitializerKind::ConvenienceFactory);
}

static void
addEmptyArgNamesForClangFunction(const clang::FunctionDecl *funcDecl,
SmallVectorImpl<StringRef> &argumentNames) {
for (size_t i = 0; i < funcDecl->param_size(); ++i)
argumentNames.push_back(StringRef());
if (funcDecl->isVariadic())
argumentNames.push_back(StringRef());
}

ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
ImportNameVersion version,
clang::DeclarationName givenName) {
Expand Down Expand Up @@ -1599,7 +1608,19 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
SmallString<16> selectorSplitScratch;
ArrayRef<const clang::ParmVarDecl *> params;
switch (D->getDeclName().getNameKind()) {
case clang::DeclarationName::CXXConstructorName:
case clang::DeclarationName::CXXConstructorName: {
isInitializer = true;
isFunction = true;
result.info.initKind = CtorInitializerKind::Designated;
baseName = "init";
auto ctor = dyn_cast<clang::CXXConstructorDecl>(D);
if (auto templateCtor = dyn_cast<clang::FunctionTemplateDecl>(D))
ctor = cast<clang::CXXConstructorDecl>(templateCtor->getAsFunction());
assert(ctor && "Unkown decl with CXXConstructorName.");
addEmptyArgNamesForClangFunction(ctor, argumentNames);
break;
}

case clang::DeclarationName::CXXConversionFunctionName:
case clang::DeclarationName::CXXDestructorName:
case clang::DeclarationName::CXXLiteralOperatorName:
Expand Down Expand Up @@ -1670,16 +1691,9 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
}
}

// For C functions, create empty argument names.
if (auto function = dyn_cast<clang::FunctionDecl>(D)) {
isFunction = true;
params = {function->param_begin(), function->param_end()};
for (auto param : params) {
(void)param;
argumentNames.push_back(StringRef());
}
if (function->isVariadic())
argumentNames.push_back(StringRef());
addEmptyArgNamesForClangFunction(function, argumentNames);
}
break;

Expand Down
18 changes: 18 additions & 0 deletions lib/IRGen/GenCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1269,6 +1269,16 @@ void SignatureExpansion::expandExternalSignatureTypes() {
SmallVector<clang::CanQualType,4> paramTys;
auto const &clangCtx = IGM.getClangASTContext();

bool formalIndirectResult = FnType->getNumResults() > 0 &&
FnType->getSingleResult().isFormalIndirect();
if (formalIndirectResult) {
auto resultType = getSILFuncConventions().getSingleSILResultType(
IGM.getMaximalTypeExpansionContext());
auto clangTy =
IGM.getClangASTContext().getPointerType(IGM.getClangType(resultType));
paramTys.push_back(clangTy);
}

switch (FnType->getRepresentation()) {
case SILFunctionTypeRepresentation::ObjCMethod: {
// ObjC methods take their 'self' argument first, followed by an
Expand Down Expand Up @@ -1900,6 +1910,9 @@ class SyncCallEmission final : public CallEmission {
// This can happen when calling C functions, or class method dispatch thunks
// for methods that have covariant ABI-compatible overrides.
auto expectedNativeResultType = nativeSchema.getExpandedType(IGF.IGM);
// If the expected result type is void, bail.
if (expectedNativeResultType->isVoidTy())
return;
if (result->getType() != expectedNativeResultType) {
result =
IGF.coerceValue(result, expectedNativeResultType, IGF.IGM.DataLayout);
Expand Down Expand Up @@ -2797,6 +2810,11 @@ static void externalizeArguments(IRGenFunction &IGF, const Callee &callee,
== SILFunctionTypeRepresentation::Block) {
// Ignore the physical block-object parameter.
firstParam += 1;
// Or the indirect result parameter.
} else if (fnType->getNumResults() > 0 &&
fnType->getSingleResult().isFormalIndirect()) {
// Ignore the indirect result parameter.
firstParam += 1;
}

for (unsigned i = firstParam, e = FI.arg_size(); i != e; ++i) {
Expand Down
78 changes: 78 additions & 0 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/CodeGen/CodeGenABITypes.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/GlobalAlias.h"
Expand Down Expand Up @@ -2761,6 +2763,77 @@ void IRGenModule::emitDynamicReplacementOriginalFunctionThunk(SILFunction *f) {
IGF.Builder.CreateRet(Res);
}

/// If the calling convention for `ctor` doesn't match the calling convention
/// that we assumed for it when we imported it as `initializer`, emit and
/// return a thunk that conforms to the assumed calling convention. The thunk
/// is marked `alwaysinline`, so it doesn't generate any runtime overhead.
/// If the assumed calling convention was correct, just return `ctor`.
///
/// See also comments in CXXMethodConventions in SIL/IR/SILFunctionType.cpp.
static llvm::Constant *
emitCXXConstructorThunkIfNeeded(IRGenModule &IGM, SILFunction *initializer,
const clang::CXXConstructorDecl *ctor,
const LinkEntity &entity,
llvm::Constant *ctorAddress) {
Signature signature = IGM.getSignature(initializer->getLoweredFunctionType());

llvm::FunctionType *assumedFnType = signature.getType();
llvm::FunctionType *ctorFnType =
cast<llvm::FunctionType>(ctorAddress->getType()->getPointerElementType());

if (assumedFnType == ctorFnType) {
return ctorAddress;
}

// The thunk has private linkage, so it doesn't need to have a predictable
// mangled name -- we just need to make sure the name is unique.
llvm::SmallString<32> name;
llvm::raw_svector_ostream stream(name);
stream << "__swift_cxx_ctor";
entity.mangle(stream);

llvm::Function *thunk = llvm::Function::Create(
assumedFnType, llvm::Function::PrivateLinkage, name, &IGM.Module);

thunk->setCallingConv(llvm::CallingConv::C);

llvm::AttrBuilder attrBuilder;
IGM.constructInitialFnAttributes(attrBuilder);
attrBuilder.addAttribute(llvm::Attribute::AlwaysInline);
llvm::AttributeList attr = signature.getAttributes().addAttributes(
IGM.getLLVMContext(), llvm::AttributeList::FunctionIndex, attrBuilder);
thunk->setAttributes(attr);

IRGenFunction subIGF(IGM, thunk);
if (IGM.DebugInfo)
IGM.DebugInfo->emitArtificialFunction(subIGF, thunk);

SmallVector<llvm::Value *, 8> Args;
for (auto i = thunk->arg_begin(), e = thunk->arg_end(); i != e; ++i) {
auto *argTy = i->getType();
auto *paramTy = ctorFnType->getParamType(i - thunk->arg_begin());
if (paramTy != argTy)
Args.push_back(subIGF.coerceValue(i, paramTy, IGM.DataLayout));
else
Args.push_back(i);
}

clang::CodeGen::ImplicitCXXConstructorArgs implicitArgs =
clang::CodeGen::getImplicitCXXConstructorArgs(IGM.ClangCodeGen->CGM(),
ctor);
for (size_t i = 0; i < implicitArgs.Prefix.size(); ++i) {
Args.insert(Args.begin() + 1 + i, implicitArgs.Prefix[i]);
}
for (const auto &arg : implicitArgs.Suffix) {
Args.push_back(arg);
}

subIGF.Builder.CreateCall(ctorFnType, ctorAddress, Args);
subIGF.Builder.CreateRetVoid();

return thunk;
}

/// Find the entry point for a SIL function.
llvm::Function *IRGenModule::getAddrOfSILFunction(
SILFunction *f, ForDefinition_t forDefinition,
Expand Down Expand Up @@ -2789,6 +2862,11 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(
if (auto clangDecl = f->getClangDecl()) {
auto globalDecl = getClangGlobalDeclForFunction(clangDecl);
clangAddr = getAddrOfClangGlobalDecl(globalDecl, forDefinition);

if (auto ctor = dyn_cast<clang::CXXConstructorDecl>(clangDecl)) {
clangAddr =
emitCXXConstructorThunkIfNeeded(*this, f, ctor, entity, clangAddr);
}
}

bool isDefinition = f->isDefinition();
Expand Down
Loading