This is an archive of the discontinued LLVM Phabricator instance.

[CFG] [analyzer] Add MaterializeTemporaryExpr into the construction context.
ClosedPublic

Authored by NoQ on Feb 19 2018, 11:57 AM.

Details

Summary

MaterializeTemporaryExpr captures lifetime extension information. In the analyzer it is important to have this information at construction site because we would be dealing only with rvalue expressions for quite a while, but in order to perform lifetime extension later, we need to put extra effort into keeping the lvalue around.

MaterializeTemporaryExpr is added either as a trigger statement if there's no CXXBindTemporaryExpr within it (eg. const C &c(123); or the actual (not the elidable copy) constructor in C foo() { return C(123); }), or as a parent construction context if there already is a CXXBindTemporary. In the latter case, it is included in the dump and tested accordingly. I understand that this is quite counter-intuitive at the moment (easy to construct, hard to use) and i'd make super sure that ConstructionContext is refactored later to become easy to understand and work with - separation of different "kinds" of construction contexts and fancy kind-specific helper methods are absolutely necessary. But for now i'm focused on getting it to work.

Diff Detail

Repository
rL LLVM

Event Timeline

NoQ created this revision.Feb 19 2018, 11:57 AM
NoQ added a comment.Feb 19 2018, 12:46 PM

eg. const C &c(123); or the actual (not the elidable copy) constructor in C foo() { return C(123); }

Emm, sry, never mind, forget it, i was trying to say that the reason why we don't have a CXXBindTemporary is because we don't have a destructor in class C, not because we have specific syntax patterns.

This fix does not cause changes in the analyzer yet. Even though we are providing construction contexts in a bit more cases, and even if we used them in the analyzer, we wouldn't get any functional change yet, because temporary constructors that require no destructors are inlined anyway, regardless of construction context, and an exact same temporary region is accidentally created for them. So the point of this patch is not to make more construction contexts available, but to provide better construction contexts in the CXXBindTemporary case, which will be used later.

This revision is now accepted and ready to land.Feb 20 2018, 8:44 PM
This revision was automatically updated to reflect the committed changes.
This revision was automatically updated to reflect the committed changes.
lxfind added a subscriber: lxfind.Aug 2 2021, 10:21 PM

Hi! I have a question regarding the implementation of "VisitMaterializeTemporaryExpr". Specifically, I wonder if we should skip visiting the children? Would't visiting the children of MaterializeTemporaryExpr cause the same expression to be visited twice?

I am debugging a crash in ThreadSafetyAnalyzer, which triggers this assertion: https://github.com/llvm/llvm-project/blob/main/clang/lib/Analysis/ThreadSafety.cpp#L534
Basically it's adding the same declaration twice from the same CFG block.
And I found that the redundant declaration is added during CFG construction, when processing cpp source code like this:

co_return ({ static constexpr mydomain::logdevice::ErrorStacktrace::Frame frame{ __FUNCTION__, "logdevice/common/ZookeeperClientBase.cpp", 103}; mydomain::logdevice::detail::makeUnexpected(&frame, toStatus(result.rc_)); });

which corresponds to the following AST:

