Skip to content

Commit 74b7341

Browse files
authored
Merge pull request #32378 from zoecarver/cxx/copy-const
[cxx-interop] Use user defined copy constructor to copy C++ objects.
2 parents 7e65384 + b2b7f7b commit 74b7341

16 files changed

+392
-73
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "swift/AST/Pattern.h"
3636
#include "swift/AST/PrettyStackTrace.h"
3737
#include "swift/AST/ProtocolConformance.h"
38+
#include "swift/AST/SemanticAttrs.h"
3839
#include "swift/AST/Stmt.h"
3940
#include "swift/AST/TypeCheckRequests.h"
4041
#include "swift/AST/Types.h"
@@ -47,11 +48,10 @@
4748
#include "swift/Parse/Lexer.h"
4849
#include "swift/Parse/Parser.h"
4950
#include "swift/Strings.h"
50-
5151
#include "clang/AST/ASTContext.h"
5252
#include "clang/AST/Attr.h"
53-
#include "clang/AST/DeclObjCCommon.h"
5453
#include "clang/AST/DeclCXX.h"
54+
#include "clang/AST/DeclObjCCommon.h"
5555
#include "clang/Basic/CharInfo.h"
5656
#include "clang/Basic/Specifiers.h"
5757
#include "clang/Basic/TargetInfo.h"
@@ -3481,6 +3481,11 @@ namespace {
34813481
ctors.push_back(createDefaultConstructor(Impl, result));
34823482
}
34833483

