Function body type

Hi,

Small problem: the type of a function's body is currently CompoundStmt.
However, in C++ we have function-try-blocks:

void f() try {
  // things that could throw
} catch(something) {
  // ...
} catch(...) {
  // ...
}

The obvious solution here is to change FunctionDecl so that its body can
be either a CompoundStmt or a CXXTryStmt (by having getBody() return
either a Stmt* or a PointerVariant). However, this change is quite
disrupting to many, many components.
The alternative is to represent such functions as if they were written as

void f() {
  try { .... } ...
}

There exists a difference in semantics for constructors and destructors
only (where a function-try-block covers base and member
initializers/destructors, whereas a try-block does not), which could be
stored in a flag in those functions. But I don't like this workaround.

Thoughts?

Sebastian

Hi,

Small problem: the type of a function's body is currently CompoundStmt.
However, in C++ we have function-try-blocks:

void f() try {
// things that could throw
} catch(something) {
// ...
} catch(...) {
// ...
}

The obvious solution here is to change FunctionDecl so that its body can
be either a CompoundStmt or a CXXTryStmt (by having getBody() return
either a Stmt* or a PointerVariant). However, this change is quite
disrupting to many, many components.

I think using Stmt* is perfectly reasonable. I am not at all worried about this change touching many components; it's a mechanical transformation that is unlikely to break anything and it is the right thing to do.

It probably makes sense to add a

   CompoundStmt *getCompoundBody(ASTContext &Context, const FunctionDecl *&Definition) {
     return dyn_cast_or_null<CompoundStmt>(getBody(Context, Definition));
   }

so that we've encapsulated the dyn_cast_or_null into a single place.

The alternative is to represent such functions as if they were written as

void f() {
try { .... } ...
}

There exists a difference in semantics for constructors and destructors
only (where a function-try-block covers base and member
initializers/destructors, whereas a try-block does not), which could be
stored in a flag in those functions. But I don't like this workaround.

I don't like this workaround, either. We want the AST to describe the syntax and semantics naturally, and this kind of flag would really confuse the issue.

  - Doug

Douglas Gregor wrote:

I am not at all worried about this change touching many components;
it's a mechanical transformation that is unlikely to break anything
and it is the right thing to do.

There is a virtual CompoundStmt* getBody(ASTContext&) in Decl. It's
widely used. :frowning:

Sebastian

Two weeks ago, that function was

  virtual CompoundStmt *getBody()

Changing signatures throughout Clang isn't such a huge thing.

  - Doug

Agreed. Clients will also need to reason about the correct Stmt anyway. An imaginary CompoundStmt that wraps a CXXTryStmt down the road just seems problematic for clients that actually care about the "real" function body.

I strongly prefer a PointVariant unless there is a compelling reason not to use one. It directly documents in the type signature of the method what the possible returned Stmts can be. Not everyone using the ASTs is going to be a language expert.