Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 45 additions & 11 deletions src/interpret.c
Original file line number Diff line number Diff line change
Expand Up @@ -26097,10 +26097,12 @@ represented by an entry of type a_constant (ck_address or ck_integer).
new_con->variant.address.offset -=
(a_targ_ptrdiff_t)prev_bcp->offset;
if (type_is(tp, tk_void) ||
same_entities(tp, prev_bcp->derived_class)) {
(same_entities(tp, prev_bcp->derived_class) &&
prev_bcp->has_public_derivation)) {
/* A cast to the most-derived class (either by casting to void*
or because the destination type happens to match the most-
derived class type). */
derived class type, of which the operand is a public base
class subobject). */
remove_trailing_subobject_path_entry(new_con);
clear_runtime_constant_address(result_storage, new_con);
goto done;
Expand All @@ -26113,7 +26115,28 @@ represented by an entry of type a_constant (ck_address or ck_integer).
bcdp = bcdp->next;
for (; dsp != tail->next; dsp = dsp->next) {
if (same_entities(dsp->base_class->type, tp)) {
if (new_bcp == NULL) {
a_derivation_step_ptr step;
a_type_ptr step_class = dsp->base_class->type;
a_boolean is_public_base = TRUE;
/* The operand must be a public base class subobject of the
destination subobject (but not necessarily of the
most-derived object). The base classes of the derivation
steps are relative to the most-derived class, so check the
corresponding direct base classes instead. */
for (step = dsp->next; step != tail->next;
step = step->next) {
a_base_class_ptr direct_bcp =
find_direct_base_class_of(
step_class, step->base_class->type);
if (!direct_bcp->has_public_derivation) {
is_public_base = FALSE;
} /* if */
step_class = step->base_class->type;
} /* for */
if (!is_public_base) {
/* Ignore this derivation path. Another path may reach
the same virtual base class subobject publicly. */
} else if (new_bcp == NULL) {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
new_bcp = dsp->base_class;
} else if (dsp->base_class != new_bcp) {
/* The subobject on the derivation path is not unambiguous.
Expand All @@ -26139,7 +26162,11 @@ represented by an entry of type a_constant (ck_address or ck_integer).
&expr->position, new_bcp->type, ips);
} /* if */
goto done;
} else if (!is_accessible_base_class(new_bcp)) {
} else if (!prev_bcp->has_public_derivation ||
!new_bcp->has_public_derivation) {
/* The operand must be a public base class subobject of the
most-derived object, and the destination a public base
class of the most-derived type. */
if (pointer_case) {
clear_address(result_storage, NULL);
} else {
Expand Down Expand Up @@ -26189,6 +26216,7 @@ represented by an entry of type a_constant (ck_address or ck_integer).
a_base_class_ptr bcp, baseward_bcp;
a_type_ptr dtp;
a_byte *subobj = opnd_addr->address;
a_boolean is_public_base = TRUE;
/* Check for the null address case first. */
if (subobj == NULL) {
/* Casting a null pointer value results in a null pointer value. */
Expand All @@ -26205,8 +26233,8 @@ represented by an entry of type a_constant (ck_address or ck_integer).
do_constexpr_fail(result);
goto done;
} /* if */
if (type_is(tp, tk_void) &&
(baseward_bcp = find_base_in_type(opnd_type, tp))!= NULL) {
if (!type_is(tp, tk_void) &&
(baseward_bcp = find_base_in_type(opnd_type, tp)) != NULL) {
/* The dynamic cast is actually a (static) derived-to-base cast. */
if (baseward_bcp->ambiguous) {
do_constexpr_fail(result);
Expand All @@ -26230,13 +26258,17 @@ represented by an entry of type a_constant (ck_address or ck_integer).
a_byte_count offset;
get_mapped_byte_count(&persistent_map, bcp, offset);
result_addr->address -= offset;
if (!bcp->has_public_derivation) is_public_base = FALSE;
dtp = bcp->derived_class;
if (same_entities(dtp, tp)) {
/* We found a derived subobject of the right type. Since types with
virtual bases cannot be literal types (and thus cannot be stored
in interpreter memory), there is no concern about this being
ambiguous. */
goto done;
ambiguous. The cast succeeds only if the operand is a public base
class subobject of it; otherwise, it also is not a public base
class subobject of the most-derived object, and so the cast fails
below. */
if (is_public_base) goto done;
} /* if */
if (is_initialized(result_addr)) {
bcp = *(a_base_class_ptr*)result_addr->address;
Expand All @@ -26253,11 +26285,13 @@ represented by an entry of type a_constant (ck_address or ck_integer).
goto done;
} /* if */
/* We've reached the most derived type (dtp) without encountering the
destination type. This might still be a "sideways" cast. */
destination type as a class of which the operand is a public base
class subobject. This might still be a "sideways" cast. */
baseward_bcp = find_base_in_type(dtp, tp);
if (baseward_bcp != NULL) {
/* There is a subobject of the right kind: Verify that it is
unambiguous and accessible. */
unambiguous and that it and the operand are public base class
subobjects of the most-derived object. */
if (baseward_bcp->ambiguous) {
if (pointer_case) {
clear_address(result_storage, NULL);
Expand All @@ -26267,7 +26301,7 @@ represented by an entry of type a_constant (ck_address or ck_integer).
&expr->position, tp, ips);
} /* if */
goto done;
} else if (!is_accessible_base_class(baseward_bcp)) {
} else if (!is_public_base || !baseward_bcp->has_public_derivation) {
if (pointer_case) {
clear_address(result_storage, NULL);
} else {
Expand Down
19 changes: 19 additions & 0 deletions src/pending_changes/gh-398-dynamic-cast-public-base.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[GH #398]
dynamic_cast in constant evaluation requires public base class subobjects

During constant evaluation, the run-time checks of a dynamic_cast did not
require the operand to point to (or refer to) a public base class subobject
of the destination object (for a cast to a derived class) or of the most
derived object (for a cast to a sibling base class, which also requires the
destination type to be a public base class of the most derived type).
Instead, the accessibility of the base classes in the context of the cast
was checked. For example, with --c++20:

struct B { virtual void b() {} };
struct D : private B {};
struct F : D {};
constexpr F f;
static_assert(dynamic_cast<const D*>((const B*)&f) == nullptr);
// Previously failed.

This is now fixed.
1 change: 0 additions & 1 deletion tests/expectations/edg_x86_64/imported.log
Original file line number Diff line number Diff line change
Expand Up @@ -7076,7 +7076,6 @@ gnu/cpp/cpp2a/consteval13.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
gnu/cpp/cpp2a/consteval25.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
gnu/cpp/cpp2a/consteval32.sft.cpp:fn:-DTEST_NUMBER=1 --c++20:FAIL:
gnu/cpp/cpp2a/constexpr-99287.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
gnu/cpp/cpp2a/constexpr-dynamic13.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
gnu/cpp/cpp2a/constexpr-init18.sft.cpp:fp:-DTEST_NUMBER=1 --c++20:FAIL:
gnu/cpp/cpp2a/constexpr-inst1.sft.cpp:fn:-DTEST_NUMBER=1 --c++14:FAIL:
gnu/cpp/cpp2a/constexpr-inst2.sft.cpp:fp:-DTEST_NUMBER=1 --c++11:FAIL:
Expand Down
59 changes: 59 additions & 0 deletions tests/tests/changes/.gh-398-vbase.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
fe_only -DTEST_NUMBER=1 --c++26 Test_name.c
"Test_name.c", line 20: error: initialization is not constant
constexpr D d;
^
"Test_name.c", line 20: note: cannot call non-constexpr function "D::D() noexcept" (declared implicitly)
constexpr D d;
^

"Test_name.c", line 20: error: a constexpr variable must have a literal type or a reference type
constexpr D d;
^

"Test_name.c", line 21: error: expression must have a constant value
static_assert(dynamic_cast<const Good*>((const B*)&d) == (const Good*)&d);
^
"Test_name.c", line 21: note: cannot fold operation involving virtual base class ("B")
static_assert(dynamic_cast<const Good*>((const B*)&d) == (const Good*)&d);
^

"Test_name.c", line 22: error: expression must have a constant value
static_assert(dynamic_cast<const P*>((const B*)&d) ==
^
"Test_name.c", line 22: note: cannot fold operation involving virtual base class ("B")
static_assert(dynamic_cast<const P*>((const B*)&d) ==
^

"Test_name.c", line 24: error: expression must have a constant value
static_assert(dynamic_cast<const D*>((const B*)&d) == nullptr);
^
"Test_name.c", line 24: note: cannot fold operation involving virtual base class ("B")
static_assert(dynamic_cast<const D*>((const B*)&d) == nullptr);
^

"Test_name.c", line 25: error: initialization is not constant
constexpr D2 d2;
^
"Test_name.c", line 25: note: cannot call non-constexpr function "D2::D2() noexcept" (declared implicitly)
constexpr D2 d2;
^

"Test_name.c", line 25: error: a constexpr variable must have a literal type or a reference type
constexpr D2 d2;
^

"Test_name.c", line 26: error: expression must have a constant value
static_assert(dynamic_cast<const Good2*>((const B*)&d2) == (const Good2*)&d2);
^
"Test_name.c", line 26: note: cannot fold operation involving virtual base class ("B")
static_assert(dynamic_cast<const Good2*>((const B*)&d2) == (const Good2*)&d2);
^

"Test_name.c", line 35: error: expression must have a constant value
static_assert(local());
^
"Test_name.c", line 29: note: cannot call non-constexpr function "D::D() noexcept" (declared implicitly)
D x;
^

9 errors detected in the compilation of "Test_name.c".
1 change: 1 addition & 0 deletions tests/tests/changes/.gh-398.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=1 --c++20 Test_name.c
35 changes: 35 additions & 0 deletions tests/tests/changes/gh-398-vbase.sft.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//type:fn
//options:--c++26
//remark:dynamic_cast in constant evaluation with virtual base classes

// These cases cannot be constant-evaluated yet: constexpr objects of classes
// with virtual base classes are not supported (that requires P3533 "constexpr
// virtual inheritance"). When that support is added, the errors below go
// away and these assertions must pass. In particular, a downcast must
// succeed when the operand is a public base class subobject of the
// destination object through at least one derivation path, even if another
// path to the same virtual base class subobject is not public.
struct B { virtual void f() {} };
struct Q : private virtual B {};
struct P : public virtual B {};
struct Good : public Q, public P {};
struct D : private Good {};
struct Good2 : public P, public Q {};
struct D2 : private Good2 {};

constexpr D d;
static_assert(dynamic_cast<const Good*>((const B*)&d) == (const Good*)&d);
static_assert(dynamic_cast<const P*>((const B*)&d) ==
(const P*)(const Good*)&d);
static_assert(dynamic_cast<const D*>((const B*)&d) == nullptr);
constexpr D2 d2;
static_assert(dynamic_cast<const Good2*>((const B*)&d2) == (const Good2*)&d2);

constexpr bool local() {
D x;
const B* pb = (const B*)&x;
return dynamic_cast<const Good*>(pb) == (const Good*)&x &&
dynamic_cast<const P*>(pb) == (const P*)(const Good*)&x &&
dynamic_cast<const D*>(pb) == nullptr;
}
static_assert(local());
81 changes: 81 additions & 0 deletions tests/tests/changes/gh-398.sft.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//type:fp
//options:--c++20
//remark:dynamic_cast in constant evaluation requires public base subobjects

// The run-time checks require public base class subobjects, independent of
// access in the context of the cast.
struct B { virtual void b() {} };
struct C { virtual void c() {} };
struct D : private B {
virtual void d() {}
constexpr const B* pb() const { return this; }
};
struct E { virtual void e() {} };
struct F : D, E, private C {
virtual void f() {}
constexpr bool m() const {
return dynamic_cast<const F*>((const C*)this) == nullptr &&
dynamic_cast<const E*>((const C*)this) == nullptr;
}
};

constexpr F gf;
// Each of the following three assertions previously failed.
static_assert(dynamic_cast<const D*>((const B*)&gf) == nullptr);
static_assert(dynamic_cast<const E*>((const B*)&gf) == nullptr);
static_assert(dynamic_cast<const F*>((const C*)&gf) == nullptr);
static_assert(dynamic_cast<const F*>((const D*)&gf) == &gf);
static_assert(dynamic_cast<const F*>((const E*)&gf) == &gf);
static_assert(dynamic_cast<const E*>((const D*)&gf) == &gf);
static_assert(dynamic_cast<const void*>((const B*)&gf) == &gf);
static_assert(gf.m()); // Previously failed.

constexpr bool local() {
F f;
const B* pb = (const B*)&f;
const C* pc = (const C*)&f;
return dynamic_cast<const D*>(pb) == nullptr &&
dynamic_cast<const E*>(pb) == nullptr &&
dynamic_cast<const F*>(pc) == nullptr &&
dynamic_cast<const E*>(pc) == nullptr &&
dynamic_cast<const D*>(f.pb()) == nullptr &&
dynamic_cast<const F*>((const D*)&f) == &f &&
dynamic_cast<const E*>((const D*)&f) == &f &&
dynamic_cast<const D*>((const E*)&f) == &f &&
dynamic_cast<const void*>(pb) == &f &&
f.m();
}
static_assert(local()); // Previously failed.

struct G : B {};
struct H : G, E {};
constexpr bool public_bases() {
H h;
const B* pb = &h;
return dynamic_cast<const G*>(pb) == &h &&
dynamic_cast<const H*>(pb) == &h &&
dynamic_cast<const E*>(pb) == &h &&
dynamic_cast<const B*>((const E*)&h) == pb;
}
static_assert(public_bases());

// A downcast only requires the operand to be a public base class subobject
// of the destination object, not of the most-derived object.
struct K : B {};
struct L : K {};
struct M : private L, E {};
constexpr M gm;
static_assert(dynamic_cast<const K*>((const B*)&gm) == (const K*)&gm);
static_assert(dynamic_cast<const L*>((const B*)&gm) == (const L*)&gm);
static_assert(dynamic_cast<const M*>((const B*)&gm) == nullptr);
static_assert(dynamic_cast<const E*>((const B*)&gm) == nullptr);
constexpr bool private_most_derived() {
M m;
const B* pb = (const B*)&m;
return dynamic_cast<const K*>((const B*)&m) == (const K*)&m &&
dynamic_cast<const L*>((const B*)&m) == (const L*)&m &&
dynamic_cast<const L*>(pb) == (const L*)&m &&
dynamic_cast<const M*>(pb) == nullptr &&
dynamic_cast<const E*>(pb) == nullptr;
}
static_assert(private_most_derived());
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,13 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g);
^

"SemaCXX/constant-expression-cxx2a.cpp", line 274: error: expression must have a constant value
constexpr int e_f = (dynamic_cast<F&>((E&)g), 0);
^
"SemaCXX/constant-expression-cxx2a.cpp", line 274: note: conversion to inaccessible base class "DynamicCast::F" is not allowed
constexpr int e_f = (dynamic_cast<F&>((E&)g), 0);
^

"SemaCXX/constant-expression-cxx2a.cpp", line 278: error: expression must have a constant value
constexpr int b_e = (dynamic_cast<E&>((B&)g), 0);
^
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,13 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g);
^

"SemaCXX/constant-expression-cxx2a.cpp", line 274: error: expression must have a constant value
constexpr int e_f = (dynamic_cast<F&>((E&)g), 0);
^
"SemaCXX/constant-expression-cxx2a.cpp", line 274: note: conversion to inaccessible base class "DynamicCast::F" is not allowed
constexpr int e_f = (dynamic_cast<F&>((E&)g), 0);
^

"SemaCXX/constant-expression-cxx2a.cpp", line 278: error: expression must have a constant value
constexpr int b_e = (dynamic_cast<E&>((B&)g), 0);
^
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,20 +156,20 @@ fe_only -DTEST_NUMBER=1 --clang -tused -e 200 --no_wrap_diagnostics --clang_vers
constexpr int d_a = (dynamic_cast<const A&>(static_cast<const D&>(g)), 0);
^

"SemaCXX/constant-expression-cxx2a.cpp", line 264: error: expression must have a constant value
static_assert(&dynamic_cast<A&>((B&)g) == &(A&)(B&)g);
^
"SemaCXX/constant-expression-cxx2a.cpp", line 264: note: base class "DynamicCast::A" is ambiguous
static_assert(&dynamic_cast<A&>((B&)g) == &(A&)(B&)g);
^

"SemaCXX/constant-expression-cxx2a.cpp", line 267: error: expression must have a constant value
static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g);
^
"SemaCXX/constant-expression-cxx2a.cpp", line 267: note: base class "DynamicCast::A" is ambiguous
static_assert(&dynamic_cast<A&>((D&)g) == &(A&)(B&)g);
^

"SemaCXX/constant-expression-cxx2a.cpp", line 274: error: expression must have a constant value
constexpr int e_f = (dynamic_cast<F&>((E&)g), 0);
^
"SemaCXX/constant-expression-cxx2a.cpp", line 274: note: conversion to inaccessible base class "DynamicCast::F" is not allowed
constexpr int e_f = (dynamic_cast<F&>((E&)g), 0);
^

"SemaCXX/constant-expression-cxx2a.cpp", line 278: error: expression must have a constant value
constexpr int b_e = (dynamic_cast<E&>((B&)g), 0);
^
Expand Down
Loading
Loading