[cfe-dev] Question about ASTMatchers

Christian Schafmeister chris.schaf at verizon.net
Sun Feb 23 17:53:34 PST 2014


Dear Manuel,

Thank you - after banging my head against the wall for a couple of days I realized that I needed to extract the template arguments from the ClassTemplateSpecializationDecl myself using ClassTemplateSpecializationDecl::getTemplateArgs

I’m writing a program that digs through the 165 C++ source files that is the C++ core of my Common Lisp system and analyzes classes and variables to construct the interface to a copying, generational garbage collector (the Memory-Pool-System (MPS) by Ravenbrook systems).  I’m working on adding this garbage collector to my Common Lisp system and I don’t want to do it by hand because it would be a huge job to write and maintain.  I have hundreds of classes/instantiated template classes and thousands of variable declarations that need to be matched to each other in the analysis.

For instance:

class Bar {…};
class Foo {
	smart_ptr<Bar>  _bar;
};

void func() {
    Foo    a;   ;; <- Nothing needs to be defined as a root because smart_ptr<Bar> is on the stack and MPS will find it there.
    Foo*   b = new Foo();  ;; <— MPS needs to be informed that this is a root because the *b is on the heap and the smart_ptr<Bar> b->_bar is on the heap.
}

So I need to analyze every class (matcher: recordDecl(…) ) like Foo and classify them whether they store pointers on the heap for the situation where an instance is stored on the stack or the heap.  Then I use that info to construct the garbage collector interface or print warnings so that I can go in and by hand and fix to be compatible with the garbage collector.

I also need to identify every varDecl() and fieldDecl and match them up to their respective RecordDecl.

I’ve been struggling to construct a unique key for each RecordDecl given that I have CXXRecordDecls and instantiated ClassTemplateSpecializationDecls all over the place and I need to generate matching keys from the type of each varDecl.

I can’t use the AST node directly because I’m scanning 165 files and I can’t load all of the AST’s into memory at once - that requires tens of gigabytes and brings my machine to it’s knees.  Once one file is processed, its AST nodes get destroyed so I need to generate keys based on the RecordDecl’s names.

I figured it out though - I wrote these functions (Common Lisp) that generate the name of each RecordDecl by checking what kind of RecordDecl it is and doing different things based on that.  If it’s a ClassTemplateSpecializationDecl it gets the arguments using getTemplateArgs and builds a list of argument names and attaches it to the name of the template class.

(defun template-arg-as-string (template-arg)
  (let* ((template-arg-kind (cast:get-kind template-arg)))
    (case template-arg-kind
      (ast-tooling:type
       (cast:get-as-string (cast:get-as-type template-arg)))
      (ast-tooling:integral
       (format nil "INTEGRAL[~a]" (llvm:to-string (cast:get-as-integral template-arg) 10 t)))
      (otherwise
       (error "Add support for template-arg-as-string of kind: ~a" template-arg-kind)))))



(defun record-key (decl-node)
  (or decl-node (error "There is a problem, the decl-node is nil"))
  (case (type-of decl-node)
    (cast:cxxrecord-decl
     (format nil "class_~a" (cast:get-name decl-node)))
    (cast:record-decl
     (format nil "struct_~a" (cast:get-name decl-node)))
    (cast:enum-decl
     (format nil "enum_~a" (cast:get-name decl-node)))
    (cast:class-template-specialization-decl
     (let* ((decl-name (cast:get-name decl-node))
            (template-args (cast:get-template-args decl-node))
            (template-args-as-list (loop :for i :from 0 :below (cast:size template-args)
                                        :collect (let* ((template-arg (cast:template-argument-list-get template-args i)))
                                                   (template-arg-as-string template-arg)))))
       (format nil "class-template-specialization-decl_~a<~{~a~^,~}>" (cast:get-name decl-node) template-args-as-list)))
    (cast:class-template-partial-specialization-decl
     (let* ((decl-name (cast:get-name decl-node))
            (template-args (cast:get-template-args decl-node))
            (template-args-as-list (loop :for i :from 0 :below (cast:size template-args)
                                        :collect (cast:get-as-string (cast:get-as-type (cast:template-argument-list-get template-args i))))))
       (format nil "class-template-partial-specialization-decl_~a<~{~a~^,~}>" (cast:get-name decl-node) template-args-as-list)))
    (otherwise
     (format t "Add support for record-key for ~a  get-name->~a~%" decl-node (cast:get-name decl-node))
     (break "Check the decl-node"))))




Christian Schafmeister
chris.schaf at verizon.net


On Feb 23, 2014, at 12:31 PM, Manuel Klimek <klimek at google.com> wrote:

