[cfe-dev] A disappearing CXXBindTemporaryExpr.

Richard Smith via cfe-dev cfe-dev at lists.llvm.org
Thu Mar 29 14:10:06 PDT 2018


Looks like a bug to me, probably template instantiation for the initializer
is stripping this node out and we're failing to recreate it when redoing
the initialization in the instantiation.

But I wonder whether we should just ditch the CXXBindTemporaryExpr node
entirely. The purpose of marking where a temporary is created that needs a
cleanup is handled much better by looking for CXXMaterializeTemporaryExprs
(since they also track whether the temporary was lifetime-extended), and it
doesn't seem like an imposition to ask clients to work out for themselves
whether the type of the temporary needs destruction.

This would require us to start creating CXXMaterializeTemporaryExprs in
C++98 for cases like "A().n", where we currently avoid doing so because the
language rules say that expression is an rvalue, which in turn means
(carefully) extending the C++11 xvalue rules to C++98 mode. But that seems
feasible -- the hardest part is likely going to be avoiding using the term
"xvalue" in C++98 diagnostics :)

On 27 March 2018 at 13:43, Artem Dergachev via cfe-dev <
cfe-dev at lists.llvm.org> wrote:

> Hmm, i understand why field 'a' being public is relevant. That's because
> it turns B into an aggregate.
>
> But i still don't understand why templating/un-templating foo() makes a
> difference.
>
>
> On 3/26/18 8:01 PM, Artem Dergachev wrote:
>
>> Sorry, accidentally sent two concatenated emails. The corrected email
>> should be:
>>
>> On 3/26/18 7:58 PM, Artem Dergachev wrote:
>>
>>> I've a C++ AST question.
>>>
>>> I've got a piece of code that doesn't contain CXXBindTemporaryExpr in
>>> its AST, but would have contained it if only i explicitly unwrapped a
>>> template that contains it *or* changed a field visibility in a certain
>>> class from private to public. I want to understand if it's intentional and
>>> if so what's the language feature that provides such behavior. A few hours
>>> of debugging Sema didn't quite help me (but i'm not super good with it).
>>>
>>> The code looks like this and compiles under -std=c++14:
>>>
>>> ```
>>> class A {
>>> public:
>>>   ~A();
>>> };
>>>
>>> class B {
>>>   A a;
>>> };
>>>
>>> template <typename T> void foo(T) {
>>>   B i;
>>>   i = {};
>>> }
>>>
>>> int main() {
>>>   foo(1);
>>>   return 0;
>>> }
>>> ```
>>>
>>> AST for foo():
>>>
>>>
>>> ```
>>> |-FunctionTemplateDecl 0x7fd5a8800f50 <line:10:1, line:13:1> line:10:28
>>> foo
>>> | |-TemplateTypeParmDecl 0x7fd5a8800ce8 <col:11, col:20> col:20
>>> referenced typename depth 0 index 0 T
>>> | |-FunctionDecl 0x7fd5a8800eb0 <col:23, line:13:1> line:10:28 foo 'void
>>> (T)'
>>> | | |-ParmVarDecl 0x7fd5a8800db0 <col:32> col:33 'T'
>>> | | `-CompoundStmt 0x7fd5a8801740 <col:35, line:13:1>
>>> | |   |-DeclStmt 0x7fd5a8801320 <line:11:3, col:6>
>>> | |   | `-VarDecl 0x7fd5a8800ff8 <col:3, col:5> col:5 referenced i 'B'
>>> callinit
>>> | |   |   `-CXXConstructExpr 0x7fd5a88012f0 <col:5> 'B' 'void ()
>>> noexcept'
>>> | |   `-ExprWithCleanups 0x7fd5a8801728 <line:12:3, col:8> 'B' lvalue
>>> | |     `-CXXOperatorCallExpr 0x7fd5a88016e0 <col:3, col:8> 'B' lvalue
>>> | |       |-ImplicitCastExpr 0x7fd5a88016c8 <col:5> 'B &(*)(B &&)
>>> noexcept' <FunctionToPointerDecay>
>>> | |       | `-DeclRefExpr 0x7fd5a8801648 <col:5> 'B &(B &&) noexcept'
>>> lvalue CXXMethod 0x7fd5a785de28 'operator=' 'B &(B &&) noexcept'
>>> | |       |-DeclRefExpr 0x7fd5a8801338 <col:3> 'B' lvalue Var
>>> 0x7fd5a8800ff8 'i' 'B'
>>> | |       `-MaterializeTemporaryExpr 0x7fd5a88015b0 <col:7, col:8> 'B'
>>> xvalue
>>> | |         `-CXXBindTemporaryExpr 0x7fd5a8801590 <col:7, col:8> 'B'
>>> (CXXTemporary 0x7fd5a8801588)
>>> | |           `-CXXConstructExpr 0x7fd5a8801558 <col:7, col:8> 'B' 'void
>>> () noexcept' list zeroing
>>> | `-FunctionDecl 0x7fd5a8801b10 <line:10:23, line:13:1> line:10:28 used
>>> foo 'void (int)'
>>> |   |-TemplateArgument type 'int'
>>> |   |-ParmVarDecl 0x7fd5a8801a08 <col:32> col:33 'int':'int'
>>> |   `-CompoundStmt 0x7fd5a8802e28 <col:35, line:13:1>
>>> |     |-DeclStmt 0x7fd5a88029f8 <line:11:3, col:6>
>>> |     | `-VarDecl 0x7fd5a8802958 <col:3, col:5> col:5 used i 'B' callinit
>>> |     |   `-CXXConstructExpr 0x7fd5a88029b8 <col:5> 'B' 'void ()
>>> noexcept'
>>> |     `-ExprWithCleanups 0x7fd5a8802e10 <line:12:3, col:8> 'B' lvalue
>>> |       `-CXXOperatorCallExpr 0x7fd5a8802dc8 <col:3, col:8> 'B' lvalue
>>> |         |-ImplicitCastExpr 0x7fd5a8802db0 <col:5> 'B &(*)(B &&)
>>> noexcept' <FunctionToPointerDecay>
>>> |         | `-DeclRefExpr 0x7fd5a8802d88 <col:5> 'B &(B &&) noexcept'
>>> lvalue CXXMethod 0x7fd5a785de28 'operator=' 'B &(B &&) noexcept'
>>> |         |-DeclRefExpr 0x7fd5a8802d48 <col:3> 'B' lvalue Var
>>> 0x7fd5a8802958 'i' 'B'
>>> |         `-MaterializeTemporaryExpr 0x7fd5a8802d70 <col:7, col:8> 'B'
>>> xvalue
>>> |           `-CXXConstructExpr 0x7fd5a8801558 <col:7, col:8> 'B' 'void
>>> () noexcept' list zeroing
>>> ```
>>>
>>> As you see, there's a CXXBindTemporaryExpr in the template, but it's not
>>> there in the instantiation. I expected the CXXBindTemporaryExpr to be there
>>> whenever the object has a non-trivial destructor, but in this case it's
>>> suddenly missing.
>>>
>>> However, if i replace "template <typename T> void foo(T)" with "void
>>> foo(int)" (to which it should resolve anyway), CXXBindTemporaryExpr would
>>> be present.
>>>
>>> Moreover, if instead of unrolling the template i simply add "public:"
>>> before "A a;", the mysterious CXXBindTemporaryExpr would also be present.
>>>
>>> WHY :)
>>>
>>
>>
> _______________________________________________
> cfe-dev mailing list
> cfe-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20180329/506c623a/attachment.html>


More information about the cfe-dev mailing list