Skip to content

Commit 25ef5d7

Browse files
authored
Merge pull request #66220 from apple/egorzhdan/c-default-init
[cxx-interop] Synthesize a deprecated zero initializer for C++ structs
2 parents 27cd942 + fec48f9 commit 25ef5d7

File tree

6 files changed

+36
-9
lines changed

6 files changed

+36
-9
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2379,11 +2379,30 @@ namespace {
23792379
hasMemberwiseInitializer = false;
23802380
}
23812381

2382-
if (hasZeroInitializableStorage && !cxxRecordDecl) {
2382+
if (hasZeroInitializableStorage &&
2383+
!(cxxRecordDecl && cxxRecordDecl->hasDefaultConstructor())) {
23832384
// Add default constructor for the struct if compiling in C mode.
2384-
// If we're compiling for C++, we'll import the C++ default constructor
2385-
// (if there is one), so we don't need to synthesize one here.
2386-
ctors.push_back(synthesizer.createDefaultConstructor(result));
2385+
// If we're compiling for C++:
2386+
// 1. If a default constructor is declared, don't synthesize one.
2387+
// 2. If a default constructor is deleted, don't try to synthesize one.
2388+
// 3. If there is no default constructor, synthesize a C-like default
2389+
// constructor that zero-initializes the backing memory of the
2390+
// struct. This is important to maintain source compatibility when a
2391+
// client enables C++ interop in an existing project that uses C
2392+
// interop and might rely on the fact that C structs have a default
2393+
// constructor available in Swift.
2394+
ConstructorDecl *defaultCtor =
2395+
synthesizer.createDefaultConstructor(result);
2396+
ctors.push_back(defaultCtor);
2397+
if (cxxRecordDecl) {
2398+
auto attr = AvailableAttr::createPlatformAgnostic(
2399+
defaultCtor->getASTContext(),
2400+
"This zero-initializes the backing memory of the struct, which "
2401+
"is unsafe for some C++ structs. Consider adding an explicit "
2402+
"default initializer for this C++ struct.",
2403+
"", PlatformAgnosticAvailabilityKind::Deprecated);
2404+
defaultCtor->getAttrs().add(attr);
2405+
}
23872406
}
23882407

23892408
// We can assume that it is possible to correctly construct the object by

lib/ClangImporter/SwiftDeclSynthesizer.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -438,10 +438,6 @@ synthesizeStructDefaultConstructorBody(AbstractFunctionDecl *afd,
438438
ASTContext &ctx = constructor->getASTContext();
439439
auto structDecl = static_cast<StructDecl *>(context);
440440

441-
// We should call into C++ constructors directly.
442-
assert(!isa<clang::CXXRecordDecl>(structDecl->getClangDecl()) &&
443-
"Should not synthesize a C++ object constructor.");
444-
445441
// Use a builtin to produce a zero initializer, and assign it to self.
446442

447443
// Construct the left-hand reference to self.

test/Interop/Cxx/class/constructors-module-interface.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@
2020
// CHECK-NEXT: }
2121
// CHECK-NEXT: struct ConstructorWithParam {
2222
// CHECK-NEXT: init(_ val: Int32)
23+
// CHECK-NEXT: @available(*, deprecated, message
24+
// CHECK-NEXT: init()
2325
// CHECK-NEXT: var x: Int32
2426
// CHECK-NEXT: }
2527
// CHECK-NEXT: struct CopyAndMoveConstructor {
28+
// CHECK-NEXT: @available(*, deprecated, message
29+
// CHECK-NEXT: init()
2630
// CHECK-NEXT: }
2731
// CHECK-NEXT: struct Base {
2832
// CHECK-NEXT: init()
@@ -38,10 +42,14 @@
3842
// CHECK-NEXT: }
3943
// CHECK: struct TemplatedConstructor {
4044
// CHECK-NEXT: init<T>(_ value: T)
45+
// CHECK-NEXT: @available(*, deprecated, message
46+
// CHECK-NEXT: init()
4147
// CHECK-NEXT: var value: ArgType
4248
// CHECK-NEXT: }
4349
// CHECK: struct TemplatedConstructorWithExtraArg {
4450
// CHECK-NEXT: init<T>(_: Int32, _ value: T)
4551
// CHECK-NEXT: init<T>(_ value: T, _: Int32)
4652
// CHECK-NEXT: init<T, U>(_ value: T, _ other: U)
53+
// CHECK-NEXT: @available(*, deprecated, message
54+
// CHECK-NEXT: init()
4755
// CHECK-NEXT: }

test/Interop/Cxx/class/constructors-objc-module-interface.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77

88
// CHECK: struct ConstructorWithNSArrayParam {
99
// CHECK-NEXT: init(_ array: [Any]!)
10+
// CHECK-NEXT: @available(*, deprecated, message
11+
// CHECK-NEXT: init()
1012
// CHECK-NEXT: }

test/Interop/Cxx/class/constructors-typechecker.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ let explicit = ExplicitDefaultConstructor()
66

77
let implicit = ImplicitDefaultConstructor()
88

9-
let deletedImplicitly = ConstructorWithParam() // expected-error {{missing argument for parameter #1 in call}}
9+
let deletedImplicitly = ConstructorWithParam() // expected-warning {{'init()' is deprecated}}
1010

1111
let deletedExplicitly = DefaultConstructorDeleted() // expected-error {{missing argument for parameter 'a' in call}}
1212

test/Interop/Cxx/union/anonymous-union-partly-invalid-module-interface.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// CHECK: class C {
44
// CHECK-NEXT: }
55
// CHECK-NEXT: struct S {
6+
// CHECK-NEXT: @available(*, deprecated, message
7+
// CHECK-NEXT: init()
68
// CHECK-NEXT: mutating func f() -> Int32
79
// CHECK-NEXT: }
810
// CHECK-NEXT: func getSPtr() -> UnsafeMutablePointer<S>!

0 commit comments

Comments
 (0)