[cfe-dev] AST-dump

Pedro Delgado Perez pedro.delgado at uca.es
Mon Jun 10 01:13:21 PDT 2013


 
Hi,

Well, I'm going to rewind and try to give as much information of the problem as possible to clarify it to everyone.

My intention is the next: I want to retrieve the default constructor of a class (not trivial if possible) and then to delete it so that the compiler provides one. For this purpose, I need to verify that the class that default constructor belongs to, doesn't have any other constructors (with at least another constructor, the compiler doesn't provide the default constructor).

So, I created the next matcher:

DeclarationMatcher DefaultConstructorMatcher =
 recordDecl(
      unless(hasMethod(constructorDecl(
            allOf(hasAnyParameter(anything()), unless(isImplicit()))))),
        hasMethod(constructorDecl(isDefinition(), parameterCountIs(0),
             anyOf(hasAnyConstructorInitializer(isWritten()), has(compoundStmt(has(stmt()))))).bind("default_constructor"))
 ).bind("class_default_constructor");

The first part helps me to discard classes with a constructor different from the default constructor and the second part gives me the default constructor I'm looking for. 

(Note: I put "unless(isImplicit())" to avoid other implicit constructors provides by the compiler)

Then, I can do:

 if (Result.Nodes.getNodeAs<clang::CXXRecordDecl>("class_default_constructor"))
       const CXXConstructorDecl *FS = Result.Nodes.getNodeAs<clang::CXXConstructorDecl>("default_constructor");

The problem now is:

- Case 1:
class E{
   public:
      E(){int a = 3;}; 
      int e;
};

Constructor E() is retrieved.

- Case 2: 
class E{
   public:
      E(); 
      int e;
};

E::E(){
  int a = 3;
}

Constructor E() is not retrieved. I think the problem is that there are two CXXConstructors in the AST, I suppose one for the declaration and the other for the definition:

<CXXConstructor ptr="0xc7de070" name="E" prototype="true">
  <FunctionProtoType ptr="0xc7de040" canonical="0xc7de040">
   <BuiltinType ptr="0xc7ddc30" canonical="0xc7ddc30"/>
   <parameters/>
  </FunctionProtoType>
 </CXXConstructor>

....

<CXXConstructor used="1" ptr="0xc7de270" name="E" previous="0xc7de070" prototype="true">
 <FunctionProtoType ptr="0xc7de040" canonical="0xc7de040">
  <BuiltinType ptr="0xc7ddc30" canonical="0xc7ddc30"/>
  <parameters/>
 </FunctionProtoType>
 <Stmt>
