Cannot access the offset of a virtual base class in a given case

Hi Everyone,

I have a question about the layout of a C++ record.

Given the following class hierarchy:

// A virtual class
class Foo {
public:
  virtual void f() {};
};

// A virtual class
class Bar {
public:
  virtual void g() {};
};

// Two virtual bases
class Zoo : virtual public Foo, virtual public Bar {
public:
  int x;
  int y;
  virtual void g() { x = x + y; };
  virtual void f() { x = x - y; };
};

Following is the code I use to access the offset of the virtual bases of a
class:
    
    // Suppose that "Record" denotes a RecordDecl

    // The layout code generation for the backend, e.g., LLVM IR
    const CGRecordLayout &Layout = CGM.getTypes().getCGRecordLayout(Record);
    // In C++ mode
    const CXXRecordDecl *CXXRecord = cast<CXXRecordDecl>(Record);

    // Virtual bases
    for (CXXRecordDecl::base_class_const_iterator I = CXXRecord->vbases_begin(),
         E = CXXRecord->vbases_end(); I != E; ++I) {
      // The type of a virtual base
      const RecordType *BaseTy = I->getType()->getAs<RecordType>();
      // The decl of the virtual base
      const CXXRecordDecl *BaseDecl = dyn_cast<CXXRecordDecl>(BaseTy->getDecl());
      // Accessing the index of the base
      unsigned fieldNo = Layout.getVirtualBaseIndex(BaseDecl);
    }

Fed with the given class hierarchy, the last statement of the above
code, i.e., "getVirtualBaseIndex(BaseDecl)", causes an assertion
failure.

  Assertion failed: (CompleteObjectVirtualBases.count(base) && "Invalid virtual base!"), ...

The failure means that the virtual base "Foo" is not in the
layout mapping from the virtual bases of the class "Zoo" to their
offsets. So, can someone point out any hints about this missing?

P.S. When the class "Foo" contains one data member, the above
issue goes away.

Xiaolong

My stab in the dark would be that this sounds rather like the Empty
Base Class optimization at work (
http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=319 ).
If Foo has no members then there's no reason to ever access it so its
offset doesn't need to be computable (because it doesn't actually
exist).

No doubt someone with more actual clang knowledge can confirm/provide
more detail, but perhaps this'll help put you in the right direction
until then.

- David

Thanks David,

>
> Hi Everyone,
>
> I have a question about the layout of a C++ record.
>
> Given the following class hierarchy:
>
> // A virtual class
> class Foo {
> public:
> virtual void f() {};
> };
>
> // A virtual class
> class Bar {
> public:
> virtual void g() {};
> };
>
> // Two virtual bases
> class Zoo : virtual public Foo, virtual public Bar {
> public:
> int x;
> int y;
> virtual void g() { x = x + y; };
> virtual void f() { x = x - y; };
> };
>
>
> Following is the code I use to access the offset of the virtual bases of a
> class:
>
> // Suppose that "Record" denotes a RecordDecl
>
> // The layout code generation for the backend, e.g., LLVM IR
> const CGRecordLayout &Layout = CGM.getTypes().getCGRecordLayout(Record);
> // In C++ mode
> const CXXRecordDecl *CXXRecord = cast<CXXRecordDecl>(Record);
>
> // Virtual bases
> for (CXXRecordDecl::base_class_const_iterator I = CXXRecord->vbases_begin(),
> E = CXXRecord->vbases_end(); I != E; ++I) {
> // The type of a virtual base
> const RecordType *BaseTy = I->getType()->getAs<RecordType>();
> // The decl of the virtual base
> const CXXRecordDecl *BaseDecl = dyn_cast<CXXRecordDecl>(BaseTy->getDecl());
> // Accessing the index of the base
> unsigned fieldNo = Layout.getVirtualBaseIndex(BaseDecl);
> }
>
> Fed with the given class hierarchy, the last statement of the above
> code, i.e., "getVirtualBaseIndex(BaseDecl)", causes an assertion
> failure.
>
> Assertion failed: (CompleteObjectVirtualBases.count(base) && "Invalid virtual base!"), ...
>
> The failure means that the virtual base "Foo" is not in the
> layout mapping from the virtual bases of the class "Zoo" to their
> offsets. So, can someone point out any hints about this missing?
>
> P.S. When the class "Foo" contains one data member, the above
> issue goes away.

My stab in the dark would be that this sounds rather like the Empty
Base Class optimization at work (
http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=319 ).
If Foo has no members then there's no reason to ever access it so its
offset doesn't need to be computable (because it doesn't actually
exist).

In my case, "Foo" is a polymophic class, having a size greater than
zero. Moreover, "Foo" is a virtual base of "Zoo". Consider the
statements:
  Zoo zoo;
  Foo *foo = &zoo;
  foo->f();
I guess, to ensure the correctness of "foo->f()", the "Foo" subobject
in a "Zoo" object must be present. My guess seems to agree with the
LLVM IR type system for the given class hierarchy:

%class.Foo = type { i32 (...)** }
%class.Bar = type { i32 (...)** }
%class.Zoo = type { [8 x i8], i32, i32, [8 x i8] }

There are two virtual tables, respectively, for the two virtual bases
of the class "Zoo". %class.Bar is identified as the primary base class
of Zoo in this case, and has the offset 0. %class.Foo has a non-zero
offset.

I am, however, not sure if this is an empty-based optimization
issue. If it is, what predicates can I use to check the specialty of
such a base class "Foo"?

No doubt someone with more actual clang knowledge can

confirm/provide

more detail, but perhaps this'll help put you in the right direction
until then.

Xiaolong

See if, CGDebugInfo::CollectCXXBases() helps you.