Skip to content

Commit 0b3990c

Browse files
scentinihlopko
andauthored
[cxx-interop] Generate IR for decls called from members (#35056)
Currently the following code doesn't work when `callConstructor()` is called from Swift: ```cc inline int increment(int value) { return value + 1; } struct Incrementor { int incrementee; Incrementor(int value) : incrementee(increment(value)) {} } int callConstructor(int value) { return Incrementor(value).incrementee; } ``` The issue is that we don't generate `IR` for the `increment()` function when it's only called from a constructor or a method. Swift is aware of the existence of `increment()` and we see it in `IR` as `declare incrementEi`, however, as we don't to emit a definition, we get the following error: ``` Incrementor::Incrementor(int): error: undefined reference to 'increment(int)' ``` This PR fixes this by visiting constructors and methods in `IRGen` and calling `HandleTopLevelDecl()` with all used declarations, which results in emitting definitions for the used declarations. Co-authored-by: Marcel Hlopko <[email protected]>
1 parent daa134f commit 0b3990c

26 files changed

+381
-17
lines changed

lib/IRGen/GenClangDecl.cpp

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212

1313
#include "IRGenModule.h"
1414
#include "clang/AST/Decl.h"
15+
#include "clang/AST/DeclCXX.h"
1516
#include "clang/AST/DeclGroup.h"
17+
#include "clang/AST/Expr.h"
18+
#include "clang/AST/ExprCXX.h"
1619
#include "clang/AST/GlobalDecl.h"
1720
#include "clang/AST/RecursiveASTVisitor.h"
1821
#include "clang/CodeGen/ModuleBuilder.h"
@@ -22,15 +25,32 @@ using namespace swift;
2225
using namespace irgen;
2326

2427
namespace {
25-
class ClangDeclRefFinder
26-
: public clang::RecursiveASTVisitor<ClangDeclRefFinder> {
27-
std::function<void(const clang::DeclRefExpr *)> callback;
28+
class ClangDeclFinder
29+
: public clang::RecursiveASTVisitor<ClangDeclFinder> {
30+
std::function<void(const clang::Decl *)> callback;
2831
public:
2932
template <typename Fn>
30-
explicit ClangDeclRefFinder(Fn fn) : callback(fn) {}
33+
explicit ClangDeclFinder(Fn fn) : callback(fn) {}
3134

3235
bool VisitDeclRefExpr(clang::DeclRefExpr *DRE) {
33-
callback(DRE);
36+
if (isa<clang::FunctionDecl>(DRE->getDecl()) ||
37+
isa<clang::VarDecl>(DRE->getDecl())) {
38+
callback(DRE->getDecl());
39+
}
40+
return true;
41+
}
42+
43+
bool VisitMemberExpr(clang::MemberExpr *ME) {
44+
if (isa<clang::FunctionDecl>(ME->getMemberDecl()) ||
45+
isa<clang::VarDecl>(ME->getMemberDecl()) ||
46+
isa<clang::FieldDecl>(ME->getMemberDecl())) {
47+
callback(ME->getMemberDecl());
48+
}
49+
return true;
50+
}
51+
52+
bool VisitCXXConstructExpr(clang::CXXConstructExpr *CXXCE) {
53+
callback(CXXCE->getConstructor());
3454
return true;
3555
}
3656
};
@@ -51,8 +71,11 @@ clang::Decl *getDeclWithExecutableCode(clang::Decl *decl) {
5171
if (initializingDecl) {
5272
return initializingDecl;
5373
}
74+
} else if (auto fd = dyn_cast<clang::FieldDecl>(decl)) {
75+
if(fd->hasInClassInitializer()) {
76+
return fd;
77+
}
5478
}
55-
5679
return nullptr;
5780
}
5881

@@ -74,21 +97,23 @@ void IRGenModule::emitClangDecl(const clang::Decl *decl) {
7497
SmallVector<const clang::Decl *, 8> stack;
7598
stack.push_back(decl);
7699

77-
ClangDeclRefFinder refFinder([&](const clang::DeclRefExpr *DRE) {
78-
const clang::Decl *D = DRE->getDecl();
79-
// Check that this is a file-level declaration and not inside a function.
80-
// If it's a member of a file-level decl, like a C++ static member variable,
81-
// we want to add the entire file-level declaration because Clang doesn't
82-
// expect to see members directly here.
100+
ClangDeclFinder refFinder([&](const clang::Decl *D) {
83101
for (auto *DC = D->getDeclContext();; DC = DC->getParent()) {
84-
if (DC->isFunctionOrMethod())
102+
// Check that this is not a local declaration inside a function.
103+
if (DC->isFunctionOrMethod()) {
85104
return;
86-
if (DC->isFileContext())
105+
}
106+
if (DC->isFileContext()) {
107+
break;
108+
}
109+
if (isa<clang::TagDecl>(DC)) {
87110
break;
111+
}
88112
D = cast<const clang::Decl>(DC);
89113
}
90-
if (!GlobalClangDecls.insert(D->getCanonicalDecl()).second)
114+
if (!GlobalClangDecls.insert(D->getCanonicalDecl()).second) {
91115
return;
116+
}
92117
stack.push_back(D);
93118
});
94119

@@ -101,8 +126,10 @@ void IRGenModule::emitClangDecl(const clang::Decl *decl) {
101126

102127
if (auto var = dyn_cast<clang::VarDecl>(next))
103128
if (!var->isFileVarDecl())
104-
continue;
105-
129+
continue;
130+
if (isa<clang::FieldDecl>(next)) {
131+
continue;
132+
}
106133
ClangCodeGen->HandleTopLevelDecl(clang::DeclGroupRef(next));
107134
}
108135
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#ifndef TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_CONSTRUCTOR_CALLS_FUNCTION_FROM_NESTED_STRUCT_H
2+
#define TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_CONSTRUCTOR_CALLS_FUNCTION_FROM_NESTED_STRUCT_H
3+
4+
inline int increment(int t) { return t + 1; }
5+
6+
struct IncrementUser {
7+
struct Incrementor {
8+
int value;
9+
Incrementor(int v) { value = increment(v); }
10+
};
11+
};
12+
13+
inline int callConstructor(int value) {
14+
return IncrementUser::Incrementor(value).value;
15+
}
16+
17+
#endif // TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_CONSTRUCTOR_CALLS_FUNCTION_FROM_NESTED_STRUCT_H
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#ifndef TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_CONSTRUCTOR_CALLS_FUNCTION_H
2+
#define TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_CONSTRUCTOR_CALLS_FUNCTION_H
3+
4+
inline int increment(int t) { return t + 1; }
5+
6+
struct Incrementor {
7+
int incrementee;
8+
Incrementor(int value) : incrementee(increment(value)) {}
9+
};
10+
11+
inline int callConstructor(int value) { return Incrementor(value).incrementee; }
12+
13+
#endif // TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_CONSTRUCTOR_CALLS_FUNCTION_H
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#ifndef TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_CONSTRUCTOR_CALLS_METHOD_H
2+
#define TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_CONSTRUCTOR_CALLS_METHOD_H
3+
4+
struct Incrementor {
5+
int increment(int t) { return t + 1; }
6+
};
7+
8+
struct IncrementUser {
9+
int incrementee;
10+
IncrementUser(int value) { incrementee = Incrementor().increment(value); }
11+
};
12+
13+
inline int callConstructor(int value) {
14+
return IncrementUser(value).incrementee;
15+
}
16+
17+
#endif // TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_CONSTRUCTOR_CALLS_METHOD_H
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_FIELD_INIT_CALLS_FUNCTION_H
2+
#define TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_FIELD_INIT_CALLS_FUNCTION_H
3+
4+
inline int increment(int t) { return t + 1; }
5+
6+
struct Incrementor {
7+
int incrementee = increment(41);
8+
};
9+
10+
inline int initializeField() { return Incrementor().incrementee; }
11+
12+
#endif // TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_FIELD_INIT_CALLS_FUNCTION_H
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_METHOD_CALLS_FUNCTION_H
2+
#define TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_METHOD_CALLS_FUNCTION_H
3+
4+
inline int increment(int t) { return t + 1; }
5+
6+
struct Incrementor {
7+
int callIncrement(int value) { return increment(value); }
8+
};
9+
10+
inline int callMethod(int value) { return Incrementor().callIncrement(value); }
11+
12+
#endif // TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_METHOD_CALLS_FUNCTION_H
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#ifndef TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_METHOD_CALLS_METHOD_FROM_NESTED_STRUCT_H
2+
#define TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_METHOD_CALLS_METHOD_FROM_NESTED_STRUCT_H
3+
4+
struct IncrementUser {
5+
struct Incrementor {
6+
int increment(int t) { return t + 1; }
7+
};
8+
int callIncrement(int value) { return Incrementor().increment(value); }
9+
};
10+
11+
inline int callMethod(int value) {
12+
return IncrementUser().callIncrement(value);
13+
}
14+
15+
#endif // TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_METHOD_CALLS_METHOD_FROM_NESTED_STRUCT_H
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_METHOD_CALLS_METHOD_H
2+
#define TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_METHOD_CALLS_METHOD_H
3+
4+
struct Incrementor {
5+
int increment(int t) { return t + 1; }
6+
};
7+
8+
struct IncrementUser {
9+
int callIncrement(int value) { return Incrementor().increment(value); }
10+
};
11+
12+
inline int callMethod(int value) {
13+
return IncrementUser().callIncrement(value);
14+
}
15+
16+
#endif // TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_METHOD_CALLS_METHOD_H
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
module ConstructorCallsFunction {
2+
header "constructor-calls-function.h"
3+
}
4+
5+
module ConstructorCallsFunctionFromNestedStruct {
6+
header "constructor-calls-function-from-nested-struct.h"
7+
}
8+
9+
module ConstructorCallsMethod {
10+
header "constructor-calls-method.h"
11+
}
12+
13+
module FieldInitCallsFunction {
14+
header "field-init-calls-function.h"
15+
}
16+
17+
module MethodCallsFunction {
18+
header "method-calls-function.h"
19+
}
20+
21+
module MethodCallsMethod {
22+
header "method-calls-method.h"
23+
}
24+
25+
module MethodCallsMethodFromNestedStruct {
26+
header "method-calls-method-from-nested-struct.h"
27+
}
28+
29+
module StaticVarInitCallsFunction {
30+
header "static-var-init-calls-function.h"
31+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_STATIC_VAR_INIT_CALLS_FUNCTION_H
2+
#define TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_STATIC_VAR_INIT_CALLS_FUNCTION_H
3+
4+
inline int increment(int t) { return t + 1; }
5+
6+
struct Incrementor {
7+
static int incrementee;
8+
};
9+
10+
int Incrementor::incrementee = increment(41);
11+
12+
inline int initializeStaticVar() {
13+
return Incrementor::incrementee;
14+
}
15+
16+
#endif // TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_STATIC_VAR_INIT_CALLS_FUNCTION_H
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-cxx-interop)
2+
//
3+
// REQUIRES: executable_test
4+
5+
import ConstructorCallsFunction
6+
import StdlibUnittest
7+
8+
var MembersTestSuite = TestSuite("MembersTestSuite")
9+
10+
MembersTestSuite.test("constructor calls function") {
11+
expectEqual(42, callConstructor(41))
12+
}
13+
14+
runAllTests()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-cxx-interop)
2+
//
3+
// REQUIRES: executable_test
4+
5+
import ConstructorCallsFunctionFromNestedStruct
6+
import StdlibUnittest
7+
8+
var MembersTestSuite = TestSuite("MembersTestSuite")
9+
10+
MembersTestSuite.test("constructor calls function from nested struct") {
11+
expectEqual(42, callConstructor(41))
12+
}
13+
14+
runAllTests()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s
2+
3+
import ConstructorCallsFunctionFromNestedStruct
4+
5+
public func getIncrementorValue() -> CInt {
6+
return callConstructor(41)
7+
}
8+
9+
// CHECK: define linkonce_odr{{( dso_local)?}} i32 @{{_Z9incrementi|"\?increment@@YAHH@Z"}}(i32 %t)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s
2+
3+
import ConstructorCallsFunction
4+
5+
public func getIncrementorValue() -> CInt {
6+
return callConstructor(41)
7+
}
8+
9+
// CHECK: define linkonce_odr{{( dso_local)?}} i32 @{{_Z9incrementi|"\?increment@@YAHH@Z"}}(i32 %t)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-cxx-interop)
2+
//
3+
// REQUIRES: executable_test
4+
5+
import ConstructorCallsMethod
6+
import StdlibUnittest
7+
8+
var MembersTestSuite = TestSuite("MembersTestSuite")
9+
10+
MembersTestSuite.test("constructor calls method") {
11+
expectEqual(42, callConstructor(41))
12+
}
13+
14+
runAllTests()
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s
2+
3+
import ConstructorCallsMethod
4+
5+
public func getIncrementorValue() -> CInt {
6+
return callConstructor(41)
7+
}
8+
9+
// CHECK: define linkonce_odr{{( dso_local)?}} i32 @{{_ZN11Incrementor9incrementEi|"\?increment@Incrementor@@QEAAHH@Z"}}(%struct.Incrementor* %this, i32 %t)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-cxx-interop)
2+
//
3+
// REQUIRES: executable_test
4+
5+
import FieldInitCallsFunction
6+
import StdlibUnittest
7+
8+
var MembersTestSuite = TestSuite("MembersTestSuite")
9+
10+
MembersTestSuite.test("field init calls function") {
11+
expectEqual(42, initializeField())
12+
}
13+
14+
runAllTests()
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s
2+
3+
import FieldInitCallsFunction
4+
5+
public func getInitializedField() -> CInt {
6+
return initializeField()
7+
}
8+
9+
// CHECK: define linkonce_odr{{( dso_local)?}} i32 @{{_Z9incrementi|"\?increment@@YAHH@Z"}}(i32 %t)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-cxx-interop)
2+
//
3+
// REQUIRES: executable_test
4+
5+
import MethodCallsFunction
6+
import StdlibUnittest
7+
8+
var MembersTestSuite = TestSuite("MembersTestSuite")
9+
10+
MembersTestSuite.test("method calls function") {
11+
expectEqual(42, callMethod(41))
12+
}
13+
14+
runAllTests()
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s
2+
3+
import MethodCallsFunction
4+
5+
public func getValueFromMethod() -> CInt {
6+
return callMethod(41)
7+
}
8+
9+
// CHECK: define linkonce_odr{{( dso_local)?}} i32 @{{_Z9incrementi|"\?increment@@YAHH@Z"}}(i32 %t)

0 commit comments

Comments
 (0)