[Lldb-commits] [lldb] r213433 - LLDB now correctly handles virtual inheritance.

Greg Clayton gclayton at apple.com
Fri Jul 18 17:12:58 PDT 2014


Author: gclayton
Date: Fri Jul 18 19:12:57 2014
New Revision: 213433

URL: http://llvm.org/viewvc/llvm-project?rev=213433&view=rev
Log:
LLDB now correctly handles virtual inheritance.

Test case added as well.

<rdar://problem/16785904>


Added:
    lldb/trunk/test/lang/cpp/diamond/
    lldb/trunk/test/lang/cpp/diamond/Makefile
    lldb/trunk/test/lang/cpp/diamond/TestDiamond.py
    lldb/trunk/test/lang/cpp/diamond/main.cpp
Modified:
    lldb/trunk/include/lldb/Core/ValueObject.h
    lldb/trunk/include/lldb/Symbol/ClangASTType.h
    lldb/trunk/source/Core/ValueObject.cpp
    lldb/trunk/source/Core/ValueObjectConstResultImpl.cpp
    lldb/trunk/source/Symbol/ClangASTType.cpp

Modified: lldb/trunk/include/lldb/Core/ValueObject.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Core/ValueObject.h?rev=213433&r1=213432&r2=213433&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Core/ValueObject.h (original)
+++ lldb/trunk/include/lldb/Core/ValueObject.h Fri Jul 18 19:12:57 2014
@@ -715,6 +715,10 @@ public:
     {
     }
 
+    // Find the address of the C++ vtable pointer
+    virtual lldb::addr_t
+    GetCPPVTableAddress(AddressType &address_type);
+    
     virtual lldb::ValueObjectSP
     Cast (const ClangASTType &clang_ast_type);
     

Modified: lldb/trunk/include/lldb/Symbol/ClangASTType.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Symbol/ClangASTType.h?rev=213433&r1=213432&r2=213433&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Symbol/ClangASTType.h (original)
+++ lldb/trunk/include/lldb/Symbol/ClangASTType.h Fri Jul 18 19:12:57 2014
@@ -426,7 +426,6 @@ public:
     
     ClangASTType
     GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
-                              const char *parent_name,
                               size_t idx,
                               bool transparent_pointers,
                               bool omit_empty_base_classes,
@@ -437,7 +436,8 @@ public:
                               uint32_t &child_bitfield_bit_size,
                               uint32_t &child_bitfield_bit_offset,
                               bool &child_is_base_class,
-                              bool &child_is_deref_of_parent) const;
+                              bool &child_is_deref_of_parent,
+                              ValueObject *valobj) const;
     
     // Lookup a child given a name. This function will match base class names
     // and member member names in "clang_type" only, not descendants.

