Skip to content

Commit 8478f70

Browse files
committed
Address improved documentation requests from review
1 parent 16b4c18 commit 8478f70

File tree

3 files changed

+62
-19
lines changed

3 files changed

+62
-19
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,26 @@ C++ Specific Potentially Breaking Changes
4343
regressions if your build system supports two-phase compilation model but haven't support
4444
reduced BMI or it is a compiler bug or a bug in users code.
4545

46+
- Clang now correctly diagnoses during constant expression evaluation undefined behavior due to member
47+
pointer access to a member which is not a direct or indirect member of the most-derived object
48+
of the accessed object but is instead located directly in a sibling class to one of the classes
49+
along the inheritance hierarchy of the most-derived object as ill-formed.
50+
Other scenarios in which the member is not member of the most derived object were already
51+
diagnosed previously. (#GH150709)
52+
53+
.. code-block:: c++
54+
55+
struct A {};
56+
struct B : A {};
57+
struct C : A { constexpr int foo() const { return 1; } };
58+
constexpr A a;
59+
constexpr B b;
60+
constexpr C c;
61+
constexpr auto mp = static_cast<int(A::*)() const>(&C::foo);
62+
static_assert((a.*mp)() == 1); // continues to be rejected
63+
static_assert((b.*mp)() == 1); // newly rejected
64+
static_assert((c.*mp)() == 1); // accepted
65+
4666
ABI Changes in This Version
4767
---------------------------
4868

clang/lib/AST/ExprConstant.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5035,6 +5035,9 @@ static const ValueDecl *HandleMemberPointerAccess(EvalInfo &Info,
50355035
// This is a member of some derived class. Truncate LV appropriately.
50365036
// The end of the derived-to-base path for the base object must match the
50375037
// derived-to-base path for the member pointer.
5038+
// C++23 [expr.mptr.oper]p4:
5039+
// If the result of E1 is an object [...] whose most derived object does
5040+
// not contain the member to which E2 refers, the behavior is undefined.
50385041
if (LV.Designator.MostDerivedPathLength + MemPtr.Path.size() >
50395042
LV.Designator.Entries.size()) {
50405043
Info.FFDiag(RHS);
@@ -5051,7 +5054,15 @@ static const ValueDecl *HandleMemberPointerAccess(EvalInfo &Info,
50515054
return nullptr;
50525055
}
50535056
}
5054-
// Consider member in a sibling.
5057+
// MemPtr.Path only contains the base classes of the class directly
5058+
// containing the member E2. It is still necessary to check that the class
5059+
// directly containing the member E2 lies on the derived-to-base path of E1
5060+
// to avoid incorrectly permitting member pointer access into a sibling
5061+
// class of the class containing the member E2. If this class would
5062+
// correspond to the most-derived class of E1, it either isn't contained in
5063+
// LV.Designator.Entries or the corresponding entry refers to an array
5064+
// element instead. Therefore get the most derived class directly in this
5065+
// case. Otherwise the previous entry should correpond to this class.
50555066
const CXXRecordDecl *LastLVDecl =
50565067
(PathLengthToMember > LV.Designator.MostDerivedPathLength)
50575068
? getAsBaseClass(LV.Designator.Entries[PathLengthToMember - 1])

clang/test/SemaCXX/constant-expression-cxx11.cpp

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,24 +1210,6 @@ namespace MemberPointer {
12101210
return (a.*f)();
12111211
}
12121212
static_assert(apply(A(2), &A::f) == 5, "");
1213-
1214-
struct C { };
1215-
struct D : C {
1216-
constexpr int f() const { return 1; };
1217-
};
1218-
struct E : C { };
1219-
struct F : D { };
1220-
constexpr C c1, c2[2];
1221-
constexpr D d1, d2[2];
1222-
constexpr E e1, e2[2];
1223-
constexpr F f;
1224-
static_assert((c1.*(static_cast<int (C::*)() const>(&D::f)))() == 1, ""); // expected-error {{constant expression}}
1225-
static_assert((d1.*(static_cast<int (C::*)() const>(&D::f)))() == 1, "");
1226-
static_assert((e1.*(static_cast<int (C::*)() const>(&D::f)))() == 1, ""); // expected-error {{constant expression}}
1227-
static_assert((f.*(static_cast<int (C::*)() const>(&D::f)))() == 1, "");
1228-
static_assert((c2[0].*(static_cast<int (C::*)() const>(&D::f)))() == 1, ""); // expected-error {{constant expression}}
1229-
static_assert((d2[0].*(static_cast<int (C::*)() const>(&D::f)))() == 1, "");
1230-
static_assert((e2[0].*(static_cast<int (C::*)() const>(&D::f)))() == 1, ""); // expected-error {{constant expression}}
12311213
}
12321214

12331215
namespace ArrayBaseDerived {
@@ -2633,3 +2615,33 @@ namespace DoubleCapture {
26332615
};
26342616
}
26352617
}
2618+
2619+
namespace GH150709 {
2620+
struct C { };
2621+
struct D : C {
2622+
constexpr int f() const { return 1; };
2623+
};
2624+
struct E : C { };
2625+
struct F : D { };
2626+
struct G : E { };
2627+
2628+
constexpr C c1, c2[2];
2629+
constexpr D d1, d2[2];
2630+
constexpr E e1, e2[2];
2631+
constexpr F f;
2632+
constexpr G g;
2633+
2634+
constexpr auto mp = static_cast<int (C::*)() const>(&D::f);
2635+
2636+
// sanity checks for fix of GH150709 (unchanged behavior)
2637+
static_assert((c1.*mp)() == 1, ""); // expected-error {{constant expression}}
2638+
static_assert((d1.*mp)() == 1, "");
2639+
static_assert((f.*mp)() == 1, "");
2640+
static_assert((c2[0].*mp)() == 1, ""); // expected-error {{constant expression}}
2641+
static_assert((d2[0].*mp)() == 1, "");
2642+
2643+
// incorrectly undiagnosed before fix of GH150709
2644+
static_assert((e1.*mp)() == 1, ""); // expected-error {{constant expression}}
2645+
static_assert((e2[0].*mp)() == 1, ""); // expected-error {{constant expression}}
2646+
static_assert((g.*mp)() == 1, ""); // expected-error {{constant expression}}
2647+
}

0 commit comments

Comments
 (0)