Skip to content

fix GetVariableOffset in cases of multi-level and multiple inheritance #396

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged

Conversation

Vipul-Cariappa
Copy link
Collaborator

Description

Fixes the calculation of data member offsets of a class C. Where C inherits from multiple classes and these classes themselves inherit from other classes.

Fixes # (issue)

I found this issue when tinkering with cross-inheritance in cppyy.
It looks like upstream cppyy also faces a similar problem. Opened a PR stating the issue at wlav/cppyy#280.

Does not contribute to test cases that pass in cppyy.

Type of change

Please tick all options which are relevant.

  • Bug fix
  • New feature
  • Requires documentation updates

Testing

Added in tests with multi-level and multiple inheritance.

Checklist

  • I have read the contribution guide recently

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 10 out of 19. Check the log or trigger a new build to see more.

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: parameter 'b' is passed by value and only copied once; consider moving it to avoid unnecessary copies [performance-unnecessary-value-param]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:270: expanded from macro 'CODE'

    BaseB(int x, std::string b) : BaseA(x), b(b) {}                            \
                                              ^

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: parameter 's' is passed by value and only copied once; consider moving it to avoid unnecessary copies [performance-unnecessary-value-param]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:277: expanded from macro 'CODE'

    Base1(int i, std::string s) : i(i), s(s) {}                                \
                                          ^

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: pass by value and use std::move [modernize-pass-by-value]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:270: expanded from macro 'CODE'

    BaseB(int x, std::string b) : BaseA(x), b(b) {}                            \
                 ^

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'my_k' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:285: expanded from macro 'CODE'

  } my_k(5, 4, 3, "Cpp", "Python");
    ^

