Skip to content

Commit 7ca1163

Browse files
committed
[cxx-interop] Avoid linker errors when calling a defaulted constructor
When a default constructor is declared, but does not have a body because it is defaulted (`= default;`), Swift did not emit the IR for it. This was causing linker error for types such as `std::map` in libstdc++ when someone tried to initialize such types from Swift. rdar://110638499 / resolves #61412
1 parent 4e2f3b1 commit 7ca1163

File tree

5 files changed

+45
-19
lines changed

5 files changed

+45
-19
lines changed

lib/ClangImporter/ImportDecl.cpp

+29-17
Original file line numberDiff line numberDiff line change
@@ -2634,30 +2634,38 @@ namespace {
26342634
}
26352635
clang::CXXConstructorDecl *copyCtor = nullptr;
26362636
clang::CXXConstructorDecl *moveCtor = nullptr;
2637+
clang::CXXConstructorDecl *defaultCtor = nullptr;
26372638
if (decl->needsImplicitCopyConstructor()) {
26382639
copyCtor = clangSema.DeclareImplicitCopyConstructor(
26392640
const_cast<clang::CXXRecordDecl *>(decl));
2640-
} else if (decl->needsImplicitMoveConstructor()) {
2641+
}
2642+
if (decl->needsImplicitMoveConstructor()) {
26412643
moveCtor = clangSema.DeclareImplicitMoveConstructor(
26422644
const_cast<clang::CXXRecordDecl *>(decl));
2643-
} else {
2644-
// We may have a defaulted copy constructor that needs to be defined.
2645-
// Try to find it.
2646-
for (auto methods : decl->methods()) {
2647-
if (auto declCtor = dyn_cast<clang::CXXConstructorDecl>(methods)) {
2648-
if (declCtor->isDefaulted() &&
2649-
declCtor->getAccess() == clang::AS_public &&
2650-
!declCtor->isDeleted() &&
2651-
// Note: we use "doesThisDeclarationHaveABody" here because
2652-
// that's what "DefineImplicitCopyConstructor" checks.
2653-
!declCtor->doesThisDeclarationHaveABody()) {
2654-
if (declCtor->isCopyConstructor()) {
2645+
}
2646+
if (decl->needsImplicitDefaultConstructor()) {
2647+
defaultCtor = clangSema.DeclareImplicitDefaultConstructor(
2648+
const_cast<clang::CXXRecordDecl *>(decl));
2649+
}
2650+
// We may have a defaulted copy/move/default constructor that needs to
2651+
// be defined. Try to find it.
2652+
for (auto methods : decl->methods()) {
2653+
if (auto declCtor = dyn_cast<clang::CXXConstructorDecl>(methods)) {
2654+
if (declCtor->isDefaulted() &&
2655+
declCtor->getAccess() == clang::AS_public &&
2656+
!declCtor->isDeleted() &&
2657+
// Note: we use "doesThisDeclarationHaveABody" here because
2658+
// that's what "DefineImplicitCopyConstructor" checks.
2659+
!declCtor->doesThisDeclarationHaveABody()) {
2660+
if (declCtor->isCopyConstructor()) {
2661+
if (!copyCtor)
26552662
copyCtor = declCtor;
2656-
break;
2657-
} else if (declCtor->isMoveConstructor()) {
2663+
} else if (declCtor->isMoveConstructor()) {
2664+
if (!moveCtor)
26582665
moveCtor = declCtor;
2659-
break;
2660-
}
2666+
} else if (declCtor->isDefaultConstructor()) {
2667+
if (!defaultCtor)
2668+
defaultCtor = declCtor;
26612669
}
26622670
}
26632671
}
@@ -2670,6 +2678,10 @@ namespace {
26702678
clangSema.DefineImplicitMoveConstructor(clang::SourceLocation(),
26712679
moveCtor);
26722680
}
2681+
if (defaultCtor) {
2682+
clangSema.DefineImplicitDefaultConstructor(clang::SourceLocation(),
2683+
defaultCtor);
2684+
}
26732685

26742686
if (decl->needsImplicitDestructor()) {
26752687
auto dtor = clangSema.DeclareImplicitDestructor(

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

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ struct ImplicitDefaultConstructor {
1010
int x = 42;
1111
};
1212

13+
struct DefaultedDefaultConstructor {
14+
int x = 42;
15+
DefaultedDefaultConstructor() = default;
16+
};
17+
1318
struct MemberOfClassType {
1419
ImplicitDefaultConstructor member;
1520
};

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

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ CxxConstructorTestSuite.test("ImplicitDefaultConstructor") {
1919
expectEqual(42, instance.x)
2020
}
2121

22+
CxxConstructorTestSuite.test("DefaultedDefaultConstructor") {
23+
let instance = DefaultedDefaultConstructor()
24+
25+
expectEqual(42, instance.x)
26+
}
27+
2228
CxxConstructorTestSuite.test("MemberOfClassType") {
2329
let instance = MemberOfClassType()
2430

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

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
// CHECK-NEXT: init(x: Int32)
1010
// CHECK-NEXT: var x: Int32
1111
// CHECK-NEXT: }
12+
// CHECK-NEXT: struct DefaultedDefaultConstructor {
13+
// CHECK-NEXT: init()
14+
// CHECK-NEXT: init(x: Int32)
15+
// CHECK-NEXT: var x: Int32
16+
// CHECK-NEXT: }
1217
// CHECK-NEXT: struct MemberOfClassType {
1318
// CHECK-NEXT: init()
1419
// CHECK-NEXT: init(member: ImplicitDefaultConstructor)

test/Interop/Cxx/stdlib/use-std-map.swift

-2
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,11 @@ import Cxx
1111

1212
var StdMapTestSuite = TestSuite("StdMap")
1313

14-
#if !os(Linux) // https://github.com/apple/swift/issues/61412
1514
StdMapTestSuite.test("init") {
1615
let m = Map()
1716
expectEqual(m.size(), 0)
1817
expectTrue(m.empty())
1918
}
20-
#endif
2119

2220
StdMapTestSuite.test("Map.subscript") {
2321
// This relies on the `std::map` conformance to `CxxDictionary` protocol.

0 commit comments

Comments
 (0)