-
Notifications
You must be signed in to change notification settings - Fork 10.4k
/
Copy pathGenClangDecl.cpp
407 lines (349 loc) · 14.4 KB
/
GenClangDecl.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
//===--- GenClangDecl.cpp - Swift IRGen for imported Clang declarations ---===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "IRGenModule.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ClangModuleLoader.h"
#include "swift/AST/Expr.h"
#include "swift/AST/IRGenOptions.h"
#include "swift/AST/Stmt.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclGroup.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Sema/Sema.h"
#include "llvm/ADT/SmallPtrSet.h"
using namespace swift;
using namespace irgen;
namespace {
class ClangDeclFinder
: public clang::RecursiveASTVisitor<ClangDeclFinder> {
std::function<void(const clang::Decl *)> callback;
ClangModuleLoader *clangModuleLoader;
public:
template <typename Fn>
explicit ClangDeclFinder(Fn fn, ClangModuleLoader *clangModuleLoader)
: callback(fn), clangModuleLoader(clangModuleLoader) {}
bool VisitDeclRefExpr(clang::DeclRefExpr *DRE) {
if (isa<clang::FunctionDecl>(DRE->getDecl()) ||
isa<clang::VarDecl>(DRE->getDecl())) {
callback(DRE->getDecl());
}
return true;
}
bool VisitMemberExpr(clang::MemberExpr *ME) {
if (isa<clang::FunctionDecl>(ME->getMemberDecl()) ||
isa<clang::VarDecl>(ME->getMemberDecl()) ||
isa<clang::FieldDecl>(ME->getMemberDecl())) {
callback(ME->getMemberDecl());
}
return true;
}
bool VisitFunctionDecl(clang::FunctionDecl *functionDecl) {
for (auto paramDecl : functionDecl->parameters()) {
if (paramDecl->hasDefaultArg()) {
if (FuncDecl *defaultArgGenerator =
clangModuleLoader->getDefaultArgGenerator(paramDecl)) {
// Deconstruct the Swift function that was created in
// SwiftDeclSynthesizer::makeDefaultArgument and extract the
// underlying Clang function that was also synthesized.
BraceStmt *body = defaultArgGenerator->getTypecheckedBody();
auto returnStmt =
cast<ReturnStmt>(body->getSingleActiveElement().get<Stmt *>());
auto callExpr = cast<CallExpr>(returnStmt->getResult());
auto calledFuncDecl = cast<FuncDecl>(callExpr->getCalledValue());
auto calledClangFuncDecl =
cast<clang::FunctionDecl>(calledFuncDecl->getClangDecl());
callback(calledClangFuncDecl);
}
}
}
return true;
}
bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *CXXCD) {
callback(CXXCD);
for (clang::CXXCtorInitializer *CXXCI : CXXCD->inits()) {
if (clang::FieldDecl *FD = CXXCI->getMember()) {
callback(FD);
// A throwing constructor might throw after the field is initialized,
// emitting additional cleanup code that destroys the field. Make sure
// we record the destructor of the field in that case as it might need
// to be potentially emitted.
if (auto *recordType = FD->getType()->getAsCXXRecordDecl()) {
if (auto *destructor = recordType->getDestructor()) {
if (!destructor->isDeleted())
callback(destructor);
}
}
}
}
return true;
}
bool VisitCXXConstructExpr(clang::CXXConstructExpr *CXXCE) {
callback(CXXCE->getConstructor());
return true;
}
bool VisitCXXDeleteExpr(clang::CXXDeleteExpr *deleteExpr) {
if (auto cxxRecord = deleteExpr->getDestroyedType()->getAsCXXRecordDecl())
if (auto dtor = cxxRecord->getDestructor())
callback(dtor);
return true;
}
bool VisitVarDecl(clang::VarDecl *VD) {
if (auto cxxRecord = VD->getType()->getAsCXXRecordDecl())
if (auto dtor = cxxRecord->getDestructor())
callback(dtor);
return true;
}
bool VisitCXXBindTemporaryExpr(clang::CXXBindTemporaryExpr *BTE) {
// This is a temporary value with a custom destructor. C++ will implicitly
// call the destructor at some point. Make sure we emit IR for it.
callback(BTE->getTemporary()->getDestructor());
return true;
}
bool VisitCXXNewExpr(clang::CXXNewExpr *NE) {
callback(NE->getOperatorNew());
return true;
}
bool VisitBindingDecl(clang::BindingDecl *BD) {
if (auto *holdingVar = BD->getHoldingVar()) {
if (holdingVar->hasInit())
TraverseStmt(holdingVar->getInit());
}
return true;
}
bool VisitCXXInheritedCtorInitExpr(clang::CXXInheritedCtorInitExpr *CIE) {
if (auto ctor = CIE->getConstructor())
callback(ctor);
return true;
}
// Do not traverse unevaluated expressions. Doing to might result in compile
// errors if we try to instantiate an un-instantiatable template.
bool TraverseCXXNoexceptExpr(clang::CXXNoexceptExpr *NEE) { return true; }
bool TraverseCXXTypeidExpr(clang::CXXTypeidExpr *TIE) {
if (TIE->isPotentiallyEvaluated())
clang::RecursiveASTVisitor<ClangDeclFinder>::TraverseCXXTypeidExpr(TIE);
return true;
}
bool TraverseRequiresExpr(clang::RequiresExpr *RE) { return true; }
// Do not traverse type locs, as they might contain expressions that reference
// code that should not be instantiated and/or emitted.
bool TraverseTypeLoc(clang::TypeLoc TL) { return true; }
bool shouldVisitTemplateInstantiations() const { return true; }
bool shouldVisitImplicitCode() const { return true; }
};
// If any (re)declaration of `decl` contains executable code, returns that
// redeclaration; otherwise, returns nullptr.
// In the case of a function, executable code is contained in the function
// definition. In the case of a variable, executable code can be contained in
// the initializer of the variable.
clang::Decl *getDeclWithExecutableCode(clang::Decl *decl) {
if (auto fd = dyn_cast<clang::FunctionDecl>(decl)) {
const clang::FunctionDecl *definition;
if (fd->hasBody(definition)) {
return const_cast<clang::FunctionDecl *>(definition);
}
// If this is a potentially not-yet-instantiated template, we might
// still have a body.
if (fd->getTemplateInstantiationPattern())
return fd;
} else if (auto vd = dyn_cast<clang::VarDecl>(decl)) {
clang::VarDecl *initializingDecl = vd->getInitializingDeclaration();
if (initializingDecl) {
return initializingDecl;
}
} else if (auto fd = dyn_cast<clang::FieldDecl>(decl)) {
if(fd->hasInClassInitializer()) {
return fd;
}
}
return nullptr;
}
} // end anonymous namespace
void IRGenModule::emitClangDecl(const clang::Decl *decl) {
// Ignore this decl if we've seen it before.
if (!GlobalClangDecls.insert(decl->getCanonicalDecl()).second)
return;
// Fast path for the case where `decl` doesn't contain executable code, so it
// can't reference any other declarations that we would need to emit.
if (getDeclWithExecutableCode(const_cast<clang::Decl *>(decl)) == nullptr) {
ClangCodeGen->HandleTopLevelDecl(
clang::DeclGroupRef(const_cast<clang::Decl*>(decl)));
return;
}
SmallVector<const clang::Decl *, 8> stack;
stack.push_back(decl);
auto callback = [&](const clang::Decl *D) {
for (auto *DC = D->getDeclContext();; DC = DC->getParent()) {
// Check that this is not a local declaration inside a function.
if (DC->isFunctionOrMethod()) {
return;
}
if (DC->isFileContext()) {
break;
}
if (isa<clang::TagDecl>(DC)) {
break;
}
if (isa<clang::LinkageSpecDecl>(DC)) {
break;
}
D = cast<const clang::Decl>(DC);
}
if (!GlobalClangDecls.insert(D->getCanonicalDecl()).second) {
return;
}
stack.push_back(D);
};
ClangModuleLoader *clangModuleLoader = Context.getClangModuleLoader();
ClangDeclFinder refFinder(callback, clangModuleLoader);
auto &clangSema = clangModuleLoader->getClangSema();
while (!stack.empty()) {
auto *next = const_cast<clang::Decl *>(stack.pop_back_val());
// If this is a static member of a class, it might be defined out of line.
// If the class is templated, the definition of its static member might be
// templated as well. If it is, instantiate it here.
if (auto var = dyn_cast<clang::VarDecl>(next)) {
if (var->isStaticDataMember() &&
var->getTemplateSpecializationKind() ==
clang::TemplateSpecializationKind::TSK_ImplicitInstantiation)
clangSema.InstantiateVariableDefinition(var->getLocation(), var);
}
// If a function calls another method in a class template specialization, we
// need to instantiate that other function. Do that here.
if (auto *fn = dyn_cast<clang::FunctionDecl>(next)) {
// Make sure that this method is part of a class template specialization.
if (fn->getTemplateInstantiationPattern())
clangSema.InstantiateFunctionDefinition(fn->getLocation(), fn);
}
if (clang::Decl *executableDecl = getDeclWithExecutableCode(next)) {
refFinder.TraverseDecl(executableDecl);
next = executableDecl;
}
// Unfortunately, implicitly defined CXXDestructorDecls don't have a real
// body, so we need to traverse these manually.
if (auto *dtor = dyn_cast<clang::CXXDestructorDecl>(next)) {
if (dtor->isImplicit() && dtor->isDefaulted() && !dtor->isDeleted() &&
!dtor->doesThisDeclarationHaveABody())
clangSema.DefineImplicitDestructor(dtor->getLocation(), dtor);
if (dtor->isImplicit() || dtor->hasBody()) {
auto cxxRecord = dtor->getParent();
for (auto field : cxxRecord->fields()) {
if (auto fieldCxxRecord = field->getType()->getAsCXXRecordDecl())
if (auto *fieldDtor = fieldCxxRecord->getDestructor())
callback(fieldDtor);
}
for (auto base : cxxRecord->bases()) {
if (auto baseCxxRecord = base.getType()->getAsCXXRecordDecl())
if (auto *baseDtor = baseCxxRecord->getDestructor())
callback(baseDtor);
}
}
}
// If something from a C++ class is used, emit all virtual methods of this
// class because they might be emitted in the vtable even if not used
// directly from Swift.
if (auto *record = dyn_cast<clang::CXXRecordDecl>(next->getDeclContext())) {
if (auto *destructor = record->getDestructor()) {
// Ensure virtual destructors have the body defined, even if they're
// not used directly, as they might be referenced by the emitted vtable.
if (destructor->isVirtual() && !destructor->isDeleted())
ensureImplicitCXXDestructorBodyIsDefined(destructor);
}
for (auto *method : record->methods()) {
if (method->isVirtual()) {
callback(method);
}
}
}
if (auto var = dyn_cast<clang::VarDecl>(next))
if (!var->isFileVarDecl())
continue;
if (isa<clang::FieldDecl>(next)) {
continue;
}
ClangCodeGen->HandleTopLevelDecl(clang::DeclGroupRef(next));
}
}
llvm::Constant *
IRGenModule::getAddrOfClangGlobalDecl(clang::GlobalDecl global,
ForDefinition_t forDefinition) {
// Register the decl with the clang code generator.
if (auto decl = global.getDecl())
emitClangDecl(decl);
return ClangCodeGen->GetAddrOfGlobal(global, (bool) forDefinition);
}
void IRGenModule::finalizeClangCodeGen() {
// FIXME: We try to avoid looking for PragmaCommentDecls unless we need to,
// since clang::DeclContext::decls_begin() can trigger expensive
// de-serialization.
if (Triple.isWindowsMSVCEnvironment() || Triple.isWindowsItaniumEnvironment() ||
IRGen.Opts.LLVMLTOKind != IRGenLLVMLTOKind::None) {
// Ensure that code is emitted for any `PragmaCommentDecl`s. (These are
// always guaranteed to be directly below the TranslationUnitDecl.)
// In Clang, this happens automatically during the Sema phase, but here we
// need to take care of it manually because our Clang CodeGenerator is not
// attached to Clang Sema as an ASTConsumer.
for (const auto *D : ClangASTContext->getTranslationUnitDecl()->decls()) {
if (const auto *PCD = dyn_cast<clang::PragmaCommentDecl>(D)) {
emitClangDecl(PCD);
}
}
}
ClangCodeGen->HandleTranslationUnit(
*const_cast<clang::ASTContext *>(ClangASTContext));
}
void IRGenModule::ensureImplicitCXXDestructorBodyIsDefined(
clang::CXXDestructorDecl *destructor) {
if (destructor->isUserProvided() ||
destructor->doesThisDeclarationHaveABody())
return;
assert(!destructor->isDeleted() &&
"Swift cannot handle a type with no known destructor.");
// Make sure we define the destructor so we have something to call.
auto &sema = Context.getClangModuleLoader()->getClangSema();
sema.DefineImplicitDestructor(clang::SourceLocation(), destructor);
}
/// Retrieves the base classes of a C++ struct/class ordered by their offset in
/// the derived type's memory layout.
SmallVector<CXXBaseRecordLayout, 1>
irgen::getBasesAndOffsets(const clang::CXXRecordDecl *decl) {
auto &layout = decl->getASTContext().getASTRecordLayout(decl);
// Collect the offsets and sizes of base classes within the memory layout
// of the derived class.
SmallVector<CXXBaseRecordLayout, 1> baseOffsetsAndSizes;
for (auto base : decl->bases()) {
if (base.isVirtual())
continue;
auto baseType = base.getType().getCanonicalType();
auto baseRecordType = cast<clang::RecordType>(baseType);
auto baseRecord = baseRecordType->getAsCXXRecordDecl();
assert(baseRecord && "expected a base C++ record");
if (baseRecord->isEmpty())
continue;
auto offset = Size(layout.getBaseClassOffset(baseRecord).getQuantity());
auto size =
Size(decl->getASTContext().getTypeSizeInChars(baseType).getQuantity());
baseOffsetsAndSizes.push_back({baseRecord, offset, size});
}
// In C++, base classes might get reordered if the primary base was not
// the first base type on the declaration of the class.
llvm::sort(baseOffsetsAndSizes, [](const CXXBaseRecordLayout &lhs,
const CXXBaseRecordLayout &rhs) {
return lhs.offset < rhs.offset;
});
return baseOffsetsAndSizes;
}