This is an archive of the discontinued LLVM Phabricator instance.

[AST] [NFC] Introduce an abstract superclass for CallExpr | CXXConstructExpr | ObjCMessageExpr
AbandonedPublic

Authored by george.karpenkov on Jul 5 2018, 7:32 PM.

Diff Detail

Event Timeline

rsmith requested changes to this revision.Jul 5 2018, 9:18 PM
rsmith added inline comments.
clang/include/clang/AST/Expr.h
2280–2285

Replace with using Expr::Expr;?

2292–2316

Don't use virtual functions here; we don't want to pay for a vtable in each CallExpr. Instead, make these non-virtual functions that dispatch based on the actual type (using dyn_cast / isa).

2305–2309

If we want to guarantee that the arguments are contiguous, I don't think there's any need to expose getArgs here; new code should use arguments instead. (Also the return type of getArgs is wrong resulting in a violation of the aliasing rules -- another reason to avoid it.)

2311

This setter should not be in the abstract interface. (It probably shouldn't be in the subtypes either.)

This revision now requires changes to proceed.Jul 5 2018, 9:18 PM
clang/include/clang/AST/Expr.h
2292–2316

Ah right, I was wondering whether it would be acceptable while writing this.
Any protips on avoiding 6-lines boilerplate on every single method here?
One way would be just to let the abstract superclass handle the arguments.

george.karpenkov marked 5 inline comments as done.

@rsmith i hope i'm on the right track here.

It would be nice to throw out all the 10+ methods argument-getter and just use an ArrayRef, but it's a larger/scarier diff.
Also caching constructed ArrayRef doesn't work, as different parts of the codebase mutate expressions after construction =/

rsmith added inline comments.Jul 6 2018, 3:15 PM
clang/include/clang/AST/Expr.h
2387

This is a violation of the aliasing rules that we're trying to move away from (it's why we have the ExprIterator / ConstExprIterator classes rather than using ArrayRef here). Can we keep using the iterator_range<arg_iterator> approach?

clang/include/clang/AST/Expr.h
2387

Right, I see now.
Keeping iterator ranges requires having ~10 getters, with 6 lines of boilerplate code each for doing RTTI. Do you have other suggestions?

NoQ added a comment.Jul 16 2018, 5:09 PM

I realized that some of the motivating examples behind this patch also require uniform support for CXXNewExpr's placement arguments, otherwise we'd still have to duplicate code. And those have slightly different syntax, so it won't be a transparent change anymore (though it might be possible to avoid breaking the API by providing duplicate methods).

@rjmccall do you think you would have any suggestions on what can be done here?
The currently proposed version would have a non-virtual superclass which would use RTTI to dispatch to the generic parametersX methods.
That would require duplicating around 7 lines of boilerplate RTTI-dispatch code per each getter, and there are about 6 of them.

@rjmccall do you think you would have any suggestions on what can be done here?
The currently proposed version would have a non-virtual superclass which would use RTTI to dispatch to the generic parametersX methods.
That would require duplicating around 7 lines of boilerplate RTTI-dispatch code per each getter, and there are about 6 of them.

Er, by RTTI you mean just switching on getStmtClass()?

I'm not really opposed to this change in general, but I wonder if it's really the right way of achieving its goal, which appears to just be to make it easier to recognize user-level calls in the AST. Would it be better to just have a dedicated CallVisitor class that gets notified about all the different options, i.e. CallExpr, ObjCMessageExpr, CXXConstructExpr, operator new calls, and so on?

Er, by RTTI you mean just switching on getStmtClass()?

As far as I understand yes, basically doing dyn_cast myself as opposed to using virtual dispatch.

Would it be better to just have a dedicated CallVisitor class that gets notified about all the different options, i.e. CallExpr, ObjCMessageExpr, CXXConstructExpr, operator new calls, and so on?

We sort of have it in the analyzer, cf. CallEvent. A lot of code still ends up being duplicated/or worse, not general enough.

Er, by RTTI you mean just switching on getStmtClass()?

As far as I understand yes, basically doing dyn_cast myself as opposed to using virtual dispatch.

Would it be better to just have a dedicated CallVisitor class that gets notified about all the different options, i.e. CallExpr, ObjCMessageExpr, CXXConstructExpr, operator new calls, and so on?

We sort of have it in the analyzer, cf. CallEvent. A lot of code still ends up being duplicated/or worse, not general enough.

CallEvent looks like a good API to me. What do you think would become easier about it if you had this superclass? Or do people just not use CallEvent enough?

@rjmccall It's not sufficient.

  1. It requires context, which is not always available (that's the biggest problem).
  2. It can be too verbose - need to create a temporary object just when a simple isa/dyn_cast should suffice

For needed use cases: e.g. consider the duplication required in https://reviews.llvm.org/D48249

@rjmccall It's not sufficient.

  1. It requires context, which is not always available (that's the biggest problem).
  2. It can be too verbose - need to create a temporary object just when a simple isa/dyn_cast should suffice

Okay, so you want an AST CallSite equivalent that just a failable constructor/factory? Something with the basic interface of ApplySite in Swift SIL:

https://github.com/apple/swift/blob/463a46704694ef27680f27095c05f1b901e07b80/include/swift/SIL/SILInstruction.h#L7501

Because this superclass gets you, like, 10% of the way there, and then will never ever get you any closer.

@rjmccall Sorry, I'm not quite sure what do you mean.
All the cases we have seen in the analyzer so far (and we have seen quite a lot) would have been solved by having generic arguments().
What currently ends up happening is that in the best case the good is duplicated, and in the worst case, the code does not handle unexpected types of calls correctly (objc-message-send instead of a function call)

@rjmccall Sorry, I'm not quite sure what do you mean.
All the cases we have seen in the analyzer so far (and we have seen quite a lot) would have been solved by having generic arguments().
What currently ends up happening is that in the best case the good is duplicated, and in the worst case, the code does not handle unexpected types of calls correctly (objc-message-send instead of a function call)

Well, part of what I'm saying is that if you're willing to break away from the assumption that this is an abstract base class of various Expr classes, and instead just make it a separate class that just has its own convenient testing operations when you're starting with an Expr*, you can actually model calls that aren't just expressions with the same abstractions. Like you can have a CallSite that refers to the call to operator new in a CXXNewExpr, or to an implicit destructor call, or whatever.

george.karpenkov abandoned this revision.Aug 29 2018, 11:29 AM