Repository navigation
dynamic_cast in constant evaluation requires public bases [GH #398] #416
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+415
−243
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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". |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| fe_only -DTEST_NUMBER=1 --c++20 Test_name.c |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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()); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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()); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.