Modified: lldb/trunk/source/Core/ValueObject.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Core/ValueObject.cpp?rev=213433&r1=213432&r2=213433&view=diff
==============================================================================
--- lldb/trunk/source/Core/ValueObject.cpp (original)
+++ lldb/trunk/source/Core/ValueObject.cpp Fri Jul 18 19:12:57 2014
@@ -793,7 +793,6 @@ ValueObject::CreateChildAtIndex (size_t
     ExecutionContext exe_ctx (GetExecutionContextRef());
     
     child_clang_type = GetClangType().GetChildClangTypeAtIndex (&exe_ctx,
-                                                                GetName().GetCString(),
                                                                 idx,
                                                                 transparent_pointers,
                                                                 omit_empty_base_classes,
@@ -804,7 +803,8 @@ ValueObject::CreateChildAtIndex (size_t
                                                                 child_bitfield_bit_size,
                                                                 child_bitfield_bit_offset,
                                                                 child_is_base_class,
-                                                                child_is_deref_of_parent);
+                                                                child_is_deref_of_parent,
+                                                                this);
     if (child_clang_type)
     {
         if (synthetic_index)
@@ -3468,6 +3468,38 @@ ValueObject::CreateConstantValue (const
     return valobj_sp;
 }
 
+lldb::addr_t
+ValueObject::GetCPPVTableAddress (AddressType &address_type)
+{
+    ClangASTType pointee_type;
+    ClangASTType this_type(GetClangType());
+    uint32_t type_info = this_type.GetTypeInfo(&pointee_type);
+    if (type_info)
+    {
+        bool ptr_or_ref = false;
+        if (type_info & (ClangASTType::eTypeIsPointer | ClangASTType::eTypeIsReference))
+        {
+            ptr_or_ref = true;
+            type_info = pointee_type.GetTypeInfo();
+        }
+        
+        const uint32_t cpp_class = ClangASTType::eTypeIsClass | ClangASTType::eTypeIsCPlusPlus;
+        if ((type_info & cpp_class) == cpp_class)
+        {
+            if (ptr_or_ref)
+            {
+                address_type = GetAddressTypeOfChildren();
+                return GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+            }
+            else
+                return GetAddressOf (false, &address_type);
+        }
+    }
+
+    address_type = eAddressTypeInvalid;
+    return LLDB_INVALID_ADDRESS;
+}
+
 ValueObjectSP
 ValueObject::Dereference (Error &error)
 {
@@ -3494,7 +3526,6 @@ ValueObject::Dereference (Error &error)
         ExecutionContext exe_ctx (GetExecutionContextRef());
         
         child_clang_type = clang_type.GetChildClangTypeAtIndex (&exe_ctx,
-                                                                GetName().GetCString(),
                                                                 0,
                                                                 transparent_pointers,
                                                                 omit_empty_base_classes,
@@ -3505,7 +3536,8 @@ ValueObject::Dereference (Error &error)
                                                                 child_bitfield_bit_size,
                                                                 child_bitfield_bit_offset,
                                                                 child_is_base_class,
-                                                                child_is_deref_of_parent);
+                                                                child_is_deref_of_parent,
+                                                                this);
         if (child_clang_type && child_byte_size)
         {
             ConstString child_name;

Modified: lldb/trunk/source/Core/ValueObjectConstResultImpl.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Core/ValueObjectConstResultImpl.cpp?rev=213433&r1=213432&r2=213433&view=diff
==============================================================================
--- lldb/trunk/source/Core/ValueObjectConstResultImpl.cpp (original)
+++ lldb/trunk/source/Core/ValueObjectConstResultImpl.cpp Fri Jul 18 19:12:57 2014
@@ -109,7 +109,6 @@ ValueObjectConstResultImpl::CreateChildA
     ExecutionContext exe_ctx (m_impl_backend->GetExecutionContextRef());
     
     child_clang_type = clang_type.GetChildClangTypeAtIndex (&exe_ctx,
-                                                            m_impl_backend->GetName().GetCString(),
                                                             idx,
                                                             transparent_pointers,
                                                             omit_empty_base_classes,
@@ -120,7 +119,8 @@ ValueObjectConstResultImpl::CreateChildA
                                                             child_bitfield_bit_size,
                                                             child_bitfield_bit_offset,
                                                             child_is_base_class,
-                                                            child_is_deref_of_parent);
+                                                            child_is_deref_of_parent,
+                                                            m_impl_backend);
     if (child_clang_type && child_byte_size)
     {
         if (synthetic_index)

Modified: lldb/trunk/source/Symbol/ClangASTType.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Symbol/ClangASTType.cpp?rev=213433&r1=213432&r2=213433&view=diff
==============================================================================
--- lldb/trunk/source/Symbol/ClangASTType.cpp (original)
+++ lldb/trunk/source/Symbol/ClangASTType.cpp Fri Jul 18 19:12:57 2014
@@ -22,6 +22,7 @@
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/RecordLayout.h"
 #include "clang/AST/Type.h"
+#include "clang/AST/VTableBuilder.h"
 
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/IdentifierTable.h"
@@ -3068,7 +3069,6 @@ ClangASTType::GetNumPointeeChildren () c
 
 ClangASTType
 ClangASTType::GetChildClangTypeAtIndex (ExecutionContext *exe_ctx,
-                                        const char *parent_name,
                                         size_t idx,
                                         bool transparent_pointers,
                                         bool omit_empty_base_classes,
@@ -3079,7 +3079,8 @@ ClangASTType::GetChildClangTypeAtIndex (
                                         uint32_t &child_bitfield_bit_size,
                                         uint32_t &child_bitfield_bit_offset,
                                         bool &child_is_base_class,
-                                        bool &child_is_deref_of_parent) const
+                                        bool &child_is_deref_of_parent,
+                                        ValueObject *valobj) const
 {
     if (!IsValid())
         return ClangASTType();
@@ -3146,7 +3147,74 @@ ClangASTType::GetChildClangTypeAtIndex (
                             
                             
                             if (base_class->isVirtual())
-                                bit_offset = record_layout.getVBaseClassOffset(base_class_decl).getQuantity() * 8;
+                            {
+                                bool handled = false;
+                                if (valobj)
+                                {
+                                    Error err;
+                                    AddressType addr_type = eAddressTypeInvalid;
+                                    lldb::addr_t vtable_ptr_addr = valobj->GetCPPVTableAddress(addr_type);
+                                    
+                                    if (vtable_ptr_addr != LLDB_INVALID_ADDRESS && addr_type == eAddressTypeLoad)
+                                    {
+                                        
+                                        ExecutionContext exe_ctx (valobj->GetExecutionContextRef());
+                                        Process *process = exe_ctx.GetProcessPtr();
+                                        if (process)
+                                        {
+                                            clang::VTableContextBase *vtable_ctx = m_ast->getVTableContext();
+                                            if (vtable_ctx)
+                                            {
+                                                if (vtable_ctx->isMicrosoft())
+                                                {
+                                                    clang::MicrosoftVTableContext *msoft_vtable_ctx = static_cast<clang::MicrosoftVTableContext *>(vtable_ctx);
+                                                    
+                                                    if (vtable_ptr_addr)
+                                                    {
+                                                        const lldb::addr_t vbtable_ptr_addr = vtable_ptr_addr + record_layout.getVBPtrOffset().getQuantity();
+                                                        
+                                                        const lldb::addr_t vbtable_ptr = process->ReadPointerFromMemory(vbtable_ptr_addr, err);
+                                                        if (vbtable_ptr != LLDB_INVALID_ADDRESS)
+                                                        {
+                                                            // Get the index into the virtual base table. The index is the index in uint32_t from vbtable_ptr
+                                                            const unsigned vbtable_index = msoft_vtable_ctx->getVBTableIndex(cxx_record_decl, base_class_decl);
+                                                            const lldb::addr_t base_offset_addr = vbtable_ptr + vbtable_index * 4;
+                                                            const uint32_t base_offset = process->ReadUnsignedIntegerFromMemory(base_offset_addr, 4, UINT32_MAX, err);
+                                                            if (base_offset != UINT32_MAX)
+                                                            {
+                                                                handled = true;
+                                                                bit_offset = base_offset * 8;
+                                                            }
+                                                        }
+                                                    }
+                                                }
+                                                else
+                                                {
+                                                    clang::ItaniumVTableContext *itanium_vtable_ctx = static_cast<clang::ItaniumVTableContext *>(vtable_ctx);
+                                                    if (vtable_ptr_addr)
+                                                    {
+                                                        const lldb::addr_t vtable_ptr = process->ReadPointerFromMemory(vtable_ptr_addr, err);
+                                                        if (vtable_ptr != LLDB_INVALID_ADDRESS)
+                                                        {
+                                                            clang::CharUnits base_offset_offset = itanium_vtable_ctx->getVirtualBaseOffsetOffset(cxx_record_decl, base_class_decl);
+                                                            const lldb::addr_t base_offset_addr = vtable_ptr + base_offset_offset.getQuantity();
+                                                            const uint32_t base_offset = process->ReadUnsignedIntegerFromMemory(base_offset_addr, 4, UINT32_MAX, err);
+                                                            if (base_offset != UINT32_MAX)
+                                                            {
+                                                                handled = true;
+                                                                bit_offset = base_offset * 8;
+                                                            }
+                                                        }
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    }
+
+                                }
+                                if (!handled)
+                                    bit_offset = record_layout.getVBaseClassOffset(base_class_decl).getQuantity() * 8;
+                            }
                             else
                                 bit_offset = record_layout.getBaseClassOffset(base_class_decl).getQuantity() * 8;
                             
@@ -3321,7 +3389,6 @@ ClangASTType::GetChildClangTypeAtIndex (
                     child_is_deref_of_parent = false;
                     bool tmp_child_is_deref_of_parent = false;
                     return pointee_clang_type.GetChildClangTypeAtIndex (exe_ctx,
-                                                                        parent_name,
                                                                         idx,
                                                                         transparent_pointers,
                                                                         omit_empty_base_classes,
@@ -3332,11 +3399,13 @@ ClangASTType::GetChildClangTypeAtIndex (
                                                                         child_bitfield_bit_size,
                                                                         child_bitfield_bit_offset,
                                                                         child_is_base_class,
-                                                                        tmp_child_is_deref_of_parent);
+                                                                        tmp_child_is_deref_of_parent,
+                                                                        valobj);
                 }
                 else
                 {
                     child_is_deref_of_parent = true;
+                    const char *parent_name = valobj ? valobj->GetName().GetCString() : NULL;
                     if (parent_name)
                     {
                         child_name.assign(1, '*');
@@ -3411,7 +3480,6 @@ ClangASTType::GetChildClangTypeAtIndex (
                     child_is_deref_of_parent = false;
                     bool tmp_child_is_deref_of_parent = false;
                     return pointee_clang_type.GetChildClangTypeAtIndex (exe_ctx,
-                                                                        parent_name,
                                                                         idx,
                                                                         transparent_pointers,
                                                                         omit_empty_base_classes,
@@ -3422,12 +3490,14 @@ ClangASTType::GetChildClangTypeAtIndex (
                                                                         child_bitfield_bit_size,
                                                                         child_bitfield_bit_offset,
                                                                         child_is_base_class,
-                                                                        tmp_child_is_deref_of_parent);
+                                                                        tmp_child_is_deref_of_parent,
+                                                                        valobj);
                 }
                 else
                 {
                     child_is_deref_of_parent = true;
                     
+                    const char *parent_name = valobj ? valobj->GetName().GetCString() : NULL;
                     if (parent_name)
                     {
                         child_name.assign(1, '*');
@@ -3456,7 +3526,6 @@ ClangASTType::GetChildClangTypeAtIndex (
                     child_is_deref_of_parent = false;
                     bool tmp_child_is_deref_of_parent = false;
                     return pointee_clang_type.GetChildClangTypeAtIndex (exe_ctx,
-                                                                        parent_name,
                                                                         idx,
                                                                         transparent_pointers,
                                                                         omit_empty_base_classes,
@@ -3467,10 +3536,12 @@ ClangASTType::GetChildClangTypeAtIndex (
                                                                         child_bitfield_bit_size,
                                                                         child_bitfield_bit_offset,
                                                                         child_is_base_class,
-                                                                        tmp_child_is_deref_of_parent);
+                                                                        tmp_child_is_deref_of_parent,
+                                                                        valobj);
                 }
                 else
                 {
+                    const char *parent_name = valobj ? valobj->GetName().GetCString() : NULL;
                     if (parent_name)
                     {
                         child_name.assign(1, '&');
@@ -3492,7 +3563,6 @@ ClangASTType::GetChildClangTypeAtIndex (
             {
                 ClangASTType typedefed_clang_type (m_ast, llvm::cast<clang::TypedefType>(parent_qual_type)->getDecl()->getUnderlyingType());
                 return typedefed_clang_type.GetChildClangTypeAtIndex (exe_ctx,
-                                                                      parent_name,
                                                                       idx,
                                                                       transparent_pointers,
                                                                       omit_empty_base_classes,
@@ -3503,7 +3573,8 @@ ClangASTType::GetChildClangTypeAtIndex (
                                                                       child_bitfield_bit_size,
                                                                       child_bitfield_bit_offset,
                                                                       child_is_base_class,
-                                                                      child_is_deref_of_parent);
+                                                                      child_is_deref_of_parent,
+                                                                      valobj);
             }
             break;
             
@@ -3511,7 +3582,6 @@ ClangASTType::GetChildClangTypeAtIndex (
             {
                 ClangASTType elaborated_clang_type (m_ast, llvm::cast<clang::ElaboratedType>(parent_qual_type)->getNamedType());
                 return elaborated_clang_type.GetChildClangTypeAtIndex (exe_ctx,
-                                                                       parent_name,
                                                                        idx,
                                                                        transparent_pointers,
                                                                        omit_empty_base_classes,
@@ -3522,14 +3592,14 @@ ClangASTType::GetChildClangTypeAtIndex (
                                                                        child_bitfield_bit_size,
                                                                        child_bitfield_bit_offset,
                                                                        child_is_base_class,
-                                                                       child_is_deref_of_parent);
+                                                                       child_is_deref_of_parent,
+                                                                       valobj);
             }
             
         case clang::Type::Paren:
             {
                 ClangASTType paren_clang_type (m_ast, llvm::cast<clang::ParenType>(parent_qual_type)->desugar());
                 return paren_clang_type.GetChildClangTypeAtIndex (exe_ctx,
-                                                                  parent_name,
                                                                   idx,
                                                                   transparent_pointers,
                                                                   omit_empty_base_classes,
@@ -3540,7 +3610,8 @@ ClangASTType::GetChildClangTypeAtIndex (
                                                                   child_bitfield_bit_size,
                                                                   child_bitfield_bit_offset,
                                                                   child_is_base_class,
-                                                                  child_is_deref_of_parent);
+                                                                  child_is_deref_of_parent,
+                                                                  valobj);
             }
             
             

Added: lldb/trunk/test/lang/cpp/diamond/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lang/cpp/diamond/Makefile?rev=213433&view=auto
==============================================================================
--- lldb/trunk/test/lang/cpp/diamond/Makefile (added)
+++ lldb/trunk/test/lang/cpp/diamond/Makefile Fri Jul 18 19:12:57 2014
@@ -0,0 +1,5 @@
+LEVEL = ../../../make
+
+CXX_SOURCES := main.cpp
+
+include $(LEVEL)/Makefile.rules

Added: lldb/trunk/test/lang/cpp/diamond/TestDiamond.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lang/cpp/diamond/TestDiamond.py?rev=213433&view=auto
==============================================================================
--- lldb/trunk/test/lang/cpp/diamond/TestDiamond.py (added)
+++ lldb/trunk/test/lang/cpp/diamond/TestDiamond.py Fri Jul 18 19:12:57 2014
@@ -0,0 +1,63 @@
+"""
+Tests that bool types work
+"""
+import lldb
+from lldbtest import *
+import lldbutil
+
+class CPPTestDiamondInheritance(TestBase):
+    
+    mydir = TestBase.compute_mydir(__file__)
+    
+    @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
+    @dsym_test
+    def test_with_dsym_and_run_command(self):
+        """Test that virtual base classes work in when SBValue objects are used to explore the variable value"""
+        self.buildDsym()
+        self.diamong_inheritace()
+
+    @dwarf_test
+    def test_with_dwarf_and_run_command(self):
+        """Test that virtual base classes work in when SBValue objects are used to explore the variable value"""
+        self.buildDwarf()
+        self.diamong_inheritace()
+
+    def setUp(self):
+        TestBase.setUp(self)
+    
+    def set_breakpoint(self, line):
+        # Some compilers (for example GCC 4.4.7 and 4.6.1) emit multiple locations for the statement with the ternary
+        # operator in the test program, while others emit only 1.
+        lldbutil.run_break_set_by_file_and_line (self, "main.cpp", line, num_expected_locations=-1, loc_exact=False)
+
+    def diamong_inheritace(self):
+        """Test that virtual base classes work in when SBValue objects are used to explore the variable value"""
+        
+        exe = os.path.join(os.getcwd(), "a.out")
+        
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(target, VALID_TARGET)
+        self.set_breakpoint(line_number('main.cpp', '// breakpoint 1'))
+        self.set_breakpoint(line_number('main.cpp', '// breakpoint 2'))
+        process = target.LaunchSimple (None, None, self.get_process_working_directory())
+        self.assertTrue(process, PROCESS_IS_VALID)
+        thread = process.GetThreadAtIndex(0)
+        frame = thread.GetFrameAtIndex(0)
+        j1 = frame.FindVariable("j1")
+        j1_Derived1 = j1.GetChildAtIndex(0)
+        j1_Derived2 = j1.GetChildAtIndex(1)
+        j1_Derived1_VBase = j1_Derived1.GetChildAtIndex(0)
+        j1_Derived2_VBase = j1_Derived2.GetChildAtIndex(0)
+        j1_Derived1_VBase_m_value = j1_Derived1_VBase.GetChildAtIndex(0)
+        j1_Derived2_VBase_m_value = j1_Derived2_VBase.GetChildAtIndex(0)
+        self.assertTrue(j1_Derived1_VBase.GetLoadAddress() == j1_Derived2_VBase.GetLoadAddress(), "ensure virtual base class is the same between Derived1 and Derived2")
+        self.assertTrue(j1_Derived1_VBase_m_value.GetValueAsUnsigned(1) == j1_Derived2_VBase_m_value.GetValueAsUnsigned(2), "ensure m_value in VBase is the same")
+        self.assertTrue(frame.FindVariable("d").GetChildAtIndex(0).GetChildAtIndex(0).GetValueAsUnsigned(0) == 12345, "ensure Derived2 from j1 is correct");
+        thread.StepOver()
+        self.assertTrue(frame.FindVariable("d").GetChildAtIndex(0).GetChildAtIndex(0).GetValueAsUnsigned(0) == 12346, "ensure Derived2 from j2 is correct");
+        
+if __name__ == '__main__':
+    import atexit
+    lldb.SBDebugger.Initialize()
+    atexit.register(lambda: lldb.SBDebugger.Terminate())
+    unittest2.main()

Added: lldb/trunk/test/lang/cpp/diamond/main.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/test/lang/cpp/diamond/main.cpp?rev=213433&view=auto
==============================================================================
--- lldb/trunk/test/lang/cpp/diamond/main.cpp (added)
+++ lldb/trunk/test/lang/cpp/diamond/main.cpp Fri Jul 18 19:12:57 2014
@@ -0,0 +1,85 @@
+//===-- main.cpp ------------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include <stdio.h>
+
+static int g_next_value = 12345;
+
+class VBase
+{
+public:
+    VBase() : m_value(g_next_value++) {}
+    virtual ~VBase() {}
+    void Print() 
+    {
+        printf("%p: %s\n%p: m_value = 0x%8.8x\n", this, __PRETTY_FUNCTION__, &m_value, m_value);
+    }
+    int m_value;
+};
+
+class Derived1 : public virtual VBase
+{
+public:
+    Derived1() {};
+    void Print ()
+    {
+        printf("%p: %s\n", this, __PRETTY_FUNCTION__);
+        VBase::Print();
+    }
+
+};
+
+class Derived2 : public virtual VBase
+{
+public:
+    Derived2() {};
+    
+    void Print ()
+    {
+        printf("%p: %s\n", this, __PRETTY_FUNCTION__);
+        VBase::Print();
+    }
+};
+
+class Joiner1 : public Derived1, public Derived2
+{
+public:
+    Joiner1() : 
+        m_joiner1(3456), 
+        m_joiner2(6789) {}
+    void Print ()
+    {
+        printf("%p: %s \n%p: m_joiner1 = 0x%8.8x\n%p: m_joiner2 = 0x%8.8x\n",
+               this,
+               __PRETTY_FUNCTION__,
+               &m_joiner1,
+               m_joiner1,
+               &m_joiner2,
+               m_joiner2);
+        Derived1::Print();
+        Derived2::Print();
+    }
+    int m_joiner1;
+    int m_joiner2;
+};
+
+class Joiner2 : public Derived2
+{
+    int m_stuff[32];
+};
+
+int main(int argc, const char * argv[])
+{
+    Joiner1 j1;
+    Joiner2 j2;
+    j1.Print();
+    j2.Print();
+    Derived2 *d = &j1;
+    d = &j2;  // breakpoint 1
+    return 0; // breakpoint 2
+}





More information about the lldb-commits mailing list