3484+
// We can assume that it is possible to correctly construct the object by
3485+
// simply initializing its member variables to arbitrary supplied values
3486+
// only when the same is possible in C++. While we could check for that
3487+
// exactly, checking whether the C++ class is an aggregate
3488+
// (C++ [dcl.init.aggr]) has the same effect.
34843489
bool isAggregate = !cxxRecordDecl || cxxRecordDecl->isAggregate();
34853490
if (hasReferenceableFields && hasMemberwiseInitializer && isAggregate) {
34863491
// The default zero initializer suppresses the implicit value
@@ -3513,8 +3518,14 @@ namespace {
35133518
result->setIsCxxNonTrivial(!cxxRecordDecl->isTriviallyCopyable());
35143519

35153520
for (auto ctor : cxxRecordDecl->ctors()) {
3516-
if (ctor->isCopyConstructor() &&
3517-
(ctor->isDeleted() || ctor->getAccess() != clang::AS_public)) {
3521+
if (ctor->isCopyConstructor()) {
3522+
// If we have no way of copying the type we can't import the class
3523+
// at all because we cannot express the correct semantics as a swift
3524+
// struct.
3525+
if (ctor->isDeleted() || ctor->getAccess() != clang::AS_public)
3526+
return nullptr;
3527+
}
3528+
if (ctor->getAccess() != clang::AS_public) {
35183529
result->setIsCxxNonTrivial(true);
35193530
break;
35203531
}
@@ -3540,19 +3551,45 @@ namespace {
35403551
return VisitRecordDecl(decl);
35413552

35423553
auto &clangSema = Impl.getClangSema();
3543-
// Make Clang define the implicit default constructor if the class needs
3544-
// it. Make sure we only do this if the class has been fully defined and
3545-
// we're not in a dependent context (this is equivalent to the logic in
3546-
// CanDeclareSpecialMemberFunction in Clang's SemaLookup.cpp).
3554+
// Make Clang define any implicit constructors it may need (copy,
3555+
// default). Make sure we only do this if the class has been fully defined
3556+
// and we're not in a dependent context (this is equivalent to the logic
3557+
// in CanDeclareSpecialMemberFunction in Clang's SemaLookup.cpp).
35473558
if (decl->getDefinition() && !decl->isBeingDefined() &&
3548-
!decl->isDependentContext() &&
3549-
decl->needsImplicitDefaultConstructor()) {
3550-
clang::CXXConstructorDecl *ctor =
3551-
clangSema.DeclareImplicitDefaultConstructor(
3552-
const_cast<clang::CXXRecordDecl *>(decl->getDefinition()));
3553-
if (!ctor->isDeleted())
3554-
clangSema.DefineImplicitDefaultConstructor(clang::SourceLocation(),
3555-
ctor);
3559+
!decl->isDependentContext()) {
3560+
if (decl->needsImplicitDefaultConstructor()) {
3561+
clang::CXXConstructorDecl *ctor =
3562+
clangSema.DeclareImplicitDefaultConstructor(
3563+
const_cast<clang::CXXRecordDecl *>(decl->getDefinition()));
3564+
if (!ctor->isDeleted())
3565+
clangSema.DefineImplicitDefaultConstructor(clang::SourceLocation(),
3566+
ctor);
3567+
}
3568+
clang::CXXConstructorDecl *copyCtor = nullptr;
3569+
if (decl->needsImplicitCopyConstructor()) {
3570+
copyCtor = clangSema.DeclareImplicitCopyConstructor(
3571+
const_cast<clang::CXXRecordDecl *>(decl));
3572+
} else {
3573+
// We may have a defaulted copy constructor that needs to be defined.
3574+
// Try to find it.
3575+
for (auto methods : decl->methods()) {
3576+
if (auto declCtor = dyn_cast<clang::CXXConstructorDecl>(methods)) {
3577+
if (declCtor->isCopyConstructor() && declCtor->isDefaulted() &&
3578+
declCtor->getAccess() == clang::AS_public &&
3579+
!declCtor->isDeleted() &&
3580+
// Note: we use "doesThisDeclarationHaveABody" here because
3581+
// that's what "DefineImplicitCopyConstructor" checks.
3582+
!declCtor->doesThisDeclarationHaveABody()) {
3583+
copyCtor = declCtor;
3584+
break;
3585+
}
3586+
}
3587+
}
3588+
}
3589+
if (copyCtor) {
3590+
clangSema.DefineImplicitCopyConstructor(clang::SourceLocation(),
3591+
copyCtor);
3592+
}
35563593
}
35573594

35583595
return VisitRecordDecl(decl);

lib/IRGen/GenDecl.cpp

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2793,20 +2793,10 @@ void IRGenModule::emitDynamicReplacementOriginalFunctionThunk(SILFunction *f) {
27932793
IGF.Builder.CreateRet(Res);
27942794
}
27952795

2796-
/// If the calling convention for `ctor` doesn't match the calling convention
2797-
/// that we assumed for it when we imported it as `initializer`, emit and
2798-
/// return a thunk that conforms to the assumed calling convention. The thunk
2799-
/// is marked `alwaysinline`, so it doesn't generate any runtime overhead.
2800-
/// If the assumed calling convention was correct, just return `ctor`.
2801-
///
2802-
/// See also comments in CXXMethodConventions in SIL/IR/SILFunctionType.cpp.
2803-
static llvm::Constant *
2804-
emitCXXConstructorThunkIfNeeded(IRGenModule &IGM, SILFunction *initializer,
2805-
const clang::CXXConstructorDecl *ctor,
2806-
const LinkEntity &entity,
2807-
llvm::Constant *ctorAddress) {
2808-
Signature signature = IGM.getSignature(initializer->getLoweredFunctionType());
2809-
2796+
llvm::Constant *swift::irgen::emitCXXConstructorThunkIfNeeded(
2797+
IRGenModule &IGM, Signature signature,
2798+
const clang::CXXConstructorDecl *ctor, StringRef name,
2799+
llvm::Constant *ctorAddress) {
28102800
llvm::FunctionType *assumedFnType = signature.getType();
28112801
llvm::FunctionType *ctorFnType =
28122802
cast<llvm::FunctionType>(ctorAddress->getType()->getPointerElementType());
@@ -2815,13 +2805,6 @@ emitCXXConstructorThunkIfNeeded(IRGenModule &IGM, SILFunction *initializer,
28152805
return ctorAddress;
28162806
}
28172807

2818-
// The thunk has private linkage, so it doesn't need to have a predictable
2819-
// mangled name -- we just need to make sure the name is unique.
2820-
llvm::SmallString<32> name;
2821-
llvm::raw_svector_ostream stream(name);
2822-
stream << "__swift_cxx_ctor";
2823-
entity.mangle(stream);
2824-
28252808
llvm::Function *thunk = llvm::Function::Create(
28262809
assumedFnType, llvm::Function::PrivateLinkage, name, &IGM.Module);
28272810

@@ -2897,11 +2880,20 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(
28972880
} else {
28982881
auto globalDecl = getClangGlobalDeclForFunction(clangDecl);
28992882
clangAddr = getAddrOfClangGlobalDecl(globalDecl, forDefinition);
2883+
}
29002884

2901-
if (auto ctor = dyn_cast<clang::CXXConstructorDecl>(clangDecl)) {
2902-
clangAddr =
2903-
emitCXXConstructorThunkIfNeeded(*this, f, ctor, entity, clangAddr);
2904-
}
2885+
if (auto ctor = dyn_cast<clang::CXXConstructorDecl>(clangDecl)) {
2886+
Signature signature = getSignature(f->getLoweredFunctionType());
2887+
2888+
// The thunk has private linkage, so it doesn't need to have a predictable
2889+
// mangled name -- we just need to make sure the name is unique.
2890+
llvm::SmallString<32> name;
2891+
llvm::raw_svector_ostream stream(name);
2892+
stream << "__swift_cxx_ctor";
2893+
entity.mangle(stream);
2894+
2895+
clangAddr = emitCXXConstructorThunkIfNeeded(*this, signature, ctor, name,
2896+
clangAddr);
29052897
}
29062898
}
29072899

lib/IRGen/GenDecl.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@
1717
#ifndef SWIFT_IRGEN_GENDECL_H
1818
#define SWIFT_IRGEN_GENDECL_H
1919

20+
#include "DebugTypeInfo.h"
21+
#include "IRGen.h"
2022
#include "swift/Basic/OptimizationMode.h"
2123
#include "swift/SIL/SILLocation.h"
24+
#include "clang/AST/DeclCXX.h"
2225
#include "llvm/IR/CallingConv.h"
2326
#include "llvm/Support/CommandLine.h"
24-
#include "DebugTypeInfo.h"
25-
#include "IRGen.h"
2627

2728
namespace llvm {
2829
class AttributeList;
@@ -57,6 +58,18 @@ namespace irgen {
5758
createLinkerDirectiveVariable(IRGenModule &IGM, StringRef Name);
5859

5960
void disableAddressSanitizer(IRGenModule &IGM, llvm::GlobalVariable *var);
61+
62+
/// If the calling convention for `ctor` doesn't match the calling convention
63+
/// that we assumed for it when we imported it as `initializer`, emit and
64+
/// return a thunk that conforms to the assumed calling convention. The thunk
65+
/// is marked `alwaysinline`, so it doesn't generate any runtime overhead.
66+
/// If the assumed calling convention was correct, just return `ctor`.
67+
///
68+
/// See also comments in CXXMethodConventions in SIL/IR/SILFunctionType.cpp.
69+
llvm::Constant *
70+
emitCXXConstructorThunkIfNeeded(IRGenModule &IGM, Signature signature,
71+
const clang::CXXConstructorDecl *ctor,
72+
StringRef name, llvm::Constant *ctorAddress);
6073
}
6174
}
6275

lib/IRGen/GenStruct.cpp

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
#include "swift/AST/Decl.h"
2121
#include "swift/AST/IRGenOptions.h"
2222
#include "swift/AST/Pattern.h"
23+
#include "swift/AST/SemanticAttrs.h"
2324
#include "swift/AST/SubstitutionMap.h"
2425
#include "swift/AST/Types.h"
2526
#include "swift/IRGen/Linking.h"
27+
#include "swift/SIL/SILFunctionBuilder.h"
2628
#include "swift/SIL/SILModule.h"
2729
#include "clang/AST/ASTContext.h"
2830
#include "clang/AST/Attr.h"
@@ -36,17 +38,19 @@
3638
#include "llvm/IR/DerivedTypes.h"
3739
#include "llvm/IR/Function.h"
3840

41+
#include "GenDecl.h"
3942
#include "GenMeta.h"
4043
#include "GenRecord.h"
4144
#include "GenType.h"
4245
#include "IRGenFunction.h"
4346
#include "IRGenModule.h"
4447
#include "IndirectTypeInfo.h"
4548
#include "MemberAccessStrategy.h"
49+
#include "MetadataLayout.h"
4650
#include "NonFixedTypeInfo.h"
4751
#include "ResilientTypeInfo.h"
52+
#include "Signature.h"
4853
#include "StructMetadataVisitor.h"
49-
#include "MetadataLayout.h"
5054

5155
#pragma clang diagnostic ignored "-Winconsistent-missing-override"
5256

@@ -326,6 +330,7 @@ namespace {
326330
public StructTypeInfoBase<LoadableClangRecordTypeInfo, LoadableTypeInfo,
327331
ClangFieldInfo> {
328332
const clang::RecordDecl *ClangDecl;
333+
329334
public:
330335
LoadableClangRecordTypeInfo(ArrayRef<ClangFieldInfo> fields,
331336
unsigned explosionSize,
@@ -374,6 +379,73 @@ namespace {
374379
ClangFieldInfo> {
375380
const clang::RecordDecl *ClangDecl;
376381

382+
const clang::CXXConstructorDecl *findCopyConstructor() const {
383+
const clang::CXXRecordDecl *cxxRecordDecl =
384+
dyn_cast<clang::CXXRecordDecl>(ClangDecl);
385+
if (!cxxRecordDecl)
386+
return nullptr;
387+
for (auto method : cxxRecordDecl->methods()) {
388+
if (auto ctor = dyn_cast<clang::CXXConstructorDecl>(method)) {
389+
if (ctor->isCopyConstructor())
390+
return ctor;
391+
}
392+
}
393+
return nullptr;
394+
}
395+
396+
CanSILFunctionType createCXXCopyConstructorFunctionType(IRGenFunction &IGF,
397+
SILType T) const {
398+
// Create the following function type:
399+
// @convention(c) (UnsafePointer<T>) -> @out T
400+
// This is how clang *would* import the copy constructor. So, later, when
401+
// we pass it to "emitCXXConstructorThunkIfNeeded" we get a thunk with
402+
// the following LLVM function type:
403+
// void (%struct.T* %this, %struct.T* %0)
404+
auto ptrTypeDecl =
405+
IGF.getSILModule().getASTContext().getUnsafePointerDecl();
406+
auto subst = SubstitutionMap::get(ptrTypeDecl->getGenericSignature(),
407+
{T.getASTType()},
408+
ArrayRef<ProtocolConformanceRef>{});
409+
auto ptrType = ptrTypeDecl->getDeclaredInterfaceType().subst(subst);
410+
SILParameterInfo ptrParam(ptrType->getCanonicalType(),
411+
ParameterConvention::Direct_Unowned);
412+
SILResultInfo result(T.getASTType(), ResultConvention::Indirect);
413+
414+
return SILFunctionType::get(
415+
GenericSignature(),
416+
SILFunctionType::ExtInfo().withRepresentation(
417+
SILFunctionTypeRepresentation::CFunctionPointer),
418+
SILCoroutineKind::None,
419+
/*callee=*/ParameterConvention::Direct_Unowned,
420+
/*params*/ {ptrParam},
421+
/*yields*/ {}, /*results*/ {result},
422+
/*error*/ None,
423+
/*pattern subs*/ SubstitutionMap(),
424+
/*invocation subs*/ SubstitutionMap(), IGF.IGM.Context);
425+
}
426+
427+
void emitCopyWithCopyConstructor(
428+
IRGenFunction &IGF, SILType T,
429+
const clang::CXXConstructorDecl *copyConstructor, llvm::Value *src,
430+
llvm::Value *dest) const {
431+
auto fnType = createCXXCopyConstructorFunctionType(IGF, T);
432+
auto globalDecl =
433+
clang::GlobalDecl(copyConstructor, clang::Ctor_Complete);
434+
auto clangFnAddr =
435+
IGF.IGM.getAddrOfClangGlobalDecl(globalDecl, NotForDefinition);
436+
auto callee = cast<llvm::Function>(clangFnAddr->stripPointerCasts());
437+
Signature signature = IGF.IGM.getSignature(fnType);
438+
std::string name = "__swift_cxx_copy_ctor" + callee->getName().str();
439+
clangFnAddr = emitCXXConstructorThunkIfNeeded(
440+
IGF.IGM, signature, copyConstructor, name, clangFnAddr);
441+
callee = cast<llvm::Function>(clangFnAddr);
442+
dest = IGF.coerceValue(dest, callee->getFunctionType()->getParamType(0),
443+
IGF.IGM.DataLayout);
444+
src = IGF.coerceValue(src, callee->getFunctionType()->getParamType(1),
445+
IGF.IGM.DataLayout);
446+
IGF.Builder.CreateCall(callee, {dest, src});
447+
}
448+
377449
public:
378450
AddressOnlyClangRecordTypeInfo(ArrayRef<ClangFieldInfo> fields,
379451
llvm::Type *storageType, Size size,
@@ -451,6 +523,21 @@ namespace {
451523
"member functions.");
452524
}
453525

526+
void initializeWithCopy(IRGenFunction &IGF, Address destAddr,
527+
Address srcAddr, SILType T,
528+
bool isOutlined) const override {
529+
if (auto copyConstructor = findCopyConstructor()) {
530+
emitCopyWithCopyConstructor(IGF, T, copyConstructor,
531+
srcAddr.getAddress(),
532+
destAddr.getAddress());
533+
return;
534+
}
535+
StructTypeInfoBase<AddressOnlyClangRecordTypeInfo, FixedTypeInfo,
536+
ClangFieldInfo>::initializeWithCopy(IGF, destAddr,
537+
srcAddr, T,
538+
isOutlined);
539+
}
540+
454541
llvm::NoneType getNonFixedOffsets(IRGenFunction &IGF) const { return None; }
455542
llvm::NoneType getNonFixedOffsets(IRGenFunction &IGF, SILType T) const {
456543
return None;

stdlib/public/SwiftShims/RefCount.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
// This definition is a placeholder for importing into Swift.
1919
// It provides size and alignment but cannot be manipulated safely there.
2020
typedef struct {
21-
__swift_uintptr_t refCounts SWIFT_ATTRIBUTE_UNAVAILABLE;
21+
__swift_uintptr_t refCounts;
2222
} InlineRefCountsPlaceholder;
2323

2424
#if defined(__swift__)

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,17 @@ struct TemplatedConstructorWithExtraArg {
6363
TemplatedConstructorWithExtraArg(T value, U other) { }
6464
};
6565

66+
struct HasUserProvidedCopyConstructor {
67+
int numCopies;
68+
HasUserProvidedCopyConstructor(int numCopies = 0) : numCopies(numCopies) {}
69+
HasUserProvidedCopyConstructor(const HasUserProvidedCopyConstructor &other)
70+
: numCopies(other.numCopies + 1) {}
71+
};
72+
73+
struct DeletedCopyConstructor {
74+
DeletedCopyConstructor(const DeletedCopyConstructor &) = delete;
75+
};
76+
6677
// TODO: we should be able to import this constructor correctly. Until we can,
6778
// make sure not to crash.
6879
struct UsingBaseConstructor : ConstructorWithParam {

0 commit comments

Comments
 (0)