Skip to content

Incorrect vtable indexing in C++ to C transformation #701

@BigFish2086

Description

@BigFish2086

The following C++ example is actually taken from Back to Basics: Object-Oriented Programming in C++ - Andreas Fertig - CppCon 2024

#include <stdio.h>

struct Fruit {
  double mWeight{};
  virtual ~Fruit() { puts("~Fruit"); }
  virtual void Print() { puts("Fruit's Print"); }
};

struct Apple : Fruit {
  int mRipeGrade{5};
  void Print() override { printf("Apple's Print: %d\n", mRipeGrade); }
};

struct PinkLady : Apple {
  int mColorGrade{8};
  void Print() override { printf("Pink Ladies Print: %d\n", mColorGrade); }
};

int main() {
  PinkLady delicious{};
  delicious.Print();
  Fruit *f{static_cast<Fruit *>(&delicious)};
  f->Print();
}

which outputs:

$ g++ -o example ./example.c && ./example
Pink Ladies Print: 8
Pink Ladies Print: 8
~Fruit

But the generated code which can be found here, its main is like:

// ...
int __main(void)
{
  PinkLady delicious;
  Constructor_PinkLady((PinkLady *)&delicious);
  (*((void (*)(PinkLady *))((&delicious)->__vptrFruit[0]).f))((((PinkLady *)(char *)(&delicious)) + ((&delicious)->__vptrFruit[0]).d));
  Fruit * f = (Fruit *)&delicious;
  (*((void (*)(Fruit *))((f)->__vptrFruit[0]).f))((((Fruit *)(char *)(f)) + ((f)->__vptrFruit[0]).d));
  return 0;
  (*((void (*)(PinkLady *))((&delicious)->__vptrFruit[0]).f))((((PinkLady *)(char *)(&delicious)) + ((&delicious)->__vptrFruit[0]).d));
}
// ...
__mptr __vtbl_Fruit[2] = {{0, 0, (__vptp)Destructor_Fruit}, {0, 0, (__vptp)PrintFruit}};
__mptr __vtbl_Apple[2] = {{0, 0, (__vptp)Destructor_Apple}, {0, 0, (__vptp)PrintApple}};
__mptr __vtbl_PinkLady[2] = {{0, 0, (__vptp)Destructor_PinkLady}, {0, 0, (__vptp)PrintPinkLady}};

__mptr * __vtbl_array[3] = {__vtbl_Fruit, __vtbl_Apple, __vtbl_PinkLady};
// ...

which outputs:

$ gcc -o example ./example.c && ./example
~Fruit
~Fruit

which doesn't match the C++ example output. I enabled only the following two options:

  • Show all implicit casts
  • Show C++ to C transformation

I think the problem is that the code doesn't use correct indecies __vptrFruit[i] to refer to the correct functions to be used since they're pointing to the Destructors, not the Print functions.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions