-
Notifications
You must be signed in to change notification settings - Fork 10.6k
[cxx-interop] Use user defined copy constructor to copy C++ objects. #32378
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,9 +20,11 @@ | |
| #include "swift/AST/Decl.h" | ||
| #include "swift/AST/IRGenOptions.h" | ||
| #include "swift/AST/Pattern.h" | ||
| #include "swift/AST/SemanticAttrs.h" | ||
| #include "swift/AST/SubstitutionMap.h" | ||
| #include "swift/AST/Types.h" | ||
| #include "swift/IRGen/Linking.h" | ||
| #include "swift/SIL/SILFunctionBuilder.h" | ||
| #include "swift/SIL/SILModule.h" | ||
| #include "clang/AST/ASTContext.h" | ||
| #include "clang/AST/Attr.h" | ||
|
|
@@ -36,17 +38,19 @@ | |
| #include "llvm/IR/DerivedTypes.h" | ||
| #include "llvm/IR/Function.h" | ||
|
|
||
| #include "GenDecl.h" | ||
| #include "GenMeta.h" | ||
| #include "GenRecord.h" | ||
| #include "GenType.h" | ||
| #include "IRGenFunction.h" | ||
| #include "IRGenModule.h" | ||
| #include "IndirectTypeInfo.h" | ||
| #include "MemberAccessStrategy.h" | ||
| #include "MetadataLayout.h" | ||
| #include "NonFixedTypeInfo.h" | ||
| #include "ResilientTypeInfo.h" | ||
| #include "Signature.h" | ||
| #include "StructMetadataVisitor.h" | ||
| #include "MetadataLayout.h" | ||
|
|
||
| #pragma clang diagnostic ignored "-Winconsistent-missing-override" | ||
|
|
||
|
|
@@ -326,6 +330,7 @@ namespace { | |
| public StructTypeInfoBase<LoadableClangRecordTypeInfo, LoadableTypeInfo, | ||
| ClangFieldInfo> { | ||
| const clang::RecordDecl *ClangDecl; | ||
|
|
||
| public: | ||
| LoadableClangRecordTypeInfo(ArrayRef<ClangFieldInfo> fields, | ||
| unsigned explosionSize, | ||
|
|
@@ -374,6 +379,73 @@ namespace { | |
| ClangFieldInfo> { | ||
| const clang::RecordDecl *ClangDecl; | ||
|
|
||
| const clang::CXXConstructorDecl *findCopyConstructor() const { | ||
| const clang::CXXRecordDecl *cxxRecordDecl = | ||
| dyn_cast<clang::CXXRecordDecl>(ClangDecl); | ||
| if (!cxxRecordDecl) | ||
| return nullptr; | ||
| for (auto method : cxxRecordDecl->methods()) { | ||
| if (auto ctor = dyn_cast<clang::CXXConstructorDecl>(method)) { | ||
| if (ctor->isCopyConstructor()) | ||
| return ctor; | ||
| } | ||
| } | ||
| return nullptr; | ||
| } | ||
|
|
||
| CanSILFunctionType createCXXCopyConstructorFunctionType(IRGenFunction &IGF, | ||
| SILType T) const { | ||
| // Create the following function type: | ||
| // @convention(c) (UnsafePointer<T>) -> @out T | ||
| // This is how clang *would* import the copy constructor. So, later, when | ||
| // we pass it to "emitCXXConstructorThunkIfNeeded" we get a thunk with | ||
| // the following LLVM function type: | ||
| // void (%struct.T* %this, %struct.T* %0) | ||
| auto ptrTypeDecl = | ||
| IGF.getSILModule().getASTContext().getUnsafePointerDecl(); | ||
| auto subst = SubstitutionMap::get(ptrTypeDecl->getGenericSignature(), | ||
| {T.getASTType()}, | ||
| ArrayRef<ProtocolConformanceRef>{}); | ||
| auto ptrType = ptrTypeDecl->getDeclaredInterfaceType().subst(subst); | ||
| SILParameterInfo ptrParam(ptrType->getCanonicalType(), | ||
| ParameterConvention::Direct_Unowned); | ||
| SILResultInfo result(T.getASTType(), ResultConvention::Indirect); | ||
|
|
||
| return SILFunctionType::get( | ||
| GenericSignature(), | ||
| SILFunctionType::ExtInfo().withRepresentation( | ||
| SILFunctionTypeRepresentation::CFunctionPointer), | ||
| SILCoroutineKind::None, | ||
| /*callee=*/ParameterConvention::Direct_Unowned, | ||
| /*params*/ {ptrParam}, | ||
| /*yields*/ {}, /*results*/ {result}, | ||
| /*error*/ None, | ||
| /*pattern subs*/ SubstitutionMap(), | ||
| /*invocation subs*/ SubstitutionMap(), IGF.IGM.Context); | ||
| } | ||
|
|
||
| void emitCopyWithCopyConstructor( | ||
| IRGenFunction &IGF, SILType T, | ||
| const clang::CXXConstructorDecl *copyConstructor, llvm::Value *src, | ||
| llvm::Value *dest) const { | ||
| auto fnType = createCXXCopyConstructorFunctionType(IGF, T); | ||
| auto globalDecl = | ||
| clang::GlobalDecl(copyConstructor, clang::Ctor_Complete); | ||
| auto clangFnAddr = | ||
| IGF.IGM.getAddrOfClangGlobalDecl(globalDecl, NotForDefinition); | ||
| auto callee = cast<llvm::Function>(clangFnAddr->stripPointerCasts()); | ||
| Signature signature = IGF.IGM.getSignature(fnType); | ||
| std::string name = "__swift_cxx_copy_ctor" + callee->getName().str(); | ||
| clangFnAddr = emitCXXConstructorThunkIfNeeded( | ||
| IGF.IGM, signature, copyConstructor, name, clangFnAddr); | ||
| callee = cast<llvm::Function>(clangFnAddr); | ||
| dest = IGF.coerceValue(dest, callee->getFunctionType()->getParamType(0), | ||
| IGF.IGM.DataLayout); | ||
| src = IGF.coerceValue(src, callee->getFunctionType()->getParamType(1), | ||
| IGF.IGM.DataLayout); | ||
| IGF.Builder.CreateCall(callee, {dest, src}); | ||
|
||
| } | ||
|
|
||
| public: | ||
| AddressOnlyClangRecordTypeInfo(ArrayRef<ClangFieldInfo> fields, | ||
| llvm::Type *storageType, Size size, | ||
|
|
@@ -451,6 +523,21 @@ namespace { | |
| "member functions."); | ||
| } | ||
|
|
||
| void initializeWithCopy(IRGenFunction &IGF, Address destAddr, | ||
| Address srcAddr, SILType T, | ||
| bool isOutlined) const override { | ||
| if (auto copyConstructor = findCopyConstructor()) { | ||
| emitCopyWithCopyConstructor(IGF, T, copyConstructor, | ||
| srcAddr.getAddress(), | ||
| destAddr.getAddress()); | ||
| return; | ||
| } | ||
| StructTypeInfoBase<AddressOnlyClangRecordTypeInfo, FixedTypeInfo, | ||
| ClangFieldInfo>::initializeWithCopy(IGF, destAddr, | ||
| srcAddr, T, | ||
| isOutlined); | ||
| } | ||
|
|
||
| llvm::NoneType getNonFixedOffsets(IRGenFunction &IGF) const { return None; } | ||
| llvm::NoneType getNonFixedOffsets(IRGenFunction &IGF, SILType T) const { | ||
| return None; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,7 +18,7 @@ | |
| // This definition is a placeholder for importing into Swift. | ||
| // It provides size and alignment but cannot be manipulated safely there. | ||
| typedef struct { | ||
| __swift_uintptr_t refCounts SWIFT_ATTRIBUTE_UNAVAILABLE; | ||
|
||
| __swift_uintptr_t refCounts; | ||
| } InlineRefCountsPlaceholder; | ||
|
|
||
| #if defined(__swift__) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to admit I don't understand the logic that existed here before this PR, and which was introduced by #31707.
AFAICT, the definition of "trivially copyable" says nothing about the access level of the copy constructor. It does say something about the copy constructor being deleted, namely that this is compatible with the class being trivially copyable.
Beyond that, I'm not sure why simply taking the result of
cxxRecordDecl->isTriviallyCopyable()isn't sufficient here.@MForster I think you introduced this logic -- can you comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[class.prop]/p1 defines a trivially copyable class as a class, "that has at least one eligible copy constructor..." I think the important word there is eligible. A private copy constructor is not eligible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that's how C++ defines "eligible":
https://eel.is/c++draft/class#special-6
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True. However, I think we have to still check for public access level, since it would be otherwise quite weird that Swift can use a private constructor -- even if it is trivial.