[cfe-dev] C++ Constructors & Destructors in the AST

Chris Lattner clattner at apple.com
Mon Apr 20 16:40:36 PDT 2009


Anders, Doug, Daniel and I discussed the representation of  
temporaries, constructors and destructors in the AST.

The basic goal is to make it explicit what temporaries are available,  
allow (only) the C++ front-end to reason about the lifetimes of  
temporaries (e.g. temps destroyed at the end of statements), allow the  
c++ front-end to do copy constructor elision optimizations, and to  
allow downstream clients (codegen, static analyzer etc) to know where  
destructors are run, etc.

We decided to make a new form of VarDecl, named CXXTempVarDecl.  These  
are always implicitly generated by the compiler, they never have an  
explicit representation in the source code.  These CXXTempVarDecls are  
inserted into the declcontext of the function, but they have no  
declstmt or other defining point in the code.

We introduce two new expressions "CXXConstruct" and "CXXDestroy".   
CXXConstruct becomes the only way to reference a constructor decl, and  
DeclRefExpr will assert if you try to create it with a constructor.   
CXXConstruct is a variadic node similar to a call, which takes a decl  
to initialize, a decl for the constructor to call (e.g. copy ctor,  
default ctor, converting ctor, etc) and an optional variadic list of  
arguments to pass into the constructor.   CXXDestroy takes a decl that  
is destroyed at that point - it basically represents running the  
destructor on the decl.

Some examples:

This:
{ T x;
...
}

Turns into:

(VarDecl 'x' Type=T,
    Init = CXXConstruct("x", "T::T()")
...
(CXXDestroy "x")

If you have a constructor with direct initialization, we'd get:

{ T x(4);
...
}

(VarDecl 'x' Type=T,
    Init = CXXConstruct("x", "T::T(int)", 4)
...
(CXXDestroy "x")


If you have indirect initialization and the copy constructor has not  
been elided, then we get a temporary:

{ T x = 4;
...
}

(VarDecl 'x' Type=T,
    Init = CXXConstruct("x", "T::T(const T&)",
                 CXXConstruct("somecxxtempdecl", "T::T(int)", 4))
(CXXDestroy "somecxxtempdecl")
...
(CXXDestroy "x")


This illustrates the need for an explicit CXXDestroy: statement local  
temporaries need to have specific controlled lifetimes that we don't  
want clients to have to reason about.


Clients that want to walk the AST, such as codegen, need to maintain a  
cleanup stack.  This is already required for VLAS, try block,  
@synchronized, etc.  This stack would get an entry for a  
"constructible"  decl when a CXXConstruct is codegen'd.  For example,  
if an exception is thrown after a VarDecl for a constructible type is  
seen but before it is constructed, it should not be destroyed.   
Between these two points, the VarDecl wouldn't exist on the cleanup  
stack.

The idea is that CXXDestroy would explicitly remove these decls from  
the cleanup stack, which is why we need an explicit marker that sema  
can place to say where things are known to be constructed and when  
they are known to be destroyed.  If there is no CXXDestroy for a decl,  
then it is live until the containing scope is done.  This is useful  
for the common case when a decl is live to the end of its scope, as  
when extending the lifetime of a temporary with const& or in the case  
of normal variable decls.

Still to discuss after the basic pieces are done:
1. Global variable initialization, where to CXXTempVarDecls go?  Just  
cram them into the containing declcontext?  Should sema explicitly  
generate the "translation unit constructor" function or not?
2. Conditional liveness of temporaries, how to we represent the  
condition to destroy a temp.

-Chris




More information about the cfe-dev mailing list