Doubts about constructor calls (and functional casts).

Hello.

I am a bit confused regarding the AST that is generated by clang++ when handling implicit/explicit calls to constructors. I would be grateful if someone would clarify a few points by going through the following examples.

Let us consider the following struct definition:

Hello.

I have seen no answer to this post:

http://lists.cs.uiuc.edu/pipermail/cfe-dev/2010-February/008049.html

I know people is busy due to the release ... this is just a reminder.

Cheers,
Enea Zaffanella.

Hello.

I am a bit confused regarding the AST that is generated by clang++ when
handling implicit/explicit calls to constructors. I would be grateful if
someone would clarify a few points by going through the following examples.

Let us consider the following struct definition:

struct S {
  S(int);
};

which is dumped by clang++ as follows (no surprise here):

struct S {
public:
    struct S;
    S(int);
    inline S(struct S const &);
    inline struct S &operator=(struct S const &);
    inline void ~S();
};

If I construct an S object using direct initialization
    S s1(1);
I get the following (which is what I am expecting):

  (DeclStmt 0x1d5eba0 <line:6:3, col:10>
    0x1d5ea60 "struct S s1 =
      (CXXConstructExpr 0x1d5eb30 <col:5, col:8> 'struct S''void (int)'
        (IntegerLiteral 0x1d5eac0 <col:8> 'int' 1))"

If I now try with this variant
     S s2 = 2;
I get the following ... again, this is more or less what I was expecting:

  (DeclStmt 0x1d5f1c0 <line:7:3, col:11>
    0x1d5f000 "struct S s2 =
      (CXXConstructExpr 0x1d5f150 <col:5, col:10> 'struct S''void
(struct S const &)' elidable
        (ImplicitCastExpr 0x1d5f110 <col:10> 'struct S'
<ConstructorConversion>
          (CXXConstructExpr 0x1d5f0a0 <col:10> 'struct S''void (int)'
            (IntegerLiteral 0x1d5f060 <col:10> 'int' 2))))"

I guess that the implicit cast between the two constructor calls is
meant to convert the plain S type into a "const S&" type, so that it can
be passed as an argument to the (elidable) copy constructor.
Am I correct?

But then I get to the following case
    S s3 = S(3);
for which I get the following AST:

  (DeclStmt 0x1d5f430 <line:8:3, col:14>
    0x1d5f1f0 "struct S s3 =
      (ImplicitCastExpr 0x1d5f3f0 <col:10, col:13> 'struct S'
<ConstructorConversion>
        (CXXConstructExpr 0x1d5f380 <col:10, col:13> 'struct S''void
(struct S const &)' elidable
          (ImplicitCastExpr 0x1d5f340 <col:10, col:13> 'struct S const'
<NoOp>
            (CXXFunctionalCastExpr 0x1d5f300 <col:10, col:13> 'struct
S' functional cast to struct S
              (CXXConstructExpr 0x1d5f290 <col:10, col:12> 'struct
S''void (int)'
                (IntegerLiteral 0x1d5f250 <col:12> 'int' 3))))))"

Here there are two things that I do not understand.

First, I cannot see why the result of the inner CXXConstructorExpr call
should be fed to a CXXFunctionalCastExpr. I may agree that the
initializer of s3 is a functional cast expression ... but then I would
expect that the argument of this functional cast is the integer literal,
not the struct S object returned by the constructor.
In other words, to my eyes, the presence of the functional cast should
exclude the presence of the inner constructor call, or vice versa.
Is my reasoning missing some important point?

This is a strange representation, and it looks even stranger when the functional cast is actually invoking a user-defined conversion. CodeGen aggressively elides the constructor calls (in most cases; I don't enough about that corner of the standard to know if it's missing legal elisions), so it's just an internal-representation issue rather than also being a performance issue. Still, it's not a good representation, and if the elision optimization is disabled (which is a supported configuration, I think) it might mean we make too many copies.

Second, I cannot see why the result of the external (elidable) copy
constructor call is (again) implicitly cast before being used as an
initializer for s3. There seems to be no reason for such an implicit
cast and, as a matter of fact, such a cast was not used in the previous
example (when initializing s2).

I don't understand this either.

John.

Actually, it’s not that strange :slight_smile:

The CXXFunctionalCastExpr is just the syntactical representation, and the inner CXXConstructExpr has all the constructor information - which constructor to call, default arguments etc.

Anders

Okay, so you’re saying that a CXXFunctionalCastExpr always contains a CastExpr as its immediate sub-expressions, and that that CastExpr is what actually conveys the semantics of the cast? I’m not thrilled with that, but it’s probably better than duplicating the cast logic N times over.

That said, can you similarly justify this representation for conversion operators? It looks like we get an extra, unelidable CXXConstructExpr.

struct S { S(unsigned); S(const S &value); };
struct T { operator S() const; };
void test() {
T t;
S x = S(t);
}

void test() (CompoundStmt 0x103618f20 </tmp/red.cpp:10:13, line:13:1>
(DeclStmt 0x103618b60 <line:11:3, col:6>
0x103618b10 “T t”
(DeclStmt 0x103617d10 <line:12:3, col:13>
0x103617080 “S x =
(ImplicitCastExpr 0x103618ef0 <col:9, col:12> ‘struct S’
(CXXConstructExpr 0x103618ea0 <col:9, col:12> ‘struct S’‘void (struct S const &)’ elidable
(ImplicitCastExpr 0x103618e70 <col:9, col:12> ‘struct S const’
(CXXFunctionalCastExpr 0x103618e30 <col:9, col:12> ‘struct S’ functional cast to struct S
(CXXConstructExpr 0x103618de0 <col:9, col:11> ‘struct S’‘void (struct S const &)’
(ImplicitCastExpr 0x103618db0 col:11 ‘struct S const’
(ImplicitCastExpr 0x103618d80 col:11 ‘struct S’
(CXXMemberCallExpr 0x103618d40 col:11 ‘struct S’
(MemberExpr 0x103618d00 <col:11, > ‘struct S (void) const’ .operator S 0x103618590
(ImplicitCastExpr 0x103618cd0 col:11 ‘struct T const’ lvalue
(DeclRefExpr 0x103618be0 col:11 ‘struct T’ Var=‘t’ 0x103618b10)))))))))))”)

John.

This strangeness looks to be cured:

code:

A x = 3;
A y = A(3);

now is parsed to:

(DeclStmt 0x32815f0 <line:10:3, col:10>
0x32814e0 “A x =
(CXXConstructExpr 0x3281580 <col:5, col:9> ‘class A’‘void (class A const &)’ elidable
(ImplicitCastExpr 0x3281540 col:9 ‘class A’
(CXXConstructExpr 0x3282760 col:9 ‘class A’‘void (int)’
(IntegerLiteral 0x32827b0 col:9 ‘int’ 3))))”
(DeclStmt 0x3281640 <line:11:3, col:13>
0x3280660 “A y =
(ImplicitCastExpr 0x3281d60 <col:9, col:12> ‘class A’
(CXXConstructExpr 0x3281cf0 <col:9, col:12> ‘class A’‘void (class A const &)’ elidable
(ImplicitCastExpr 0x3281cb0 <col:9, col:12> ‘class A const’
(CXXFunctionalCastExpr 0x3281c70 <col:9, col:12> ‘class A’ functional cast to class A
(CXXConstructExpr 0x3281c20 <col:9, col:11> ‘class A’‘void (int)’
(IntegerLiteral 0x3281b60 col:11 ‘int’ 3))))))”)

2010/2/27 Enea Zaffanella <zaffanella@cs.unipr.it>