A disappearing CXXBindTemporaryExpr.

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

A disappearing CXXBindTemporaryExpr.

Manuel Klimek via cfe-dev
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 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
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: A disappearing CXXBindTemporaryExpr.

Manuel Klimek via cfe-dev
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
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: A disappearing CXXBindTemporaryExpr.

Manuel Klimek via cfe-dev
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
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: A disappearing CXXBindTemporaryExpr.

Manuel Klimek via cfe-dev
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 <[hidden email]> 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
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev


_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: A disappearing CXXBindTemporaryExpr.

Manuel Klimek via cfe-dev
On Mar 29, 2018, at 5:10 PM, Richard Smith via cfe-dev <[hidden email]> wrote:

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 :)

If we're going to do this, I think it would make sense to just store a "semantic value kind" that papers over as many of these language differences as possible, with the understanding that queries about the formal value kind have to be parameterized by LangOpts.  So it would have options like:
  - complete pr-value
  - pr-value subobject
  - l-value
  - x-value
  - function reference (because C considers these r-values)

Obviously, some differences (like compound literals) are real semantic differences between languages, but most of the rest of these are extremely artificial and clients ought  to be able to ignore them.

John.


On 27 March 2018 at 13:43, Artem Dergachev via cfe-dev <[hidden email]> 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
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev

_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev


_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: A disappearing CXXBindTemporaryExpr.

Manuel Klimek via cfe-dev
On 29 March 2018 at 14:19, John McCall via cfe-dev <[hidden email]> wrote:
On Mar 29, 2018, at 5:10 PM, Richard Smith via cfe-dev <[hidden email]> wrote:

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 :)

If we're going to do this, I think it would make sense to just store a "semantic value kind" that papers over as many of these language differences as possible, with the understanding that queries about the formal value kind have to be parameterized by LangOpts.  So it would have options like:
  - complete pr-value
  - pr-value subobject
  - l-value
  - x-value
  - function reference (because C considers these r-values)

Obviously, some differences (like compound literals) are real semantic differences between languages, but most of the rest of these are extremely artificial and clients ought  to be able to ignore them.

Oh great, now we have a "naming things" problem too ;-)

Yes, I agree, that makes a lot of sense.

If we're going to take on this work, I think we should also consider what should become of ExprClassification.cpp versus the value kind + object kind bits on Expr. It doesn't make much sense to have two independent mechanisms computing largely the same information. Also, do we need the ObjC parameter / subscript ObjectKind given that we have PseudoObject placeholder types to identify the relevant expressions? (Conversely, compatibility with modern GCC suggests we should have an "underaligned field" object kind, reference bindings to which implicitly create temporaries just like for bit-fields.)

John.


On 27 March 2018 at 13:43, Artem Dergachev via cfe-dev <[hidden email]> 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
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev

_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev


_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev



_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: A disappearing CXXBindTemporaryExpr.

Manuel Klimek via cfe-dev


On Mar 29, 2018, at 5:32 PM, Richard Smith <[hidden email]> wrote:

On 29 March 2018 at 14:19, John McCall via cfe-dev <[hidden email]> wrote:
On Mar 29, 2018, at 5:10 PM, Richard Smith via cfe-dev <[hidden email]> wrote:

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 :)

If we're going to do this, I think it would make sense to just store a "semantic value kind" that papers over as many of these language differences as possible, with the understanding that queries about the formal value kind have to be parameterized by LangOpts.  So it would have options like:
  - complete pr-value
  - pr-value subobject
  - l-value
  - x-value
  - function reference (because C considers these r-values)

Obviously, some differences (like compound literals) are real semantic differences between languages, but most of the rest of these are extremely artificial and clients ought  to be able to ignore them.

Oh great, now we have a "naming things" problem too ;-)

Heh, yeah, I was writing that and questioning everything I put down. :)

Yes, I agree, that makes a lot of sense.

If we're going to take on this work, I think we should also consider what should become of ExprClassification.cpp versus the value kind + object kind bits on Expr. It doesn't make much sense to have two independent mechanisms computing largely the same information.

I agree that we might be able to get rid of ExprClassification.

Also, do we need the ObjC parameter / subscript ObjectKind given that we have PseudoObject placeholder types to identify the relevant expressions?

Yes, that's true, I think we no longer need a special object kind for these.

(Conversely, compatibility with modern GCC suggests we should have an "underaligned field" object kind, reference bindings to which implicitly create temporaries just like for bit-fields.)

Hmm, I feel like this might be more a special-case semantic rule for reference-binding than an actual difference in the expression.

John.


John.


On 27 March 2018 at 13:43, Artem Dergachev via cfe-dev <[hidden email]> 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
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev

_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev


_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev




_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: A disappearing CXXBindTemporaryExpr.

Manuel Klimek via cfe-dev
On 29 March 2018 at 14:42, John McCall via cfe-dev <[hidden email]> wrote:
On Mar 29, 2018, at 5:32 PM, Richard Smith <[hidden email]> wrote:

On 29 March 2018 at 14:19, John McCall via cfe-dev <[hidden email]> wrote:
On Mar 29, 2018, at 5:10 PM, Richard Smith via cfe-dev <[hidden email]> wrote:

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 :)

If we're going to do this, I think it would make sense to just store a "semantic value kind" that papers over as many of these language differences as possible, with the understanding that queries about the formal value kind have to be parameterized by LangOpts.  So it would have options like:
  - complete pr-value
  - pr-value subobject
  - l-value
  - x-value
  - function reference (because C considers these r-values)

Obviously, some differences (like compound literals) are real semantic differences between languages, but most of the rest of these are extremely artificial and clients ought  to be able to ignore them.

Oh great, now we have a "naming things" problem too ;-)

Heh, yeah, I was writing that and questioning everything I put down. :)

