Skip to content

Commit

Permalink
Fix resolution of certain recursive types.
Browse files Browse the repository at this point in the history
Summary:
If a struct type S has a member T that has a member that is a function that
returns a typedef of S* the respective field would be duplicated, which caused
an assert down the line in RecordLayoutBuilder. This patch adds a check that
removes the possibility of trying to resolve the same type twice within the
same callstack.

This commit also adds unit tests for these failures.

Fixes https://llvm.org/bugs/show_bug.cgi?id=20486.

Patch by Cristian Hancila.

Test Plan: Run unit tests.

Reviewers: clayborg spyffe

Subscribers: lldb-commits

Differential Revision: http://reviews.llvm.org/D8561

llvm-svn: 234441
  • Loading branch information
sas committed Apr 8, 2015
1 parent 576ff12 commit 9901e9c
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 1 deletion.
2 changes: 2 additions & 0 deletions lldb/include/lldb/Expression/ClangASTSource.h
Expand Up @@ -51,6 +51,7 @@ class ClangASTSource :
m_lookups_enabled (false),
m_target (target),
m_ast_context (NULL),
m_active_lexical_decls (),
m_active_lookups ()
{
m_ast_importer = m_target->GetClangASTImporter();
Expand Down Expand Up @@ -404,6 +405,7 @@ class ClangASTSource :
const lldb::TargetSP m_target; ///< The target to use in finding variables and types.
clang::ASTContext *m_ast_context; ///< The AST context requests are coming in for.
ClangASTImporter *m_ast_importer; ///< The target's AST importer.
std::set<const clang::Decl *> m_active_lexical_decls;
std::set<const char *> m_active_lookups;
};

Expand Down
37 changes: 37 additions & 0 deletions lldb/source/Expression/ClangASTSource.cpp
Expand Up @@ -30,6 +30,31 @@
using namespace clang;
using namespace lldb_private;

//------------------------------------------------------------------
// Scoped class that will remove an active lexical decl from the set
// when it goes out of scope.
//------------------------------------------------------------------
namespace {
class ScopedLexicalDeclEraser
{
public:
ScopedLexicalDeclEraser(std::set<const clang::Decl *> &decls,
const clang::Decl *decl)
: m_active_lexical_decls(decls), m_decl(decl)
{
}

~ScopedLexicalDeclEraser()
{
m_active_lexical_decls.erase(m_decl);
}

private:
std::set<const clang::Decl *> &m_active_lexical_decls;
const clang::Decl *m_decl;
};
}

ClangASTSource::~ClangASTSource()
{
m_ast_importer->ForgetDestination(m_ast_context);
Expand Down Expand Up @@ -191,6 +216,12 @@ ClangASTSource::CompleteType (TagDecl *tag_decl)
dumper.ToLog(log, " [CTD] ");
}

auto iter = m_active_lexical_decls.find(tag_decl);
if (iter != m_active_lexical_decls.end())
return;
m_active_lexical_decls.insert(tag_decl);
ScopedLexicalDeclEraser eraser(m_active_lexical_decls, tag_decl);

if (!m_ast_importer->CompleteTagDecl (tag_decl))
{
// We couldn't complete the type. Maybe there's a definition
Expand Down Expand Up @@ -402,6 +433,12 @@ ClangASTSource::FindExternalLexicalDecls (const DeclContext *decl_context,
if (!context_decl)
return ELR_Failure;

auto iter = m_active_lexical_decls.find(context_decl);
if (iter != m_active_lexical_decls.end())
return ELR_Failure;
m_active_lexical_decls.insert(context_decl);
ScopedLexicalDeclEraser eraser(m_active_lexical_decls, context_decl);

static unsigned int invocation_id = 0;
unsigned int current_id = invocation_id++;

Expand Down
7 changes: 6 additions & 1 deletion lldb/source/Symbol/ClangASTImporter.cpp
Expand Up @@ -286,7 +286,12 @@ ClangASTImporter::RequireCompleteType (clang::QualType type)

if (const TagType *tag_type = type->getAs<TagType>())
{
return CompleteTagDecl(tag_type->getDecl());
TagDecl *tag_decl = tag_type->getDecl();

if (tag_decl->getDefinition() || tag_decl->isBeingDefined())
return true;

return CompleteTagDecl(tag_decl);
}
if (const ObjCObjectType *objc_object_type = type->getAs<ObjCObjectType>())
{
Expand Down
68 changes: 68 additions & 0 deletions lldb/test/types/TestRecursiveTypes.py
@@ -0,0 +1,68 @@
"""
Test that recursive types are handled correctly.
"""

import lldb
import lldbutil
import sys
import unittest2
from lldbtest import *

class RecursiveTypesTestCase(TestBase):

mydir = TestBase.compute_mydir(__file__)

def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
# disable "There is a running process, kill it and restart?" prompt
self.runCmd("settings set auto-confirm true")
self.addTearDownHook(lambda: self.runCmd("settings clear auto-confirm"))
# Find the line number to break for main.c.
self.line = line_number('recursive_type_main.cpp',
'// Test at this line.')

self.d1 = {'CXX_SOURCES': 'recursive_type_main.cpp recursive_type_1.cpp'}
self.d2 = {'CXX_SOURCES': 'recursive_type_main.cpp recursive_type_2.cpp'}

@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
@dsym_test
def test_recursive_dsym_type_1(self):
"""Test that recursive structs are displayed correctly."""
self.buildDsym(dictionary=self.d1)
self.print_struct()

@dwarf_test
def test_recursive_dwarf_type_1(self):
"""Test that recursive structs are displayed correctly."""
self.buildDwarf(dictionary=self.d1)
self.print_struct()

@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
@dsym_test
def test_recursive_dsym_type_2(self):
"""Test that recursive structs are displayed correctly."""
self.buildDsym(dictionary=self.d2)
self.print_struct()

@dwarf_test
def test_recursive_dwarf_type_2(self):
"""Test that recursive structs are displayed correctly."""
self.buildDwarf(dictionary=self.d2)
self.print_struct()

def print_struct(self):
self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)

lldbutil.run_break_set_by_file_and_line (self, "recursive_type_main.cpp", self.line, num_expected_locations=-1, loc_exact=True)

self.runCmd("run", RUN_SUCCEEDED)

self.expect("print tpi", RUN_SUCCEEDED)
self.expect("print *tpi", RUN_SUCCEEDED)

if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()
12 changes: 12 additions & 0 deletions lldb/test/types/recursive_type_1.cpp
@@ -0,0 +1,12 @@
typedef struct t *tp;
typedef tp (*get_tp)();

struct s {
get_tp get_tp_p;
};

struct t {
struct s *s;
};

struct t t;
10 changes: 10 additions & 0 deletions lldb/test/types/recursive_type_2.cpp
@@ -0,0 +1,10 @@
typedef struct t *tp;
typedef tp (*get_tp)();

struct t {
struct {
get_tp get_tp_p;
};
};

struct t t;
8 changes: 8 additions & 0 deletions lldb/test/types/recursive_type_main.cpp
@@ -0,0 +1,8 @@
typedef struct t *tp;
extern struct t t;

int main() {
tp tpi = &t;
// Test at this line.
return 0;
}

0 comments on commit 9901e9c

Please sign in to comment.