CompoundStmt 0xc7de378 <./ABC.h:8:7, line:10:1>
`-DeclStmt 0xc7de368 <line:9:2, col:11>
 `-VarDecl 0xc7de320 <col:2, col:10> a 'int'
   `-IntegerLiteral 0xc7de350 <col:10> 'int' 3

 </Stmt>
 </CXXConstructor>


With all this information everyone could test that this is true. Now, responding your message:

> > Did you mean "clang::Redeclarable< decl_type >" class?
> 
> 
> Yes.
> 
I've been looking for a solution with this, but I definitively have no idea what can I do with it.

> > I think your suggestion might be useful to me, so that the second declaration is not taken into account
> 
> 
> Yes, that is the idea.
> 
Yes, but I need this only for the first part of the matcher, not for the second part as I need to determine whether the constructor is trivial or not.

> > the matcher you are suggesting is previous to this or is included in the same matcher I'm trying to build?
> 
> 
> I don't understand what you mean.
> 
What I was asking was whether I needed to change my matcher "DefaultConstructorMatcher" to do what you proposed or I had to create a new one to act before my matcher.

Thanks for your concern,

Pedro.



El dia 10 jun 2013 00:50, Gábor Kozár <kozargabor at gmail.com> escribió:
> 
> Hi,
> I reread your previous e-mail, and I do not know why your matcher is not working as intended in the example - I cannot find any problems with it. Did you try filtering out the methods that have been declared before, to see whether the E(); line or the E::E() line causes the matcher to fail?
> > Did you mean "clang::Redeclarable< decl_type >" class?
> 
> 
> Yes.
> 
> > I think your suggestion might be useful to me, so that the second declaration is not taken into account
> 
> 
> Yes, that is the idea.
> 
> > but I can't still understand why the matcher is doing that if the second declaration doesn't fit that matcher's conditions.
> 
> 
> Me neither, it appears that we're missing something.
> 
> > the matcher you are suggesting is previous to this or is included in the same matcher I'm trying to build?
> 
> 
> I don't understand what you mean.
> 
> Gabor
> 
> 
> 
> 2013/5/31 Pedro Delgado Perez <pedro.delgado at uca.es [ mailto:pedro.delgado at uca.es ]>
> 
> 
> Did you mean "clang::Redeclarable< decl_type >" class? I think your suggestion might be useful to me, so that the second declaration is not taken into account,  but I can't still understand why the matcher is doing that if the second declaration doesn't fit that matcher's conditions.
> 
> I will look into this, but the matcher you are suggesting is previous to this or is included in the same matcher I'm trying to build?
> 
> Thanks,
> 
> Pedro.
> 
> El dia 30 may 2013 21:36, Gábor Kozár <kozargabor at gmail.com [ mailto:kozargabor at gmail.com ]> escribió:
> 
> 
> 
> Well, try filtering out declarations that are not first declarations - it looks to me that would solve your issue.
> I think you'll need to create a new matcher for this. In fact, this might be useful to implement generally and contribute to Clang. I believe there is a base class for redeclarable AST nodes, but can't remember the exact name - you should use that for your matcher.
> 
> 
> 
> 2013/5/30 Pedro Delgado Perez <pedro.delgado at uca.es [ mailto:pedro.delgado at uca.es ]>
> 
> 
> Hi,
> 
> 
> Default, copy, and move constructors are provided implicitly (under various conditions - obviously if you have members that aren't default, copy, or move constructible, you might not/cannot get all of those) 
> 
Ok, but this keeps giving me trouble. I've used the isImplicit() matcher to avoid those implicit constructors, but it isn't working how I need yet.

recordDecl(
   unless(hasMethod(constructorDecl(
            allOf(hasAnyParameter(anything()), unless(isImplicit()))

I know I shouldn't use the -ast-dump-xml option, but look at this. If I have a class like this:

class E{
   public:
      E(){int a = 3;}; 
      int e;
};

This is the dump of the constructor:

<CXXConstructor used="1" ptr="0xd7d8070" name="E" prototype="true">
  <FunctionProtoType ptr="0xd7d8040" canonical="0xd7d8040">
   <BuiltinType ptr="0xd7d7c30" canonical="0xd7d7c30"/>
   <parameters/>
  </FunctionProtoType>
  <Stmt>
CompoundStmt 0xd7d81a8 <./ABC.h:4:6, col:17>
`-DeclStmt 0xd7d8198 <col:7, col:16>
 `-VarDecl 0xd7d8150 <col:7, col:15> a 'int'
   `-IntegerLiteral 0xd7d8180 <col:15> 'int' 3

  </Stmt>
 </CXXConstructor>

And this case is working fine. But when the class is this other way:

class E{
   public:
      E(); 
      int e;
};

E::E(){
  int a = 3;
}

The dump creates two CXXConstructors, I suppose one for the declaration and the other for the definition:

<CXXConstructor ptr="0xc7de070" name="E" prototype="true">
  <FunctionProtoType ptr="0xc7de040" canonical="0xc7de040">
   <BuiltinType ptr="0xc7ddc30" canonical="0xc7ddc30"/>
   <parameters/>
  </FunctionProtoType>
 </CXXConstructor>

....

<CXXConstructor used="1" ptr="0xc7de270" name="E" previous="0xc7de070" prototype="true">
 <FunctionProtoType ptr="0xc7de040" canonical="0xc7de040">
  <BuiltinType ptr="0xc7ddc30" canonical="0xc7ddc30"/>
  <parameters/>
 </FunctionProtoType>
 <Stmt>