Yes, I agree, that makes a lot of sense.

If we're going to take on this work, I think we should also consider what should become of ExprClassification.cpp versus the value kind + object kind bits on Expr. It doesn't make much sense to have two independent mechanisms computing largely the same information.

I agree that we might be able to get rid of ExprClassification.

Also, do we need the ObjC parameter / subscript ObjectKind given that we have PseudoObject placeholder types to identify the relevant expressions?

Yes, that's true, I think we no longer need a special object kind for these.

(Conversely, compatibility with modern GCC suggests we should have an "underaligned field" object kind, reference bindings to which implicitly create temporaries just like for bit-fields.)

Hmm, I feel like this might be more a special-case semantic rule for reference-binding than an actual difference in the expression.

As far as I can tell, it propagates through expressions exactly like bit-field-ness, and generally acts like the expression is half-way to being a bit-field (https://godbolt.org/g/teWxN7), affecting reference binding but not preventing taking the address.

John.


John.


On 27 March 2018 at 13:43, Artem Dergachev via cfe-dev <[hidden email]> 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
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev

_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev


_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev




_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev



_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: A disappearing CXXBindTemporaryExpr.

Manuel Klimek via cfe-dev


On Mar 29, 2018, at 7:31 PM, Richard Smith <[hidden email]> wrote:

On 29 March 2018 at 14:42, John McCall via cfe-dev <[hidden email]> wrote:
On Mar 29, 2018, at 5:32 PM, Richard Smith <[hidden email]> wrote:

On 29 March 2018 at 14:19, John McCall via cfe-dev <[hidden email]> wrote:
On Mar 29, 2018, at 5:10 PM, Richard Smith via cfe-dev <[hidden email]> wrote:

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 :)

If we're going to do this, I think it would make sense to just store a "semantic value kind" that papers over as many of these language differences as possible, with the understanding that queries about the formal value kind have to be parameterized by LangOpts.  So it would have options like:
  - complete pr-value
  - pr-value subobject
  - l-value
  - x-value
  - function reference (because C considers these r-values)

Obviously, some differences (like compound literals) are real semantic differences between languages, but most of the rest of these are extremely artificial and clients ought  to be able to ignore them.

Oh great, now we have a "naming things" problem too ;-)

Heh, yeah, I was writing that and questioning everything I put down. :)

Yes, I agree, that makes a lot of sense.

If we're going to take on this work, I think we should also consider what should become of ExprClassification.cpp versus the value kind + object kind bits on Expr. It doesn't make much sense to have two independent mechanisms computing largely the same information.

I agree that we might be able to get rid of ExprClassification.

Also, do we need the ObjC parameter / subscript ObjectKind given that we have PseudoObject placeholder types to identify the relevant expressions?

Yes, that's true, I think we no longer need a special object kind for these.

(Conversely, compatibility with modern GCC suggests we should have an "underaligned field" object kind, reference bindings to which implicitly create temporaries just like for bit-fields.)

Hmm, I feel like this might be more a special-case semantic rule for reference-binding than an actual difference in the expression.

As far as I can tell, it propagates through expressions exactly like bit-field-ness, and generally acts like the expression is half-way to being a bit-field (https://godbolt.org/g/teWxN7), affecting reference binding but not preventing taking the address.

That's fair.

John.

_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: A disappearing CXXBindTemporaryExpr.

Manuel Klimek via cfe-dev
Thanks for the help!

I don't think i'll be able to help with the proposed refactoring on the AST side, but i'll be happy to update the analyzer when CXXBindTemporaryExpr is removed. This sounds like something we could totally work with, and it'd be much cleaner than what we currently have.

On 3/29/18 9:54 PM, John McCall via cfe-dev wrote:
On Mar 29, 2018, at 7:31 PM, Richard Smith <[hidden email]> wrote:

On 29 March 2018 at 14:42, John McCall via cfe-dev <[hidden email]> wrote:
On Mar 29, 2018, at 5:32 PM, Richard Smith <[hidden email]> wrote:

On 29 March 2018 at 14:19, John McCall via cfe-dev <[hidden email]> wrote:
On Mar 29, 2018, at 5:10 PM, Richard Smith via cfe-dev <[hidden email]> wrote:

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 :)

If we're going to do this, I think it would make sense to just store a "semantic value kind" that papers over as many of these language differences as possible, with the understanding that queries about the formal value kind have to be parameterized by LangOpts.  So it would have options like:
  - complete pr-value
  - pr-value subobject
  - l-value
  - x-value
  - function reference (because C considers these r-values)

Obviously, some differences (like compound literals) are real semantic differences between languages, but most of the rest of these are extremely artificial and clients ought  to be able to ignore them.

Oh great, now we have a "naming things" problem too ;-)

Heh, yeah, I was writing that and questioning everything I put down. :)

Yes, I agree, that makes a lot of sense.

If we're going to take on this work, I think we should also consider what should become of ExprClassification.cpp versus the value kind + object kind bits on Expr. It doesn't make much sense to have two independent mechanisms computing largely the same information.

I agree that we might be able to get rid of ExprClassification.

Also, do we need the ObjC parameter / subscript ObjectKind given that we have PseudoObject placeholder types to identify the relevant expressions?

Yes, that's true, I think we no longer need a special object kind for these.

(Conversely, compatibility with modern GCC suggests we should have an "underaligned field" object kind, reference bindings to which implicitly create temporaries just like for bit-fields.)

Hmm, I feel like this might be more a special-case semantic rule for reference-binding than an actual difference in the expression.

As far as I can tell, it propagates through expressions exactly like bit-field-ness, and generally acts like the expression is half-way to being a bit-field (https://godbolt.org/g/teWxN7), affecting reference binding but not preventing taking the address.

That's fair.

John.

_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev


_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev