mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Merge pull request #72903 from apple/egorzhdan/zero-init
[cxx-interop] Zero-initialize C++ structs when calling their default constructors
This commit is contained in:
@@ -3476,7 +3476,12 @@ llvm::Constant *swift::irgen::emitCXXConstructorThunkIfNeeded(
|
||||
llvm::FunctionType *ctorFnType =
|
||||
cast<llvm::FunctionType>(clangFunc->getValueType());
|
||||
|
||||
if (assumedFnType == ctorFnType) {
|
||||
// Only need a thunk if either:
|
||||
// 1. The calling conventions do not match, and we need to pass arguments
|
||||
// differently.
|
||||
// 2. This is a default constructor, and we need to zero the backing memory of
|
||||
// the struct.
|
||||
if (assumedFnType == ctorFnType && !ctor->isDefaultConstructor()) {
|
||||
return ctorAddress;
|
||||
}
|
||||
|
||||
@@ -3510,6 +3515,7 @@ llvm::Constant *swift::irgen::emitCXXConstructorThunkIfNeeded(
|
||||
Args.push_back(i);
|
||||
}
|
||||
|
||||
if (assumedFnType != ctorFnType) {
|
||||
clang::CodeGen::ImplicitCXXConstructorArgs implicitArgs =
|
||||
clang::CodeGen::getImplicitCXXConstructorArgs(IGM.ClangCodeGen->CGM(),
|
||||
ctor);
|
||||
@@ -3519,6 +3525,25 @@ llvm::Constant *swift::irgen::emitCXXConstructorThunkIfNeeded(
|
||||
for (const auto &arg : implicitArgs.Suffix) {
|
||||
Args.push_back(arg);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctor->isDefaultConstructor()) {
|
||||
assert(Args.size() > 0 && "expected at least 1 argument (result address) "
|
||||
"for default constructor");
|
||||
|
||||
// Zero out the backing memory of the struct.
|
||||
// This makes default initializers for C++ structs behave consistently with
|
||||
// the synthesized empty initializers for C structs. When C++ interop is
|
||||
// enabled in a project, all imported C structs are treated as C++ structs,
|
||||
// which sometimes means that Clang will synthesize a default constructor
|
||||
// for the C++ struct that does not zero out trivial fields of a struct.
|
||||
auto cxxRecord = ctor->getParent();
|
||||
clang::ASTContext &ctx = cxxRecord->getASTContext();
|
||||
auto typeSize = ctx.getTypeSizeInChars(ctx.getRecordType(cxxRecord));
|
||||
subIGF.Builder.CreateMemSet(Args[0],
|
||||
llvm::ConstantInt::get(subIGF.IGM.Int8Ty, 0),
|
||||
typeSize.getQuantity(), llvm::MaybeAlign());
|
||||
}
|
||||
|
||||
auto *call =
|
||||
emitCXXConstructorCall(subIGF, ctor, ctorFnType, ctorAddress, Args);
|
||||
|
||||
@@ -32,6 +32,9 @@ struct ConstructorWithParam {
|
||||
struct CopyAndMoveConstructor {
|
||||
CopyAndMoveConstructor(const CopyAndMoveConstructor &) = default;
|
||||
CopyAndMoveConstructor(CopyAndMoveConstructor &&) = default;
|
||||
|
||||
int value = 123;
|
||||
int *ptr = nullptr;
|
||||
};
|
||||
|
||||
struct Base {};
|
||||
|
||||
@@ -44,4 +44,23 @@ CxxConstructorTestSuite.test("TemplatedConstructor") {
|
||||
expectEqual(2, instance.value.i)
|
||||
}
|
||||
|
||||
CxxConstructorTestSuite.test("implicit default ctor") {
|
||||
// Make sure that fields of C++ structs are zeroed out.
|
||||
|
||||
let instance1 = ConstructorWithParam()
|
||||
expectEqual(0, instance1.x)
|
||||
|
||||
let instance2 = IntWrapper()
|
||||
expectEqual(0, instance2.x)
|
||||
|
||||
// CopyAndMoveConstructor is not default-initializable in C++, however, Swift
|
||||
// generates an implicit deprecated default constructor for C++ structs for
|
||||
// compatibility with C. This constructor will zero out the entire backing
|
||||
// memory of the struct, including fields that have an init expression.
|
||||
// See `SwiftDeclSynthesizer::createDefaultConstructor`.
|
||||
let instance3 = CopyAndMoveConstructor()
|
||||
expectEqual(0, instance3.value)
|
||||
expectNil(instance3.ptr)
|
||||
}
|
||||
|
||||
runAllTests()
|
||||
|
||||
@@ -23,6 +23,7 @@ public func createImplicitDefaultConstructor() -> ImplicitDefaultConstructor {
|
||||
// ITANIUM_ARM: define protected swiftcc i32 @"$s7MySwift32createImplicitDefaultConstructorSo0deF0VyF"()
|
||||
// ITANIUM_ARM-NOT: define
|
||||
// Note `this` return type.
|
||||
// ITANIUM_ARM: call void @llvm.memset.p0.i64
|
||||
// ITANIUM_ARM: call void @_ZN26ImplicitDefaultConstructorC2Ev(ptr %{{[0-9]+}})
|
||||
return ImplicitDefaultConstructor()
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ public func createHasVirtualBase() -> HasVirtualBase {
|
||||
public func createImplicitDefaultConstructor() -> ImplicitDefaultConstructor {
|
||||
// ITANIUM_X64: define swiftcc i32 @"$s7MySwift32createImplicitDefaultConstructorSo0deF0VyF"()
|
||||
// ITANIUM_X64-NOT: define
|
||||
// ITANIUM_X64: call void @llvm.memset.p0.i64
|
||||
// ITANIUM_X64: call void @_ZN26ImplicitDefaultConstructorC1Ev(ptr %{{[0-9]+}})
|
||||
return ImplicitDefaultConstructor()
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ public func createImplicitDefaultConstructor() -> ImplicitDefaultConstructor {
|
||||
// MICROSOFT_X64: define dllexport swiftcc i32 @"$s7MySwift32createImplicitDefaultConstructorSo0{{bcD0VyF|deF0VyF}}"()
|
||||
// MICROSOFT_X64-NOT: define
|
||||
// Note `this` return type but no implicit "most derived" argument.
|
||||
// MICROSOFT_X64: call void @llvm.memset.p0.i64
|
||||
// MICROSOFT_X64: call ptr @"??0ImplicitDefaultConstructor@@QEAA@XZ"(ptr %{{[0-9]+}})
|
||||
return ImplicitDefaultConstructor()
|
||||
}
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
// CHECK-NEXT: struct CopyAndMoveConstructor {
|
||||
// CHECK-NEXT: @available(*, deprecated, message
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: init(value: Int32, ptr: UnsafeMutablePointer<Int32>!)
|
||||
// CHECK-NEXT: var value: Int32
|
||||
// CHECK-NEXT: var ptr: UnsafeMutablePointer<Int32>!
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: struct Base {
|
||||
// CHECK-NEXT: init()
|
||||
|
||||
@@ -7,6 +7,7 @@ let explicit = ExplicitDefaultConstructor()
|
||||
let implicit = ImplicitDefaultConstructor()
|
||||
|
||||
let deletedImplicitly = ConstructorWithParam() // expected-warning {{'init()' is deprecated}}
|
||||
let onlyCopyAndMove = CopyAndMoveConstructor() // expected-warning {{'init()' is deprecated}}
|
||||
|
||||
let deletedExplicitly = DefaultConstructorDeleted() // expected-error {{missing argument for parameter 'a' in call}}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user