TEST(VariableReflectionTest, VariableOffsetsWithInheritance) {
Cpp::Declare("#include<string>");

#define Stringify(s) Stringifyx(s)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: function-like macro 'Stringify' used; consider a 'constexpr' template function [cppcoreguidelines-macro-usage]

#define Stringify(s) Stringifyx(s)
        ^

Cpp::Declare("#include<string>");

#define Stringify(s) Stringifyx(s)
#define Stringifyx(...) #__VA_ARGS__
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variadic macro 'Stringifyx' used; consider using a 'constexpr' variadic template function [cppcoreguidelines-macro-usage]

#define Stringifyx(...) #__VA_ARGS__
        ^

Cpp::TCppScope_t myklass = Cpp::GetNamed("MyKlass");
EXPECT_TRUE(myklass);

size_t num_bases = Cpp::GetNumBases(myklass);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: no header providing "size_t" is directly included [misc-include-cleaner]

unittests/CppInterOp/VariableReflectionTest.cpp:6:

+ #include <cstddef>

EXPECT_EQ(datamembers.size(), 5);

EXPECT_EQ(Cpp::GetVariableOffset(datamembers[0], myklass),
((intptr_t)&(my_k.k)) - ((intptr_t)&(my_k)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]

            ((intptr_t)&(my_k.k)) - ((intptr_t)&(my_k)));
                                     ^

@vgvassilev
Copy link
Contributor

We should probably turn off clang tidy from the unittests.

@Vipul-Cariappa
Copy link
Collaborator Author

Vipul-Cariappa commented Dec 17, 2024

Looks like, if we include a virtual destructor in the base classes these changes are not required.

EDIT
The virtual destructor thing only works for multi-level inheritance. i.e.

class BaseA {};
class BaseB: public BaseA {};

But does not work with multiple inheritance i.e.

class BaseA {};
class BaseB {};
class Klass: public BaseA, public BaseB {};

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 10 out of 17. Check the log or trigger a new build to see more.

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: class 'Base1' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:275: expanded from macro 'CODE'

  class Base1 {                                                                \
        ^

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: class 'BaseA' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:261: expanded from macro 'CODE'

  class BaseA {                                                                \
        ^

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: class 'BaseB' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:268: expanded from macro 'CODE'

  class BaseB : public BaseA {                                                 \
        ^

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: class 'MyKlass' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:283: expanded from macro 'CODE'

  class MyKlass : public BaseB, public Base1 {                                 \
        ^

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: parameter 'b' is passed by value and only copied once; consider moving it to avoid unnecessary copies [performance-unnecessary-value-param]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:272: expanded from macro 'CODE'

    BaseB(int x, std::string b) : BaseA(x), b(b) {}                            \
                                              ^

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: parameter 's' is passed by value and only copied once; consider moving it to avoid unnecessary copies [performance-unnecessary-value-param]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:280: expanded from macro 'CODE'

    Base1(int i, std::string s) : i(i), s(s) {}                                \
                                          ^

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: pass by value and use std::move [modernize-pass-by-value]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:272: expanded from macro 'CODE'

    BaseB(int x, std::string b) : BaseA(x), b(b) {}                            \
                 ^

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'my_k' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:289: expanded from macro 'CODE'

  } my_k(5, 4, 3, "Cpp", "Python");
    ^

EXPECT_EQ(datamembers.size(), 5);

EXPECT_EQ(Cpp::GetVariableOffset(datamembers[0], myklass),
((intptr_t)&(my_k.k)) - ((intptr_t)&(my_k)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]

            ((intptr_t)&(my_k.k)) - ((intptr_t)&(my_k)));
             ^

((intptr_t)&(my_k.k)) - ((intptr_t)&(my_k)));

EXPECT_EQ(Cpp::GetVariableOffset(datamembers[1], myklass),
((intptr_t)&(my_k.a)) - ((intptr_t)&(my_k)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]

            ((intptr_t)&(my_k.a)) - ((intptr_t)&(my_k)));
                                     ^

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

((intptr_t)&(my_k.k)) - ((intptr_t)&(my_k)));

EXPECT_EQ(Cpp::GetVariableOffset(datamembers[1], myklass),
((intptr_t)&(my_k.a)) - ((intptr_t)&(my_k)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]

            ((intptr_t)&(my_k.a)) - ((intptr_t)&(my_k)));
             ^

((intptr_t)&(my_k.a)) - ((intptr_t)&(my_k)));

EXPECT_EQ(Cpp::GetVariableOffset(datamembers[2], myklass),
((intptr_t)&(my_k.b)) - ((intptr_t)&(my_k)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]

            ((intptr_t)&(my_k.b)) - ((intptr_t)&(my_k)));
                                     ^

((intptr_t)&(my_k.a)) - ((intptr_t)&(my_k)));

EXPECT_EQ(Cpp::GetVariableOffset(datamembers[2], myklass),
((intptr_t)&(my_k.b)) - ((intptr_t)&(my_k)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]

            ((intptr_t)&(my_k.b)) - ((intptr_t)&(my_k)));
             ^

((intptr_t)&(my_k.b)) - ((intptr_t)&(my_k)));

EXPECT_EQ(Cpp::GetVariableOffset(datamembers[3], myklass),
((intptr_t)&(my_k.i)) - ((intptr_t)&(my_k)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]

            ((intptr_t)&(my_k.i)) - ((intptr_t)&(my_k)));
                                     ^

((intptr_t)&(my_k.b)) - ((intptr_t)&(my_k)));

EXPECT_EQ(Cpp::GetVariableOffset(datamembers[3], myklass),
((intptr_t)&(my_k.i)) - ((intptr_t)&(my_k)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]

            ((intptr_t)&(my_k.i)) - ((intptr_t)&(my_k)));
             ^

((intptr_t)&(my_k.i)) - ((intptr_t)&(my_k)));

EXPECT_EQ(Cpp::GetVariableOffset(datamembers[4], myklass),
((intptr_t)&(my_k.s)) - ((intptr_t)&(my_k)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]

            ((intptr_t)&(my_k.s)) - ((intptr_t)&(my_k)));
                                     ^

((intptr_t)&(my_k.i)) - ((intptr_t)&(my_k)));

