[lldb-dev] type completion

Greg Clayton via lldb-dev lldb-dev at lists.llvm.org
Wed Sep 2 15:48:57 PDT 2015


> On Sep 2, 2015, at 3:15 PM, Ryan Brown via lldb-dev <lldb-dev at lists.llvm.org> wrote:
> 
> I'm trying to implement a DWARFASTParser for go, and have hit an issue with fields for structs.
> As I understand it, DWARFASTParserClang loads minimal type info for structs at first, and registers the type in SymbolFileDWARF's m_forward_decl_clang_type_to_die. 
> Then later when you call type.GetFullCompilerType() it calls back into the DWARFASTParser to complete the type.
> 
> But it seems like the SBValue's in the python API only get Forward types, and never complete them. For example, I do:
> var = frame.FindVariable('theStruct')
> self.assertEqual(2, var.GetNumChildren())
> 
> But I get 0 children, because the type is incomplete and there doesn't seem to be any way to complete it.
> ValueObject::GetNumChildren goes through ValueObject::MaybeCalculateCompleteType, which seems promising. But it only completes the type for objc. 
> Would a clang variable already have a full type at this point for some reason? If so, how do I arrange that for go?
> 
> Also, TypeSystem has a GetCompleteType method. I don't understand when this would be called or how I can implement it without a reference to SymbolFileDWARF.

The story goes:

lldb_private::ClangASTContext inherits from lldb_private::TypeSystem so it is the type system. The ClangASTContext also signs up to be a clang::ExternalASTSource so that it can complete types via:

ClangASTContext::CompleteTagDecl()
ClangASTContext::CompleteObjCInterfaceDecl()

ClangASTContext::CompleteTagDecl() completes C and C++ structs, unions and classes.

ClangASTContext::CompleteObjCInterfaceDecl() completes Objective C stuff.

The function that actually figures out how many children will be routed through "CompilerType::GetNumChildren(bool)" with code like this:

size_t
ValueObjectVariable::CalculateNumChildren()
{    
    CompilerType type(GetCompilerType());
    
    if (!type.IsValid())
        return 0;
    
    const bool omit_empty_base_classes = true;
    return type.GetNumChildren(omit_empty_base_classes);
}


CompilerType passes the ball back to the TypeSystem class (ClangASTContext in this case):

uint32_t
CompilerType::GetNumChildren (bool omit_empty_base_classes) const
{
    if (!IsValid())
        return 0;
    return m_type_system->GetNumChildren(m_type, omit_empty_base_classes);
}


Down in ClangASTContext it does:


uint32_t
ClangASTContext::GetNumChildren (void* type, bool omit_empty_base_classes)
{
    if (!type)
        return 0;
    
    uint32_t num_children = 0;
    clang::QualType qual_type(GetQualType(type));
    const clang::Type::TypeClass type_class = qual_type->getTypeClass();
    switch (type_class)
    {
        ...
        case clang::Type::Record:
            if (GetCompleteQualType (getASTContext(), qual_type))
            {




So it is a static function named GetCompleteQualType() in ClangASTContext.cpp that completes the type if it needs to:


static bool
GetCompleteQualType (clang::ASTContext *ast, clang::QualType qual_type, bool allow_completion = true)
{
    const clang::Type::TypeClass type_class = qual_type->getTypeClass();
    switch (type_class)
    {
        ...
        case clang::Type::Record:
        case clang::Type::Enum:
        {
            const clang::TagType *tag_type = llvm::dyn_cast<clang::TagType>(qual_type.getTypePtr());
            if (tag_type)
            {
                clang::TagDecl *tag_decl = tag_type->getDecl();
                if (tag_decl)
                {
                    if (tag_decl->isCompleteDefinition())
                        return true;
                    
                    if (!allow_completion)
                        return false;
                    
                    if (tag_decl->hasExternalLexicalStorage())
                    {
                        if (ast)
                        {
                            clang::ExternalASTSource *external_ast_source = ast->getExternalSource();
                            if (external_ast_source)
                            {
                                external_ast_source->CompleteType(tag_decl);



So it will ask the clang::ExternalASTSource to complete the type which should call ClangASTContext::CompleteTagDecl() to complete your type.

So this is how it would work if you used DWARFASTParserClang. Are you using that? Or are you using your own DWARFASTParserGo? If so, then you need to implement this type completion in your own TypeSystem. Your type system subclass is required to implement many different function for type introspection, one of which is:

class TypeSystem {
 
   virtual uint32_t
    GetNumChildren (void *type, bool omit_empty_base_classes) = 0;

};

So the clang version in ClangASTContext::GetNumChildren() knows to complete the type if it a forward declaration. If you have a GoASTContext class that inherits from TypeSystem, then all function that might need to know about the contents of a class or struct, will need to know to complete the type. Or, you can fully parse the structs/unions/classes as you parse the DWARF and not worry about implementing lazy type completion for Go.

So the main questions I have for you are:
- Do you have a GoASTContext that inherits from TypeSystem?
- If you do, in order for DWARF to parse Go types that use the GoASTContext, you will need to write a DWARFASTParser subclass that constructs Go types from DWARF. Then your TypeSystem subclass will need to implement:

TypeSystem {

    virtual DWARFASTParser *
    GetDWARFParser ();

};




More information about the lldb-dev mailing list