|   `-CompoundStmt 0x227b13f8
|     `-CoreturnStmt 0x227b13d0
|       |-CXXBindTemporaryExpr 0x22776218 'folly::Unexpected<Error>':'class folly::Unexpected<struct mydomain::logdevice::Error>' (CXXTemporary 0x22776218)
|       | `-StmtExpr 0x227761f0 'folly::Unexpected<Error>':'class folly::Unexpected<struct mydomain::logdevice::Error>'
|       |   `-CompoundStmt 0x227761d0
|       |     |-DeclStmt 0x22771e88
|       |     | `-VarDecl 0x22771c50  used frame 'const mydomain::logdevice::class ErrorStacktrace::Frame':'const struct mydomain::logdevice::ErrorStacktrace::Frame' static constexpr listinit
|       |     |   |-value: Struct
|       |     |   | `-fields: LValue <todo>, LValue <todo>, Int 103
|       |     |   `-InitListExpr 0x22771da8 'const mydomain::logdevice::class ErrorStacktrace::Frame':'const struct mydomain::logdevice::ErrorStacktrace::Frame'
|       |     |     |-ImplicitCastExpr 0x22771e00 'const char *' <ArrayToPointerDecay>
|       |     |     | `-PredefinedExpr 0x22771cd8 'const char [8]' lvalue __FUNCTION__
|       |     |     |   `-StringLiteral 0x22771cb8 'const char [8]' lvalue "getData"
|       |     |     |-ImplicitCastExpr 0x22771e18 'const char *' <ArrayToPointerDecay>
|       |     |     | `-StringLiteral 0x22771cf0 'const char [41]' lvalue "logdevice/common/ZookeeperClientBase.cpp"
|       |     |     `-IntegerLiteral 0x22771d30 'int' 103
|       |     `-ExprWithCleanups 0x227761b8 'folly::Unexpected<Error>':'class folly::Unexpected<struct mydomain::logdevice::Error>'
|       |       `-CXXBindTemporaryExpr 0x22776198 'folly::Unexpected<Error>':'class folly::Unexpected<struct mydomain::logdevice::Error>' (CXXTemporary 0x22776198)
|       |         `-CallExpr 0x22776160 'folly::Unexpected<Error>':'class folly::Unexpected<struct mydomain::logdevice::Error>'
|       |           |-ImplicitCastExpr 0x22776148 'folly::Unexpected<Error> (*)(const class ErrorStacktrace::Frame *, mydomain::logdevice::Status)' <FunctionToPointerDecay>
|       |           | `-DeclRefExpr 0x227760b8 'folly::Unexpected<Error> (const class ErrorStacktrace::Frame *, mydomain::logdevice::Status)' lvalue Function 0x18b49568 'makeUnexpected' 'folly::Unexpected<Error> (const class ErrorStacktrace::Frame *, mydomain::logdevice::Status)'
|       |           |-UnaryOperator 0x22771fb8 'const mydomain::logdevice::class ErrorStacktrace::Frame *' prefix '&' cannot overflow
|       |           | `-DeclRefExpr 0x22771f68 'const mydomain::logdevice::class ErrorStacktrace::Frame':'const struct mydomain::logdevice::ErrorStacktrace::Frame' lvalue Var 0x22771c50 'frame' 'const mydomain::logdevice::class ErrorStacktrace::Frame':'const struct mydomain::logdevice::ErrorStacktrace::Frame'
|       |           `-CallExpr 0x227720e0 'mydomain::logdevice::Status':'enum mydomain::logdevice::E'
|       |             |-ImplicitCastExpr 0x227720c8 'mydomain::logdevice::Status (*)(int)' <FunctionToPointerDecay>
|       |             | `-DeclRefExpr 0x22771ff8 'mydomain::logdevice::Status (int)' lvalue CXXMethod 0x221cddd0 'toStatus' 'mydomain::logdevice::Status (int)'
|       |             `-ImplicitCastExpr 0x22772108 'int' <LValueToRValue>
|       |               `-MemberExpr 0x22772038 'int' lvalue .rc_ 0x21f24480
|       |                 `-DeclRefExpr 0x22772018 'struct mydomain::logdevice::zk::GetResponse':'struct mydomain::logdevice::zk::GetResponse' lvalue Var 0x22731508 'result' 'struct mydomain::logdevice::zk::GetResponse':'struct mydomain::logdevice::zk::GetResponse'
|       `-ExprWithCleanups 0x227b13b8 'void'
|         `-CXXMemberCallExpr 0x227b1378 'void'
|           |-MemberExpr 0x227b1330 '<bound member function type>' .return_value 0x227b1230
|           | `-DeclRefExpr 0x22776238 'std::__coroutine_traits_impl<class folly::coro::Task<class folly::Expected<struct mydomain::logdevice::zk::GetResponse, struct mydomain::logdevice::Error> > >::promise_type':'class folly::coro::detail::TaskPromise<class folly::Expected<struct mydomain::logdevice::zk::GetResponse, struct mydomain::logdevice::Error> >' lvalue Var 0x227330a0 '__promise' 'std::__coroutine_traits_impl<class folly::coro::Task<class folly::Expected<struct mydomain::logdevice::zk::GetResponse, struct mydomain::logdevice::Error> > >::promise_type':'class folly::coro::detail::TaskPromise<class folly::Expected<struct mydomain::logdevice::zk::GetResponse, struct mydomain::logdevice::Error> >'
|           `-MaterializeTemporaryExpr 0x227b13a0 'folly::Unexpected<Error>':'class folly::Unexpected<struct mydomain::logdevice::Error>' xvalue
|             `-CXXBindTemporaryExpr 0x22776218 'folly::Unexpected<Error>':'class folly::Unexpected<struct mydomain::logdevice::Error>' (CXXTemporary 0x22776218)
|               `-StmtExpr 0x227761f0 'folly::Unexpected<Error>':'class folly::Unexpected<struct mydomain::logdevice::Error>'
|                 `-CompoundStmt 0x227761d0
|                   |-DeclStmt 0x22771e88
|                   | `-VarDecl 0x22771c50  used frame 'const mydomain::logdevice::class ErrorStacktrace::Frame':'const struct mydomain::logdevice::ErrorStacktrace::Frame' static constexpr listinit
|                   |   |-value: Struct
|                   |   | `-fields: LValue <todo>, LValue <todo>, Int 103
|                   |   `-InitListExpr 0x22771da8 'const mydomain::logdevice::class ErrorStacktrace::Frame':'const struct mydomain::logdevice::ErrorStacktrace::Frame'
|                   |     |-ImplicitCastExpr 0x22771e00 'const char *' <ArrayToPointerDecay>
|                   |     | `-PredefinedExpr 0x22771cd8 'const char [8]' lvalue __FUNCTION__
|                   |     |   `-StringLiteral 0x22771cb8 'const char [8]' lvalue "getData"
|                   |     |-ImplicitCastExpr 0x22771e18 'const char *' <ArrayToPointerDecay>
|                   |     | `-StringLiteral 0x22771cf0 'const char [41]' lvalue "logdevice/common/ZookeeperClientBase.cpp"
|                   |     `-IntegerLiteral 0x22771d30 'int' 103
|                   `-ExprWithCleanups 0x227761b8 'folly::Unexpected<Error>':'class folly::Unexpected<struct mydomain::logdevice::Error>'
|                     `-CXXBindTemporaryExpr 0x22776198 'folly::Unexpected<Error>':'class folly::Unexpected<struct mydomain::logdevice::Error>' (CXXTemporary 0x22776198)
|                       `-CallExpr 0x22776160 'folly::Unexpected<Error>':'class folly::Unexpected<struct mydomain::logdevice::Error>'
|                         |-ImplicitCastExpr 0x22776148 'folly::Unexpected<Error> (*)(const class ErrorStacktrace::Frame *, mydomain::logdevice::Status)' <FunctionToPointerDecay>
|                         | `-DeclRefExpr 0x227760b8 'folly::Unexpected<Error> (const class ErrorStacktrace::Frame *, mydomain::logdevice::Status)' lvalue Function 0x18b49568 'makeUnexpected' 'folly::Unexpected<Error> (const class ErrorStacktrace::Frame *, mydomain::logdevice::Status)'
|                         |-UnaryOperator 0x22771fb8 'const mydomain::logdevice::class ErrorStacktrace::Frame *' prefix '&' cannot overflow
|                         | `-DeclRefExpr 0x22771f68 'const mydomain::logdevice::class ErrorStacktrace::Frame':'const struct mydomain::logdevice::ErrorStacktrace::Frame' lvalue Var 0x22771c50 'frame' 'const mydomain::logdevice::class ErrorStacktrace::Frame':'const struct mydomain::logdevice::ErrorStacktrace::Frame'
|                         `-CallExpr 0x227720e0 'mydomain::logdevice::Status':'enum mydomain::logdevice::E'
|                           |-ImplicitCastExpr 0x227720c8 'mydomain::logdevice::Status (*)(int)' <FunctionToPointerDecay>
|                           | `-DeclRefExpr 0x22771ff8 'mydomain::logdevice::Status (int)' lvalue CXXMethod 0x221cddd0 'toStatus' 'mydomain::logdevice::Status (int)'
|                           `-ImplicitCastExpr 0x22772108 'int' <LValueToRValue>
|                             `-MemberExpr 0x22772038 'int' lvalue .rc_ 0x21f24480
|                               `-DeclRefExpr 0x22772018 'struct mydomain::logdevice::zk::GetResponse':'struct mydomain::logdevice::zk::GetResponse' lvalue Var 0x22731508 'result' 'struct mydomain::logdevice::zk::GetResponse':'struct mydomain::logdevice::zk::GetResponse'

Unfortunately I haven't been able to get a small repro. But I wonder if the problem makes sense to you?