EXPECT_EQ(Cpp::GetVariableOffset(datamembers[4], myklass),
((intptr_t)&(my_k.s)) - ((intptr_t)&(my_k)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]

            ((intptr_t)&(my_k.s)) - ((intptr_t)&(my_k)));
             ^

Copy link

codecov bot commented Dec 17, 2024

Codecov Report

Attention: Patch coverage is 97.29730% with 1 line in your changes missing coverage. Please review.

Project coverage is 70.84%. Comparing base (ef2f0a1) to head (0e30059).
Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
lib/Interpreter/CppInterOp.cpp 97.29% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #396      +/-   ##
==========================================
+ Coverage   70.62%   70.84%   +0.21%     
==========================================
  Files           9        9              
  Lines        3500     3529      +29     
==========================================
+ Hits         2472     2500      +28     
- Misses       1028     1029       +1     
Files with missing lines Coverage Δ
include/clang/Interpreter/CppInterOp.h 96.29% <ø> (ø)
lib/Interpreter/CppInterOp.cpp 80.16% <97.29%> (+0.25%) ⬆️
Files with missing lines Coverage Δ
include/clang/Interpreter/CppInterOp.h 96.29% <ø> (ø)
lib/Interpreter/CppInterOp.cpp 80.16% <97.29%> (+0.25%) ⬆️

Copy link
Contributor

@vgvassilev vgvassilev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just make clang-tidy happy about this missing include.

@mcbarton
Copy link
Collaborator

mcbarton commented Dec 17, 2024

We should probably turn off clang tidy from the unittests.

@vgvassilev @Vipul-Cariappa If you want clang tidy to not provide comments on unit tests then you'll want to change

- '**.h'
- '**.cpp'
to only look in the lib and include folders.

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clang-tidy made some suggestions

@@ -7,6 +7,8 @@

#include "gtest/gtest.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: 'gtest/gtest.h' file not found [clang-diagnostic-error]

#include "gtest/gtest.h"
         ^

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: class 'Base1' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:277: expanded from macro 'CODE'

  class Base1 {                                                                \
        ^

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: class 'BaseA' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:263: expanded from macro 'CODE'

  class BaseA {                                                                \
        ^

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: class 'BaseB' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:270: expanded from macro 'CODE'

  class BaseB : public BaseA {                                                 \
        ^

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: class 'MyKlass' defines a non-default destructor but does not define a copy constructor, a copy assignment operator, a move constructor or a move assignment operator [cppcoreguidelines-special-member-functions]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:285: expanded from macro 'CODE'

  class MyKlass : public BaseB, public Base1 {                                 \
        ^

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: parameter 'b' is passed by value and only copied once; consider moving it to avoid unnecessary copies [performance-unnecessary-value-param]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:274: expanded from macro 'CODE'

    BaseB(int x, std::string b) : BaseA(x), b(b) {}                            \
                                              ^

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: parameter 's' is passed by value and only copied once; consider moving it to avoid unnecessary copies [performance-unnecessary-value-param]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:282: expanded from macro 'CODE'

    Base1(int i, std::string s) : i(i), s(s) {}                                \
                                          ^

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: pass by value and use std::move [modernize-pass-by-value]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:274: expanded from macro 'CODE'

    BaseB(int x, std::string b) : BaseA(x), b(b) {}                            \
                 ^

: BaseB(x, b), Base1(i, s), k(k) {} \
} my_k(5, 4, 3, "Cpp", "Python");

CODE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: variable 'my_k' is non-const and globally accessible, consider making it const [cppcoreguidelines-avoid-non-const-global-variables]

CODE
^
Additional context

unittests/CppInterOp/VariableReflectionTest.cpp:291: expanded from macro 'CODE'

  } my_k(5, 4, 3, "Cpp", "Python");
    ^

@Vipul-Cariappa Vipul-Cariappa merged commit 5d81a80 into compiler-research:main Dec 18, 2024
47 checks passed
@Vipul-Cariappa Vipul-Cariappa deleted the dev/offset-inheritance branch February 22, 2025 10:03
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants