[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