[cfe-dev] Further AST spelunking

Peter Stirling peter at pjstirling.plus.com
Thu Oct 9 13:25:54 PDT 2014


On 08/10/14 17:22, Reid Kleckner wrote:
> On Wed, Oct 8, 2014 at 6:02 AM, Peter Stirling 
> <peter at pjstirling.plus.com <mailto:peter at pjstirling.plus.com>> wrote:
>
>     Hi again,
>
>     I'm still plodding away at my SWIG alternative. As I said in my
>     previous email, typedefs that are declared within templates but
>     which do not depend on any of the template arguments are held in
>     the AST as children of a CXXRecordDecl ignorant of the fact that
>     it should be a template and what its arguments should be. This is
>     a problem for me because it means that I can't print a properly
>     scoped name to identify the type for c++ code. I have managed to
>     work around this by adding a check for whether a typedef is a
>     child of a CXXRecordDecl but should really be a child of a
>     template. The code I'm using appears to work, but I'd like to
>     double-check that it is appropriate:
>
>     bool shouldBeTemplate(clang::DeclContext const* context) {
>       if(context->getDeclKind() == clang::Decl::CXXRecord) {
>         auto record = llvm::cast<clang::CXXRecordDecl>(context);
>         auto dname = record->getDeclName();
>         // there will always be a parent
>         auto cursor = context->getParent()->lookup(dname);
>         for(auto named : cursor) {
>           if(llvm::isa<clang::ClassTemplateDecl>(named)) {
>             return true;
>           }
>         }
>       }
>       return false;
>     }
>
>
> I think what you are trying to compute is equivalent to 
> context->isDependentContext(), which returns true if this context is 
> nested inside any template, class or function.
Yes, this works, thanks.
>
>     Another issue is that, while it is safe to handle values that are
>     pointers to incomplete structure types, it is illegal to call a
>     function that takes an argument value or returns a result value
>     that is an incomplete type. My first attempt was to check for
>     RecordTypes and call CXXRecordDecl::getDefinition(), but for some
>     (but confusingly to me, not all) template types getDefinition()
>     returns NULL even though the type isn't incomplete, is this
>     expected? The code that I'm using is below, I also wonder whether
>     getCanonicalDecl() is overkill here:
>
>     bool isIncompleteType(clang::QualType inType) {
>       if(inType->isRecordType()) {
>         auto realType = inType->getAs<clang::RecordType>();`
>         auto decl = realType->getDecl()->getCanonicalDecl();
>         if(decl->getDefinition() ||
>     llvm::isa<clang::ClassTemplateSpecializationDecl>(decl)) {
>           return false;
>         }
>         else {
>           return true;
>         }
>       }
>       return false;
>     }
>
>
> I think you can compute this more directly with 
> inType->isIncompleteType().

Thanks for the suggestion, I hadn't seen that either. Unfortunately it 
doesn't work, for the same cases as getDefinition(), these are (from my 
test data):

   std::fpos<__mbstate_t >
   std::reverse_iterator<__gnu_cxx::__normal_iterator<char const * , 
std::basic_string<char , std::char_traits<char > , std::allocator<char > 
 > > >
   std::reverse_iterator<__gnu_cxx::__normal_iterator<char* , 
std::basic_string<char , std::char_traits<char > , std::allocator<char > 
 > > >
   std::reverse_iterator<__gnu_cxx::__normal_iterator<wchar_t const * , 
std::basic_string<wchar_t , std::char_traits<wchar_t > , 
std::allocator<wchar_t > > > >
   std::reverse_iterator<__gnu_cxx::__normal_iterator<wchar_t* , 
std::basic_string<wchar_t , std::char_traits<wchar_t > , 
std::allocator<wchar_t > > > >
   __gnu_cxx::__normal_iterator<char16_t* , std::basic_string<char16_t , 
std::char_traits<char16_t > , std::allocator<char16_t > > >
   __gnu_cxx::__normal_iterator<char16_t const * , 
std::basic_string<char16_t , std::char_traits<char16_t > , 
std::allocator<char16_t > > >
   std::reverse_iterator<__gnu_cxx::__normal_iterator<char16_t const * , 
std::basic_string<char16_t , std::char_traits<char16_t > , 
std::allocator<char16_t > > > >
   std::reverse_iterator<__gnu_cxx::__normal_iterator<char16_t* , 
std::basic_string<char16_t , std::char_traits<char16_t > , 
std::allocator<char16_t > > > >
   std::initializer_list<char16_t >
   __gnu_cxx::__normal_iterator<char32_t* , std::basic_string<char32_t , 
std::char_traits<char32_t > , std::allocator<char32_t > > >
   __gnu_cxx::__normal_iterator<char32_t const * , 
std::basic_string<char32_t , std::char_traits<char32_t > , 
std::allocator<char32_t > > >
   std::reverse_iterator<__gnu_cxx::__normal_iterator<char32_t const * , 
std::basic_string<char32_t , std::char_traits<char32_t > , 
std::allocator<char32_t > > > >
   std::reverse_iterator<__gnu_cxx::__normal_iterator<char32_t* , 
std::basic_string<char32_t , std::char_traits<char32_t > , 
std::allocator<char32_t > > > >
   std::initializer_list<char32_t >
   std::istreambuf_iterator<char , std::char_traits<char > >
   std::ostreambuf_iterator<char , std::char_traits<char > >
   std::istreambuf_iterator<wchar_t , std::char_traits<wchar_t > >
   std::ostreambuf_iterator<wchar_t , std::char_traits<wchar_t > >
   __gnu_cxx::__normal_iterator<char* , std::vector<char , 
std::allocator<char > > >
   __gnu_cxx::__normal_iterator<char const * , std::vector<char , 
std::allocator<char > > >
   std::reverse_iterator<__gnu_cxx::__normal_iterator<char const * , 
std::vector<char , std::allocator<char > > > >
   std::reverse_iterator<__gnu_cxx::__normal_iterator<char* , 
std::vector<char , std::allocator<char > > > >
   __gnu_cxx::__normal_iterator<double const * , std::vector<double , 
std::allocator<double > > >
   std::reverse_iterator<__gnu_cxx::__normal_iterator<double const * , 
std::vector<double , std::allocator<double > > > >
   std::reverse_iterator<__gnu_cxx::__normal_iterator<double* , 
std::vector<double , std::allocator<double > > > >
   __gnu_cxx::__normal_iterator<unsigned int* , std::vector<unsigned int 
, std::allocator<unsigned int > > >
   std::reverse_iterator<__gnu_cxx::__normal_iterator<unsigned int const 
* , std::vector<unsigned int , std::allocator<unsigned int > > > >
   std::reverse_iterator<__gnu_cxx::__normal_iterator<unsigned int* , 
std::vector<unsigned int , std::allocator<unsigned int > > > >
   std::initializer_list<unsigned int >
   std::_List_iterator<TagLib::ByteVector >
   std::_List_const_iterator<TagLib::ByteVector >
std::reverse_iterator<std::_List_const_iterator<TagLib::ByteVector > >
   std::reverse_iterator<std::_List_iterator<TagLib::ByteVector > >
   std::initializer_list<TagLib::ByteVector >
   std::_List_iterator<TagLib::String >
   std::_List_const_iterator<TagLib::String >
std::reverse_iterator<std::_List_const_iterator<TagLib::String > >
   std::reverse_iterator<std::_List_iterator<TagLib::String > >
   std::initializer_list<TagLib::String >

>
>     I've also discovered that parsing code that calls a builtin
>     function causes a no-argument, returns-int declaration to be
>     inserted. It's been a while, but as I remember, in C this kind of
>     declaration actually means that the function takes an unspecified
>     number of arguments, but each one passed should be promoted to the
>     size of an int (did they update this since pointers became much
>     larger than ints?). In C++ it means something rather different. It
>     seems a bit odd to me that builtin functions don't have the
>     correct declaration inserted, since the compiler must have them on
>     hand somewhere.
>
>
> Yes, in C, this is a no-prototype, implicit int return function. I'm 
> not sure what kind of builtin function you're referring to. If the 
> name starts with __builtin_, then the compiler knows the prototype. If 
> it's a libc function like "fprintf()", then you will probably get a 
> warning and the implicit declaration you describe. In C++, you 
> shouldn't get these implicit declarations, it's just an error.

The functions that I've hit in my test data are:
   __atomic_fetch_add();
   __builtin_isfinite();
   __builtin_isinf();
   __builtin_isnan();
   __builtin_isnormal();
   __builtin_isgreater();
   __builtin_isgreaterequal();
   __builtin_isless();
   __builtin_islessequal();
   __builtin_islessgreater();

For all of the above FunctionDecl::getBuiltinID() returns non-zero.

>     Finally, I need to generate the list of base classes that a
>     derived class can be implicitly converted to. I have an intuitive
>     understanding of when it should work, but when I try to think of
>     an algorithm to sort out arbitrarily-evil trees combining virtual
>     and normal inheritance I go a bit cross-eyed. Can anyone point me
>     at something that will help me figure this out?
>
>
> The inheritance hierarchy is always a directed acyclic graph, so you 
> can walk it recursively as long as you remember what you've already 
> seen. If you have a class with multiple base subobjects of the same 
> type and you don't care about which one an implicit conversion would 
> pick, then you can do depth-first search and throw everything into a set:
>   // Result is the set BasesSeen.
>   void doit(BasesSeen, RD)
>     if (RD in BasesSeen)
>       return
>     BasesSeen.insert(RD)
>     for (Base in RD.bases())
>       doit(BasesSeen, Base)
>
> Hope that helps!

I'll give this a bash, though I'll need to keep track of virtual and 
normal parents separately. I was trying to imagine a bottom-up recursive 
algorithm, which wasn't working. Thanks for the suggestion!

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


More information about the cfe-dev mailing list