> On Thu, Feb 20, 2014 at 8:08 PM, Christian Schafmeister <chris.schaf at verizon.net> wrote:
> I’m creating AST Matchers to match recordDecls and varDecls and match the types for the varDecls to recordDecls.
> In many cases the varDecls are instantiating template classes and some of the template classes are specialized.
> 
> If I use this matcher:
> recordDecl(isTemplateInstantiation(), isSameOrDerivedFrom(recordDecl(hasName("BAR")))).bind(“WHOLE”)    —> FOO<A> node
> or
> recordDecl(isExplicitTemplateSpecialization(), isSameOrDerivedFrom(recordDecl(hasName("BAR")))).bind(“WHOLE”)   —> FOO<int> node
> 
> How do I get the full name of the nodes that match and are bound to “WHOLE”?
> 
> node.getName() —>  “FOO” not “FOO<A>”  or “FOO<int>"
> 
> I don't have a good answer. Generally, getName() (or getNameAsString()) will print the full declaration for anything that's a declaration context. So if you match a member m in FOO, you'd get FOO<int>::m. This will be expanded for everything but the last part.
> I don't know whether there's a good way to get the expansion for everything. I've never needed it, though; out of curiosity, what do you need it for?
>  
> 
> 
> 
> Here is the sample code:
> 
> #include <stdio.h>
> #include <core/foundation.h>
> #include <core/object.h>
> #include <core/holder.h>
> 
> namespace asttooling {
> 
>     class BAR {};
> 
>     template<typename T> class FOO : public BAR {};
>     class A {};
>     
>     template<> class FOO<int> : public BAR {};
> 
>     void tinyFunc()
>     {
>         FOO<A> xa;
>         FOO<int> xi;
>         printf("Hi there, this is tinyFunc @%p %p\n", &xa, &xi);
>     }
> 
> };
> 
> 
> Christian Schafmeister
> chris.schaf at verizon.net
> 
> 
> On Feb 20, 2014, at 6:56 AM, Manuel Klimek <klimek at google.com> wrote:
> 
>> 
>> On Thu, Feb 20, 2014 at 3:49 PM, Christian Schafmeister <chris.schaf at verizon.net> wrote:
>> Manuel,
>> 
>> I completely missed those predicates - thank you!
>> 
>> How do I get the full name of an instantiated template recordDecl?
>> 
>> As in:
>> template <class T> class X;
>> class A;
>> 
>> X<A> xa;
>> 
>> If a varDecl(…) matches on xa - how do I get the "X<A>” name?
>> 
>> Do you want to "get" it or "match" it. Assuming you want to "get" it, you'd say something like varDecl(hasType(qualType().bind("t"))) (untested ;), and then in the callback extract the QualType and call getAsString() on it.
>>  
>> 
>> Best,
>> 
>> .Chris.
>> Christian Schafmeister
>> chris.schaf at verizon.net
>> 
>> 
>> On Feb 20, 2014, at 12:53 AM, Manuel Klimek <klimek at google.com> wrote:
>> 
>>> hasAncestor(decl(anyOf(recordDecl(
>>>   isTemplateInstantiation()),
>>>   functionDecl(isTemplateInstantiation()))
>>> 
>>> should do what you want.
>>> 
>>> 
>>> On Wed, Feb 19, 2014 at 11:01 PM, Christian Schafmeister <chris.schaf at verizon.net> wrote:
>>> 
>>> If I match a varDecl() that is in a method of a template class I get a match for the template and instantiated templates 
>>> - is there a way to tell the difference by matching some difference in the ancestors of the varDecl node?
>>> 
>>> I get a match for the translate::from_object<T> a0(*args);  varDecl in the method activate (see below) that has this type:
>>> from_object<type-parameter-0-0, struct std::__1::integral_constant<_Bool, true> >
>>> 
>>> I also get a match that I think is from the instantiation TestFunctoid<int> j(“test”) in tinyFunc - it has this type: "struct translate::from_object<int, std::true_type>"
>>> 
>>> There is a clear difference in the name - but I was looking for something that didn’t feel like a hack.
>>> 
>>> Are there differences in the AST class node for TestFunctoid when it is a template class vs when it is an instantiated template class?
>>> 
>>> 
>>> 
>>> #include <stdio.h>
>>> #include <core/foundation.h>
>>> #include <core/object.h>
>>> #include <core/holder.h>
>>> 
>>> namespace asttooling {
>>> 
>>> 
>>>     template<typename T>
>>>     class TestFunctoid : public core::Functoid {
>>>     public:
>>> 
>>>         TestFunctoid(const string& name) : core::Functoid(name) {};
>>>         core::T_mv activate( core::const_ActivationFrame_spREF closedOverFrame, int numArgs, ArgArray args )
>>>         {
>>>             translate::from_object<T> a0(*args);
>>>             printf( "Address of a0= %p\n", &a0);
>>>             return Values0<core::T_O>();
>>>         }
>>>     };
>>> 
>>> 
>>> 
>>>     void tinyFunc()
>>>     {
>>>         TestFunctoid<int> j("test");
>>>         TinyStruct x(10);
>>>         tinyFunc(x);
>>>         printf("Hi there, this is tinyFunc\n");
>>>     }
>>> 
>>> };
>>> 
>>> Christian Schafmeister
>>> Associate Professor
>>> Chemistry Department
>>> Temple University
>>> 
>>> 
>> 
>> 
> 
> 

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


More information about the cfe-dev mailing list