From 9a27075bee8dfcdc561861345a58c4e52750e86d Mon Sep 17 00:00:00 2001 From: Max Sagebaum Date: Wed, 7 Jun 2023 13:02:45 +0200 Subject: [PATCH 1/2] Fix for downcast to parent. --- include/cpp2util.h | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/include/cpp2util.h b/include/cpp2util.h index 584513e0e0..9939c34d73 100644 --- a/include/cpp2util.h +++ b/include/cpp2util.h @@ -994,9 +994,15 @@ auto as( X const& x ) -> decltype(auto) { return x; } +template< typename C, typename X > + requires std::is_same_v +auto as( X& x ) -> decltype(auto) { + return x; +} + template< typename C, typename X > auto as( X const& x ) -> auto - requires (!std::is_same_v && requires { C{x}; }) + requires (!std::is_same_v && !std::is_base_of_v && requires { C{x}; }) { // Experiment: Recognize the nested `::value_type` pattern for some dynamic library types // like std::optional, and try to prevent accidental narrowing conversions even when @@ -1010,9 +1016,15 @@ auto as( X const& x ) -> auto } template< typename C, typename X > - requires std::is_base_of_v -auto as( X&& x ) -> C&& { - return CPP2_FORWARD(x); + requires (std::is_base_of_v && !std::is_same_v) +auto as( X& x ) -> C& { + return x; +} + +template< typename C, typename X > + requires (std::is_base_of_v && !std::is_same_v) +auto as( X const& x ) -> C const& { + return x; } template< typename C, typename X > From 5745cb677c34c7169edef4be6d5b5b83dfcf6ee2 Mon Sep 17 00:00:00 2001 From: Max Sagebaum Date: Wed, 14 Jun 2023 16:47:23 +0200 Subject: [PATCH 2/2] Regression tests for down and upcast in class hierarchy. --- regression-tests/pure2-types-down-upcast.cpp2 | 85 ++++++++++ .../pure2-types-down-upcast.cpp.execution | 24 +++ .../gcc-10/pure2-types-down-upcast.cpp.output | 0 .../test-results/pure2-types-down-upcast.cpp | 147 ++++++++++++++++++ .../test-results/pure2-types-down-upcast.cpp2 | 2 + .../pure2-types-down-upcast.cpp2.output | 2 + 6 files changed, 260 insertions(+) create mode 100644 regression-tests/pure2-types-down-upcast.cpp2 create mode 100644 regression-tests/test-results/gcc-10/pure2-types-down-upcast.cpp.execution create mode 100644 regression-tests/test-results/gcc-10/pure2-types-down-upcast.cpp.output create mode 100644 regression-tests/test-results/pure2-types-down-upcast.cpp create mode 100644 regression-tests/test-results/pure2-types-down-upcast.cpp2 create mode 100644 regression-tests/test-results/pure2-types-down-upcast.cpp2.output diff --git a/regression-tests/pure2-types-down-upcast.cpp2 b/regression-tests/pure2-types-down-upcast.cpp2 new file mode 100644 index 0000000000..c2c4ad0dce --- /dev/null +++ b/regression-tests/pure2-types-down-upcast.cpp2 @@ -0,0 +1,85 @@ +A: type = { + public i: int = 0; + + const_foo: (virtual this) = { std::cout << "const foo \n"; } + mut_foo: (inout this) = { std::cout << "foo \n"; } +} + +B: type = { + this: A = (); + public d: double = 0.0; +} + +func_mut: (inout a: A) -> void = { std::cout << "Call A mut: (a.i)$" << std::endl; } +func_mut: (inout b: B) -> void = { std::cout << "Call B mut: (b.d)$" << std::endl; } +func_const: (a: A) -> void = { std::cout << "Call A const: (a.i)$" << std::endl; } +func_const: (b: B) -> void = { std::cout << "Call B const: (b.d)$" << std::endl; } + +test_const_foo: () = { + s: A =(); + sC: *const A = s&; + s.const_foo(); + sC*.const_foo(); + (s as A).const_foo(); + (sC* as A).const_foo(); + _ = s; + _ = sC; +} + +test_mut_foo: () = { + s: A =(); + s.mut_foo(); + (s as A).mut_foo(); + _ = s; +} + +test_up: () = { + b: B = (); + bC: *const B = b&; + + func_const(b); + func_const(b as B); + func_const(b as A); + func_const(bC*); + func_const(bC* as B); + func_const(bC* as A); + + func_mut(b); + func_mut(b as B); + func_mut(b as A); + + _ = b; + _ = bC; +} + +test_down: () = { + b: B = (); + bC: *const B = b&; + a: *A = (b as A)&; + aC: *const A = (b as A)&; + + func_const(a*); + func_const(a* as B); + func_const(a* as A); + func_const(aC*); + func_const(aC* as B); + func_const(aC* as A); + func_mut(a*); + func_mut(a* as B); + func_mut(a* as A); + + _ = b; + _ = bC; + _ = a; + _ = aC; +} + +main: () -> int = { + + test_const_foo(); + test_mut_foo(); + test_up(); + test_down(); + + return 0; +} diff --git a/regression-tests/test-results/gcc-10/pure2-types-down-upcast.cpp.execution b/regression-tests/test-results/gcc-10/pure2-types-down-upcast.cpp.execution new file mode 100644 index 0000000000..20751af062 --- /dev/null +++ b/regression-tests/test-results/gcc-10/pure2-types-down-upcast.cpp.execution @@ -0,0 +1,24 @@ +const foo +const foo +const foo +const foo +foo +foo +Call B const: 0.000000 +Call B const: 0.000000 +Call A const: 0 +Call B const: 0.000000 +Call B const: 0.000000 +Call A const: 0 +Call B mut: 0.000000 +Call B mut: 0.000000 +Call A mut: 0 +Call A const: 0 +Call B const: 0.000000 +Call A const: 0 +Call A const: 0 +Call B const: 0.000000 +Call A const: 0 +Call A mut: 0 +Call B mut: 0.000000 +Call A mut: 0 diff --git a/regression-tests/test-results/gcc-10/pure2-types-down-upcast.cpp.output b/regression-tests/test-results/gcc-10/pure2-types-down-upcast.cpp.output new file mode 100644 index 0000000000..e69de29bb2 diff --git a/regression-tests/test-results/pure2-types-down-upcast.cpp b/regression-tests/test-results/pure2-types-down-upcast.cpp new file mode 100644 index 0000000000..319fcaf0e0 --- /dev/null +++ b/regression-tests/test-results/pure2-types-down-upcast.cpp @@ -0,0 +1,147 @@ + +#define CPP2_USE_MODULES Yes + +//=== Cpp2 type declarations ==================================================== + + +#include "cpp2util.h" + +#line 1 "pure2-types-down-upcast.cpp2" +class A; + + +#line 8 "pure2-types-down-upcast.cpp2" +class B; + + +//=== Cpp2 type definitions and function declarations =========================== + +#line 1 "pure2-types-down-upcast.cpp2" +class A { + public: int i {0}; + + public: virtual auto const_foo() const -> void; + public: auto mut_foo() -> void; + + public: A() = default; + public: A(A const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(A const&) -> void = delete; +#line 6 "pure2-types-down-upcast.cpp2" +}; + +class B: public A { + + public: double d {0.0}; + public: B() = default; + public: B(B const&) = delete; /* No 'that' constructor, suppress copy */ + public: auto operator=(B const&) -> void = delete; + +#line 11 "pure2-types-down-upcast.cpp2" +}; + +auto func_mut(A& a) -> void; +auto func_mut(B& b) -> void; +auto func_const(cpp2::in a) -> void; +auto func_const(cpp2::in b) -> void; + +auto test_const_foo() -> void; + + +#line 29 "pure2-types-down-upcast.cpp2" +auto test_mut_foo() -> void; + + +#line 36 "pure2-types-down-upcast.cpp2" +auto test_up() -> void; + + +#line 55 "pure2-types-down-upcast.cpp2" +auto test_down() -> void; + + +#line 77 "pure2-types-down-upcast.cpp2" +[[nodiscard]] auto main() -> int; + + +//=== Cpp2 function definitions ================================================= + + +#line 4 "pure2-types-down-upcast.cpp2" + auto A::const_foo() const -> void{std::cout << "const foo \n"; } + auto A::mut_foo() -> void{std::cout << "foo \n"; } + +#line 13 "pure2-types-down-upcast.cpp2" +auto func_mut(A& a) -> void {std::cout << "Call A mut: " + cpp2::to_string(a.i) << std::endl;} +auto func_mut(B& b) -> void {std::cout << "Call B mut: " + cpp2::to_string(b.d) << std::endl;} +auto func_const(cpp2::in a) -> void{std::cout << "Call A const: " + cpp2::to_string(a.i) << std::endl;} +auto func_const(cpp2::in b) -> void{std::cout << "Call B const: " + cpp2::to_string(b.d) << std::endl;} + +auto test_const_foo() -> void{ + A s {}; + A const* sC {&s}; + CPP2_UFCS_0(const_foo, s); + CPP2_UFCS_0(const_foo, (*cpp2::assert_not_null(sC))); + CPP2_UFCS_0(const_foo, (cpp2::as_(s))); + CPP2_UFCS_0(const_foo, (cpp2::as_(*cpp2::assert_not_null(sC)))); + (void) std::move(s); + (void) std::move(sC); +} + +auto test_mut_foo() -> void{ + A s {}; + CPP2_UFCS_0(mut_foo, s); + CPP2_UFCS_0(mut_foo, (cpp2::as_(s))); + (void) std::move(s); +} + +auto test_up() -> void{ + B b {}; + B const* bC {&b}; + + func_const(b); + func_const(cpp2::as_(b)); + func_const(cpp2::as_(b)); + func_const(*cpp2::assert_not_null(bC)); + func_const(cpp2::as_(*cpp2::assert_not_null(bC))); + func_const(cpp2::as_(*cpp2::assert_not_null(bC))); + + func_mut(b); + func_mut(cpp2::as_(b)); + func_mut(cpp2::as_(b)); + + (void) std::move(b); + (void) std::move(bC); +} + +auto test_down() -> void{ + B b {}; + B const* bC {&b}; + A* a {&(cpp2::as_(b))}; + A const* aC {&(cpp2::as_(b))}; + + func_const(*cpp2::assert_not_null(a)); + func_const(cpp2::as_(*cpp2::assert_not_null(a))); + func_const(cpp2::as_(*cpp2::assert_not_null(a))); + func_const(*cpp2::assert_not_null(aC)); + func_const(cpp2::as_(*cpp2::assert_not_null(aC))); + func_const(cpp2::as_(*cpp2::assert_not_null(aC))); + func_mut(*cpp2::assert_not_null(a)); + func_mut(cpp2::as_(*cpp2::assert_not_null(a))); + func_mut(cpp2::as_(*cpp2::assert_not_null(a))); + + (void) std::move(b); + (void) std::move(bC); + (void) std::move(a); + (void) std::move(aC); +} + +[[nodiscard]] auto main() -> int{ + + test_const_foo(); + test_mut_foo(); + test_up(); + test_down(); + + return 0; +} + diff --git a/regression-tests/test-results/pure2-types-down-upcast.cpp2 b/regression-tests/test-results/pure2-types-down-upcast.cpp2 new file mode 100644 index 0000000000..f4a49f3a37 --- /dev/null +++ b/regression-tests/test-results/pure2-types-down-upcast.cpp2 @@ -0,0 +1,2 @@ +pure2-types-down-upcast.cpp2... ok (all Cpp2, passes safety checks) + diff --git a/regression-tests/test-results/pure2-types-down-upcast.cpp2.output b/regression-tests/test-results/pure2-types-down-upcast.cpp2.output new file mode 100644 index 0000000000..f4a49f3a37 --- /dev/null +++ b/regression-tests/test-results/pure2-types-down-upcast.cpp2.output @@ -0,0 +1,2 @@ +pure2-types-down-upcast.cpp2... ok (all Cpp2, passes safety checks) +