-
Notifications
You must be signed in to change notification settings - Fork 10.4k
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
[cxx-interop] Use user defined copy constructor to copy C++ objects. #32378
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}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't correct on all ABIs -- some ABIs add implicit arguments to constructor calls in some situations. Look at #30630 and, in particular, You'll either have to call Also, add a test that makes sure the implicit arguments are added when appropriate. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I created a helper function and generalized There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @martinboehme Looking at the tests from #30630. I'm getting:
What target builds the windows stdlib? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Hm. I had forgotten that I think that the way you're calling the modified function doesn't work though. I think the way you're calling it, Maybe, at the end of the day, it's easier to do things at the SIL level after all though -- i.e. import the copy constructor as a hidden initializer, emit a call to that, and let the existing machinery take care of creating a thunk if needed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Not sure what you mean -- are you getting this error when running existing tests on #30630 (I think they should all be passing on CI though?) or when trying to create new tests in the spirit of the tests in #30630? It's unfortunately not possible currently to cross-compile stdlib, IIUC. In other words, you can't create a Windows stdlib on Linux, for example. It would be great if this were possible, but for the time being, if you want to do portable IRGen tests (i.e. run Windows IRGen tests on Linux, for example), you can't use stdlib in them. This is pretty restrictive, but you can usually still find a way to test what you want to test -- for an example, see There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think you're right.
We could also import it as a Alternatively, we could try to build the "correct" function type in IRGen and then use that in the signature argument. |
||
} | ||
|
||
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, in C-mode this attribute is meaningless which is why we didn't see the error before now. If we want to make sure that in runtime mode we don't use this type, I'll need to create a separate and more involved fix. Let me know if that needs to be done. Another "fix" could be to only add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Which error? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When we have a reference type, for example an array, we try to add an implicit copy constructor in C++ mode. When we try to do this, we get an error that
|
||
__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.