Generating function definition for function that's only called during unwinding


I’m trying to understand how clang keeps track of which declarations are called within a translation unit and decides to codegen their definitions.

DeclBase.h has a markUsed to keep track of ODR use, and I think that the decl can be found from the symbol table via ASTContext.h (for example looking up a template via GetQualifiedTemplateName → getAsTemplateDecl → setIsUsed ). This is just a result of fumbling around in the dark, I could be totally wrong.

I’d like to know what part of the system is responsible for tracking usage, and whether that’s the same mechanism that decides whether to CodeGen basic blocks with function definitions.

Particularly, I’m curious about how the decision should be made whether to emit a definition for a destructor that it is only called in a cleanup block that’s only invoked during unwinding. I don’t actually understand how or whether the control flow for unwinding is expressed in the AST at all. I can’t see anything related to unwinding control flow with -ast-dump for instance.

I’ve been spending some time debugging a particular test program at bug 33778 that fails to link because a block only referenced from an LSDA contains a call instruction to a destructor that has no generated definition. I’m hitting a wall because I’m pretty ignorant of the structure of the codebase and any guidance would be appreciated.

Any general guidance or documentation to help find my way around clang would be awesome too.


P.S. The program that doesn’t link without -fno-exceptions:


struct X {
std::function<int()> F;
std::function<int()> G;

void foo(const std::function<int()>& a) {
new X{a, a};

int main() {
return 0;

Hmmm… It seems like I should check out how the UseList on Value (and its child BasicBlock) work.

I could be wrong here, but AFAICT, the implementation is allowed to elide initialisation/allocation if it can prove that the allocation is not observable. In this case, I would look at the generated IR and whether the call to ‘new’ turns into a ‘malloc’ that gets elided later. -O0 and/or other optimisation levels might do different things too.

If you change this to return the pointer and print it, do you get the destructor code-gen’ed?

You're probably barking up the wrong tree; the problem has nothing to do with code generation. Consider the following testcase:

template<typename T> struct Z {
T t;
Z(const Z&) { throw 1; }
~Z() { *t = 10; }
struct X {
Z<int> x;
Z<int> y;
void foo(Z<int>& a) {
new X{a, a};

This should emit an error because "t" can't be dereferenced. gcc correctly the error; clang doesn't.

Not exactly sure why the destructor doesn't get marked odr-used, but I'd start with Sema::BuildCXXNew.