Constexpr pointer to data member conversion

I believe GCC and MSVC are correct, this code should compile.

data_p points to the member foo of Data. derived_p points to the member foo of the Data base class subobject of a Derived via implicit pointer to member conversion [conv.mem]/2.

From [expr.static.cast]/12

A prvalue of type “pointer to member of D of type cv1 T” can be converted to a prvalue of type “pointer to member of B of type cv2 T”, where B is a base class of D, if cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. […] 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. [ Note: Although class B need not contain the original member, the dynamic type of the object with which indirection through the pointer to member is performed must contain the original member; see [expr.mptr.oper].  — end note ]

As pointed out by @geza in his comment below, the class Base is a base class of Derived, the latter of which contains the original member Data::foo in its Data base class subobject (the Note in the quote above would seem to be further evidence in support of this interpretation). Thus, the static_cast used to initialize base_pis well-formed and has well-defined behavior. The resulting pointer points to the Data::foo member of a Derived object from the perspective of the Base base class subobject of that Derived object.

To initialize a constexpr object, a constant expression is required [dcl.constexpr]/9. Our expression (the result of the static_cast) is a core constant expression because there is nothing in [expr.const]/2 that would say otherwise. And it is also a constant expression because it is a prvalue that satisfies all the constraints laid out in [expr.const]/5.