CompoundStmt 0xc7de378 <./ABC.h:8:7, line:10:1>
`-DeclStmt 0xc7de368 <line:9:2, col:11>
 `-VarDecl 0xc7de320 <col:2, col:10> a 'int'
   `-IntegerLiteral 0xc7de350 <col:10> 'int' 3

 </Stmt>
 </CXXConstructor>


And this is the case my matcher is not retrieving the class E. I don't know if clang it's considering that first CxxConstructor as implicit... The only difference I can see is the "used" attribute. I've tried creating a simple matcher to use the isUsed() method of Decl class:

namespace clang{ 
   namespace ast_matchers{ 
      AST_MATCHER(Decl, isUsed) 
      { 
         return Node.isUsed(); 
      } 
   } 
}

but, this is having no influence.

Maybe, I'm ignoring something.

Thanks,

Pedro.




El dia 28 may 2013 18:18, David Blaikie <dblaikie at gmail.com [ mailto:dblaikie at gmail.com ]> escribió:









On Tue, May 28, 2013 at 2:07 AM, Pedro Delgado Perez <pedro.delgado at uca.es [ mailto:pedro.delgado at uca.es ]> wrote:



That seems like an approach that works. I don't know that there's a different common property. Why's the default constructor different from the copy constructor here?

First of all, sorry but I made a mistake in the example:



class A{
1.   A(){...}
2.   A(int a){...}
3.   A(const A& a){... ...}
};


What I mean is that I need to find if there is at least one constructor that explicitly overloads the default constructor so that if I delete the default constructor the compiler won't provide the default constructor. So, my question was totally erroneous as the copy constructor does overload the default constructor if provided, but I don't want to take it into account if it wasn't explicitly provided.

A better question is: which are the kind of constructors that the compiler provides if no constructors are explicitly supplied? Only the default and the copy constructor (in some situations)?

Default, copy, and move constructors are provided implicitly (under various conditions - obviously if you have members that aren't default, copy, or move constructible, you might not/cannot get all of those) 

If "unless(allOf(... ...))" is the best solution, could someone enumerate the kind of constructors I must to indicate in that matcher?


I'd suggest to not use ast-dump-xml any more. It's basically deprecated, and hopefully it'll be removed soon. -ast-dump gives you all the information (and more).

Ok, I didn't know it.


... you can see that one is the default constructor and the other one is the copy constructor. If you just put "G g;" into the code instead of the second class, you'll also see that a simple use of G already triggers the copy constructor to appear. Others are probably better able to explain exactly when a copy constructor is created.

Further information will be well received.

Regards,

Pedro.

El dia 27 may 2013 12:24, Manuel Klimek <klimek at google.com [ mailto:klimek at google.com ]> escribió:


Hi Pedro,
first, please always send those mails also to cfe-dev. There are people who are much more knowledgable about the AST on that list, and you'll often get better answers faster that way :)
On Mon, May 27, 2013 at 10:00 AM, Pedro Delgado Perez <pedro.delgado at uca.es [ mailto:pedro.delgado at uca.es ]> wrote:



Sorry Manuel, but I prefer to ask you all the things I don't know before you reply me:

How do I know that a CXXConstructorDecl is a "simple" constructor of a class. I mean, I want to find all the constructors in a class, but not the copy, the move or things like this constructors. I'm not able to find this in the documentation. To clarify this, I'm going to put an example:

class A{
1.   A(){...}
2.   A(int a){...}
3.   A(const &a){... ...}
};

I want to look only for 1 and 2 and not for 3 when I ask:
recordDecl(hasMethod(constructDecl(...)));

What can I do? Do I have to write "unless(allOf(isCopyConstructor(), isMoveConstructor()...))" for each type of existing constructors?

That seems like an approach that works. I don't know that there's a different common property. Why's the default constructor different from the copy constructor here?
 

Thanks,

Pedro.




Hi Manuel,

I would like to ask you something about the AST built by Clang if you don't mind.  I've just have a look at your video  of introduction to Clang AST (By the way, nice tutorial!) and I think that you may have an answer for my trouble.

Look, I was trying to look for classes that had only the default constructor through ASTMatchers. Well, I tested my matcher with the next classes:

class G{
public:
    G():a(1){}
    int a;
    virtual int ma(int arg);
};

class L: public G{
    public:
        L(){v = 1;}
        int b;
        int h;
        virtual int ma(int arg);
    private:
        int v;
        int f();
};

My matcher only retrieved the class 'L' and not the 'G'. I was wondering what would be the problem when I had a look to the ast-dump-xml option:

I'd suggest to not use ast-dump-xml any more. It's basically deprecated, and hopefully it'll be removed soon. -ast-dump gives you all the information (and more).

 <CXXRecord ptr="0xd695f30" name="G" typeptr="0xd695f80">
 <CXXRecord ptr="0xd695fd0" name="G" typeptr="0xd695f80"/>
 <AccessSpec ptr="0xd696020" access="public"/>
 <CXXConstructor used="1" ptr="0xd696080" name="G" prototype="true">
  <FunctionProtoType ptr="0xd696050" canonical="0xd696050">
  <BuiltinType ptr="0xd695c40" canonical="0xd695c40"/>
  <parameters/>
  </FunctionProtoType>
  <Stmt>
CompoundStmt 0xd696450 <tst.cpp:7:10, col:11>
  </Stmt>
 </CXXConstructor>

....

<CXXConstructor ptr="0xd6aefc0" name="G" prototype="true" inline="true">
  <FunctionProtoType ptr="0xd6af050" canonical="0xd6af030" exception_spec="unevaluated">
  <BuiltinType ptr="0xd695c40" canonical="0xd695c40"/>
  <parameters>
   <LValueReferenceType ptr="0xd696240" canonical="0xd696240">
   <QualType const="true">
    <RecordType ptr="0xd695f80" canonical="0xd695f80">
    <CXXRecord ref="0xd695f30"/>
    </RecordType>
   </QualType>
   </LValueReferenceType>
  </parameters>
  </FunctionProtoType>
  <ParmVar ptr="0xd6af070" name="" initstyle="c">
  <LValueReferenceType ptr="0xd696240" canonical="0xd696240">
   <QualType const="true">
   <RecordType ptr="0xd695f80" canonical="0xd695f80">
    <CXXRecord ref="0xd695f30"/>
   </RecordType>
   </QualType>
  </LValueReferenceType>
  </ParmVar>
 </CXXConstructor>
 </CXXRecord>


Why class 'G' has two constructors? If I change the test program in order that class 'L' doesn't inherits from class 'G', this doesn't happen (class 'G' only has one constructor in the ast-dump-xml) Could you lend me a hand?

If you look at -ast-dump:

$ clang -cc1 -ast-dump t4.cc
<snip>
|-CXXRecordDecl 0x37faf80 <t4.cc:1:1, line:6:1> class G
<snip>
| |-CXXConstructorDecl 0x37fb1c0 <line:3:3, col:15> G 'void (void)'
| | |-CXXCtorInitializer Field 0x37fb290 'a' 'int'
| | | |-IntegerLiteral 0x382d018 <col:11> 'int' 1
| | `-CompoundStmt 0x382d0a0 <col:14, col:15>
<snip>
| `-CXXConstructorDecl 0x382da70 <col:7> G 'void (const class G &)' inline
|  `-ParmVarDecl 0x382f820 <col:7> 'const class G &'
<snip>
... you can see that one is the default constructor and the other one is the copy constructor. If you just put "G g;" into the code instead of the second class, you'll also see that a simple use of G already triggers the copy constructor to appear. Others are probably better able to explain exactly when a copy constructor is created.
cheers,
/Manuel

_______________________________________________
cfe-dev mailing list
cfe-dev at cs.uiuc.edu [ mailto:cfe-dev at cs.uiuc.edu ]

http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev [ http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev ]


_______________________________________________
cfe-dev mailing list
cfe-dev at cs.uiuc.edu [ mailto:cfe-dev at cs.uiuc.edu ]

http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev [ http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev ]

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20130610/af8d9dd2/attachment.html>


More information about the cfe-dev mailing list