[cfe-dev] A disappearing CXXBindTemporaryExpr.

Artem Dergachev via cfe-dev cfe-dev at lists.llvm.org
Mon Mar 26 19:58:50 PDT 2018


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



More information about the cfe-dev mailing list