Skip to content

Commit 5dc8166

Browse files
dynamic_cast in constant evaluation requires public bases [GH #398]
The run-time checks of a dynamic_cast during constant evaluation now require the operand to be a public base class subobject of the most derived object (and, for a cast to a sibling base class, the destination type to be a public base class of the most derived type), instead of checking the accessibility of the base classes in the context of the cast. Also make the derived-to-base shortcut for interpreter objects reachable (its condition was inverted).
1 parent f51894d commit 5dc8166

16 files changed

Lines changed: 293 additions & 243 deletions

File tree

‎src/interpret.c‎

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26097,10 +26097,12 @@ represented by an entry of type a_constant (ck_address or ck_integer).
2609726097
new_con->variant.address.offset -=
2609826098
(a_targ_ptrdiff_t)prev_bcp->offset;
2609926099
if (type_is(tp, tk_void) ||
26100-
same_entities(tp, prev_bcp->derived_class)) {
26100+
(same_entities(tp, prev_bcp->derived_class) &&
26101+
prev_bcp->has_public_derivation)) {
2610126102
/* A cast to the most-derived class (either by casting to void*
2610226103
or because the destination type happens to match the most-
26103-
derived class type). */
26104+
derived class type, of which the operand is a public base
26105+
class subobject). */
2610426106
remove_trailing_subobject_path_entry(new_con);
2610526107
clear_runtime_constant_address(result_storage, new_con);
2610626108
goto done;
@@ -26113,7 +26115,21 @@ represented by an entry of type a_constant (ck_address or ck_integer).
2611326115
bcdp = bcdp->next;
2611426116
for (; dsp != tail->next; dsp = dsp->next) {
2611526117
if (same_entities(dsp->base_class->type, tp)) {
26116-
if (new_bcp == NULL) {
26118+
a_derivation_step_ptr step;
26119+
a_boolean is_public_base = TRUE;
26120+
/* The operand must be a public base class subobject of the
26121+
destination subobject. */
26122+
for (step = dsp->next; step != tail->next;
26123+
step = step->next) {
26124+
if (!step->base_class->has_public_derivation) {
26125+
is_public_base = FALSE;
26126+
} /* if */
26127+
} /* for */
26128+
if (!is_public_base) {
26129+
/* Move on to the final strategy (which will fail too). */
26130+
new_bcp = NULL;
26131+
bcdp = NULL;
26132+
} else if (new_bcp == NULL) {
2611726133
new_bcp = dsp->base_class;
2611826134
} else if (dsp->base_class != new_bcp) {
2611926135
/* The subobject on the derivation path is not unambiguous.
@@ -26139,7 +26155,11 @@ represented by an entry of type a_constant (ck_address or ck_integer).
2613926155
&expr->position, new_bcp->type, ips);
2614026156
} /* if */
2614126157
goto done;
26142-
} else if (!is_accessible_base_class(new_bcp)) {
26158+
} else if (!prev_bcp->has_public_derivation ||
26159+
!new_bcp->has_public_derivation) {
26160+
/* The operand must be a public base class subobject of the
26161+
most-derived object, and the destination a public base
26162+
class of the most-derived type. */
2614326163
if (pointer_case) {
2614426164
clear_address(result_storage, NULL);
2614526165
} else {
@@ -26189,6 +26209,7 @@ represented by an entry of type a_constant (ck_address or ck_integer).
2618926209
a_base_class_ptr bcp, baseward_bcp;
2619026210
a_type_ptr dtp;
2619126211
a_byte *subobj = opnd_addr->address;
26212+
a_boolean is_public_base = TRUE;
2619226213
/* Check for the null address case first. */
2619326214
if (subobj == NULL) {
2619426215
/* Casting a null pointer value results in a null pointer value. */
@@ -26205,8 +26226,8 @@ represented by an entry of type a_constant (ck_address or ck_integer).
2620526226
do_constexpr_fail(result);
2620626227
goto done;
2620726228
} /* if */
26208-
if (type_is(tp, tk_void) &&
26209-
(baseward_bcp = find_base_in_type(opnd_type, tp))!= NULL) {
26229+
if (!type_is(tp, tk_void) &&
26230+
(baseward_bcp = find_base_in_type(opnd_type, tp)) != NULL) {
2621026231
/* The dynamic cast is actually a (static) derived-to-base cast. */
2621126232
if (baseward_bcp->ambiguous) {
2621226233
do_constexpr_fail(result);
@@ -26230,13 +26251,17 @@ represented by an entry of type a_constant (ck_address or ck_integer).
2623026251
a_byte_count offset;
2623126252
get_mapped_byte_count(&persistent_map, bcp, offset);
2623226253
result_addr->address -= offset;
26254+
if (!bcp->has_public_derivation) is_public_base = FALSE;
2623326255
dtp = bcp->derived_class;
2623426256
if (same_entities(dtp, tp)) {
2623526257
/* We found a derived subobject of the right type. Since types with
2623626258
virtual bases cannot be literal types (and thus cannot be stored
2623726259
in interpreter memory), there is no concern about this being
26238-
ambiguous. */
26239-
goto done;
26260+
ambiguous. The cast succeeds only if the operand is a public base
26261+
class subobject of it; otherwise, it also is not a public base
26262+
class subobject of the most-derived object, and so the cast fails
26263+
below. */
26264+
if (is_public_base) goto done;
2624026265
} /* if */
2624126266
if (is_initialized(result_addr)) {
2624226267
bcp = *(a_base_class_ptr*)result_addr->address;
@@ -26253,11 +26278,13 @@ represented by an entry of type a_constant (ck_address or ck_integer).
2625326278
goto done;
2625426279
} /* if */
2625526280
/* We've reached the most derived type (dtp) without encountering the
26256-
destination type. This might still be a "sideways" cast. */
26281+
destination type as a class of which the operand is a public base
26282+
class subobject. This might still be a "sideways" cast. */
2625726283
baseward_bcp = find_base_in_type(dtp, tp);
2625826284
if (baseward_bcp != NULL) {
2625926285
/* There is a subobject of the right kind: Verify that it is
26260-
unambiguous and accessible. */
26286+
unambiguous and that it and the operand are public base class
26287+
subobjects of the most-derived object. */
2626126288
if (baseward_bcp->ambiguous) {
2626226289
if (pointer_case) {
2626326290
clear_address(result_storage, NULL);
@@ -26267,7 +26294,7 @@ represented by an entry of type a_constant (ck_address or ck_integer).
2626726294
&expr->position, tp, ips);
2626826295
} /* if */
2626926296
goto done;
26270-
} else if (!is_accessible_base_class(baseward_bcp)) {
26297+
} else if (!is_public_base || !baseward_bcp->has_public_derivation) {
2627126298
if (pointer_case) {
2627226299
clear_address(result_storage, NULL);
2627326300
} else {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[GH #398]
2+
dynamic_cast in constant evaluation requires public base class subobjects
3+
4+
During constant evaluation, the run-time checks of a dynamic_cast did not
5+
require the operand to point to (or refer to) a public base class subobject
6+
of the most derived object, nor (for a cast to a sibling base class) the
7+
destination type to be a public base class of the most derived type.
8+
Instead, the accessibility of the base classes in the context of the cast
9+
was checked. For example, with --c++20:
10+
11+
struct B { virtual void b() {} };
12+
struct D : private B {};
13+
struct F : D {};
14+
constexpr F f;
15+
static_assert(dynamic_cast<const D*>((const B*)&f) == nullptr);
16+
// Previously failed.
17+
18+
This is now fixed.

‎tests/expectations/edg_x86_64/imported.log‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7076,7 +7076,6 @@ gnu/cpp/cpp2a/consteval13.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
70767076
gnu/cpp/cpp2a/consteval25.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
70777077
gnu/cpp/cpp2a/consteval32.sft.cpp:fn:-DTEST_NUMBER=1 --c++20:FAIL:
70787078
gnu/cpp/cpp2a/constexpr-99287.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
7079-
gnu/cpp/cpp2a/constexpr-dynamic13.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
70807079
gnu/cpp/cpp2a/constexpr-init18.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
70817080
gnu/cpp/cpp2a/constexpr-inst1.sft.cpp:fn:-DTEST_NUMBER=1 --c++14:FAIL:
70827081
gnu/cpp/cpp2a/constexpr-inst2.sft.cpp:fp:-DTEST_NUMBER=1 --c++11:FAIL:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fe_only -DTEST_NUMBER=1 --c++20 Test_name.c

‎tests/tests/changes/gh-398.sft.cpp‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//type:fp
2+
//options:--c++20
3+
//remark:[GH #398] The run-time checks of a dynamic_cast in constant
4+
//remark:evaluation require public base class subobjects
5+
6+
// The run-time checks require public base class subobjects, independent of
7+
// access in the context of the cast.
8+
struct B { virtual void b() {} };
9+
struct C { virtual void c() {} };
10+
struct D : private B {
11+
virtual void d() {}
12+
constexpr const B* pb() const { return this; }
13+
};
14+
struct E { virtual void e() {} };
15+
struct F : D, E, private C {
16+
virtual void f() {}
17+
constexpr bool m() const {
18+
return dynamic_cast<const F*>((const C*)this) == nullptr &&
19+
dynamic_cast<const E*>((const C*)this) == nullptr;
20+
}
21+
};
22+
23+
constexpr F gf;
24+
// Each of the following three assertions previously failed.
25+
static_assert(dynamic_cast<const D*>((const B*)&gf) == nullptr);
26+
static_assert(dynamic_cast<const E*>((const B*)&gf) == nullptr);
27+
static_assert(dynamic_cast<const F*>((const C*)&gf) == nullptr);
28+
static_assert(dynamic_cast<const F*>((const D*)&gf) == &gf);
29+
static_assert(dynamic_cast<const F*>((const E*)&gf) == &gf);
30+
static_assert(dynamic_cast<const E*>((const D*)&gf) == &gf);
31+
static_assert(dynamic_cast<const void*>((const B*)&gf) == &gf);
32+
static_assert(gf.m()); // Previously failed.
33+
34+
constexpr bool local() {
35+
F f;
36+
const B* pb = (const B*)&f;
37+
const C* pc = (const C*)&f;
38+
return dynamic_cast<const D*>(pb) == nullptr &&
39+
dynamic_cast<const E*>(pb) == nullptr &&
40+
dynamic_cast<const F*>(pc) == nullptr &&
41+
dynamic_cast<const E*>(pc) == nullptr &&
42+
dynamic_cast<const D*>(f.pb()) == nullptr &&
43+
dynamic_cast<const F*>((const D*)&f) == &f &&
44+
dynamic_cast<const E*>((const D*)&f) == &f &&
45+
dynamic_cast<const D*>((const E*)&f) == &f &&
46+
dynamic_cast<const void*>(pb) == &f &&
47+
f.m();
48+
}
49+
static_assert(local()); // Previously failed.
50+
51+
struct G : B {};
52+
struct H : G, E {};
53+
constexpr bool public_bases() {
54+
H h;
55+
const B* pb = &h;
56+
return dynamic_cast<const G*>(pb) == &h &&
57+
dynamic_cast<const H*>(pb) == &h &&
58+
dynamic_cast<const E*>(pb) == &h &&
59+
dynamic_cast<const B*>((const E*)&h) == pb;
60+
}
61+
static_assert(public_bases());

‎tests/tests/imported/clang/cpp/SemaCXX/.constant-expression-cxx2a.rto/default.1.1.txt‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,13 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
156156
static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g);
157157
^
158158

159+
"SemaCXX/constant-expression-cxx2a.cpp", line 274: error: expression must have a constant value
160+
constexpr int e_f = (dynamic_cast<F&>((E&)g), 0);
161+
^
162+
"SemaCXX/constant-expression-cxx2a.cpp", line 274: note: conversion to inaccessible base class "DynamicCast::F" is not allowed
163+
constexpr int e_f = (dynamic_cast<F&>((E&)g), 0);
164+
^
165+
159166
"SemaCXX/constant-expression-cxx2a.cpp", line 278: error: expression must have a constant value
160167
constexpr int b_e = (dynamic_cast<E&>((B&)g), 0);
161168
^

‎tests/tests/imported/clang/cpp/SemaCXX/.constant-expression-cxx2a.rto/edg_x86_32.1.1.txt‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,13 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
156156
static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g);
157157
^
158158

159+
"SemaCXX/constant-expression-cxx2a.cpp", line 274: error: expression must have a constant value
160+
constexpr int e_f = (dynamic_cast<F&>((E&)g), 0);
161+
^
162+
"SemaCXX/constant-expression-cxx2a.cpp", line 274: note: conversion to inaccessible base class "DynamicCast::F" is not allowed
163+
constexpr int e_f = (dynamic_cast<F&>((E&)g), 0);
164+
^
165+
159166
"SemaCXX/constant-expression-cxx2a.cpp", line 278: error: expression must have a constant value
160167
constexpr int b_e = (dynamic_cast<E&>((B&)g), 0);
161168
^

‎tests/tests/imported/clang/cpp/SemaCXX/.constant-expression-cxx2a.rto/edg_x86_32_cp.1.1.txt‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -156,20 +156,20 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
156156
constexpr int d_a = (dynamic_cast<const A&>(static_cast<const D&>(g)), 0);
157157
^
158158

159-
"SemaCXX/constant-expression-cxx2a.cpp", line 264: error: expression must have a constant value
160-
static_assert(&dynamic_cast<A&>((B&)g) == &(A&)(B&)g);
161-
^
162-
"SemaCXX/constant-expression-cxx2a.cpp", line 264: note: base class "DynamicCast::A" is ambiguous
163-
static_assert(&dynamic_cast<A&>((B&)g) == &(A&)(B&)g);
164-
^
165-
166159
"SemaCXX/constant-expression-cxx2a.cpp", line 267: error: expression must have a constant value
167160
static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g);
168161
^
169162
"SemaCXX/constant-expression-cxx2a.cpp", line 267: note: base class "DynamicCast::A" is ambiguous
170163
static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g);
171164
^
172165

166+
"SemaCXX/constant-expression-cxx2a.cpp", line 274: error: expression must have a constant value
167+
constexpr int e_f = (dynamic_cast<F&>((E&)g), 0);
168+
^
169+
"SemaCXX/constant-expression-cxx2a.cpp", line 274: note: conversion to inaccessible base class "DynamicCast::F" is not allowed
170+
constexpr int e_f = (dynamic_cast<F&>((E&)g), 0);
171+
^
172+
173173
"SemaCXX/constant-expression-cxx2a.cpp", line 278: error: expression must have a constant value
174174
constexpr int b_e = (dynamic_cast<E&>((B&)g), 0);
175175
^

‎tests/tests/imported/clang/cpp/SemaCXX/.constant-expression-cxx2a.rto/edg_x86_64_cp.1.1.txt‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -156,20 +156,20 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
156156
constexpr int d_a = (dynamic_cast<const A&>(static_cast<const D&>(g)), 0);
157157
^
158158

159-
"SemaCXX/constant-expression-cxx2a.cpp", line 264: error: expression must have a constant value
160-
static_assert(&dynamic_cast<A&>((B&)g) == &(A&)(B&)g);
161-
^
162-
"SemaCXX/constant-expression-cxx2a.cpp", line 264: note: base class "DynamicCast::A" is ambiguous
163-
static_assert(&dynamic_cast<A&>((B&)g) == &(A&)(B&)g);
164-
^
165-
166159
"SemaCXX/constant-expression-cxx2a.cpp", line 267: error: expression must have a constant value
167160
static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g);
168161
^
169162
"SemaCXX/constant-expression-cxx2a.cpp", line 267: note: base class "DynamicCast::A" is ambiguous
170163
static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g);
171164
^
172165

166+
"SemaCXX/constant-expression-cxx2a.cpp", line 274: error: expression must have a constant value
167+
constexpr int e_f = (dynamic_cast<F&>((E&)g), 0);
168+
^
169+
"SemaCXX/constant-expression-cxx2a.cpp", line 274: note: conversion to inaccessible base class "DynamicCast::F" is not allowed
170+
constexpr int e_f = (dynamic_cast<F&>((E&)g), 0);
171+
^
172+
173173
"SemaCXX/constant-expression-cxx2a.cpp", line 278: error: expression must have a constant value
174174
constexpr int b_e = (dynamic_cast<E&>((B&)g), 0);
175175
^
Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1 @@
11
fe_only -DTEST_NUMBER=1 --gnu_version=999999 -tused -e 200 --no_wrap --no_wrap_diagnostics --c++20 Test_name.c
2-
"Test_name.c", line 25: error: static assertion failed
3-
static_assert (dynamic_cast<D*> ((A*)&d) == nullptr);
4-
^
5-
6-
"Test_name.c", line 26: error: static assertion failed
7-
static_assert (dynamic_cast<D*> ((B*)&d) == nullptr);
8-
^
9-
10-
"Test_name.c", line 28: error: static assertion failed
11-
static_assert (dynamic_cast<C*> ((B*)&d) == nullptr);
12-
^
13-
14-
"Test_name.c", line 31: error: static assertion failed
15-
static_assert (dynamic_cast<DD*> ((A*)&dd) == nullptr);
16-
^
17-
18-
"Test_name.c", line 32: error: static assertion failed
19-
static_assert (dynamic_cast<DD*> ((B*)&dd) == nullptr);
20-
^
21-
22-
"Test_name.c", line 35: error: static assertion failed
23-
static_assert (dynamic_cast<DDD*> ((A*)&ddd) == nullptr);
24-
^
25-
26-
"Test_name.c", line 36: error: static assertion failed
27-
static_assert (dynamic_cast<DDD*> ((B*)&ddd) == nullptr);
28-
^
29-
30-
"Test_name.c", line 37: error: static assertion failed
31-
static_assert (dynamic_cast<CCC*> ((B*)&ddd) == nullptr);
32-
^
33-
34-
"Test_name.c", line 46: error: static assertion failed
35-
static_assert (dynamic_cast<E*> ((A*)(D*)&e) == nullptr);
36-
^
37-
38-
"Test_name.c", line 47: error: static assertion failed
39-
static_assert (dynamic_cast<E*> ((B*)(D*)&e) == nullptr);
40-
^
41-
42-
"Test_name.c", line 53: error: static assertion failed
43-
static_assert (dynamic_cast<EE*> ((A*)(D*)&ee) == nullptr);
44-
^
45-
46-
"Test_name.c", line 54: error: static assertion failed
47-
static_assert (dynamic_cast<EE*> ((B*)(D*)&ee) == nullptr);
48-
^
49-
50-
"Test_name.c", line 70: error: static assertion failed
51-
static_assert (dynamic_cast<XX*>((B*)(CC*)(E*)&xx) == nullptr);
52-
^
53-
54-
"Test_name.c", line 73: error: static assertion failed
55-
static_assert (dynamic_cast<Y*>((B*)&y) == nullptr);
56-
^
57-
58-
"Test_name.c", line 74: error: static assertion failed
59-
static_assert (dynamic_cast<Y*>((A*)(B*)&y) == nullptr);
60-
^
61-
62-
"Test_name.c", line 82: error: static assertion failed
63-
static_assert (dynamic_cast<CC*> ((B*)(D*)&e) == nullptr);
64-
^
65-
66-
16 errors detected in the compilation of "Test_name.c".

0 commit comments

Comments
 (0)