Skip to content

Commit 781a9fc

Browse files
committed
[cxx-interop] ensure destructors referenced only by the exception cleanup landing pad in the constructor's initializer are picked up to be candidates for potential emission into one LLVM IR module
A C++ record destructor that is called implicitly in the landing pad cleanup code of a constructor after a field is initialized might only be referenced there, so the Clang decl finder should pick it up when scanning the AST for interesting Decls that might have to be emitted into one LLVM IR module.
1 parent 9caf750 commit 781a9fc

File tree

2 files changed

+65
-1
lines changed

2 files changed

+65
-1
lines changed

lib/IRGen/GenClangDecl.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,19 @@ class ClangDeclFinder
8585
bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *CXXCD) {
8686
callback(CXXCD);
8787
for (clang::CXXCtorInitializer *CXXCI : CXXCD->inits()) {
88-
if (clang::FieldDecl *FD = CXXCI->getMember())
88+
if (clang::FieldDecl *FD = CXXCI->getMember()) {
8989
callback(FD);
90+
// A throwing constructor might throw after the field is initialized,
91+
// emitting additional cleanup code that destroys the field. Make sure
92+
// we record the destructor of the field in that case as it might need
93+
// to be potentially emitted.
94+
if (auto *recordType = FD->getType()->getAsCXXRecordDecl()) {
95+
if (auto *destructor = recordType->getDestructor()) {
96+
if (!destructor->isDeleted())
97+
callback(destructor);
98+
}
99+
}
100+
}
90101
}
91102
return true;
92103
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// RUN: rm -rf %t
2+
// RUN: split-file %s %t
3+
// RUN: %target-swiftxx-frontend -emit-ir -I %t/Inputs -validate-tbd-against-ir=none %t/test.swift | %FileCheck %s
4+
5+
//--- Inputs/module.modulemap
6+
module ThrowingConstructorDestructorCleanupRef {
7+
header "test.h"
8+
requires cplusplus
9+
}
10+
//--- Inputs/test.h
11+
12+
void testFunc(int x);
13+
14+
template<class T>
15+
class HasDestructor {
16+
T x = 0;
17+
public:
18+
19+
HasDestructor() { }
20+
HasDestructor(const HasDestructor &other) : x(other.x) {}
21+
inline ~HasDestructor() { testFunc(42); }
22+
};
23+
24+
template<class T>
25+
class HasThrowingConstructor {
26+
HasDestructor<T> m;
27+
public:
28+
HasThrowingConstructor();
29+
~HasThrowingConstructor();
30+
inline HasThrowingConstructor(const HasThrowingConstructor &f) : m(f.m) {
31+
doSomethingThatMightThrow();
32+
}
33+
34+
void doSomethingThatMightThrow();
35+
};
36+
37+
inline void test33(const HasThrowingConstructor<int> x) {
38+
39+
}
40+
41+
using HasThrowingConstructorInt = HasThrowingConstructor<int>;
42+
43+
//--- test.swift
44+
45+
import ThrowingConstructorDestructorCleanupRef
46+
47+
public func test() {
48+
let x = HasThrowingConstructorInt()
49+
test33(x)
50+
}
51+
52+
// Make sure we reach the destructor of 'HasDestructor'
53+
// CHECK: define linkonce_odr {{.*}} @{{_ZN13HasDestructorIiED2Ev|"\?\?1\?\$HasDestructor@H@@QEAA@XZ"}}

0 commit comments

Comments
 (0)