Ownership of Stmts and Exprs, and destroying them

Looks great to me!

Minor stuff:

+//===--- AstGuard.h - C Language Parser -------------------------*- C++ -*-===//

Please update comment

+ // RAII guard for freeing StmtTys and ExprTys on early exit in the parser.
+ template <void (Action::*Destroyer)(void*)>

Please make this a doxygen comment and mention the two instantiations that will be used. Likewise for ASTVector.

+ void destroy() {
+ if (Node)
+ (Actions.*Destroyer)(Node);
+ }

Thanks for adding the check. This avoids the virtual dispatch in the common case when ownership is taken before it is destroyed.

+ template <unsigned N>
+ class StmtVector : public ASTVector<&Action::DeleteStmt, N>

It is probably fine to just make these typedefs with a fixed N, say "12".

Otherwise, the patch looks great. Thanks for tackling this Sebastian!


I don't want to speak for Doug, but here is some working C++0X demo code which I believe demonstrates. Parts of it were borrowed from Sebastian's patch:

#include <memory>
#include <cstdio>

class Action
     void DeleteStmt(void*) {std::printf("DeleteStmt\n");}
     void DeleteExpr(void*) {std::printf("DeleteExpr\n");}

template <void (Action::*Destroyer)(void*)>
class ASTDeleter {
   Action &Actions;

   // Reference member prevents copy assignment.

   explicit ASTDeleter(Action &actions) : Actions(actions) {}

   void operator()(void* Node) {

typedef std::unique_ptr<void, ASTDeleter<&Action::DeleteStmt> > StmtGuard;
typedef std::unique_ptr<void, ASTDeleter<&Action::DeleteExpr> > ExprGuard;

ExprGuardSource(Action& Actions)
     return ExprGuard((void*)1, ExprGuard::deleter_type(Actions));

ExprGuardSink(ExprGuard p1)
     std::printf("ExprGuardSink: begin\n");
     ExprGuard p2 = std::move(p1);
     } // DeleteExpr called here
     std::printf("ExprGuardSink: end\n");

int main()
     std::printf("main: begin\n");
     void* Body = 0;
     Action Actions;
     StmtGuard BodyGuard1(Body, StmtGuard::deleter_type(Actions));
     ExprGuard BodyGuard2 = ExprGuardSource(Actions);
     std::printf("main: end\n");


main: begin
ExprGuardSink: begin
ExprGuardSink: end
main: end


DeleteStmt never fires because the void* in BodyGuard1 is null. The void* briefly owned by BodyGuard2 in main() begins life in ExprGuardSource, gets passed up to main (without a release(), or take() as it is currently spelled), and then passed back down to ExprGuardSink which eventually runs DeleteExpr on it. Both StmtGuard and ExprGuard have member get(), with the same semantics as ASTGuard, and member release() with the same semantics as ASTGuard::take(). They lack the reset() templated on N (since unique_ptr is ignorant of Action), but have the other reset().


The above code was compiled with g++-4.3 -std=c++0x -nostdinc++ and the emulation at:


will not work in the above example (it depended on a gcc bug that has since been fixed). I believe Dave Abrahams has a move emulation package at boost that could possibly be adapted, but that is more work and complexity which may or may not be warranted. I believe it probably works similarly to today's auto_ptr, but perhaps disables moving from lvalues; <shrug> I haven't looked at it in years.

Also one needs to be careful with the Action& lifetime living in the deleter that is getting passed around inside the unique_ptr.

The main difference between unique_ptr<void, ASTDeleter<&Action::DeleteStmt> > and ASTGuard is that the former is moveable and thus both documents and enforces the pointer transfer of ownership from scope to scope. Otherwise they do the same job.


[snip code]

Yeah, that's the idea. To narrate a bit more: when there's a clear
ownership model that's tied to the type system, it becomes very easy
to enforce proper memory management. By using unique_ptr-like smart
pointers in the interface, we enforce the transfer of ownership:
passing an expression to one of Action's methods via a unique_ptr
interface transfers ownership into the implementor of Action. Since,
by default, that smart pointer will delete what it's storing, even an
empty Action method implementation will not leak. It's the same thing
with returning a unique_ptr-like object from the Action routines,
e.g., from ActOnCallExpr: if ExprResult is a smart pointer, we're
transferring ownership back from the Action implementation to the
parser. The parser can either re-transfer ownership to the Action
implementation (by passing the expression back through an Action
method) or just leave it alone, in which case it will be deallocated

The end result, while not idiot-proof, should mean that we have our
memory management on solid footing even in the tricky cases of failed
parses, semantic disasters, etc. Plus, the whole scheme works even in
the face of exceptions.

  - Doug

Ok, I'm convinced! :slight_smile: