itanium-cxx-abi / cxx-abi

C++ ABI Summary
508 stars 96 forks source link

Is it possible to form a pointer-to-data-member with offset -1 using explicit derived-to-base conversions without UB? #162

Closed frederick-vs-ja closed 1 year ago

frederick-vs-ja commented 1 year ago

PR #97 claimed that

it is possible to generate an data member pointer with an offset of -1 using explicit derived-to-base conversions.

However, it seemly raises undefined behavior to do so according to [expr.static.cast]/13:

If class B contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member. Otherwise, the behavior is undefined.

The sentence seemly states that it's UB to form a pointer-to-member that behaves "side cast". If a pointer-to-data-member with offset -1 is formed, it apparantly must be formed by side cast, which is UB.

E.g.

struct B1 {
    char i;
};

struct B2 {
    char j;
};

struct D : B1, B2 {};

constexpr auto mp = static_cast<char B2::*>(static_cast<char D::*>(&B1::i));

Clang rejects this example (while gcc generates a pointer-to-member with offset -1). Godbolt link.

CCing @rjmccall

rjmccall commented 1 year ago

Yes, I think you're right. Because data members are always laid out after non-virtual base subobjects, and (standard) data member pointers cannot identify a member of a virtual base, the restriction that the class type of the member pointer must be a base or derived class should make it impossible to have negative offsets in a data member pointer.

@zygoloid will find this interesting.

I can write up a patch.

rjmccall commented 1 year ago

Ah, unfortunately this is still a problem. Consider a member pointer of type T A::*. Members of base subobjects must always be at non-negative offsets from the address point of A. Members of classes for which A is a base subobject are usually at positive offsets from the address point of A because data members are added after non-virtual base subobjects, but there are two exceptions:

rjmccall commented 1 year ago

PR clarifying this situation here: https://github.com/itanium-cxx-abi/cxx-abi/pull/163